diff --git a/package-lock.json b/package-lock.json index 5eb33c26e..4b129830d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "time-tracker", - "version": "1.47.5", + "version": "1.47.7", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -6645,9 +6645,9 @@ } }, "datatables.net": { - "version": "1.10.21", - "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-1.10.21.tgz", - "integrity": "sha512-/bSZtxmf3GTpYcvEmwZ8q26I1yhSx8qklR2B+s1K8+/51UW/zc2zTYwJMqr/Z+iCYixAc00ildj4g2x0Qamolw==", + "version": "1.10.22", + "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-1.10.22.tgz", + "integrity": "sha512-ujn8GvkQIBYzYH54XY7OrI0Zb35TKRd9ABYfbnXgBfwTGIFT6UsmXrfHU5Yk+MSDoF0sDu2TB+31V6c+zUZ0Pw==", "requires": { "jquery": ">=1.7" } @@ -6678,6 +6678,16 @@ "requires": { "datatables.net": "1.10.21", "jquery": ">=1.7" + }, + "dependencies": { + "datatables.net": { + "version": "1.10.21", + "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-1.10.21.tgz", + "integrity": "sha512-/bSZtxmf3GTpYcvEmwZ8q26I1yhSx8qklR2B+s1K8+/51UW/zc2zTYwJMqr/Z+iCYixAc00ildj4g2x0Qamolw==", + "requires": { + "jquery": ">=1.7" + } + } } }, "datatables.net-responsive": { @@ -9848,9 +9858,9 @@ } }, "jszip": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.4.0.tgz", - "integrity": "sha512-gZAOYuPl4EhPTXT0GjhI3o+ZAz3su6EhLrKUoAivcKqyqC7laS5JEv4XWZND9BgcDcF83vI85yGbDmDR6UhrIg==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.0.tgz", + "integrity": "sha512-Y2OlFIzrDOPWUnpU0LORIcDn2xN7rC9yKffFM/7pGhQuhO+SUhfm2trkJ/S5amjFvem0Y+1EALz/MEPkvHXVNw==", "requires": { "lie": "~3.3.0", "pako": "~1.0.2", @@ -17302,7 +17312,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, "requires": { "safe-buffer": "^5.1.0" } @@ -21132,6 +21141,14 @@ "ajv-keywords": "^3.1.0" } }, + "serialize-javascript": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz", + "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==", + "requires": { + "randombytes": "^2.1.0" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", diff --git a/package.json b/package.json index 6c22242e8..85f60b509 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "time-tracker", - "version": "1.47.5", + "version": "1.47.7", "scripts": { "preinstall": "npx npm-force-resolutions", "ng": "ng", @@ -34,7 +34,7 @@ "angular-calendar": "0.28.26", "angular-datatables": "9.0.2", "bootstrap": "4.4.1", - "datatables.net": "1.10.21", + "datatables.net": "1.10.22", "datatables.net-buttons": "1.6.2", "datatables.net-buttons-dt": "1.6.2", "datatables.net-dt": "1.10.21", @@ -42,7 +42,7 @@ "datatables.net-responsive-dt": "2.2.6", "date-fns": "2.22.1", "jquery": "3.5.1", - "jszip": "3.4.0", + "jszip": "3.7.0", "minimist": "1.2.5", "moment": "2.25.3", "msal": "1.2.1", @@ -78,7 +78,7 @@ "angular-ng-autocomplete": "2.0.1", "codelyzer": "5.2.2", "commit-message-validator": "0.1.11", - "datatables.net": "1.10.21", + "datatables.net": "1.10.22", "datatables.net-dt": "1.10.21", "husky": "4.2.3", "jasmine-core": "3.5.0", @@ -100,7 +100,7 @@ "husky": { "hooks": { "commit-msg": "commit-message-validator", - "pre-commit": "ng lint" + "pre-commit": "ng lint && ng test --watch=false --browsers=ChromeHeadless" } }, "config": { diff --git a/src/app/modules/shared/components/details-fields/details-fields.component.html b/src/app/modules/shared/components/details-fields/details-fields.component.html index 766d48690..8d9d5be91 100644 --- a/src/app/modules/shared/components/details-fields/details-fields.component.html +++ b/src/app/modules/shared/components/details-fields/details-fields.component.html @@ -18,7 +18,7 @@ formControlName="project_name" [data]="listProjects" [searchKeyword]="keyword" - historyIdentifier="projectsSelected" + [historyIdentifier]="projectKeyForLocalStorage" notFoundText="No projects found" placeHolder="Enter the project name" [itemTemplate]="itemTemplate" diff --git a/src/app/modules/shared/components/details-fields/details-fields.component.ts b/src/app/modules/shared/components/details-fields/details-fields.component.ts index 59b07a5ae..e0f3e9206 100644 --- a/src/app/modules/shared/components/details-fields/details-fields.component.ts +++ b/src/app/modules/shared/components/details-fields/details-fields.component.ts @@ -27,6 +27,8 @@ import { DATE_FORMAT, DATE_FORMAT_YEAR } from 'src/environments/environment'; import { TechnologiesComponent } from '../technologies/technologies.component'; import { MatDatepicker } from '@angular/material/datepicker'; import { Observable } from 'rxjs'; +import { updateProjectStorage } from '../../utils/project-storage.util'; +import { PROJECTS_KEY_FOR_LOCAL_STORAGE } from '../../../../../environments/environment'; type Merged = TechnologyState & ProjectState & ActivityState & EntryState; @Component({ @@ -49,6 +51,7 @@ export class DetailsFieldsComponent implements OnChanges, OnInit { activities$: Observable; goingToWorkOnThis = false; shouldRestartEntry = false; + projectKeyForLocalStorage = PROJECTS_KEY_FOR_LOCAL_STORAGE; constructor( private formBuilder: FormBuilder, @@ -81,6 +84,8 @@ export class DetailsFieldsComponent implements OnChanges, OnInit { projectWithSearchField.search_field = `${project.customer.name} - ${project.name}`; this.listProjects.push(projectWithSearchField); }); + + updateProjectStorage(projects); } }); diff --git a/src/app/modules/shared/utils/project-storage.util.spec.ts b/src/app/modules/shared/utils/project-storage.util.spec.ts new file mode 100644 index 000000000..da0d580dc --- /dev/null +++ b/src/app/modules/shared/utils/project-storage.util.spec.ts @@ -0,0 +1,89 @@ +import { updateProjectStorage, getProjectsOnStorage } from './project-storage.util'; +import { Project } from '../models/project.model'; +import { PROJECTS_KEY_FOR_LOCAL_STORAGE } from '../../../../environments/environment'; + +describe('Project Storage', () => { + + let storageProjects: Project[]; + let projectsIdentifier: string; + let serverProjects: Project[]; + let testProject: Project; + let localStorageGetItemMock; + + beforeEach(() => { + projectsIdentifier = PROJECTS_KEY_FOR_LOCAL_STORAGE; + + testProject = { + customer: { + name: 'ioet Inc. (was E&Y)', + }, + id: 'f3630e59-9408-497e-945b-848112bd5a44', + name: 'Time Tracker', + customer_id: '20c96c4d-5e26-4426-a704-8bdd98c83319', + status: 'active', + }; + + storageProjects = [ + testProject + ]; + + serverProjects = [ + testProject, + { + customer: { + name: 'No Matter Name', + }, + id: 'no-matter-id', + name: 'Warby Parker', + customer_id: 'no-matter-id', + status: 'active', + } + ]; + + localStorageGetItemMock = spyOn(localStorage, 'getItem').and.returnValue(JSON.stringify(storageProjects)); + spyOn(localStorage, 'setItem'); + + }); + + it('If exists projects in localStorage and the server returns the same project, should keep the same localStorage variables', () => { + updateProjectStorage(serverProjects); + + expect(localStorage.setItem).toHaveBeenCalledWith(projectsIdentifier, JSON.stringify(storageProjects)); + }); + + it('If exists projects in localStorage and the server does not return that project, should update the localStorage variable', () => { + serverProjects.shift(); + + updateProjectStorage(serverProjects); + + expect(localStorage.setItem).toHaveBeenCalledWith(projectsIdentifier, JSON.stringify([])); + }); + + it('If Server projects is empty, should not update the localStorage', () => { + serverProjects = []; + + updateProjectStorage(serverProjects); + + expect(localStorage.setItem).toHaveBeenCalledTimes(0); + }); + + it('If variables does not exists on localStorage, getProjectsOnStorage should return undefined', () => { + projectsIdentifier = 'no-matter-identifier'; + + localStorageGetItemMock.and.returnValue(undefined); + + const projects = getProjectsOnStorage(projectsIdentifier); + + expect(projects).toBeUndefined(); + }); + + it('If variables not exists on localStorage, getProjectsOnStorage should return an array of Projects', () => { + const storageProjectsString = JSON.stringify(storageProjects); + localStorageGetItemMock.and.returnValue(storageProjectsString); + + const projects = getProjectsOnStorage(projectsIdentifier); + + expect(projects).toEqual(JSON.parse(storageProjectsString)); + }); +}); + diff --git a/src/app/modules/shared/utils/project-storage.util.ts b/src/app/modules/shared/utils/project-storage.util.ts new file mode 100644 index 000000000..4e4ee75c5 --- /dev/null +++ b/src/app/modules/shared/utils/project-storage.util.ts @@ -0,0 +1,29 @@ +import { Project } from '../models/project.model'; +import { PROJECTS_KEY_FOR_LOCAL_STORAGE as projectsKey } from '../../../../environments/environment'; +import { isEmpty } from 'lodash'; + +export function updateProjectStorage(serverProjects: Project[]): void { + const storageProjects: Project[] = getProjectsOnStorage(projectsKey); + const isServerProjectsEmpty = isEmpty(serverProjects); + const updatedStorageProjects: Project[] = []; + + if (!isServerProjectsEmpty && storageProjects) { + storageProjects.forEach((storageProject: Project) => { + const project = serverProjects.find((serverProject) => serverProject.id === storageProject.id); + + if (project) { + updatedStorageProjects.push(project); + } + }); + + const projectsForLocalStorage = JSON.stringify(updatedStorageProjects); + localStorage.setItem(projectsKey, projectsForLocalStorage); + } +} + +export function getProjectsOnStorage(projectsIdentifier: string): Project[] { + const projectsInsideLocalStorage: string = localStorage.getItem(projectsIdentifier); + return projectsInsideLocalStorage && JSON.parse(projectsInsideLocalStorage); +} + + diff --git a/src/app/modules/time-clock/components/project-list-hover/project-list-hover.component.html b/src/app/modules/time-clock/components/project-list-hover/project-list-hover.component.html index 20a27d649..0f3a9d550 100644 --- a/src/app/modules/time-clock/components/project-list-hover/project-list-hover.component.html +++ b/src/app/modules/time-clock/components/project-list-hover/project-list-hover.component.html @@ -7,7 +7,7 @@ [data]="listProjects" [searchKeyword]="keyword" [initialValue]="" - historyIdentifier="projectsSelected" + [historyIdentifier]="projectKeyForLocalStorage" notFoundText="No projects found" placeHolder="Enter the project name" [itemTemplate]="itemTemplate" diff --git a/src/app/modules/time-clock/components/project-list-hover/project-list-hover.component.ts b/src/app/modules/time-clock/components/project-list-hover/project-list-hover.component.ts index 7647fb44e..d9b2c5731 100644 --- a/src/app/modules/time-clock/components/project-list-hover/project-list-hover.component.ts +++ b/src/app/modules/time-clock/components/project-list-hover/project-list-hover.component.ts @@ -18,6 +18,8 @@ import { Activity, } from '../../../shared/models'; import { LoadActivities } from './../../../activities-management/store/activity-management.actions'; import { allActivities } from 'src/app/modules/activities-management/store/activity-management.selectors'; import { head } from 'lodash'; +import { updateProjectStorage } from '../../../shared/utils/project-storage.util'; +import { PROJECTS_KEY_FOR_LOCAL_STORAGE } from '../../../../../environments/environment'; @Component({ selector: 'app-project-list-hover', @@ -36,6 +38,7 @@ export class ProjectListHoverComponent implements OnInit, OnDestroy { projectsSubscription: Subscription; activeEntrySubscription: Subscription; loadActivitiesSubscription: Subscription; + projectKeyForLocalStorage = PROJECTS_KEY_FOR_LOCAL_STORAGE; constructor( private formBuilder: FormBuilder, @@ -57,6 +60,8 @@ export class ProjectListHoverComponent implements OnInit, OnDestroy { projectWithSearchField.search_field = `${project.customer.name} - ${project.name}`; this.listProjects.push(projectWithSearchField); }); + + updateProjectStorage(projects); this.loadActiveTimeEntry(); }); this.store.dispatch(new LoadActivities()); diff --git a/src/app/modules/time-entries/components/calendar/calendar.component.html b/src/app/modules/time-entries/components/calendar/calendar.component.html index 3f8cb8593..58d4e2fea 100644 --- a/src/app/modules/time-entries/components/calendar/calendar.component.html +++ b/src/app/modules/time-entries/components/calendar/calendar.component.html @@ -66,14 +66,14 @@
-
+
-

+

{{ currentDate | date: 'EEEE' }}

@@ -114,7 +114,7 @@