diff --git a/src/app/components/add-task/add-task.component.html b/src/app/components/add-task/add-task.component.html index 25cd1c9..4f664e4 100644 --- a/src/app/components/add-task/add-task.component.html +++ b/src/app/components/add-task/add-task.component.html @@ -34,7 +34,6 @@ id="title" name="title" #title="ngModel" - (input)="saveTaskData()" placeholder="{{ taskData.title ? taskData.title @@ -61,7 +60,6 @@ name="description" #description="ngModel" minlength="10" - (input)="saveTaskData()" placeholder="{{ taskData.description ? taskData.description @@ -79,7 +77,7 @@ @@ -177,7 +175,6 @@ id="category" name="category" #category="ngModel" - (change)="saveTaskData()" [(ngModel)]="taskData.category" required > @@ -263,7 +260,7 @@ ? 'block' : 'none' }" - (click)="removeTaskData(taskForm)" + (click)="clearTaskData(taskForm)" > subtask !== subtaskToDelete ); - this.saveTaskData(); } /** @@ -171,7 +153,9 @@ export class AddTaskComponent implements OnInit { * @returns {void} */ receiveAssigned(assigned: string[]) { - this.taskData.assigned = assigned; + this.taskData.assignees = assigned.map((userId) => ({ + userId: userId, + })); } /** @@ -203,19 +187,6 @@ export class AddTaskComponent implements OnInit { this.taskData.priority !== priority ? (this.taskData.priority = priority) : this.taskData.priority; - this.saveTaskData(); - } - - /** - * Saves the current task data to local storage. - * - * This function first runs the cross site scripting check on the task data, - * then saves the task data to local storage. - * @returns {void} - */ - saveTaskData() { - this.checkCrossSiteScripting(); - localStorage.setItem('taskData', JSON.stringify(this.taskData)); } /** @@ -223,8 +194,7 @@ export class AddTaskComponent implements OnInit { * @param form the form to reset * @returns {void} */ - removeTaskData(form: NgForm) { - localStorage.removeItem('taskData'); + clearTaskData(form: NgForm) { this.clearForm(form); this.clearFormData(); } @@ -259,7 +229,7 @@ export class AddTaskComponent implements OnInit { clearFormData() { this.taskData.date = this.currentDate; this.taskData.category = ''; - this.taskData.assigned = []; + this.taskData.assignees = []; this.taskData.subtasks = []; } @@ -292,7 +262,7 @@ export class AddTaskComponent implements OnInit { next: (response) => { this.toastNotificationService.createTaskSuccessToast(); this.updateNotifierService.notifyUpdate('task'); - this.removeTaskData(ngForm); + this.clearTaskData(ngForm); this.closeOverlay(); this.router.navigate(['/board']); }, @@ -327,6 +297,9 @@ export class AddTaskComponent implements OnInit { } replaceXSSChars(input: string) { + if (!input) { + return ''; + } return input.replace(//g, '>'); } } diff --git a/src/app/components/add-task/assigned/assigned-list/assigned-list.component.html b/src/app/components/add-task/assigned/assigned-list/assigned-list.component.html index 5d12b25..0ed8a7d 100644 --- a/src/app/components/add-task/assigned/assigned-list/assigned-list.component.html +++ b/src/app/components/add-task/assigned/assigned-list/assigned-list.component.html @@ -1,49 +1,35 @@
@if (filteredUsers.length === 0) { -

No users found

- } @else { @for (user of filteredUsers; track user.id) { +
+

No users found

+
+ } @else { @for (user of filteredUsers; track user.id) { @if (user.id) {
-
-
- {{ user.initials }} -
+
+
{{ user.initials }}
-

- {{ user.firstName }} -

+

{{ user.firstName }}

-

- {{ user.lastName }} -

+

{{ user.lastName }}

- @if (assigned.includes(user.id)) { - } @else { - - }
- } } + } } }
diff --git a/src/app/components/add-task/assigned/assigned-list/assigned-list.component.scss b/src/app/components/add-task/assigned/assigned-list/assigned-list.component.scss index d647d1e..24925d9 100644 --- a/src/app/components/add-task/assigned/assigned-list/assigned-list.component.scss +++ b/src/app/components/add-task/assigned/assigned-list/assigned-list.component.scss @@ -9,19 +9,34 @@ section { padding-top: 12px; border-radius: 0px 0px 20px 20px; border: 1px solid var(--light-gray); - border-top: 0px; + border-top: 0; background-color: var(--white); overflow-y: auto; } -.content { +.user-item { display: flex; align-items: center; padding: 6px 12px; cursor: pointer; + &:hover { background-color: var(--light-gray); } + + &.selected { + background-color: var(--dark-blue); + color: var(--white); + + .checkbox-img { + filter: brightness(0) saturate(100%) invert(100%) sepia(0%) + saturate(7500%) hue-rotate(346deg) brightness(99%) contrast(103%); + } + + &:hover { + background-color: var(--very-dark-blue); + } + } } .circle { @@ -33,6 +48,7 @@ section { height: 35px; border-radius: 100%; border: 2px solid var(--white); + .initials { font-size: 12px; font-weight: 400; @@ -41,11 +57,6 @@ section { } } -.no-users { - font-size: 18px; - font-weight: 400; -} - .details { margin-left: 24px; width: 80%; @@ -62,28 +73,27 @@ section { white-space: nowrap; font-size: 18px; font-weight: 400; + gap: 4px; + + p { + margin: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } } .last-name { - overflow: hidden; text-overflow: ellipsis; } -.checkbox { - img { - width: 18px; - height: 18px; - } +.checkbox-img { + width: 18px; + height: 18px; } -.selected { - background-color: var(--dark-blue); - color: var(--white); - img { - filter: brightness(0) saturate(100%) invert(100%) sepia(0%) saturate(7500%) - hue-rotate(346deg) brightness(99%) contrast(103%); - } - &:hover { - background-color: var(--very-dark-blue); - } +.no-users { + font-size: 18px; + font-weight: 400; + padding: 8px 12px; } diff --git a/src/app/components/add-task/assigned/assigned-list/assigned-list.component.ts b/src/app/components/add-task/assigned/assigned-list/assigned-list.component.ts index 660eb1d..9bb2715 100644 --- a/src/app/components/add-task/assigned/assigned-list/assigned-list.component.ts +++ b/src/app/components/add-task/assigned/assigned-list/assigned-list.component.ts @@ -1,6 +1,13 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { + Component, + EventEmitter, + Input, + OnChanges, + Output, +} from '@angular/core'; import { CommonModule } from '@angular/common'; import { User } from '../../../../interfaces/user.interface'; +import { Assignee } from '../../../../interfaces/task.interface'; @Component({ selector: 'app-assigned-list', @@ -9,79 +16,51 @@ import { User } from '../../../../interfaces/user.interface'; templateUrl: './assigned-list.component.html', styleUrl: './assigned-list.component.scss', }) -export class AssignedListComponent { +export class AssignedListComponent implements OnChanges { @Input() filteredUsers: User[] = []; - @Input() searchInput: boolean = false; - @Input() taskCreator: string = ''; + @Input() currentAssignees: Assignee[] = []; @Output() assignedChange = new EventEmitter(); - users: User[] = []; - assigned: string[] = []; - currentUser: User | null = null; + selectedAssignees: string[] = []; - constructor() {} - - ngOnInit() { - this.loadTaskAssignedData(); + /** + * Retrieves a set of user IDs for the current assignees. + * + * @returns A Set containing the user IDs of the current assignees. + */ + get assignedUserIds(): Set { + return new Set(this.currentAssignees.map((assignee) => assignee.userId)); } /** - * Updates the assignedChange event emitter with the current list of assigned users. - * This should be called whenever the assigned list is updated. + * Syncs the selected assignees with the current assignees whenever the + * `currentAssignees` input changes. */ - updateAssigned() { - this.assignedChange.emit(this.assigned); + ngOnChanges() { + this.selectedAssignees = [ + ...this.currentAssignees.map((assignee) => assignee.userId), + ]; } /** - * Toggles the assignment of a user to the task. - * - * If the user is not currently assigned, they are added to the assigned list. - * If the user is already assigned, they are removed from the list. - * Updates the local storage with the current list of assigned users and emits the - * assignedChange event to notify other components of the update. - * - * @param userId The ID of the user to be added or removed from the assigned list. + * Emits an event with the current list of selected assignee user IDs. */ - addAssignedToTask(userId: string) { - if (!this.assigned.includes(userId)) { - this.assigned.push(userId); + emitAssignedChange() { + this.assignedChange.emit(this.selectedAssignees); + } + + /** + * Toggles the selection status of an assignee. + * + * @param userId - The ID of the user to be toggled in the selected assignees list. + */ + toggleAssignee(userId: string) { + const index = this.selectedAssignees.indexOf(userId); + if (index === -1) { + this.selectedAssignees.push(userId); } else { - this.assigned.splice(this.assigned.indexOf(userId), 1); - } - this.saveTaskData(); - this.updateAssigned(); - } - - /** - * Saves the current list of assigned users to local storage. - * - * Retrieves the current task data from local storage, updates the assigned - * list with the current list of assigned users, and saves the updated task - * data back to local storage. - */ - saveTaskData() { - let taskDataString = localStorage.getItem('taskData'); - if (taskDataString !== null) { - let taskData = JSON.parse(taskDataString); - taskData.assigned = this.assigned; - localStorage.setItem('taskData', JSON.stringify(taskData)); - } - } - - /** - * Loads the list of assigned users from local storage. - * - * Retrieves the task data from local storage, checks if the assigned list exists, - * and if so, assigns the list to the local assigned variable. - */ - loadTaskAssignedData() { - const taskDataString = localStorage.getItem('taskData'); - if (taskDataString !== null) { - const taskData = JSON.parse(taskDataString); - if (taskData.hasOwnProperty('assigned')) { - this.assigned = taskData.assigned; - } + this.selectedAssignees.splice(index, 1); } + this.emitAssignedChange(); } } diff --git a/src/app/components/add-task/assigned/assigned.component.html b/src/app/components/add-task/assigned/assigned.component.html index ce6b974..4a090b3 100644 --- a/src/app/components/add-task/assigned/assigned.component.html +++ b/src/app/components/add-task/assigned/assigned.component.html @@ -15,8 +15,7 @@ @if (showAssignedList) { > - @for (user of users; track user; let index = $index) { @for (users of - assignedList; track users ) { @if (user.id === users) { + @for (user of users; track user) { @if (user.id && + assignedUserIds.has(user.id || '')) {
{{ user.initials }}
- } } } @if (tooltipUserId) { + } } @if (tooltipUserId) {
(); tooltipUserId: string | null = null; filteredUsers: User[] = []; users: User[] = []; searchValue: string = ''; - searchInput: boolean = false; + showSearch: boolean = false; showAssignedList: boolean = false; dialogX: number = 0; dialogY: number = 0; @@ -55,7 +56,7 @@ export class AssignedComponent { searchTask(): void { this.searchValue = this.replaceXSSChars(this.searchValue) || ''; - this.searchInput = this.searchValue.trim().length > 0; + this.showSearch = this.searchValue.trim().length > 0; const searchValue = this.searchValue.toLowerCase(); this.filteredUsers = this.users.filter( @@ -95,6 +96,10 @@ export class AssignedComponent { this.showAssignedList = !this.showAssignedList; } + get assignedUserIds(): Set { + return new Set(this.currentAssignees.map((assignee) => assignee.userId)); + } + @HostListener('document:click', ['$event']) checkOpenNavbar(event: MouseEvent) { const targetElement = event.target as HTMLElement; diff --git a/src/app/interfaces/task.interface.ts b/src/app/interfaces/task.interface.ts index 937915a..0280943 100644 --- a/src/app/interfaces/task.interface.ts +++ b/src/app/interfaces/task.interface.ts @@ -8,7 +8,6 @@ export interface Task { status: string; priority: string; subtasks: Subtask[]; - assigned: string[]; assignees: Assignee[]; userData: UserSummary[]; creator: string; @@ -22,5 +21,6 @@ export interface Subtask { } export interface Assignee { + id?: string; userId: string; }