diff --git a/src/app/components/board/task/task.component.html b/src/app/components/board/task/task.component.html index 5fc627e..a1fc126 100644 --- a/src/app/components/board/task/task.component.html +++ b/src/app/components/board/task/task.component.html @@ -1,8 +1,9 @@
- @if (task.id) { + + @if (task && task.id) {
- {{ task.category }} +

{{ task.category }}

+ + @if (pageViewMedia$ | async) { -
{{ task.title }}
+ +
{{ task.title }}
{{ task.description }}
+ + @if(task.subtasks.length > 0) {
@@ -37,46 +43,67 @@ [style.width.%]="completedSubtasksPercent()" >
-
- {{ completedSubtasks() }} / {{ task.subtasks.length }} Subtasks -
+

{{ completedSubtasks() }} / {{ task.subtasks.length }} Subtasks

} -
- @if (menuOpen){ + + + @if (mobileMenuOpen){ - } @if (AssignedDialogId) { + } + + + @if (assignedDialogId) {
- @for (user of task.userData; track user) { @if (user.id === - AssignedDialogId) { + + @for (user of task.userData; track user) { + + + @if (user.id === assignedDialogId) {

{{ user.firstName }}

diff --git a/src/app/components/board/task/task.component.scss b/src/app/components/board/task/task.component.scss index 472f7d7..0f886fb 100644 --- a/src/app/components/board/task/task.component.scss +++ b/src/app/components/board/task/task.component.scss @@ -4,8 +4,7 @@ section { position: relative; } -.content { - position: relative; +.task { display: flex; flex-direction: column; justify-content: center; @@ -13,27 +12,12 @@ section { padding: 16px; border-radius: 24px; background-color: var(--white); - box-shadow: 0px 0px 10px 3px rgba(0, 0, 0, 0.08); + box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.08); margin-bottom: 16px; cursor: pointer; - &:hover { - box-shadow: 0px 0px 10px 3px rgba(0, 0, 0, 0.3); - } -} -.dialog { - position: fixed; - width: fit-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, - span { - font-size: 18px; - font-weight: 400; + &:hover { + box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3); } } @@ -41,23 +25,31 @@ section { display: flex; align-items: center; justify-content: space-between; + .category { width: fit-content; - font-size: 16px; padding: 4px 16px; border-radius: 8px; color: var(--white); - text-shadow: 1px 1px 2px var(--black); + box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.2); + + p { + font-size: 16px; + text-shadow: 1px 1px 2px var(--black); + } } + .menu-btn { display: flex; align-items: center; justify-content: center; padding: 6px 12px; + img { height: 16px; width: auto; } + &:hover { img { filter: brightness(0) saturate(100%) invert(56%) sepia(64%) @@ -67,9 +59,9 @@ section { } } -.headline { +.title { color: var(--dark-blue); - font-size: 16px; + font-size: 18px; font-weight: 700; margin-top: 12px; } @@ -83,11 +75,18 @@ section { overflow: auto; } +// Subtasks + .subtask { display: flex; justify-content: space-between; align-items: center; margin-top: 24px; + + p { + font-size: 12px; + font-weight: 400; + } } .subtask-line { @@ -100,78 +99,101 @@ section { align-items: flex-start; border-radius: 8px; background-color: var(--very-light-gray); + + .filler-full { + border-radius: 16px; + background-color: var(--light-blue); + height: 8px; + } } -.filler-full { - border-radius: 16px; - background-color: var(--light-blue); - height: 8px; -} +// Assignees -.subtask-text { - font-size: 12px; - font-weight: 400; -} - -.footer { +.assignees { display: flex; justify-content: space-between; align-items: center; - margin-top: 24px; + margin-top: 15px; width: 100%; + + .assigned { + display: flex; + align-items: center; + position: relative; + width: 170px; + height: 45px; + gap: 6px; + overflow: auto; + } } -.footer-badge { - display: flex; - width: 170px; - overflow: auto; -} - -.footer-badged { +.assigned-badged { display: flex; justify-content: center; align-items: center; - min-width: 32px; - min-height: 32px; - border-radius: 45px; - border: 1px solid var(--white); - color: var(--white); - font-size: 12px; - font-weight: 400; + width: 32px; + height: 32px; + border-radius: 100%; + border: 1px solid var(--black); + position: relative; + transition: box-shadow 0.2s ease, transform 0.2s ease; + margin-left: 3px; + + &:nth-child(n + 2) { + margin-left: -12px; + } + + & span { + color: var(--white); + font-size: 14px; + font-weight: 700; + text-shadow: 1px 1px 2px var(--black); + } + + &:hover { + transform: translateY(-1px) scale(1.02); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + z-index: 2; + } } -.footer-badge span { - margin-right: 4px; - text-shadow: 1px 1px 2px var(--black); -} +// Priority -.footer-badge span:first-child { - margin-left: 0 !important; -} - -.footer-priority { +.priority { background-size: 32px; background-repeat: no-repeat; background-position: center; width: 32px; height: 32px; } -.priority { - background-size: 24px; - background-repeat: no-repeat; - background-position: center; - width: 24px; - height: 24px; -} -.prio-urgent { +.priority-urgent { background-image: url(./../../../../assets/img/urgent.svg); } -.prio-medium { +.priority-medium { background-image: url(./../../../../assets/img/medium.svg); } -.prio-low { +.priority-low { background-image: url(./../../../../assets/img/low.svg); } + +// Dialog + +.dialog { + position: fixed; + width: fit-content; + height: fit-content; + padding: 10px 15px; + border-radius: 0px 20px 20px 20px; + border: 1px solid var(--black); + z-index: 3; + background-color: var(--white); + + p, + span { + font-size: 18px; + font-weight: 400; + } +} diff --git a/src/app/components/board/task/task.component.ts b/src/app/components/board/task/task.component.ts index 7201912..e904c14 100644 --- a/src/app/components/board/task/task.component.ts +++ b/src/app/components/board/task/task.component.ts @@ -33,22 +33,20 @@ export class TaskComponent { readonly DIALOG_OFFSET_X = 25; readonly DIALOG_OFFSET_Y = 10; + pageViewMedia$ = this.resizeService.pageViewMedia$; + + assignees: Assignee[] = []; categoryColors = new Map([ ['User Story', '#0038ff'], ['Technical Task', '#20d7c2'], ]); - pageViewMedia$ = this.resizeService.pageViewMedia$; - - menuOpen = false; + mobileMenuOpen = false; disableDrag = false; - AssignedDialogId = ''; + assignedDialogId = ''; dialogX = 0; dialogY = 0; - creator = ''; - assignees: Assignee[] = []; - constructor( public dragDropService: DragDropService, public overlayService: OverlayService, @@ -78,20 +76,20 @@ export class TaskComponent { * If the event target is a menu button or menu img, toggle the task menu * If the event target is anything else, open the task details overlay */ - handleMenuButtonClick(event: MouseEvent, taskId: string) { + handleMenuButtonClick(event: MouseEvent, taskId: string): void { event.stopPropagation(); const targetElement = event.target as HTMLElement; targetElement.classList.contains('menu-btn') || targetElement.classList.contains('menu-img') - ? this.toggleTaskMenu() + ? this.toggleMobileMenu() : this.openTaskDetailsOverlay(taskId); } /** * Toggle the task menu for the current task */ - toggleTaskMenu() { - this.menuOpen = !this.menuOpen; + toggleMobileMenu(): void { + this.mobileMenuOpen = !this.mobileMenuOpen; } /** @@ -102,9 +100,9 @@ export class TaskComponent { * If the page is in the media view, navigate to the task details page * If the page is not in the media view, open the task overlay */ - openTaskDetailsOverlay(taskId: string) { - if (this.menuOpen) { - this.toggleTaskMenu(); + openTaskDetailsOverlay(taskId: string): void { + if (this.mobileMenuOpen) { + this.toggleMobileMenu(); } this.pageViewMedia$.pipe(take(1)).subscribe((isPageViewMedia) => { isPageViewMedia @@ -113,33 +111,13 @@ export class TaskComponent { }); } - @HostListener('document:click', ['$event']) - /** - * If the target element of the event is not a task menu button, - * task menu img, or the task menu itself, close the task menu - * @param event the MouseEvent - */ - checkToggleTaskMenu(event: MouseEvent) { - const targetElement = event.target as HTMLElement; - const menuSelectors = ['.menu-btn', '.menu-img', 'app-task-menu']; - const isMenuClicked = menuSelectors.some((selector) => - targetElement.closest(selector) - ); - - if (!isMenuClicked) { - this.menuOpen = false; - } - } - - // User Dialog - /** * 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; + openDialog(userId: string, event: MouseEvent): void { + this.assignedDialogId = userId; this.updateDialogPosition(event); } @@ -147,7 +125,7 @@ export class TaskComponent { * Updates the position of the user dialog based on the MouseEvent * @param event the MouseEvent that triggered the dialog */ - updateDialogPosition(event: MouseEvent) { + updateDialogPosition(event: MouseEvent): void { this.dialogX = event.clientX + this.DIALOG_OFFSET_X; this.dialogY = event.clientY + this.DIALOG_OFFSET_Y; } @@ -155,12 +133,10 @@ export class TaskComponent { /** * Closes the user dialog */ - closeDialog() { - this.AssignedDialogId = ''; + closeDialog(): void { + this.assignedDialogId = ''; } - // Subtasks - /** * Returns the number of completed subtasks of the task * @returns the number of completed subtasks @@ -178,26 +154,46 @@ export class TaskComponent { (subtask) => subtask.status ).length; - return (completedSubtasksCount / this.task.subtasks.length) * 100; + return this.task.subtasks.length > 0 + ? (completedSubtasksCount / this.task.subtasks.length) * 100 + : 0; } /** * Emits an event to update the status of a task. * @param event The TaskMoveEvent containing the task and the new status to move to. */ - onStatusUpdate(event: TaskMoveEvent) { + onStatusUpdate(event: TaskMoveEvent): void { this.updateStatusEmitter.emit({ task: event.task, moveTo: event.moveTo, }); } + @HostListener('document:click', ['$event']) + /** + * If the target element of the event is not a task menu button, + * task menu img, or the task menu itself, close the task menu + * @param event the MouseEvent + */ + checkToggleTaskMenu(event: MouseEvent): void { + const targetElement = event.target as HTMLElement; + const menuSelectors = ['.menu-btn', '.menu-img', 'app-task-menu']; + const isMenuClicked = menuSelectors.some((selector) => + targetElement.closest(selector) + ); + + if (!isMenuClicked) { + this.mobileMenuOpen = false; + } + } + @HostListener('window:resize') /** * Called when the window is resized. * Updates the disableDragStatus so that tasks are not draggable on mobile devices. */ - onResize() { + onResize(): void { this.updateDisableDragStatus(); } }