@@ -2,10 +2,12 @@ import { Component, OnDestroy, OnInit, inject } from '@angular/core';
22import { FormBuilder , FormGroup , ReactiveFormsModule , Validators } from '@angular/forms' ;
33import { Router } from '@angular/router' ;
44import { MenuItem , MessageService } from 'primeng/api' ;
5- import { Subject , takeUntil } from 'rxjs' ;
5+ import { Subject , forkJoin , of , takeUntil } from 'rxjs' ;
6+ import { AdminUsersApiClient } from '../../../../core/api/clients/admin-users-api.client' ;
67import { ProjectsApiClient } from '../../../../core/api/clients/projects-api.client' ;
78import { TaskItemsApiClient } from '../../../../core/api/clients/task-items-api.client' ;
89import { ProjectDto , ProjectMemberDto } from '../../../../core/api/models/project.model' ;
10+ import { UserSummaryDto } from '../../../../core/api/models/user.model' ;
911import { TaskStatus } from '../../../../core/api/models/task-status.enum' ;
1012import { AuthService } from '../../../../core/auth/services/auth.service' ;
1113import { APP_ENVIRONMENT } from '../../../../core/config/app-environment.token' ;
@@ -22,6 +24,7 @@ export class TaskItemCreateComponent implements OnInit, OnDestroy {
2224 private readonly fb = inject ( FormBuilder ) ;
2325 private readonly taskItemsApiClient = inject ( TaskItemsApiClient ) ;
2426 private readonly projectsApiClient = inject ( ProjectsApiClient ) ;
27+ private readonly adminUsersApiClient = inject ( AdminUsersApiClient ) ;
2528 private readonly router = inject ( Router ) ;
2629 private readonly messageService = inject ( MessageService ) ;
2730 private readonly authService = inject ( AuthService ) ;
@@ -298,16 +301,20 @@ export class TaskItemCreateComponent implements OnInit, OnDestroy {
298301 }
299302
300303 this . isLoadingMembers = true ;
301- this . projectsApiClient
302- . getMembers ( projectId )
304+ const members$ = this . projectsApiClient . getMembers ( projectId ) ;
305+ const allUsers$ = this . authService . hasAnyRole ( [ 'Administrator' ] )
306+ ? this . adminUsersApiClient . getUsers ( { page : 1 , pageSize : 500 } )
307+ : of ( null ) ;
308+
309+ forkJoin ( { members : members$ , usersResponse : allUsers$ } )
303310 . pipe ( takeUntil ( this . destroy$ ) )
304311 . subscribe ( {
305- next : ( members ) => {
306- this . assigneeOptions = this . mapAssigneeOptions ( members ) ;
312+ next : ( { members, usersResponse } ) => {
313+ this . assigneeOptions = this . mapAssigneeOptions ( members , usersResponse ?. items ?? [ ] ) ;
307314 this . isLoadingMembers = false ;
308315 } ,
309316 error : ( ) => {
310- this . assigneeOptions = [ { label : 'Unassigned' , value : null } ] ;
317+ this . assigneeOptions = this . mapAssigneeOptions ( [ ] , [ ] ) ;
311318 this . isLoadingMembers = false ;
312319 this . messageService . add ( {
313320 severity : 'warn' ,
@@ -318,8 +325,82 @@ export class TaskItemCreateComponent implements OnInit, OnDestroy {
318325 } ) ;
319326 }
320327
321- private mapAssigneeOptions ( members : ProjectMemberDto [ ] ) : Array < { label : string ; value : string | null } > {
322- return [ { label : 'Unassigned' , value : null } , ...members . map ( ( member ) => ( { label : member . displayName , value : member . userId } ) ) ] ;
328+ private mapAssigneeOptions (
329+ members : ProjectMemberDto [ ] ,
330+ allUsers : UserSummaryDto [ ]
331+ ) : Array < { label : string ; value : string | null } > {
332+ const optionsByUserId = new Map < string , { label : string ; value : string | null } > ( ) ;
333+
334+ for ( const member of members ) {
335+ optionsByUserId . set ( member . userId , {
336+ label : member . displayName ,
337+ value : member . userId
338+ } ) ;
339+ }
340+
341+ for ( const user of allUsers ) {
342+ if ( ! optionsByUserId . has ( user . id ) ) {
343+ optionsByUserId . set ( user . id , {
344+ label : this . resolveUserLabel ( user . displayName , user . userName , user . email , user . id ) ,
345+ value : user . id
346+ } ) ;
347+ }
348+ }
349+
350+ const currentUserId = this . authService . currentUserId ( ) ;
351+ if ( currentUserId && ! optionsByUserId . has ( currentUserId ) ) {
352+ optionsByUserId . set ( currentUserId , {
353+ label : this . currentUserLabel ( ) ,
354+ value : currentUserId
355+ } ) ;
356+ }
357+
358+ return [
359+ { label : 'Unassigned' , value : null } ,
360+ ...Array . from ( optionsByUserId . values ( ) ) . sort ( ( a , b ) => a . label . localeCompare ( b . label ) )
361+ ] ;
362+ }
363+
364+ private currentUserLabel ( ) : string {
365+ const claims = this . authService . userClaims ( ) ;
366+ const name = claims [ 'name' ] ;
367+ const userName = claims [ 'preferred_username' ] ;
368+ const email = claims [ 'email' ] ;
369+
370+ if ( typeof name === 'string' && name . trim ( ) . length > 0 ) {
371+ return name ;
372+ }
373+
374+ if ( typeof userName === 'string' && userName . trim ( ) . length > 0 ) {
375+ return userName ;
376+ }
377+
378+ if ( typeof email === 'string' && email . trim ( ) . length > 0 ) {
379+ return email ;
380+ }
381+
382+ return 'Current User' ;
383+ }
384+
385+ private resolveUserLabel (
386+ displayName : string | null | undefined ,
387+ userName : string | null | undefined ,
388+ email : string | null | undefined ,
389+ fallback : string
390+ ) : string {
391+ if ( displayName && displayName . trim ( ) . length > 0 ) {
392+ return displayName ;
393+ }
394+
395+ if ( userName && userName . trim ( ) . length > 0 ) {
396+ return userName ;
397+ }
398+
399+ if ( email && email . trim ( ) . length > 0 ) {
400+ return email ;
401+ }
402+
403+ return fallback ;
323404 }
324405
325406 private buildPreviewAssigneeOptions ( ) : Array < { label : string ; value : string | null } > {
0 commit comments