@@ -23,6 +23,11 @@ interface TaskEditForm {
2323 dueDate : Date | null ;
2424}
2525
26+ interface ProjectFilterOption {
27+ label : string ;
28+ value : string | null ;
29+ }
30+
2631@Component ( {
2732 selector : 'app-user-task-items' ,
2833 standalone : true ,
@@ -31,6 +36,7 @@ interface TaskEditForm {
3136 styleUrl : './user-task-items.component.scss'
3237} )
3338export class UserTaskItemsComponent implements OnInit , OnDestroy {
39+ private static readonly PROJECT_SELECTION_CONTEXT = 'my-tasks' ;
3440 private readonly taskItemsApiClient = inject ( TaskItemsApiClient ) ;
3541 private readonly authService = inject ( AuthService ) ;
3642 private readonly appEnvironment = inject ( APP_ENVIRONMENT ) ;
@@ -48,6 +54,7 @@ export class UserTaskItemsComponent implements OnInit, OnDestroy {
4854 ] ;
4955
5056 tasks : TaskItemDto [ ] = [ ] ;
57+ selectedProjectId : string | null = null ;
5158 isLoading = false ;
5259 isPreviewMode = false ;
5360 previewDetail : string | null = null ;
@@ -74,19 +81,19 @@ export class UserTaskItemsComponent implements OnInit, OnDestroy {
7481 }
7582
7683 get totalTasks ( ) : number {
77- return this . tasks . length ;
84+ return this . filteredTasks . length ;
7885 }
7986
8087 get todoTasks ( ) : TaskItemDto [ ] {
81- return this . tasks . filter ( ( task ) => task . status === TaskStatus . Todo ) ;
88+ return this . filteredTasks . filter ( ( task ) => task . status === TaskStatus . Todo ) ;
8289 }
8390
8491 get inProgressTasks ( ) : TaskItemDto [ ] {
85- return this . tasks . filter ( ( task ) => task . status === TaskStatus . InProgress ) ;
92+ return this . filteredTasks . filter ( ( task ) => task . status === TaskStatus . InProgress ) ;
8693 }
8794
8895 get doneTasks ( ) : TaskItemDto [ ] {
89- return this . tasks . filter ( ( task ) => task . status === TaskStatus . Done ) ;
96+ return this . filteredTasks . filter ( ( task ) => task . status === TaskStatus . Done ) ;
9097 }
9198
9299 get overdueTasksCount ( ) : number {
@@ -122,10 +129,49 @@ export class UserTaskItemsComponent implements OnInit, OnDestroy {
122129 return 'Current User' ;
123130 }
124131
132+ get projectFilterOptions ( ) : ProjectFilterOption [ ] {
133+ const uniqueProjects = new Map < string , string > ( ) ;
134+ for ( const task of this . tasks ) {
135+ if ( ! task . projectId ) {
136+ continue ;
137+ }
138+
139+ if ( ! uniqueProjects . has ( task . projectId ) ) {
140+ uniqueProjects . set ( task . projectId , task . projectName || 'Unnamed project' ) ;
141+ }
142+ }
143+
144+ const projectOptions = [ ...uniqueProjects . entries ( ) ]
145+ . map ( ( [ value , label ] ) => ( { label, value } ) )
146+ . sort ( ( left , right ) => left . label . localeCompare ( right . label ) ) ;
147+
148+ return [ { label : 'All Projects' , value : null } , ...projectOptions ] ;
149+ }
150+
151+ get selectedProjectName ( ) : string {
152+ return this . projectFilterOptions . find ( ( option ) => option . value === this . selectedProjectId ) ?. label ?? 'All Projects' ;
153+ }
154+
155+ private get filteredTasks ( ) : TaskItemDto [ ] {
156+ if ( ! this . selectedProjectId ) {
157+ return this . tasks ;
158+ }
159+
160+ return this . tasks . filter ( ( task ) => task . projectId === this . selectedProjectId ) ;
161+ }
162+
125163 refresh ( ) : void {
126164 this . loadTasks ( ) ;
127165 }
128166
167+ onProjectFilterChange ( projectId : string | null ) : void {
168+ this . selectedProjectId = projectId ;
169+
170+ if ( projectId ) {
171+ this . preferencesService . setLastSelectedProject ( UserTaskItemsComponent . PROJECT_SELECTION_CONTEXT , projectId ) ;
172+ }
173+ }
174+
129175 taskTrackBy ( _ : number , task : TaskItemDto ) : string {
130176 return task . id ;
131177 }
@@ -398,6 +444,7 @@ export class UserTaskItemsComponent implements OnInit, OnDestroy {
398444 . subscribe ( {
399445 next : ( tasks ) => {
400446 this . tasks = this . sortTasks ( tasks ) ;
447+ this . ensureSelectedProjectIsValid ( ) ;
401448 this . isLoading = false ;
402449 } ,
403450 error : ( ) => {
@@ -475,6 +522,7 @@ export class UserTaskItemsComponent implements OnInit, OnDestroy {
475522 }
476523 ] ) ;
477524
525+ this . ensureSelectedProjectIsValid ( ) ;
478526 this . isLoading = false ;
479527 }
480528
@@ -531,5 +579,21 @@ export class UserTaskItemsComponent implements OnInit, OnDestroy {
531579 const nextTasks = [ ...this . tasks ] ;
532580 nextTasks [ index ] = updatedTask ;
533581 this . tasks = this . sortTasks ( nextTasks ) ;
582+ this . ensureSelectedProjectIsValid ( ) ;
583+ }
584+
585+ private ensureSelectedProjectIsValid ( ) : void {
586+ const availableProjectIds = new Set ( this . projectFilterOptions . map ( ( option ) => option . value ) . filter ( ( value ) : value is string => ! ! value ) ) ;
587+ if ( this . selectedProjectId && availableProjectIds . has ( this . selectedProjectId ) ) {
588+ return ;
589+ }
590+
591+ const rememberedProjectId = this . preferencesService . getLastSelectedProject ( UserTaskItemsComponent . PROJECT_SELECTION_CONTEXT ) ;
592+ if ( rememberedProjectId && availableProjectIds . has ( rememberedProjectId ) ) {
593+ this . selectedProjectId = rememberedProjectId ;
594+ return ;
595+ }
596+
597+ this . selectedProjectId = null ;
534598 }
535599}
0 commit comments