diff --git a/package.json b/package.json
index 255966a..2e4ca37 100755
--- a/package.json
+++ b/package.json
@@ -18,7 +18,7 @@
"push-release": "git push --follow-tags origin main",
"prepare": "husky install",
"container:run": "docker compose -f docker-compose.yml up",
- "container:build": "docker build -t mfaouzi ."
+ "container:build": "docker build -t trakz ."
},
"private": true,
"dependencies": {
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 4bba1b6..2044bd5 100755
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -3,6 +3,7 @@ import { LayoutModule } from '@angular/cdk/layout';
import { DatePipe, NgOptimizedImage } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
+import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatLineModule } from '@angular/material/core';
import { MatIconModule } from '@angular/material/icon';
@@ -93,6 +94,7 @@ import { TaskGroupListComponent } from './pages/tasks/task-group-list/task-group
NgOptimizedImage,
HttpClientModule,
MatLineModule,
+ FormsModule,
],
providers: [DatePipe],
bootstrap: [AppComponent],
diff --git a/src/app/layouts/main-layout/right-sidebar/right-sidebar.component.html b/src/app/layouts/main-layout/right-sidebar/right-sidebar.component.html
index 8b07c5e..6b1cafe 100755
--- a/src/app/layouts/main-layout/right-sidebar/right-sidebar.component.html
+++ b/src/app/layouts/main-layout/right-sidebar/right-sidebar.component.html
@@ -90,7 +90,10 @@
>
-
+
{
- if (task) {
- const taskId = taskGeneratedId(task);
- // If clicked on the same task, close the right sidebar
- if (taskId === this.currentTaskId) {
- this.onRightSidebarClose();
- return;
- }
- this.task = task;
- this.currentTaskId = taskId;
+ if (!task) {
+ this.shouldShowNoteTextarea = false;
+ return;
+ }
+ this.shouldShowNoteTextarea = false;
+ this.shouldShowNoteTextarea = true;
+ const taskId = taskGeneratedId(task);
+ if (taskId === this.currentTaskId) {
+ this.onRightSidebarClose();
+ return;
}
+ this.task = task;
+ this.currentTaskId = taskId;
});
}
@@ -85,6 +90,7 @@ export class RightSidebarComponent implements OnDestroy, OnInit {
}
onRightSidebarClose() {
+ this.shouldShowNoteTextarea = false;
this._tasksService.setSelectedTask(null);
this.task = undefined;
this.currentTaskId = undefined;
diff --git a/src/app/layouts/main-layout/right-sidebar/task-note/task-note.component.html b/src/app/layouts/main-layout/right-sidebar/task-note/task-note.component.html
index 705062a..d734669 100755
--- a/src/app/layouts/main-layout/right-sidebar/task-note/task-note.component.html
+++ b/src/app/layouts/main-layout/right-sidebar/task-note/task-note.component.html
@@ -2,14 +2,25 @@
class="bg-inherit !flex border-gray-300 focus-within:border-gray-400 rounded border !flex-col"
>
{{ task.note?.content || '' }}
-
+
-
- Updated on {{ note.updatedAt | date : 'EEE, MMM d' }}
+
+ Last saved {{ getLastUpdateDate() }}
+
+
+ Saving...
diff --git a/src/app/layouts/main-layout/right-sidebar/task-note/task-note.component.ts b/src/app/layouts/main-layout/right-sidebar/task-note/task-note.component.ts
index e4d97aa..7d4ed3c 100755
--- a/src/app/layouts/main-layout/right-sidebar/task-note/task-note.component.ts
+++ b/src/app/layouts/main-layout/right-sidebar/task-note/task-note.component.ts
@@ -1,12 +1,49 @@
-import { Component, Input } from '@angular/core';
+import { Component, Input, OnInit } from '@angular/core';
+import { debounceTime, distinctUntilChanged, Subject, switchMap } from 'rxjs';
-import { TaskNote } from '@/models/task';
+import { Task } from '@/models/task';
+import { TaskService } from '@/services/tasks/task.service';
+import { passedTimeFormatted } from '@/utils/trakzUtils';
@Component({
selector: 'app-task-note',
templateUrl: './task-note.component.html',
styleUrls: ['./task-note.component.scss'],
})
-export class TaskNoteComponent {
- @Input() note: TaskNote | undefined;
+export class TaskNoteComponent implements OnInit {
+ @Input() task: Task = {} as Task;
+
+ withRefresh = false;
+
+ isTaskBeingUpdate = false;
+
+ private typedNoteText$ = new Subject();
+
+ constructor(private _tasksService: TaskService) {}
+
+ ngOnInit() {
+ this.typedNoteText$
+ .pipe(
+ debounceTime(1000),
+ distinctUntilChanged(),
+ switchMap((content) => {
+ this.task.note.content = content;
+ this.isTaskBeingUpdate = true;
+ return this._tasksService.updateTask(this.task);
+ }),
+ )
+ .subscribe((task) => {
+ this.task = task;
+ this.isTaskBeingUpdate = false;
+ });
+ }
+
+ onUserTyping(event: Event) {
+ const content = (event.target as HTMLTextAreaElement).value;
+ this.typedNoteText$.next(content);
+ }
+
+ getLastUpdateDate() {
+ return passedTimeFormatted(this.task.note.updatedAt);
+ }
}
diff --git a/src/app/models/task.ts b/src/app/models/task.ts
index 1ef1891..d5e34c8 100755
--- a/src/app/models/task.ts
+++ b/src/app/models/task.ts
@@ -26,8 +26,8 @@ export interface TaskStep extends TimeStampsDate {
export interface TaskNote {
content: string | '';
- createdAt?: TimeStampsDate['createdAt'];
- updatedAt?: TimeStampsDate['updatedAt'];
+ createdAt: TimeStampsDate['createdAt'];
+ updatedAt: TimeStampsDate['updatedAt'];
}
export interface Folder {
@@ -48,7 +48,7 @@ export interface Task extends TimeStampsDate {
isCompleted: boolean;
steps: TaskStep[];
recurrence?: Recurrence | null;
- note?: TaskNote;
+ note: TaskNote;
}
export enum TaskStatus {
diff --git a/src/app/pages/my-day/add-task-input/add-task-input.component.ts b/src/app/pages/my-day/add-task-input/add-task-input.component.ts
index b037ce1..4a90379 100755
--- a/src/app/pages/my-day/add-task-input/add-task-input.component.ts
+++ b/src/app/pages/my-day/add-task-input/add-task-input.component.ts
@@ -65,6 +65,7 @@ export class AddTaskInputComponent implements OnInit {
.subscribe((task) => {
// eslint-disable-next-line no-param-reassign
taskInput.value = '';
+
// scroll to the task that was just added to the list of tasks in the DOM
setTimeout(() => {
AddTaskInputComponent.scrollInToNewAddedTask(task);
diff --git a/src/app/services/tasks/task.service.ts b/src/app/services/tasks/task.service.ts
index 1d58491..11719c7 100755
--- a/src/app/services/tasks/task.service.ts
+++ b/src/app/services/tasks/task.service.ts
@@ -331,7 +331,11 @@ export class TaskService {
isImportant: isImportant ?? false,
steps: steps ?? [],
recurrence: recurrence ?? 'ONCE',
- note: note ?? { content: '' },
+ note: note ?? {
+ content: '',
+ updatedAt: new Date(),
+ createdAt: new Date(),
+ },
};
}
@@ -363,6 +367,12 @@ export class TaskService {
);
};
+ // private _updateTaskNote$ = (note: string, taskID: number) => {
+ // return this.http.patch(`${this.apiUrl}/tasks/${taskID}/note`, {
+ // content: note,
+ // });
+ // };
+
private _updateTask$ = (task: Task): Observable => {
const { id } = task;
const url = `${this.apiUrl}/tasks/${id}`;
@@ -429,6 +439,8 @@ export class TaskService {
res.data.createdAt = newTask.createdAt;
res.data.updatedAt = newTask.updatedAt;
this.updateTask(res.data);
+ newTask.id = res.data.id;
+
this._snackBar.open('Task added successfully', 'Dismiss', {
duration: 2000,
});
diff --git a/src/app/utils/trakzUtils.ts b/src/app/utils/trakzUtils.ts
index 435cead..8a78301 100755
--- a/src/app/utils/trakzUtils.ts
+++ b/src/app/utils/trakzUtils.ts
@@ -11,17 +11,22 @@ export function isOverdue(date: Date) {
export function isToday(date: Date | string) {
const today = new Date();
- const dueDate = new Date(date);
+ const comparedDate = new Date(date);
return (
- dueDate.getDate() === today.getDate() &&
- dueDate.getMonth() === today.getMonth() &&
- dueDate.getFullYear() === today.getFullYear() &&
- dueDate.getHours() <= 23 &&
- dueDate.getMinutes() <= 59 &&
- dueDate.getSeconds() <= 59
+ comparedDate.getDate() === today.getDate() &&
+ comparedDate.getMonth() === today.getMonth() &&
+ comparedDate.getFullYear() === today.getFullYear() &&
+ comparedDate.getHours() <= 23 &&
+ comparedDate.getMinutes() <= 59 &&
+ comparedDate.getSeconds() <= 59
);
}
+export function isThisYear(date: Date) {
+ const today = new Date();
+ return date.getFullYear() === today.getFullYear();
+}
+
export function capitalizeFirstLetter(string: string) {
return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
}
@@ -152,3 +157,43 @@ export function taskGeneratedId(task: Task): string {
const d2 = toDateString(task.updatedAt);
return `${d1}${d2}${task.folderName}`;
}
+
+/**
+ * - If today show time only (h:mm a) else show date and time (EEE, MMM d y, h:mm a)
+ * - If yesterday show 'Yesterday at ' + time (h:mm a)
+ * - If this year show date and time (EEE, MMM d, h:mm a)
+ * - else show date only (EEE, MMM d y)
+ * @example: Today at 2:30 PM, Yesterday at 2:30 PM, Mon, Jan 1, 2021, 2:30 PM, Mon, Jan 1, 2021
+ * @param date
+ * @param locale
+ * */
+export function passedTimeFormatted(date: string | Date, locale = 'en-US') {
+ const usedDate = new Date(date);
+ const formatOpt: Record<
+ 'today' | 'yesterday' | 'thisYear' | 'other',
+ Intl.DateTimeFormatOptions
+ > = {
+ today: { hour: 'numeric', minute: 'numeric' },
+ yesterday: { hour: 'numeric', minute: 'numeric' },
+ thisYear: { weekday: 'short', hour: 'numeric', minute: 'numeric' },
+ other: {
+ weekday: 'short',
+ month: 'short',
+ day: 'numeric',
+ year: 'numeric',
+ },
+ };
+ const toDayYesterdayFmt = new Intl.DateTimeFormat(locale, formatOpt.today);
+ if (isToday(usedDate)) {
+ return `Today at ${toDayYesterdayFmt.format(usedDate)}`;
+ }
+ if (isYesterday(usedDate)) {
+ return `Yesterday at ${toDayYesterdayFmt.format(usedDate)}`;
+ }
+ const thisYearFmt = new Intl.DateTimeFormat(locale, formatOpt.thisYear);
+ if (isThisYear(usedDate)) {
+ return thisYearFmt.format(usedDate);
+ }
+ const otherFmt = new Intl.DateTimeFormat(locale, formatOpt.other);
+ return otherFmt.format(usedDate);
+}