From 77014174e8ea47c100c66654db9819c10b69975b Mon Sep 17 00:00:00 2001 From: sercan Date: Thu, 6 May 2021 17:01:14 +0300 Subject: [PATCH] (apps/notes) New version of the Notes app --- src/app/app.routing.ts | 1 + src/app/mock-api/apps/notes/api.ts | 264 +++++++++++++ src/app/mock-api/apps/notes/data.ts | 314 ++++++++++++++++ src/app/mock-api/common/navigation/data.ts | 14 + src/app/mock-api/index.ts | 2 + .../apps/notes/details/details.component.html | 176 +++++++++ .../apps/notes/details/details.component.ts | 346 ++++++++++++++++++ .../apps/notes/labels/labels.component.html | 54 +++ .../apps/notes/labels/labels.component.ts | 112 ++++++ .../admin/apps/notes/list/list.component.html | 221 +++++++++++ .../admin/apps/notes/list/list.component.ts | 255 +++++++++++++ .../admin/apps/notes/notes.component.html | 1 + .../admin/apps/notes/notes.component.ts | 17 + .../modules/admin/apps/notes/notes.module.ts | 46 +++ .../modules/admin/apps/notes/notes.routing.ts | 16 + .../modules/admin/apps/notes/notes.service.ts | 239 ++++++++++++ .../modules/admin/apps/notes/notes.types.ts | 25 ++ 17 files changed, 2103 insertions(+) create mode 100644 src/app/mock-api/apps/notes/api.ts create mode 100644 src/app/mock-api/apps/notes/data.ts create mode 100644 src/app/modules/admin/apps/notes/details/details.component.html create mode 100644 src/app/modules/admin/apps/notes/details/details.component.ts create mode 100644 src/app/modules/admin/apps/notes/labels/labels.component.html create mode 100644 src/app/modules/admin/apps/notes/labels/labels.component.ts create mode 100644 src/app/modules/admin/apps/notes/list/list.component.html create mode 100644 src/app/modules/admin/apps/notes/list/list.component.ts create mode 100644 src/app/modules/admin/apps/notes/notes.component.html create mode 100644 src/app/modules/admin/apps/notes/notes.component.ts create mode 100644 src/app/modules/admin/apps/notes/notes.module.ts create mode 100644 src/app/modules/admin/apps/notes/notes.routing.ts create mode 100644 src/app/modules/admin/apps/notes/notes.service.ts create mode 100644 src/app/modules/admin/apps/notes/notes.types.ts diff --git a/src/app/app.routing.ts b/src/app/app.routing.ts index 4b7bbc28..d54337e0 100644 --- a/src/app/app.routing.ts +++ b/src/app/app.routing.ts @@ -90,6 +90,7 @@ export const appRoutes: Route[] = [ {path: 'file-manager', loadChildren: () => import('app/modules/admin/apps/file-manager/file-manager.module').then(m => m.FileManagerModule)}, {path: 'help-center', loadChildren: () => import('app/modules/admin/apps/help-center/help-center.module').then(m => m.HelpCenterModule)}, {path: 'mailbox', loadChildren: () => import('app/modules/admin/apps/mailbox/mailbox.module').then(m => m.MailboxModule)}, + {path: 'notes', loadChildren: () => import('app/modules/admin/apps/notes/notes.module').then(m => m.NotesModule)}, {path: 'tasks', loadChildren: () => import('app/modules/admin/apps/tasks/tasks.module').then(m => m.TasksModule)}, ]}, diff --git a/src/app/mock-api/apps/notes/api.ts b/src/app/mock-api/apps/notes/api.ts new file mode 100644 index 00000000..fe80771d --- /dev/null +++ b/src/app/mock-api/apps/notes/api.ts @@ -0,0 +1,264 @@ +import { Injectable } from '@angular/core'; +import { cloneDeep } from 'lodash-es'; +import { FuseMockApiService } from '@fuse/lib/mock-api/mock-api.service'; +import { labels as labelsData, notes as notesData } from 'app/mock-api/apps/notes/data'; +import { FuseMockApiUtils } from '@fuse/lib/mock-api'; + +@Injectable({ + providedIn: 'root' +}) +export class NotesMockApi +{ + private _labels: any[] = labelsData; + private _notes: any[] = notesData; + + /** + * Constructor + */ + constructor(private _fuseMockApiService: FuseMockApiService) + { + // Register Mock API handlers + this.registerHandlers(); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Register Mock API handlers + */ + registerHandlers(): void + { + // ----------------------------------------------------------------------------------------------------- + // @ Labels - GET + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onGet('api/apps/notes/labels') + .reply(() => { + + return [ + 200, + cloneDeep(this._labels) + ]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ Labels - POST + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onPost('api/apps/notes/labels') + .reply(({request}) => { + + // Create a new label + const label = { + id : FuseMockApiUtils.guid(), + title: request.body.title + }; + + // Update the labels + this._labels.push(label); + + return [ + 200, + cloneDeep(this._labels) + ]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ Labels - PATCH + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onPatch('api/apps/notes/labels') + .reply(({request}) => { + + // Get label + const updatedLabel = request.body.label; + + // Update the label + this._labels = this._labels.map((label) => { + if ( label.id === updatedLabel.id ) + { + return { + ...label, + title: updatedLabel.title + }; + } + + return label; + }); + + return [ + 200, + cloneDeep(this._labels) + ]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ Labels - DELETE + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onDelete('api/apps/notes/labels') + .reply(({request}) => { + + // Get label id + const id = request.params.get('id'); + + // Delete the label + this._labels = this._labels.filter((label) => label.id !== id); + + // Go through notes and delete the label + this._notes = this._notes.map((note) => ({ + ...note, + labels: note.labels.filter((item) => item !== id) + })); + + return [ + 200, + cloneDeep(this._labels) + ]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ Note Tasks - POST + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onPost('api/apps/notes/tasks') + .reply(({request}) => { + + // Get note and task + let updatedNote = request.body.note; + const task = request.body.task; + + // Update the note + this._notes = this._notes.map((note) => { + if ( note.id === updatedNote.id ) + { + // Update the tasks + if ( !note.tasks ) + { + note.tasks = []; + } + + note.tasks.push({ + id : FuseMockApiUtils.guid(), + content : task, + completed: false + }); + + // Update the updatedNote with the new task + updatedNote = cloneDeep(note); + + return { + ...note + }; + } + + return note; + }); + + return [ + 200, + updatedNote + ]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ Notes - GET + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onGet('api/apps/notes/all') + .reply(() => { + + // Clone the labels and notes + const labels = cloneDeep(this._labels); + let notes = cloneDeep(this._notes); + + // Attach the labels to the notes + notes = notes.map((note) => ( + { + ...note, + labels: note.labels.map((labelId) => labels.find((label) => label.id === labelId)) + } + )); + + return [ + 200, + notes + ]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ Notes - POST + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onPost('api/apps/notes') + .reply(({request}) => { + + // Get note + const note = request.body.note; + + // Add an id + note.id = FuseMockApiUtils.guid(); + + // Push the note + this._notes.push(note); + + return [ + 200, + note + ]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ Notes - PATCH + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onPatch('api/apps/notes') + .reply(({request}) => { + + // Get note + const updatedNote = request.body.updatedNote; + + // Update the note + this._notes = this._notes.map((note) => { + if ( note.id === updatedNote.id ) + { + return { + ...updatedNote + }; + } + + return note; + }); + + return [ + 200, + updatedNote + ]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ Notes - DELETE + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onDelete('api/apps/notes') + .reply(({request}) => { + + // Get the id + const id = request.params.get('id'); + + // Find the note and delete it + this._notes.forEach((item, index) => { + + if ( item.id === id ) + { + this._notes.splice(index, 1); + } + }); + + // Return the response + return [200, true]; + }); + } +} diff --git a/src/app/mock-api/apps/notes/data.ts b/src/app/mock-api/apps/notes/data.ts new file mode 100644 index 00000000..f10717c4 --- /dev/null +++ b/src/app/mock-api/apps/notes/data.ts @@ -0,0 +1,314 @@ +/* tslint:disable:max-line-length */ +import moment from 'moment'; + +export const labels = [ + { + id : 'f47c92e5-20b9-44d9-917f-9ff4ad25dfd0', + title: 'Family' + }, + { + id : 'e2f749f5-41ed-49d0-a92a-1c83d879e371', + title: 'Work' + }, + { + id : 'b1cde9ee-e54d-4142-ad8b-cf55dafc9528', + title: 'Tasks' + }, + { + id : '6c288794-47eb-4605-8bdf-785b61a449d3', + title: 'Priority' + }, + { + id : 'bbc73458-940b-421c-8d5f-8dcd23a9b0d6', + title: 'Personal' + }, + { + id : '2dc11344-3507-48e0-83d6-1c047107f052', + title: 'Friends' + } +]; + +export const notes = [ + { + id : '8f011ac5-b71c-4cd7-a317-857dcd7d85e0', + title : '', + content : 'Find a new company name', + tasks : null, + image : null, + reminder : null, + labels : ['e2f749f5-41ed-49d0-a92a-1c83d879e371'], + archived : false, + createdAt: moment().hour(10).minute(19).subtract(98, 'day').toISOString(), + updatedAt: null + }, + { + id : 'ced0a1ce-051d-41a3-b080-e2161e4ae621', + title : '', + content : 'Send the photos of last summer to John', + tasks : null, + image : 'assets/images/cards/14-640x480.jpg', + reminder : null, + labels : [ + 'bbc73458-940b-421c-8d5f-8dcd23a9b0d6', + 'b1cde9ee-e54d-4142-ad8b-cf55dafc9528' + ], + archived : false, + createdAt: moment().hour(15).minute(37).subtract(80, 'day').toISOString(), + updatedAt: null + }, + { + id : 'd3ac02a9-86e4-4187-bbd7-2c965518b3a3', + title : '', + content : 'Update the design of the theme', + tasks : null, + image : null, + reminder : null, + labels : ['6c288794-47eb-4605-8bdf-785b61a449d3'], + archived : false, + createdAt: moment().hour(19).minute(27).subtract(74, 'day').toISOString(), + updatedAt: moment().hour(15).minute(36).subtract(50, 'day').toISOString() + }, + { + id : '89861bd4-0144-4bb4-8b39-332ca10371d5', + title : '', + content : 'Theming support for all apps', + tasks : null, + image : null, + reminder : moment().hour(12).minute(34).add(50, 'day').toISOString(), + labels : ['e2f749f5-41ed-49d0-a92a-1c83d879e371'], + archived : false, + createdAt: moment().hour(12).minute(34).subtract(59, 'day').toISOString(), + updatedAt: null + }, + { + id : 'ffd20f3c-2d43-4c6b-8021-278032fc9e92', + title : 'Gift Ideas', + content : 'Stephanie\'s birthday is coming and I need to pick a present for her. Take a look at the below list and buy one of them (or all of them)', + tasks : [ + { + id : '330a924f-fb51-48f6-a374-1532b1dd353d', + content : 'Scarf', + completed: false + }, + { + id : '781855a6-2ad2-4df4-b0af-c3cb5f302b40', + content : 'A new bike helmet', + completed: true + }, + { + id : 'bcb8923b-33cd-42c2-9203-170994fa24f5', + content : 'Necklace', + completed: false + }, + { + id : '726bdf6e-5cd7-408a-9a4f-0d7bb98c1c4b', + content : 'Flowers', + completed: false + } + ], + image : null, + reminder : null, + labels : ['f47c92e5-20b9-44d9-917f-9ff4ad25dfd0'], + archived : false, + createdAt: moment().hour(16).minute(4).subtract(47, 'day').toISOString(), + updatedAt: null + }, + { + id : '71d223bb-abab-4183-8919-cd3600a950b4', + title : 'Shopping list', + content : '', + tasks : [ + { + id : 'e3cbc986-641c-4448-bc26-7ecfa0549c22', + content : 'Bread', + completed: true + }, + { + id : '34013111-ab2c-4b2f-9352-d2ae282f57d3', + content : 'Milk', + completed: false + }, + { + id : '0fbdea82-cc79-4433-8ee4-54fd542c380d', + content : 'Onions', + completed: false + }, + { + id : '66490222-743e-4262-ac91-773fcd98a237', + content : 'Coffee', + completed: true + }, + { + id : 'ab367215-d06a-48b0-a7b8-e161a63b07bd', + content : 'Toilet Paper', + completed: true + } + ], + image : null, + reminder : moment().hour(10).minute(44).subtract(35, 'day').toISOString(), + labels : ['b1cde9ee-e54d-4142-ad8b-cf55dafc9528'], + archived : false, + createdAt: moment().hour(10).minute(44).subtract(35, 'day').toISOString(), + updatedAt: null + }, + { + id : '11fbeb98-ae5e-41ad-bed6-330886fd7906', + title : 'Keynote Schedule', + content : '', + tasks : [ + { + id : '2711bac1-7d8a-443a-a4fe-506ef51d3fcb', + content : 'Breakfast', + completed: true + }, + { + id : 'e3a2d675-a3e5-4cef-9205-feeccaf949d7', + content : 'Opening ceremony', + completed: true + }, + { + id : '7a721b6d-9d85-48e0-b6c3-f927079af582', + content : 'Talk 1: How we did it!', + completed: true + }, + { + id : 'bdb4d5cd-5bb8-45e2-9186-abfd8307e429', + content : 'Talk 2: How can you do it!', + completed: false + }, + { + id : 'c8293bb4-8ab4-4310-bbc2-52ecf8ec0c54', + content : 'Lunch break', + completed: false + } + ], + image : null, + reminder : moment().hour(11).minute(27).subtract(14, 'day').toISOString(), + labels : [ + 'b1cde9ee-e54d-4142-ad8b-cf55dafc9528', + 'e2f749f5-41ed-49d0-a92a-1c83d879e371' + ], + archived : false, + createdAt: moment().hour(11).minute(27).subtract(24, 'day').toISOString(), + updatedAt: null + }, + { + id : 'd46dee8b-8761-4b6d-a1df-449d6e6feb6a', + title : '', + content : 'Organize the dad\'s surprise retirement party', + tasks : null, + image : null, + reminder : moment().hour(14).minute(56).subtract(25, 'day').toISOString(), + labels : ['f47c92e5-20b9-44d9-917f-9ff4ad25dfd0'], + archived : false, + createdAt: moment().hour(14).minute(56).subtract(20, 'day').toISOString(), + updatedAt: null + }, + { + id : '6bc9f002-1675-417c-93c4-308fba39023e', + title : 'Plan the road trip', + content : '', + tasks : null, + image : 'assets/images/cards/17-640x480.jpg', + reminder : null, + labels : [ + '2dc11344-3507-48e0-83d6-1c047107f052', + 'b1cde9ee-e54d-4142-ad8b-cf55dafc9528' + ], + archived : false, + createdAt: moment().hour(9).minute(32).subtract(15, 'day').toISOString(), + updatedAt: moment().hour(17).minute(6).subtract(12, 'day').toISOString() + }, + { + id : '15188348-78aa-4ed6-b5c2-028a214ba987', + title : 'Office Address', + content : '933 8th Street Stamford, CT 06902', + tasks : null, + image : null, + reminder : null, + labels : ['e2f749f5-41ed-49d0-a92a-1c83d879e371'], + archived : false, + createdAt: moment().hour(20).minute(5).subtract(12, 'day').toISOString(), + updatedAt: null + }, + { + id : '1dbfc685-1a0a-4070-9ca7-ed896c523037', + title : 'Tasks', + content : '', + tasks : [ + { + id : '004638bf-3ee6-47a5-891c-3be7b9f3df09', + content : 'Wash the dishes', + completed: true + }, + { + id : '86e6820b-1ae3-4c14-a13e-35605a0d654b', + content : 'Walk the dog', + completed: false + } + ], + image : null, + reminder : moment().hour(13).minute(43).subtract(2, 'day').toISOString(), + labels : ['bbc73458-940b-421c-8d5f-8dcd23a9b0d6'], + archived : false, + createdAt: moment().hour(13).minute(43).subtract(7, 'day').toISOString(), + updatedAt: null + }, + { + id : '49548409-90a3-44d4-9a9a-f5af75aa9a66', + title : '', + content : 'Dinner with parents', + tasks : null, + image : null, + reminder : null, + labels : [ + 'f47c92e5-20b9-44d9-917f-9ff4ad25dfd0', + '6c288794-47eb-4605-8bdf-785b61a449d3' + ], + archived : false, + createdAt: moment().hour(7).minute(12).subtract(2, 'day').toISOString(), + updatedAt: null + }, + { + id : 'c6d13a35-500d-4491-a3f3-6ca05d6632d3', + title : '', + content : 'Re-fill the medicine cabinet', + tasks : null, + image : null, + reminder : null, + labels : [ + 'bbc73458-940b-421c-8d5f-8dcd23a9b0d6', + '6c288794-47eb-4605-8bdf-785b61a449d3' + ], + archived : true, + createdAt: moment().hour(17).minute(14).subtract(100, 'day').toISOString(), + updatedAt: null + }, + { + id : 'c6d13a35-500d-4491-a3f3-6ca05d6632d3', + title : '', + content : 'Update the icons pack', + tasks : null, + image : null, + reminder : null, + labels : ['e2f749f5-41ed-49d0-a92a-1c83d879e371'], + archived : true, + createdAt: moment().hour(10).minute(29).subtract(85, 'day').toISOString(), + updatedAt: null + }, + { + id : '46214383-f8e7-44da-aa2e-0b685e0c5027', + title : 'Team Meeting', + content : 'Talk about the future of the web apps', + tasks : null, + image : null, + reminder : null, + labels : [ + 'e2f749f5-41ed-49d0-a92a-1c83d879e371', + 'b1cde9ee-e54d-4142-ad8b-cf55dafc9528' + ], + archived : true, + createdAt: moment().hour(15).minute(30).subtract(69, 'day').toISOString(), + updatedAt: null + } +]; diff --git a/src/app/mock-api/common/navigation/data.ts b/src/app/mock-api/common/navigation/data.ts index 2c6a0855..0339bfe2 100644 --- a/src/app/mock-api/common/navigation/data.ts +++ b/src/app/mock-api/common/navigation/data.ts @@ -127,6 +127,13 @@ export const defaultNavigation: FuseNavigationItem[] = [ classes: 'px-2 bg-pink-600 text-white rounded-full' } }, + { + id : 'apps.notes', + title: 'Notes', + type : 'basic', + icon : 'heroicons_outline:pencil-alt', + link : '/apps/notes' + }, { id : 'apps.tasks', title: 'Tasks', @@ -1242,6 +1249,13 @@ export const futuristicNavigation: FuseNavigationItem[] = [ classes: 'px-2 bg-black bg-opacity-25 text-white rounded-full' } }, + { + id : 'apps.notes', + title: 'Notes', + type : 'basic', + icon : 'heroicons_outline:pencil-alt', + link : '/apps/notes' + }, { id : 'apps.tasks', title: 'Tasks', diff --git a/src/app/mock-api/index.ts b/src/app/mock-api/index.ts index 4b312390..2dc55532 100644 --- a/src/app/mock-api/index.ts +++ b/src/app/mock-api/index.ts @@ -11,6 +11,7 @@ import { IconsMockApi } from 'app/mock-api/ui/icons/api'; import { MailboxMockApi } from 'app/mock-api/apps/mailbox/api'; import { MessagesMockApi } from 'app/mock-api/common/messages/api'; import { NavigationMockApi } from 'app/mock-api/common/navigation/api'; +import { NotesMockApi } from 'app/mock-api/apps/notes/api'; import { NotificationsMockApi } from 'app/mock-api/common/notifications/api'; import { ProjectMockApi } from 'app/mock-api/dashboards/project/api'; import { SearchMockApi } from 'app/mock-api/common/search/api'; @@ -32,6 +33,7 @@ export const mockApiServices = [ MailboxMockApi, MessagesMockApi, NavigationMockApi, + NotesMockApi, NotificationsMockApi, ProjectMockApi, SearchMockApi, diff --git a/src/app/modules/admin/apps/notes/details/details.component.html b/src/app/modules/admin/apps/notes/details/details.component.html new file mode 100644 index 00000000..13318a16 --- /dev/null +++ b/src/app/modules/admin/apps/notes/details/details.component.html @@ -0,0 +1,176 @@ +
+ + + +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ + + +
+
+
+ + +
+
+
+ + +
+ +
+
+ {{label.title}} +
+ +
+
+
+
+ + +
+ + +
+
+ + +
+
+ +
+ + +
+ + + + + + + + + + + + + + + +
+ + +
+
+
+
+ +
diff --git a/src/app/modules/admin/apps/notes/details/details.component.ts b/src/app/modules/admin/apps/notes/details/details.component.ts new file mode 100644 index 00000000..9be71363 --- /dev/null +++ b/src/app/modules/admin/apps/notes/details/details.component.ts @@ -0,0 +1,346 @@ +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { debounceTime, map, switchMap, takeUntil, tap } from 'rxjs/operators'; +import { Observable, of, Subject } from 'rxjs'; +import { NotesService } from 'app/modules/admin/apps/notes/notes.service'; +import { Label, Note, Task } from 'app/modules/admin/apps/notes/notes.types'; + +@Component({ + selector : 'notes-details', + templateUrl : './details.component.html', + encapsulation : ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class NotesDetailsComponent implements OnInit, OnDestroy +{ + note$: Observable; + labels$: Observable; + + noteChanged: Subject = new Subject(); + private _unsubscribeAll: Subject = new Subject(); + + /** + * Constructor + */ + constructor( + private _changeDetectorRef: ChangeDetectorRef, + @Inject(MAT_DIALOG_DATA) private _data: { note: Note }, + private _notesService: NotesService, + private _matDialogRef: MatDialogRef + ) + { + } + + // ----------------------------------------------------------------------------------------------------- + // @ Lifecycle hooks + // ----------------------------------------------------------------------------------------------------- + + /** + * On init + */ + ngOnInit(): void + { + // Edit + if ( this._data.note.id ) + { + // Request the data from the server + this._notesService.getNoteById(this._data.note.id).subscribe(); + + // Get the note + this.note$ = this._notesService.note$; + } + // Add + else + { + // Create an empty note + const note = { + id : null, + title : '', + content : '', + tasks : null, + image : null, + reminder : null, + labels : [], + archived : false, + createdAt: null, + updatedAt: null + }; + + this.note$ = of(note); + } + + // Get the labels + this.labels$ = this._notesService.labels$; + + // Subscribe to note updates + this.noteChanged + .pipe( + takeUntil(this._unsubscribeAll), + debounceTime(500), + switchMap((note) => this._notesService.updateNote(note))) + .subscribe(() => { + + // Mark for check + this._changeDetectorRef.markForCheck(); + }); + } + + /** + * On destroy + */ + ngOnDestroy(): void + { + // Unsubscribe from all subscriptions + this._unsubscribeAll.next(); + this._unsubscribeAll.complete(); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Create a new note + * + * @param note + */ + createNote(note: Note): void + { + this._notesService.createNote(note).pipe( + map(() => { + // Get the note + this.note$ = this._notesService.note$; + })).subscribe(); + } + + /** + * Upload image to given note + * + * @param note + * @param fileList + */ + uploadImage(note: Note, fileList: FileList): void + { + // Return if canceled + if ( !fileList.length ) + { + return; + } + + const allowedTypes = ['image/jpeg', 'image/png']; + const file = fileList[0]; + + // Return if the file is not allowed + if ( !allowedTypes.includes(file.type) ) + { + return; + } + + this._readAsDataURL(file).then((data) => { + + // Update the image + note.image = data; + + // Update the note + this.noteChanged.next(note); + }); + } + + /** + * Remove the image on the given note + * + * @param note + */ + removeImage(note: Note): void + { + note.image = null; + + // Update the note + this.noteChanged.next(note); + } + + /** + * Add an empty tasks array to note + * + * @param note + */ + addTasksToNote(note): void + { + if ( !note.tasks ) + { + note.tasks = []; + } + } + + /** + * Add task to the given note + * + * @param note + * @param task + */ + addTaskToNote(note: Note, task: string): void + { + if ( task.trim() === '' ) + { + return; + } + + // Add the task + this._notesService.addTask(note, task).subscribe(); + } + + /** + * Remove the given task from given note + * + * @param note + * @param task + */ + removeTaskFromNote(note: Note, task: Task): void + { + // Remove the task + note.tasks = note.tasks.filter((item) => item.id !== task.id); + + // Update the note + this.noteChanged.next(note); + } + + /** + * Update the given task on the given note + * + * @param note + * @param task + */ + updateTaskOnNote(note: Note, task: Task): void + { + // If the task is already available on the item + if ( task.id ) + { + // Update the note + this.noteChanged.next(note); + } + } + + /** + * Is the given note has the given label + * + * @param note + * @param label + */ + isNoteHasLabel(note: Note, label: Label): boolean + { + return !!note.labels.find((item) => item.id === label.id); + } + + /** + * Toggle the given label on the given note + * + * @param note + * @param label + */ + toggleLabelOnNote(note: Note, label: Label): void + { + // If the note already has the label + if ( this.isNoteHasLabel(note, label) ) + { + note.labels = note.labels.filter((item) => item.id !== label.id); + } + // Otherwise + else + { + note.labels.push(label); + } + + // Update the note + this.noteChanged.next(note); + } + + /** + * Toggle archived status on the given note + * + * @param note + */ + toggleArchiveOnNote(note: Note): void + { + note.archived = !note.archived; + + // Update the note + this.noteChanged.next(note); + + // Close the dialog + this._matDialogRef.close(); + } + + /** + * Update the note details + * + * @param note + */ + updateNoteDetails(note: Note): void + { + this.noteChanged.next(note); + } + + /** + * Delete the given note + * + * @param note + */ + deleteNote(note: Note): void + { + this._notesService.deleteNote(note) + .subscribe((isDeleted) => { + + // Return if the note wasn't deleted... + if ( !isDeleted ) + { + return; + } + + // Close the dialog + this._matDialogRef.close(); + }); + } + + /** + * Track by function for ngFor loops + * + * @param index + * @param item + */ + trackByFn(index: number, item: any): any + { + return item.id || index; + } + + // ----------------------------------------------------------------------------------------------------- + // @ Private methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Read the given file for demonstration purposes + * + * @param file + */ + private _readAsDataURL(file: File): Promise + { + // Return a new promise + return new Promise((resolve, reject) => { + + // Create a new reader + const reader = new FileReader(); + + // Resolve the promise on success + reader.onload = () => { + resolve(reader.result); + }; + + // Reject the promise on error + reader.onerror = (e) => { + reject(e); + }; + + // Read the file as the + reader.readAsDataURL(file); + }); + } +} diff --git a/src/app/modules/admin/apps/notes/labels/labels.component.html b/src/app/modules/admin/apps/notes/labels/labels.component.html new file mode 100644 index 00000000..6f9f8c6d --- /dev/null +++ b/src/app/modules/admin/apps/notes/labels/labels.component.html @@ -0,0 +1,54 @@ +
+ +
+
Edit labels
+ +
+ + + + + + +
+ + + + + + + + +
+
diff --git a/src/app/modules/admin/apps/notes/labels/labels.component.ts b/src/app/modules/admin/apps/notes/labels/labels.component.ts new file mode 100644 index 00000000..3e25b56a --- /dev/null +++ b/src/app/modules/admin/apps/notes/labels/labels.component.ts @@ -0,0 +1,112 @@ +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { NotesService } from 'app/modules/admin/apps/notes/notes.service'; +import { Label } from 'app/modules/admin/apps/notes/notes.types'; +import { debounceTime, filter, switchMap, takeUntil } from 'rxjs/operators'; +import { Observable, Subject } from 'rxjs'; + +@Component({ + selector : 'notes-labels', + templateUrl : './labels.component.html', + encapsulation : ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class NotesLabelsComponent implements OnInit, OnDestroy +{ + labels$: Observable; + + labelChanged: Subject