refactor: remove saving of current task in local storage; adjust assignees state management
This commit is contained in:
parent
eeb9f4303c
commit
7b028ab843
8 changed files with 117 additions and 170 deletions
|
|
@ -34,7 +34,6 @@
|
||||||
id="title"
|
id="title"
|
||||||
name="title"
|
name="title"
|
||||||
#title="ngModel"
|
#title="ngModel"
|
||||||
(input)="saveTaskData()"
|
|
||||||
placeholder="{{
|
placeholder="{{
|
||||||
taskData.title
|
taskData.title
|
||||||
? taskData.title
|
? taskData.title
|
||||||
|
|
@ -61,7 +60,6 @@
|
||||||
name="description"
|
name="description"
|
||||||
#description="ngModel"
|
#description="ngModel"
|
||||||
minlength="10"
|
minlength="10"
|
||||||
(input)="saveTaskData()"
|
|
||||||
placeholder="{{
|
placeholder="{{
|
||||||
taskData.description
|
taskData.description
|
||||||
? taskData.description
|
? taskData.description
|
||||||
|
|
@ -79,7 +77,7 @@
|
||||||
</div>
|
</div>
|
||||||
<app-assigned
|
<app-assigned
|
||||||
[taskCreator]="taskData.creator"
|
[taskCreator]="taskData.creator"
|
||||||
[assignedList]="taskData.assigned"
|
[currentAssignees]="taskData.assignees"
|
||||||
(assignedChange)="receiveAssigned($event)"
|
(assignedChange)="receiveAssigned($event)"
|
||||||
></app-assigned>
|
></app-assigned>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -177,7 +175,6 @@
|
||||||
id="category"
|
id="category"
|
||||||
name="category"
|
name="category"
|
||||||
#category="ngModel"
|
#category="ngModel"
|
||||||
(change)="saveTaskData()"
|
|
||||||
[(ngModel)]="taskData.category"
|
[(ngModel)]="taskData.category"
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
|
|
@ -263,7 +260,7 @@
|
||||||
? 'block'
|
? 'block'
|
||||||
: 'none'
|
: 'none'
|
||||||
}"
|
}"
|
||||||
(click)="removeTaskData(taskForm)"
|
(click)="clearTaskData(taskForm)"
|
||||||
></app-form-btn>
|
></app-form-btn>
|
||||||
<app-form-btn
|
<app-form-btn
|
||||||
[class]="'btn-delete'"
|
[class]="'btn-delete'"
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,6 @@ export class AddTaskComponent implements OnInit {
|
||||||
status: this.taskService.getStatuses()[0],
|
status: this.taskService.getStatuses()[0],
|
||||||
priority: this.taskService.getPriorities()[0],
|
priority: this.taskService.getPriorities()[0],
|
||||||
subtasks: [],
|
subtasks: [],
|
||||||
assigned: [],
|
|
||||||
assignees: [],
|
assignees: [],
|
||||||
userData: [],
|
userData: [],
|
||||||
creator: '',
|
creator: '',
|
||||||
|
|
@ -70,9 +69,8 @@ export class AddTaskComponent implements OnInit {
|
||||||
*/
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.setCurrentUserId();
|
this.setCurrentUserId();
|
||||||
this.loadEditTaskData();
|
this.loadExistingTaskData();
|
||||||
this.routeParams();
|
this.routeParams();
|
||||||
this.loadLocalStorageData();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setCurrentUserId() {
|
setCurrentUserId() {
|
||||||
|
|
@ -84,13 +82,7 @@ export class AddTaskComponent implements OnInit {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
async loadExistingTaskData() {
|
||||||
* Loads task data for editing if applicable, or sets the task status if this is a new task overlay.
|
|
||||||
* @param {string} overlayData The task id or status of the task to be loaded.
|
|
||||||
* @param {string} overlayType The type of overlay to be opened.
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
async loadEditTaskData() {
|
|
||||||
if (this.overlayData) {
|
if (this.overlayData) {
|
||||||
const taskData = await firstValueFrom(this.getTaskData(this.overlayData));
|
const taskData = await firstValueFrom(this.getTaskData(this.overlayData));
|
||||||
if (taskData) {
|
if (taskData) {
|
||||||
|
|
@ -123,14 +115,6 @@ export class AddTaskComponent implements OnInit {
|
||||||
* If no task data is present in local storage, this method saves the current taskData object to local storage.
|
* If no task data is present in local storage, this method saves the current taskData object to local storage.
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
loadLocalStorageData() {
|
|
||||||
const storedTaskData = localStorage.getItem('taskData');
|
|
||||||
if (storedTaskData) {
|
|
||||||
this.taskData = JSON.parse(storedTaskData);
|
|
||||||
} else {
|
|
||||||
this.saveTaskData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the task data from the Firebase service for the given task id.
|
* Gets the task data from the Firebase service for the given task id.
|
||||||
|
|
@ -147,14 +131,12 @@ export class AddTaskComponent implements OnInit {
|
||||||
status: false,
|
status: false,
|
||||||
};
|
};
|
||||||
this.taskData.subtasks.push(newSubtask);
|
this.taskData.subtasks.push(newSubtask);
|
||||||
this.saveTaskData();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteSubtask(subtaskToDelete: Subtask) {
|
deleteSubtask(subtaskToDelete: Subtask) {
|
||||||
this.taskData.subtasks = this.taskData.subtasks.filter(
|
this.taskData.subtasks = this.taskData.subtasks.filter(
|
||||||
(subtask) => subtask !== subtaskToDelete
|
(subtask) => subtask !== subtaskToDelete
|
||||||
);
|
);
|
||||||
this.saveTaskData();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -171,7 +153,9 @@ export class AddTaskComponent implements OnInit {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
receiveAssigned(assigned: string[]) {
|
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 = priority)
|
? (this.taskData.priority = priority)
|
||||||
: this.taskData.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
|
* @param form the form to reset
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
removeTaskData(form: NgForm) {
|
clearTaskData(form: NgForm) {
|
||||||
localStorage.removeItem('taskData');
|
|
||||||
this.clearForm(form);
|
this.clearForm(form);
|
||||||
this.clearFormData();
|
this.clearFormData();
|
||||||
}
|
}
|
||||||
|
|
@ -259,7 +229,7 @@ export class AddTaskComponent implements OnInit {
|
||||||
clearFormData() {
|
clearFormData() {
|
||||||
this.taskData.date = this.currentDate;
|
this.taskData.date = this.currentDate;
|
||||||
this.taskData.category = '';
|
this.taskData.category = '';
|
||||||
this.taskData.assigned = [];
|
this.taskData.assignees = [];
|
||||||
this.taskData.subtasks = [];
|
this.taskData.subtasks = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -292,7 +262,7 @@ export class AddTaskComponent implements OnInit {
|
||||||
next: (response) => {
|
next: (response) => {
|
||||||
this.toastNotificationService.createTaskSuccessToast();
|
this.toastNotificationService.createTaskSuccessToast();
|
||||||
this.updateNotifierService.notifyUpdate('task');
|
this.updateNotifierService.notifyUpdate('task');
|
||||||
this.removeTaskData(ngForm);
|
this.clearTaskData(ngForm);
|
||||||
this.closeOverlay();
|
this.closeOverlay();
|
||||||
this.router.navigate(['/board']);
|
this.router.navigate(['/board']);
|
||||||
},
|
},
|
||||||
|
|
@ -327,6 +297,9 @@ export class AddTaskComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceXSSChars(input: string) {
|
replaceXSSChars(input: string) {
|
||||||
|
if (!input) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
return input.replace(/</g, '<').replace(/>/g, '>');
|
return input.replace(/</g, '<').replace(/>/g, '>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,49 +1,35 @@
|
||||||
<section>
|
<section>
|
||||||
@if (filteredUsers.length === 0) {
|
@if (filteredUsers.length === 0) {
|
||||||
<div class="content"><p class="no-users">No users found</p></div>
|
<div class="no-users">
|
||||||
} @else { @for (user of filteredUsers; track user.id) {
|
<p>No users found</p>
|
||||||
|
</div>
|
||||||
|
} @else { @for (user of filteredUsers; track user.id) { @if (user.id) {
|
||||||
<div
|
<div
|
||||||
class="content"
|
class="user-item"
|
||||||
(click)="addAssignedToTask(user.id)"
|
(click)="toggleAssignee(user.id)"
|
||||||
[ngClass]="{ selected: assigned.includes(user.id) }"
|
[ngClass]="{ selected: assignedUserIds.has(user.id) }"
|
||||||
*ngIf="user.id"
|
|
||||||
>
|
>
|
||||||
<div
|
<div class="circle" [ngStyle]="{ 'background-color': user.color }">
|
||||||
class="circle"
|
<div class="initials">{{ user.initials }}</div>
|
||||||
[ngStyle]="{
|
|
||||||
'background-color': user.color
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<div class="initials">
|
|
||||||
{{ user.initials }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="details">
|
<div class="details">
|
||||||
<div class="name">
|
<div class="name">
|
||||||
<p>
|
<p>{{ user.firstName }}</p>
|
||||||
{{ user.firstName }}
|
|
||||||
</p>
|
|
||||||
<span>, </span>
|
<span>, </span>
|
||||||
<p class="last-name">
|
<p class="last-name">{{ user.lastName }}</p>
|
||||||
{{ user.lastName }}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
@if (assigned.includes(user.id)) {
|
|
||||||
<img
|
<img
|
||||||
class="checkbox-img"
|
class="checkbox-img"
|
||||||
src="./../../../../assets/img/add-task/checkbox-checked.svg"
|
[src]="
|
||||||
|
assignedUserIds.has(user.id)
|
||||||
|
? './../../../../assets/img/add-task/checkbox-checked.svg'
|
||||||
|
: './../../../../assets/img/add-task/checkbox-empty.svg'
|
||||||
|
"
|
||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
} @else {
|
|
||||||
<img
|
|
||||||
class="checkbox-img"
|
|
||||||
src="./../../../../assets/img/add-task/checkbox-empty.svg"
|
|
||||||
alt=""
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
} }
|
} } }
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -9,19 +9,34 @@ section {
|
||||||
padding-top: 12px;
|
padding-top: 12px;
|
||||||
border-radius: 0px 0px 20px 20px;
|
border-radius: 0px 0px 20px 20px;
|
||||||
border: 1px solid var(--light-gray);
|
border: 1px solid var(--light-gray);
|
||||||
border-top: 0px;
|
border-top: 0;
|
||||||
background-color: var(--white);
|
background-color: var(--white);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.user-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--light-gray);
|
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 {
|
.circle {
|
||||||
|
|
@ -33,6 +48,7 @@ section {
|
||||||
height: 35px;
|
height: 35px;
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
border: 2px solid var(--white);
|
border: 2px solid var(--white);
|
||||||
|
|
||||||
.initials {
|
.initials {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
|
@ -41,11 +57,6 @@ section {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-users {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
.details {
|
.details {
|
||||||
margin-left: 24px;
|
margin-left: 24px;
|
||||||
width: 80%;
|
width: 80%;
|
||||||
|
|
@ -62,28 +73,27 @@ section {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.last-name {
|
.last-name {
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox {
|
.checkbox-img {
|
||||||
img {
|
width: 18px;
|
||||||
width: 18px;
|
height: 18px;
|
||||||
height: 18px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.selected {
|
.no-users {
|
||||||
background-color: var(--dark-blue);
|
font-size: 18px;
|
||||||
color: var(--white);
|
font-weight: 400;
|
||||||
img {
|
padding: 8px 12px;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 { CommonModule } from '@angular/common';
|
||||||
import { User } from '../../../../interfaces/user.interface';
|
import { User } from '../../../../interfaces/user.interface';
|
||||||
|
import { Assignee } from '../../../../interfaces/task.interface';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-assigned-list',
|
selector: 'app-assigned-list',
|
||||||
|
|
@ -9,79 +16,51 @@ import { User } from '../../../../interfaces/user.interface';
|
||||||
templateUrl: './assigned-list.component.html',
|
templateUrl: './assigned-list.component.html',
|
||||||
styleUrl: './assigned-list.component.scss',
|
styleUrl: './assigned-list.component.scss',
|
||||||
})
|
})
|
||||||
export class AssignedListComponent {
|
export class AssignedListComponent implements OnChanges {
|
||||||
@Input() filteredUsers: User[] = [];
|
@Input() filteredUsers: User[] = [];
|
||||||
@Input() searchInput: boolean = false;
|
@Input() currentAssignees: Assignee[] = [];
|
||||||
@Input() taskCreator: string = '';
|
|
||||||
@Output() assignedChange = new EventEmitter<string[]>();
|
@Output() assignedChange = new EventEmitter<string[]>();
|
||||||
|
|
||||||
users: User[] = [];
|
selectedAssignees: string[] = [];
|
||||||
assigned: string[] = [];
|
|
||||||
currentUser: User | null = null;
|
|
||||||
|
|
||||||
constructor() {}
|
/**
|
||||||
|
* Retrieves a set of user IDs for the current assignees.
|
||||||
ngOnInit() {
|
*
|
||||||
this.loadTaskAssignedData();
|
* @returns A Set containing the user IDs of the current assignees.
|
||||||
|
*/
|
||||||
|
get assignedUserIds(): Set<string> {
|
||||||
|
return new Set(this.currentAssignees.map((assignee) => assignee.userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the assignedChange event emitter with the current list of assigned users.
|
* Syncs the selected assignees with the current assignees whenever the
|
||||||
* This should be called whenever the assigned list is updated.
|
* `currentAssignees` input changes.
|
||||||
*/
|
*/
|
||||||
updateAssigned() {
|
ngOnChanges() {
|
||||||
this.assignedChange.emit(this.assigned);
|
this.selectedAssignees = [
|
||||||
|
...this.currentAssignees.map((assignee) => assignee.userId),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggles the assignment of a user to the task.
|
* Emits an event with the current list of selected assignee user IDs.
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
addAssignedToTask(userId: string) {
|
emitAssignedChange() {
|
||||||
if (!this.assigned.includes(userId)) {
|
this.assignedChange.emit(this.selectedAssignees);
|
||||||
this.assigned.push(userId);
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 {
|
} else {
|
||||||
this.assigned.splice(this.assigned.indexOf(userId), 1);
|
this.selectedAssignees.splice(index, 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.emitAssignedChange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,7 @@
|
||||||
@if (showAssignedList) {
|
@if (showAssignedList) {
|
||||||
<app-assigned-list
|
<app-assigned-list
|
||||||
[filteredUsers]="filteredUsers"
|
[filteredUsers]="filteredUsers"
|
||||||
[taskCreator]="taskCreator"
|
[currentAssignees]="currentAssignees"
|
||||||
[searchInput]="searchInput"
|
|
||||||
(assignedChange)="receiveAssigned($event)"
|
(assignedChange)="receiveAssigned($event)"
|
||||||
>
|
>
|
||||||
></app-assigned-list
|
></app-assigned-list
|
||||||
|
|
@ -31,21 +30,19 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="assigned-badge">
|
<div class="assigned-badge">
|
||||||
@for (user of users; track user; let index = $index) { @for (users of
|
@for (user of users; track user) { @if (user.id &&
|
||||||
assignedList; track users ) { @if (user.id === users) {
|
assignedUserIds.has(user.id || '')) {
|
||||||
<div
|
<div
|
||||||
class="circle"
|
class="circle"
|
||||||
(mousemove)="openTooltipUser(user.id, $event)"
|
(mousemove)="openTooltipUser(user.id, $event)"
|
||||||
(mouseleave)="closeTooltipUser()"
|
(mouseleave)="closeTooltipUser()"
|
||||||
[ngStyle]="{
|
[ngStyle]="{ 'background-color': user.color }"
|
||||||
'background-color': user.color
|
|
||||||
}"
|
|
||||||
>
|
>
|
||||||
<div class="initials">
|
<div class="initials">
|
||||||
{{ user.initials }}
|
{{ user.initials }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
} } } @if (tooltipUserId) {
|
} } @if (tooltipUserId) {
|
||||||
<div
|
<div
|
||||||
class="tooltip-user"
|
class="tooltip-user"
|
||||||
[style.left.px]="dialogX"
|
[style.left.px]="dialogX"
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import { FormsModule } from '@angular/forms';
|
||||||
import { User } from '../../../interfaces/user.interface';
|
import { User } from '../../../interfaces/user.interface';
|
||||||
import { ApiService } from '../../../services/api.service';
|
import { ApiService } from '../../../services/api.service';
|
||||||
import { map, catchError, of } from 'rxjs';
|
import { map, catchError, of } from 'rxjs';
|
||||||
|
import { Assignee } from '../../../interfaces/task.interface';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-assigned',
|
selector: 'app-assigned',
|
||||||
|
|
@ -22,14 +23,14 @@ import { map, catchError, of } from 'rxjs';
|
||||||
})
|
})
|
||||||
export class AssignedComponent {
|
export class AssignedComponent {
|
||||||
@Input() taskCreator: string = '';
|
@Input() taskCreator: string = '';
|
||||||
@Input() assignedList: string[] = [];
|
@Input() currentAssignees: Assignee[] = [];
|
||||||
@Output() assignedChange = new EventEmitter<string[]>();
|
@Output() assignedChange = new EventEmitter<string[]>();
|
||||||
|
|
||||||
tooltipUserId: string | null = null;
|
tooltipUserId: string | null = null;
|
||||||
filteredUsers: User[] = [];
|
filteredUsers: User[] = [];
|
||||||
users: User[] = [];
|
users: User[] = [];
|
||||||
searchValue: string = '';
|
searchValue: string = '';
|
||||||
searchInput: boolean = false;
|
showSearch: boolean = false;
|
||||||
showAssignedList: boolean = false;
|
showAssignedList: boolean = false;
|
||||||
dialogX: number = 0;
|
dialogX: number = 0;
|
||||||
dialogY: number = 0;
|
dialogY: number = 0;
|
||||||
|
|
@ -55,7 +56,7 @@ export class AssignedComponent {
|
||||||
|
|
||||||
searchTask(): void {
|
searchTask(): void {
|
||||||
this.searchValue = this.replaceXSSChars(this.searchValue) || '';
|
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();
|
const searchValue = this.searchValue.toLowerCase();
|
||||||
this.filteredUsers = this.users.filter(
|
this.filteredUsers = this.users.filter(
|
||||||
|
|
@ -95,6 +96,10 @@ export class AssignedComponent {
|
||||||
this.showAssignedList = !this.showAssignedList;
|
this.showAssignedList = !this.showAssignedList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get assignedUserIds(): Set<string> {
|
||||||
|
return new Set(this.currentAssignees.map((assignee) => assignee.userId));
|
||||||
|
}
|
||||||
|
|
||||||
@HostListener('document:click', ['$event'])
|
@HostListener('document:click', ['$event'])
|
||||||
checkOpenNavbar(event: MouseEvent) {
|
checkOpenNavbar(event: MouseEvent) {
|
||||||
const targetElement = event.target as HTMLElement;
|
const targetElement = event.target as HTMLElement;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ export interface Task {
|
||||||
status: string;
|
status: string;
|
||||||
priority: string;
|
priority: string;
|
||||||
subtasks: Subtask[];
|
subtasks: Subtask[];
|
||||||
assigned: string[];
|
|
||||||
assignees: Assignee[];
|
assignees: Assignee[];
|
||||||
userData: UserSummary[];
|
userData: UserSummary[];
|
||||||
creator: string;
|
creator: string;
|
||||||
|
|
@ -22,5 +21,6 @@ export interface Subtask {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Assignee {
|
export interface Assignee {
|
||||||
|
id?: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue