Skip to content

Commit e38885c

Browse files
committed
fix(task-create): expand assignee options to include admin-visible users and current-user fallback
1 parent 6621472 commit e38885c

1 file changed

Lines changed: 89 additions & 8 deletions

File tree

src/app/features/task-item/components/task-item-create/task-item-create.component.ts

Lines changed: 89 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ import { Component, OnDestroy, OnInit, inject } from '@angular/core';
22
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
33
import { Router } from '@angular/router';
44
import { 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';
67
import { ProjectsApiClient } from '../../../../core/api/clients/projects-api.client';
78
import { TaskItemsApiClient } from '../../../../core/api/clients/task-items-api.client';
89
import { ProjectDto, ProjectMemberDto } from '../../../../core/api/models/project.model';
10+
import { UserSummaryDto } from '../../../../core/api/models/user.model';
911
import { TaskStatus } from '../../../../core/api/models/task-status.enum';
1012
import { AuthService } from '../../../../core/auth/services/auth.service';
1113
import { 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

Comments
 (0)