refactor: extracted assigned logic from AddTaskComponent to AssignedComponent and migrated to Django Rest

This commit is contained in:
Chneemann 2025-04-01 18:23:18 +02:00
parent 3293def254
commit ff5abfa41a
10 changed files with 500 additions and 415 deletions

View file

@ -5,14 +5,14 @@
}"
>
<div
class="headline"
class="header"
[ngStyle]="{ display: overlayData !== '' ? 'none' : 'block' }"
>
<div class="title">{{ "addTask.headline" | translate }}</div>
</div>
<form
(ngSubmit)="onSubmit(taskForm, overlayData)"
(ngSubmit)="onSubmit(taskForm)"
(keydown.enter)="handleEnterKey($event)"
#taskForm="ngForm"
onsubmit="return false"
@ -26,7 +26,9 @@
>
<div class="left-side">
<div class="title">
<p>{{ "addTask.title" | translate }}<span class="red-dot">*</span></p>
<div class="headline">
{{ "addTask.title" | translate }}<span class="red-dot">*</span>
</div>
<input
type="text"
id="title"
@ -49,10 +51,10 @@
</div>
</div>
<div class="description">
<p>
<div class="headline">
{{ "addTask.description" | translate
}}<span class="red-dot">*</span>
</p>
</div>
<textarea
id="description"
rows="5"
@ -75,62 +77,11 @@
}
</div>
</div>
<div class="assigned">
<p>{{ "addTask.assigned" | translate }}</p>
<input
#searchField
id="search-assigned"
class="search-assigned"
name="search"
placeholder="{{ 'addTask.assignedFormField' | translate }}"
type="text"
[(ngModel)]="searchValue"
(input)="searchTask(taskData.creator)"
autocomplete="off"
(click)="toggleAssignedMenu()"
/>
@if (isAssignedOpen) {
<app-assigned
[filteredUsers]="filteredUsers"
[taskCreator]="taskData.creator"
[searchInput]="searchInput"
[assignedList]="taskData.assigned"
(assignedChange)="receiveAssigned($event)"
>
></app-assigned
>
<img class="close" src="./../../../assets/img/close.svg" alt="" />
} @else {
<img
class="open"
src="./../../../assets/img/add-task/arrow-down.svg"
alt=""
/>
}
</div>
<div class="assigned-badge">
@for (user of taskData.assigned; track user ) {
<div
class="circle"
(mousemove)="openDialog(user, $event)"
(mouseleave)="closeDialog()"
[ngStyle]="{
'background-color': firebaseService.getUserDetails(user, 'color')
}"
>
<div class="initials">
{{ firebaseService.getUserDetails(user, "initials") }}
</div>
</div>
}
</div>
@if (AssignedDialogId != '') {
<div class="dialog" [style.left.px]="dialogX" [style.top.px]="dialogY">
<p>
{{ firebaseService.getUserDetails(AssignedDialogId, "firstName") }}
{{ firebaseService.getUserDetails(AssignedDialogId, "lastName") }}
</p>
</div>
}
></app-assigned>
</div>
<div class="middle-spacer"><div class="line"></div></div>
<div class="right-side">
@ -140,7 +91,9 @@
'margin-top': overlayMobile ? '28px' : ''
}"
>
<p>{{ "addTask.date" | translate }}<span class="red-dot">*</span></p>
<div class="headline">
{{ "addTask.date" | translate }}<span class="red-dot">*</span>
</div>
<input
type="date"
id="date"
@ -161,7 +114,7 @@
</div>
</div>
<div class="priority">
<p>{{ "addTask.priority" | translate }}</p>
<div class="headline">{{ "addTask.priority" | translate }}</div>
<div class="btns">
<button
type="button"
@ -217,9 +170,9 @@
</div>
</div>
<div class="category">
<p>
<div class="headline">
{{ "addTask.category" | translate }}<span class="red-dot">*</span>
</p>
</div>
<select
id="category"
name="category"
@ -246,7 +199,7 @@
</div>
</div>
<div class="subtask">
<p>{{ "addTask.subtask" | translate }}</p>
<div class="headline">{{ "addTask.subtask" | translate }}</div>
<input
type="text"
id="subtask"

View file

@ -8,21 +8,6 @@ section {
width: 100%;
}
.dialog {
position: fixed;
width: min-content;
height: fit-content;
padding: 10px 15px;
border-radius: 0px 20px 20px 20px;
border: 1px solid var(--black);
z-index: 2;
background-color: var(--white);
p {
font-size: 18px;
font-weight: 400;
}
}
.edit-task-overlay-content {
height: calc(700px - 148px);
padding: 0 6px;
@ -36,6 +21,12 @@ section {
background-color: var(--very-light-gray);
}
.headline {
font-size: 20px;
font-weight: 400;
padding-bottom: 6px;
}
input,
textarea,
select {
@ -65,12 +56,6 @@ select:valid {
border: 1px solid black;
}
p {
font-size: 20px;
font-weight: 400;
padding-bottom: 6px;
}
.red-dot {
color: var(--light-red);
}
@ -91,7 +76,7 @@ p {
/*------------- HEADER -------------*/
.headline {
.header {
margin-bottom: 48px;
.title {
font-size: 61px;
@ -105,55 +90,6 @@ p {
width: 45%;
}
.assigned {
position: relative;
.open {
position: absolute;
top: 55px;
right: 20px;
width: 14px;
height: 7px;
}
.close {
position: absolute;
top: 50px;
right: 20px;
width: 16px;
height: 16px;
cursor: pointer;
&:hover {
filter: invert(60%) sepia(58%) saturate(1629%) hue-rotate(165deg)
brightness(92%) contrast(92%);
}
}
}
.assigned-badge {
display: flex;
align-items: center;
margin: 6px;
height: 40px;
}
.circle {
display: flex;
justify-content: center;
align-items: center;
width: 30px;
min-width: 30px;
height: 30px;
border-radius: 100%;
border: 2px solid var(--white);
margin-right: 6px;
cursor: default;
.initials {
font-size: 12px;
font-weight: 400;
color: var(--white);
text-shadow: 1px 1px 2px var(--black);
}
}
.subtask {
position: relative;
.add {

View file

@ -2,7 +2,6 @@ import { CommonModule } from '@angular/common';
import { Component, HostListener, Input, OnInit } from '@angular/core';
import { FormsModule, NgForm } from '@angular/forms';
import { AssignedComponent } from './assigned/assigned.component';
import { User } from '../../interfaces/user.interface';
import { FirebaseService } from '../../services/firebase.service';
import { Task } from '../../interfaces/task.interface';
import { OverlayService } from '../../services/overlay.service';
@ -36,14 +35,7 @@ export class AddTaskComponent implements OnInit {
currentDate: string = new Date().toISOString().split('T')[0];
dateInPast: boolean = false;
isAssignedOpen: boolean = false;
subtaskValue: string = '';
searchValue: string = '';
searchInput: boolean = false;
filteredUsers: User[] = [];
AssignedDialogId: string = '';
dialogX: number = 0;
dialogY: number = 0;
constructor(
public firebaseService: FirebaseService,
@ -142,32 +134,6 @@ export class AddTaskComponent implements OnInit {
}
}
/**
* Opens the user dialog for the given user id at the position of the mouse click
* @param userId the id of the user
* @param event the MouseEvent that triggered the dialog
*/
openDialog(userId: any, event: MouseEvent) {
this.AssignedDialogId = userId;
this.updateDialogPosition(event);
}
/**
* Updates the position of the user dialog based on the MouseEvent
* @param event the MouseEvent that triggered the dialog
*/
updateDialogPosition(event: MouseEvent) {
this.dialogX = event.clientX + 25;
this.dialogY = event.clientY + 10;
}
/**
* Closes the assigned user dialog
*/
closeDialog() {
this.AssignedDialogId = '';
}
/**
* Gets the task data from the Firebase service for the given task id.
* @param taskId the id of the task
@ -200,40 +166,6 @@ export class AddTaskComponent implements OnInit {
}
}
/**
* Updates the filteredUsers array with users that match the current search value
* from the getFilteredUsers list.
* @param taskCreator the id of the task creator
*/
searchTask(taskCreator: string): void {
this.updateSearchInput();
this.filteredUsers = this.firebaseService
.getFilteredUsers(taskCreator)
.filter(
(user) =>
user.firstName.toLowerCase().includes(this.searchValue) ||
user.lastName.toLowerCase().includes(this.searchValue) ||
user.initials.toLowerCase().includes(this.searchValue)
);
}
/**
* Updates the search input flag and the search value by stripping any XSS
* characters from the search value, and then setting the search input flag
* to true if the search value is not empty, and false otherwise.
*
* @returns The updated search input flag.
*/
updateSearchInput() {
this.searchValue = this.replaceXSSChars(this.searchValue);
if (this.searchValue) {
this.searchInput = this.searchValue.toLowerCase().length > 0;
} else {
this.searchInput = false;
}
return this.searchInput;
}
/**
* Updates the subtask value by removing any potential XSS characters.
* This ensures that the subtask value is sanitized before further processing or storage.
@ -251,15 +183,6 @@ export class AddTaskComponent implements OnInit {
this.taskData.assigned = assigned;
}
/**
* Toggle the assigned menu for the current task.
* This function toggles the boolean flag isAssignedOpen, which determines whether the assigned menu is open or not.
* @returns {void}
*/
toggleAssignedMenu() {
this.isAssignedOpen = !this.isAssignedOpen;
}
/**
* Checks if the selected date is in the past.
*
@ -373,7 +296,7 @@ export class AddTaskComponent implements OnInit {
}
}
onSubmit(ngForm: NgForm, overlayData: string) {
onSubmit(ngForm: NgForm) {
if (ngForm.submitted && ngForm.form.valid) {
const { id, ...taskWithoutId } = this.taskData;
@ -412,28 +335,9 @@ export class AddTaskComponent implements OnInit {
*/
deleteTask(overlayData: string) {
this.apiService.deleteTaskById(overlayData);
this.closeDialog();
}
replaceXSSChars(input: string) {
return input.replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
@HostListener('document:click', ['$event'])
/**
* Closes the assigned dropdown menu if the user clicks anywhere outside of
* the assigned dropdown menu.
* @param event the MouseEvent
* @returns {void}
*/
checkOpenNavbar(event: MouseEvent) {
const targetElement = event.target as HTMLElement;
if (
!targetElement.closest('.search-assigned') &&
!targetElement.closest('app-assigned') &&
!targetElement.closest('.checkbox-img')
) {
this.isAssignedOpen = false;
}
}
}

View file

@ -0,0 +1,49 @@
<section>
@if (filteredUsers.length === 0) {
<div class="content"><p class="no-users">No users found</p></div>
} @else { @for (user of filteredUsers; track user.id) {
<div
class="content"
(click)="addAssignedToTask(user.id)"
[ngClass]="{ selected: assigned.includes(user.id) }"
*ngIf="user.id"
>
<div
class="circle"
[ngStyle]="{
'background-color': user.color
}"
>
<div class="initials">
{{ user.initials }}
</div>
</div>
<div class="details">
<div class="name">
<p>
{{ user.firstName }}
</p>
<span>,&nbsp;</span>
<p class="last-name">
{{ user.lastName }}
</p>
</div>
</div>
<div class="checkbox">
@if (assigned.includes(user.id)) {
<img
class="checkbox-img"
src="./../../../../assets/img/add-task/checkbox-checked.svg"
alt=""
/>
} @else {
<img
class="checkbox-img"
src="./../../../../assets/img/add-task/checkbox-empty.svg"
alt=""
/>
}
</div>
</div>
} }
</section>

View file

@ -0,0 +1,89 @@
section {
position: absolute;
top: 75px;
left: 0;
right: 0;
width: 100%;
height: auto;
max-height: 125px;
padding-top: 12px;
border-radius: 0px 0px 20px 20px;
border: 1px solid var(--light-gray);
border-top: 0px;
background-color: var(--white);
overflow-y: auto;
}
.content {
display: flex;
align-items: center;
padding: 6px 12px;
cursor: pointer;
&:hover {
background-color: var(--light-gray);
}
}
.circle {
display: flex;
justify-content: center;
align-items: center;
width: 35px;
min-width: 35px;
height: 35px;
border-radius: 100%;
border: 2px solid var(--white);
.initials {
font-size: 12px;
font-weight: 400;
color: var(--white);
text-shadow: 1px 1px 2px var(--black);
}
}
.no-users {
font-size: 18px;
font-weight: 400;
}
.details {
margin-left: 24px;
width: 80%;
overflow: hidden;
white-space: nowrap;
display: inline-block;
}
.name {
display: flex;
align-items: center;
max-width: 100%;
overflow: hidden;
white-space: nowrap;
font-size: 18px;
font-weight: 400;
}
.last-name {
overflow: hidden;
text-overflow: ellipsis;
}
.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);
}
}

View file

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AssignedListComponent } from './assigned-list.component';
describe('AssignedListComponent', () => {
let component: AssignedListComponent;
let fixture: ComponentFixture<AssignedListComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AssignedListComponent]
})
.compileComponents();
fixture = TestBed.createComponent(AssignedListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,88 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { FirebaseService } from '../../../../services/firebase.service';
import { CommonModule } from '@angular/common';
import { User } from '../../../../interfaces/user.interface';
@Component({
selector: 'app-assigned-list',
standalone: true,
imports: [CommonModule],
templateUrl: './assigned-list.component.html',
styleUrl: './assigned-list.component.scss',
})
export class AssignedListComponent {
@Input() filteredUsers: User[] = [];
@Input() searchInput: boolean = false;
@Input() taskCreator: string = '';
@Output() assignedChange = new EventEmitter<string[]>();
users: User[] = [];
assigned: string[] = [];
currentUser: User | null = null;
constructor(public firebaseService: FirebaseService) {}
ngOnInit() {
this.loadTaskAssignedData();
}
/**
* Updates the assignedChange event emitter with the current list of assigned users.
* This should be called whenever the assigned list is updated.
*/
updateAssigned() {
this.assignedChange.emit(this.assigned);
}
/**
* 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.
*/
addAssignedToTask(userId: string) {
if (!this.assigned.includes(userId)) {
this.assigned.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;
}
}
}
}

View file

@ -1,47 +1,65 @@
<section>
@for (user of displayAssigned(); track user; let index = $index) {
<div
class="content"
(click)="addAssignedToTask(user.id)"
[ngClass]="{ selected: assigned.includes(user.id) }"
*ngIf="user.id"
<div class="assigned">
<div class="headline">{{ "addTask.assigned" | translate }}</div>
<input
#searchField
id="search-assigned"
class="search-assigned"
name="search"
placeholder="{{ 'addTask.assignedFormField' | translate }}"
type="text"
[(ngModel)]="searchValue"
(input)="searchTask()"
autocomplete="off"
(click)="toggleAssignedMenu()"
/>
@if (showAssignedList) {
<app-assigned-list
[filteredUsers]="filteredUsers"
[taskCreator]="taskCreator"
[searchInput]="searchInput"
(assignedChange)="receiveAssigned($event)"
>
></app-assigned-list
>
<img class="close" src="./../../../assets/img/close.svg" alt="" />
} @else {
<img
class="open"
src="./../../../assets/img/add-task/arrow-down.svg"
alt=""
/>
}
<div class="assigned-badge">
@for (user of users; track user; let index = $index) { @for (users of
assignedList; track users ) { @if (user.id === users) {
<div
class="circle"
(mousemove)="openTooltipUser(user.id, $event)"
(mouseleave)="closeTooltipUser()"
[ngStyle]="{
'background-color': firebaseService.getUserDetails(user.id, 'color')
'background-color': user.color
}"
>
<div class="initials">
{{ firebaseService.getUserDetails(user.id, "initials") }}
{{ user.initials }}
</div>
</div>
<div class="details">
<div class="name">
} } } @if (tooltipUserId) {
<div
class="tooltip-user"
[style.left.px]="dialogX"
[style.top.px]="dialogY"
>
@for (user of users; track user) { @if (user.id === tooltipUserId) {
<p>
{{ firebaseService.getUserDetails(user.id, "firstName") }}
{{ user.firstName }}
</p>
<span>,&nbsp;</span>
<p class="last-name">
{{ firebaseService.getUserDetails(user.id, "lastName") }}
<p>
{{ user.lastName }}
</p>
}}
</div>
</div>
<div class="checkbox">
@if (assigned.includes(user.id)) {
<img
class="checkbox-img"
src="./../../../../assets/img/add-task/checkbox-checked.svg"
alt=""
/>
} @else {
<img
class="checkbox-img"
src="./../../../../assets/img/add-task/checkbox-empty.svg"
alt=""
/>
}
</div>
</div>
}
</section>

View file

@ -1,38 +1,46 @@
section {
.assigned {
position: relative;
.open {
position: absolute;
top: 75px;
left: 0;
right: 0;
width: 100%;
height: auto;
max-height: 125px;
padding-top: 12px;
border-radius: 0px 0px 20px 20px;
border: 1px solid var(--light-gray);
border-top: 0px;
background-color: var(--white);
overflow-y: auto;
top: 55px;
right: 20px;
width: 14px;
height: 7px;
}
.content {
display: flex;
align-items: center;
padding: 6px 12px;
.close {
position: absolute;
top: 50px;
right: 20px;
width: 16px;
height: 16px;
cursor: pointer;
&:hover {
background-color: var(--light-gray);
filter: invert(60%) sepia(58%) saturate(1629%) hue-rotate(165deg)
brightness(92%) contrast(92%);
}
}
}
.assigned-badge {
display: flex;
align-items: center;
margin: 6px 6px 12px 6px;
height: 40px;
.circle {
display: flex;
justify-content: center;
align-items: center;
width: 35px;
min-width: 35px;
height: 35px;
width: 30px;
min-width: 30px;
height: 30px;
border-radius: 100%;
border: 2px solid var(--white);
margin-right: 6px;
cursor: default;
.initials {
font-size: 12px;
font-weight: 400;
@ -40,45 +48,54 @@ section {
text-shadow: 1px 1px 2px var(--black);
}
}
.details {
margin-left: 24px;
width: 80%;
overflow: hidden;
white-space: nowrap;
display: inline-block;
}
.name {
display: flex;
align-items: center;
max-width: 100%;
overflow: hidden;
white-space: nowrap;
.tooltip-user {
position: fixed;
width: min-content;
height: fit-content;
padding: 10px 15px;
border-radius: 0px 20px 20px 20px;
border: 1px solid var(--black);
z-index: 2;
background-color: var(--white);
p {
font-size: 18px;
font-weight: 400;
}
.last-name {
overflow: hidden;
text-overflow: ellipsis;
}
.checkbox {
img {
width: 18px;
height: 18px;
}
.headline {
font-size: 20px;
font-weight: 400;
padding-bottom: 6px;
}
.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%);
input,
textarea,
select {
font-size: 20px;
font-weight: 400;
border-radius: 10px;
border: 1px solid var(--light-gray);
padding: 12px 21px;
width: calc(100% - 42px);
color: var(--black);
background-color: var(--white);
resize: none;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
&:hover {
background-color: var(--very-dark-blue);
input.ng-invalid.ng-touched,
textarea.ng-invalid.ng-touched,
select.ng-invalid.ng-touched {
border: 1px dashed red;
}
input:valid,
textarea:valid,
select:valid {
border: 1px solid black;
}

View file

@ -1,101 +1,109 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { FirebaseService } from '../../../services/firebase.service';
import {
Component,
EventEmitter,
HostListener,
Input,
Output,
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { AssignedListComponent } from './assigned-list/assigned-list.component';
import { TranslateModule } from '@ngx-translate/core';
import { FormsModule } from '@angular/forms';
import { User } from '../../../interfaces/user.interface';
import { ApiService } from '../../../services/api.service';
import { map, catchError, of } from 'rxjs';
@Component({
selector: 'app-assigned',
standalone: true,
imports: [CommonModule],
imports: [CommonModule, FormsModule, TranslateModule, AssignedListComponent],
templateUrl: './assigned.component.html',
styleUrl: './assigned.component.scss',
})
export class AssignedComponent {
@Input() filteredUsers: User[] = [];
@Input() searchInput: boolean = false;
@Input() taskCreator: string = '';
@Input() assignedList: string[] = [];
@Output() assignedChange = new EventEmitter<string[]>();
assigned: string[] = [];
tooltipUserId: string | null = null;
filteredUsers: User[] = [];
users: User[] = [];
searchValue: string = '';
searchInput: boolean = false;
showAssignedList: boolean = false;
dialogX: number = 0;
dialogY: number = 0;
constructor(public firebaseService: FirebaseService) {
this.loadTaskAssignedData();
constructor(private apiService: ApiService) {}
ngOnInit() {
this.loadUsers();
}
/**
* Updates the assignedChange event emitter with the current list of assigned users.
* This should be called whenever the assigned list is updated.
*/
updateAssigned() {
this.assignedChange.emit(this.assigned);
loadUsers(): void {
this.apiService
.getUsers()
.pipe(
map((users) => users.filter((user) => user.id !== this.taskCreator)),
catchError(() => of([]))
)
.subscribe((filteredUsers) => {
this.users = filteredUsers;
this.filteredUsers = filteredUsers;
});
}
/**
* 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.
*/
addAssignedToTask(userId: string) {
if (!this.assigned.includes(userId)) {
this.assigned.push(userId);
} else {
this.assigned.splice(this.assigned.indexOf(userId), 1);
}
this.saveTaskData();
this.updateAssigned();
}
searchTask(): void {
this.searchValue = this.replaceXSSChars(this.searchValue) || '';
this.searchInput = this.searchValue.trim().length > 0;
/**
* 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));
const searchValue = this.searchValue.toLowerCase();
this.filteredUsers = this.users.filter(
(user) =>
user.firstName.toLowerCase().includes(searchValue) ||
user.lastName.toLowerCase().includes(searchValue) ||
user.initials.toLowerCase().includes(searchValue)
);
if (this.filteredUsers.length === 0) {
this.filteredUsers = [];
}
}
/**
* 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;
}
}
replaceXSSChars(input: string) {
return input.replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
/**
* Returns the list of users that should be displayed in the assigned component.
*
* If the search input is active (i.e., the user is typing in the search box),
* the list of filtered users is returned. Otherwise, the list of users that are
* not the task creator is returned.
*
* @return The list of users to be displayed in the assigned component.
*/
displayAssigned() {
if (this.searchInput) {
return this.filteredUsers;
} else {
return this.firebaseService.getFilteredUsers(this.taskCreator);
openTooltipUser(userId: any, event: MouseEvent) {
this.tooltipUserId = userId;
this.updateTooltipUserPosition(event);
}
updateTooltipUserPosition(event: MouseEvent) {
this.dialogX = event.clientX + 25;
this.dialogY = event.clientY + 10;
}
closeTooltipUser() {
this.tooltipUserId = null;
}
receiveAssigned(assigned: string[]) {
this.assignedChange.emit(assigned);
}
toggleAssignedMenu() {
this.showAssignedList = !this.showAssignedList;
}
@HostListener('document:click', ['$event'])
checkOpenNavbar(event: MouseEvent) {
const targetElement = event.target as HTMLElement;
if (
!targetElement.closest('.search-assigned') &&
!targetElement.closest('app-assigned-list') &&
!targetElement.closest('.checkbox-img')
) {
this.showAssignedList = false;
}
}
}