+
{{ "addTask.assigned" | translate }}
+
+ @if (showAssignedList) {
+
+ >
+

+ } @else {
+

+ }
+
+
+ @for (user of users; track user; let index = $index) { @for (users of
+ assignedList; track users ) { @if (user.id === users) {
- {{ firebaseService.getUserDetails(user.id, "initials") }}
+ {{ user.initials }}
-
-
-
- {{ firebaseService.getUserDetails(user.id, "firstName") }}
-
-
,
-
- {{ firebaseService.getUserDetails(user.id, "lastName") }}
-
-
-
-
- @if (assigned.includes(user.id)) {
-

- } @else {
-

- }
+ } } } @if (tooltipUserId) {
+
+ }
- }
-
+
diff --git a/src/app/components/add-task/assigned/assigned.component.scss b/src/app/components/add-task/assigned/assigned.component.scss
index 206caf5..2804136 100644
--- a/src/app/components/add-task/assigned/assigned.component.scss
+++ b/src/app/components/add-task/assigned/assigned.component.scss
@@ -1,84 +1,101 @@
-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;
+.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 6px 12px 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);
+ }
+ }
+}
+
+.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);
- 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;
+ p {
+ font-size: 18px;
font-weight: 400;
- color: var(--white);
- 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;
- font-size: 18px;
+.headline {
+ font-size: 20px;
font-weight: 400;
+ padding-bottom: 6px;
}
-.last-name {
- overflow: hidden;
- text-overflow: ellipsis;
+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;
}
-.checkbox {
- img {
- width: 18px;
- height: 18px;
- }
+input.ng-invalid.ng-touched,
+textarea.ng-invalid.ng-touched,
+select.ng-invalid.ng-touched {
+ border: 1px dashed red;
}
-.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);
- }
+input:valid,
+textarea:valid,
+select:valid {
+ border: 1px solid black;
}
diff --git a/src/app/components/add-task/assigned/assigned.component.ts b/src/app/components/add-task/assigned/assigned.component.ts
index 0ed2943..c8453a2 100644
--- a/src/app/components/add-task/assigned/assigned.component.ts
+++ b/src/app/components/add-task/assigned/assigned.component.ts
@@ -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
();
- 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, '>');
}
- /**
- * 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;
}
}
}