@@ -120,6 +120,35 @@ class CollectionStore {
120120 // Raw filter rows backing the QL — kept so the SubFilterBar UI can hydrate
121121 // its builder when remounted on a different view of the same collection.
122122 subFilterRows = $state ( [ ] ) ;
123+ showCompleted = $state ( false ) ;
124+
125+ get effectiveSubFilterQL ( ) {
126+ if ( this . showCompleted ) return this . subFilterQL ;
127+ return this . subFilterQL
128+ ? `(${ this . subFilterQL } ) AND status_completed = false`
129+ : 'status_completed = false' ;
130+ }
131+
132+ #completionPreferenceKey( wsId , colId , view ) {
133+ const scope = colId ? `collection-${ colId } ` : `workspace-${ wsId } ` ;
134+ return `collection-show-completed:${ scope } :${ view ?. split ( '-' ) . at ( - 1 ) } ` ;
135+ }
136+
137+ async setShowCompleted ( value ) {
138+ this . showCompleted = value ;
139+ try {
140+ localStorage . setItem (
141+ this . #completionPreferenceKey( this . #wsId, this . #colId, this . #currentView) ,
142+ String ( value )
143+ ) ;
144+ } catch {
145+ // Keep the setting usable when browser storage is unavailable.
146+ }
147+ this . clearBoardSearch ( ) ;
148+ if ( this . #wsId || this . #colId) {
149+ await this . load ( this . #wsId, this . #colId, this . #currentView) ;
150+ }
151+ }
123152
124153 // Server-side sort state
125154 sortableFields = $state ( [ ] ) ;
@@ -189,19 +218,32 @@ class CollectionStore {
189218 const sameCollection = wsId === this . #wsId && colId === this . #colId;
190219 const viewChanged = view !== this . #currentView;
191220 const targetInitialLimit = initialItemsPageSize ( view ) ;
221+ const previousShowCompleted = this . showCompleted ;
222+ if ( ! sameCollection || viewChanged ) {
223+ try {
224+ this . showCompleted =
225+ localStorage . getItem ( this . #completionPreferenceKey( wsId , colId , view ) ) === 'true' ;
226+ } catch {
227+ this . showCompleted = false ;
228+ }
229+ }
192230
193231 // Switching between passive collection views does not need another network
194232 // roundtrip when the already-loaded item page is large enough and there is
195233 // no active server-side sort/filter. Board views may need capped-column
196234 // fetches, so they intentionally keep loading.
197- const canReuseTargetData = loadsItems ( view )
198- ? this . items . length > 0 &&
199- ! this . boardDeferred &&
200- ( this . itemsPagination ?. limit ?? 0 ) >= targetInitialLimit
201- : this . backlogPagination !== null ;
235+ const canReuseTargetData =
236+ loadsItems ( view ) &&
237+ loadsItems ( this . #currentView) &&
238+ this . items . length > 0 &&
239+ ! this . boardDeferred &&
240+ ( this . itemsPagination ?. limit ?? 0 ) >= targetInitialLimit ;
202241 if (
203242 sameCollection &&
204243 viewChanged &&
244+ previousShowCompleted === this . showCompleted &&
245+ ! this . loading &&
246+ ! BOARD_VIEWS . has ( this . #currentView) &&
205247 canReuseTargetData &&
206248 ! this . subFilterQL &&
207249 ! this . #sortBy &&
@@ -278,7 +320,7 @@ class CollectionStore {
278320 ? fetchCollectionBacklog ( wsId , colId , {
279321 page : 1 ,
280322 limit : DEFAULT_PAGE_SIZE ,
281- sub_ql : this . subFilterQL || undefined ,
323+ sub_ql : this . effectiveSubFilterQL || undefined ,
282324 collection,
283325 } )
284326 : Promise . resolve ( null ) ,
@@ -352,7 +394,7 @@ class CollectionStore {
352394 * completed statuses are paged separately so they cannot hide active work.
353395 */
354396 async #resolveBoardPartition( wsId , colId , view ) {
355- if ( ! BOARD_VIEWS . has ( view ) ) return null ;
397+ if ( ! BOARD_VIEWS . has ( view ) || ! this . showCompleted ) return null ;
356398 try {
357399 const config = await this . getBoardConfiguration ( wsId , colId ) ;
358400 let statuses = this . boardStatuses ;
@@ -371,7 +413,7 @@ class CollectionStore {
371413 }
372414
373415 const completedStatusIds = statuses
374- . filter ( ( status ) => status . is_completed || status . category_name === 'Done' )
416+ . filter ( ( status ) => status . is_completed )
375417 . map ( ( status ) => status . id ) ;
376418 const retentionDays = Number ( config ?. completed_item_retention_days ) ;
377419 const completedActivityDays =
@@ -457,7 +499,7 @@ class CollectionStore {
457499 fetchCollectionItems ( wsId , colId , {
458500 page,
459501 limit : pageLimit ,
460- sub_ql : this . subFilterQL || undefined ,
502+ sub_ql : this . effectiveSubFilterQL || undefined ,
461503 collection,
462504 ...this . #itemSortOptions( ) ,
463505 ...this . #boardExclusionFilter( boardPartition ?. statusIds ) ,
@@ -483,7 +525,7 @@ class CollectionStore {
483525 const result = await fetchCollectionItems ( wsId , colId , {
484526 page,
485527 limit : BOARD_UNFINISHED_PAGE_SIZE ,
486- sub_ql : this . subFilterQL || undefined ,
528+ sub_ql : this . effectiveSubFilterQL || undefined ,
487529 collection,
488530 ...this . #itemSortOptions( ) ,
489531 ...this . #boardExclusionFilter( boardPartition ?. statusIds ) ,
@@ -530,7 +572,7 @@ class CollectionStore {
530572 const result = await fetchCollectionItems ( wsId , colId , {
531573 page,
532574 limit : BOARD_UNFINISHED_PAGE_SIZE ,
533- sub_ql : this . subFilterQL || undefined ,
575+ sub_ql : this . effectiveSubFilterQL || undefined ,
534576 collection,
535577 ...this . #itemSortOptions( ) ,
536578 ...this . #boardExclusionFilter( boardPartition ?. statusIds ) ,
@@ -554,7 +596,7 @@ class CollectionStore {
554596 return fetchCollectionItems ( wsId , colId , {
555597 page : 1 ,
556598 limit,
557- sub_ql : this . subFilterQL || undefined ,
599+ sub_ql : this . effectiveSubFilterQL || undefined ,
558600 collection,
559601 status_id : boardPartition . statusIds . join ( ',' ) ,
560602 completed_activity_days : boardPartition . completedActivityDays || undefined ,
@@ -666,7 +708,7 @@ class CollectionStore {
666708 page,
667709 limit : BOARD_SEARCH_PAGE_SIZE ,
668710 search : query ,
669- sub_ql : this . subFilterQL || undefined ,
711+ sub_ql : this . effectiveSubFilterQL || undefined ,
670712 collection : this . boardCollection ?? undefined ,
671713 ...this . #itemSortOptions( ) ,
672714 } ) ;
@@ -701,7 +743,7 @@ class CollectionStore {
701743 const result = await fetchCollectionItems ( this . #wsId, this . #colId, {
702744 page : nextPage ,
703745 limit : pagination ?. limit ?? DEFAULT_PAGE_SIZE ,
704- sub_ql : this . subFilterQL || undefined ,
746+ sub_ql : this . effectiveSubFilterQL || undefined ,
705747 ...this . #itemSortOptions( ) ,
706748 ...( deferred
707749 ? {
@@ -747,7 +789,7 @@ class CollectionStore {
747789 const result = await fetchCollectionBacklog ( this . #wsId, this . #colId, {
748790 page : nextPage ,
749791 limit : this . backlogPagination ?. limit ?? DEFAULT_PAGE_SIZE ,
750- sub_ql : this . subFilterQL || undefined ,
792+ sub_ql : this . effectiveSubFilterQL || undefined ,
751793 } ) ;
752794
753795 if ( loadId !== this . #loadId) return ;
@@ -778,7 +820,7 @@ class CollectionStore {
778820 const result = await fetchCollectionItems ( this . #wsId, this . #colId, {
779821 page,
780822 limit,
781- sub_ql : this . subFilterQL || undefined ,
823+ sub_ql : this . effectiveSubFilterQL || undefined ,
782824 ...this . #itemSortOptions( ) ,
783825 } ) ;
784826
@@ -850,7 +892,7 @@ class CollectionStore {
850892 ? fetchCollectionBacklog ( this . #wsId, this . #colId, {
851893 page : 1 ,
852894 limit : backlogLimit ,
853- sub_ql : this . subFilterQL || undefined ,
895+ sub_ql : this . effectiveSubFilterQL || undefined ,
854896 collection,
855897 } )
856898 : Promise . resolve ( null ) ,
@@ -978,6 +1020,7 @@ class CollectionStore {
9781020
9791021 applyItem ( item ) {
9801022 if ( ! item ?. id ) return ;
1023+ if ( this . #hideCompletedItem( item ) ) return ;
9811024 const index = this . items . findIndex ( ( current ) => current . id === item . id ) ;
9821025 if ( index === - 1 ) {
9831026 this . items = [ ...this . items , item ] ;
@@ -1004,7 +1047,7 @@ class CollectionStore {
10041047 try {
10051048 const changes = await fetchCollectionItemChanges ( this . #wsId, this . #colId, {
10061049 since : this . #changesWatermark,
1007- sub_ql : this . subFilterQL || undefined ,
1050+ sub_ql : this . effectiveSubFilterQL || undefined ,
10081051 } ) ;
10091052 if ( loadId !== this . #loadId) return ;
10101053 this . #changesWatermark = changes ?. watermark ?? this . #changesWatermark;
@@ -1060,13 +1103,14 @@ class CollectionStore {
10601103 const wsId = this . #wsId;
10611104 const colId = this . #colId;
10621105 const changes = await fetchCollectionItemChanges ( wsId , colId , {
1063- sub_ql : this . subFilterQL || undefined ,
1106+ sub_ql : this . effectiveSubFilterQL || undefined ,
10641107 } ) ;
10651108 if ( loadId !== this . #loadId || wsId !== this . #wsId || colId !== this . #colId) return ;
10661109 this . #changesWatermark = changes ?. watermark ?? 0 ;
10671110 }
10681111
10691112 #applyUpdatedItem( updated ) {
1113+ if ( this . #hideCompletedItem( updated ) ) return ;
10701114 const idx = this . items . findIndex ( ( i ) => i . id === updated . id ) ;
10711115 if ( idx !== - 1 ) Object . assign ( this . items [ idx ] , updated ) ;
10721116 const bIdx = this . backlogItems . findIndex ( ( i ) => i . id === updated . id ) ;
@@ -1075,6 +1119,16 @@ class CollectionStore {
10751119 if ( searchIdx !== - 1 ) Object . assign ( this . boardSearchItems [ searchIdx ] , updated ) ;
10761120 }
10771121
1122+ #hideCompletedItem( item ) {
1123+ if ( this . showCompleted ) return false ;
1124+ const status =
1125+ workspaceDataStore . statuses . find ( ( candidate ) => candidate . id === item . status_id ) ??
1126+ this . boardStatuses . find ( ( candidate ) => candidate . id === item . status_id ) ;
1127+ if ( ! status ?. is_completed ) return false ;
1128+ this . #removeItemsById( new Set ( [ item . id ] ) ) ;
1129+ return true ;
1130+ }
1131+
10781132 #removeItemsById( ids ) {
10791133 const beforeBacklog = this . backlogItems . length ;
10801134 const deferredSet = new Set ( this . boardDeferred ?. statusIds ?? [ ] ) ;
@@ -1096,7 +1150,7 @@ class CollectionStore {
10961150 if ( removedItems > 0 && this . itemsPagination ) {
10971151 this . itemsPagination = {
10981152 ...this . itemsPagination ,
1099- total : Math . max ( 0 , ( this . itemsPagination . total ?? 0 ) - removedItems ) ,
1153+ total_items : Math . max ( 0 , ( this . itemsPagination . total_items ?? 0 ) - removedItems ) ,
11001154 } ;
11011155 }
11021156 if ( removedDeferredItems > 0 && this . boardDeferred ) {
@@ -1105,7 +1159,7 @@ class CollectionStore {
11051159 const pagination = this . boardDeferred . pagination
11061160 ? {
11071161 ...this . boardDeferred . pagination ,
1108- total,
1162+ total_items : total ,
11091163 total_pages : Math . ceil ( total / limit ) ,
11101164 }
11111165 : null ;
@@ -1122,7 +1176,7 @@ class CollectionStore {
11221176 if ( removedBacklog > 0 && this . backlogPagination ) {
11231177 this . backlogPagination = {
11241178 ...this . backlogPagination ,
1125- total : Math . max ( 0 , ( this . backlogPagination . total ?? 0 ) - removedBacklog ) ,
1179+ total_items : Math . max ( 0 , ( this . backlogPagination . total_items ?? 0 ) - removedBacklog ) ,
11261180 } ;
11271181 this . backlogHasMore = calcHasMore ( this . backlogPagination ) ;
11281182 }
0 commit comments