Skip to content

Commit fc41564

Browse files
committed
feat: TT-314 Synchronize local projects with Server projects
1 parent 5641dda commit fc41564

File tree

7 files changed

+145
-2
lines changed

7 files changed

+145
-2
lines changed

src/app/modules/shared/components/details-fields/details-fields.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
formControlName="project_name"
1919
[data]="listProjects"
2020
[searchKeyword]="keyword"
21-
historyIdentifier="projectsSelected"
21+
[historyIdentifier]="projectKeyForLocalStorage"
2222
notFoundText="No projects found"
2323
placeHolder="Enter the project name"
2424
[itemTemplate]="itemTemplate"

src/app/modules/shared/components/details-fields/details-fields.component.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import { DATE_FORMAT, DATE_FORMAT_YEAR } from 'src/environments/environment';
2727
import { TechnologiesComponent } from '../technologies/technologies.component';
2828
import { MatDatepicker } from '@angular/material/datepicker';
2929
import { Observable } from 'rxjs';
30+
import { updateProjectStorage } from '../../utils/project-storage.util';
31+
import { PROJECTS_KEY_FOR_LOCAL_STORAGE } from '../../../../../environments/environment';
3032

3133
type Merged = TechnologyState & ProjectState & ActivityState & EntryState;
3234
@Component({
@@ -49,6 +51,7 @@ export class DetailsFieldsComponent implements OnChanges, OnInit {
4951
activities$: Observable<Activity[]>;
5052
goingToWorkOnThis = false;
5153
shouldRestartEntry = false;
54+
projectKeyForLocalStorage = PROJECTS_KEY_FOR_LOCAL_STORAGE;
5255

5356
constructor(
5457
private formBuilder: FormBuilder,
@@ -81,6 +84,8 @@ export class DetailsFieldsComponent implements OnChanges, OnInit {
8184
projectWithSearchField.search_field = `${project.customer.name} - ${project.name}`;
8285
this.listProjects.push(projectWithSearchField);
8386
});
87+
88+
updateProjectStorage(projects);
8489
}
8590
});
8691

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { updateProjectStorage, getProjectsOnStorage } from './project-storage.util';
2+
import { Project } from '../models/project.model';
3+
4+
describe('Project Storage', () => {
5+
6+
let storageProjects: Project[];
7+
let projectsIdentifier: string;
8+
let serverProjects: Project[];
9+
10+
beforeEach(() => {
11+
projectsIdentifier = 'projectsSelected';
12+
13+
storageProjects = [
14+
{
15+
customer: {
16+
name: 'ioet Inc. (was E&Y)',
17+
},
18+
id: 'f3630e59-9408-497e-945b-848112bd5a44',
19+
name: 'Time Tracker',
20+
customer_id: '20c96c4d-5e26-4426-a704-8bdd98c83319',
21+
status: 'active',
22+
}
23+
];
24+
25+
serverProjects = [
26+
{
27+
customer: {
28+
name: 'ioet Inc. (was E&Y)',
29+
},
30+
id: 'f3630e59-9408-497e-945b-848112bd5a44',
31+
name: 'Time Tracker',
32+
customer_id: '20c96c4d-5e26-4426-a704-8bdd98c83319',
33+
status: 'active',
34+
},
35+
{
36+
customer: {
37+
name: 'No Matter Name',
38+
},
39+
id: 'no-matter-id',
40+
name: 'Warby Parker',
41+
customer_id: 'no-matter-id',
42+
status: 'active',
43+
}
44+
];
45+
46+
});
47+
48+
it('If exists projects in localStorage and the server returns the same project, should keep the same localStorage variables', () => {
49+
spyOn(localStorage, 'getItem').and.returnValue(JSON.stringify(storageProjects));
50+
spyOn(localStorage, 'setItem');
51+
52+
updateProjectStorage(serverProjects);
53+
54+
expect(localStorage.setItem).toHaveBeenCalledWith(projectsIdentifier, JSON.stringify(storageProjects));
55+
});
56+
57+
it('If exists projects in localStorage and the server does not return that project, should update the localStorage variable', () => {
58+
serverProjects.shift();
59+
spyOn(localStorage, 'getItem').and.returnValue(JSON.stringify(storageProjects));
60+
spyOn(localStorage, 'setItem');
61+
62+
updateProjectStorage(serverProjects);
63+
64+
expect(localStorage.setItem).toHaveBeenCalledWith(projectsIdentifier, JSON.stringify([]));
65+
});
66+
67+
it('If Server projects is empty, should not update the localStorage', () => {
68+
serverProjects = [];
69+
spyOn(localStorage, 'getItem').and.returnValue(JSON.stringify(storageProjects));
70+
spyOn(localStorage, 'setItem');
71+
72+
updateProjectStorage(serverProjects);
73+
74+
expect(localStorage.setItem).toHaveBeenCalledTimes(0);
75+
});
76+
77+
it('If variables does not exists on localStorage, getProjectsOnStorage should return undefined', () => {
78+
projectsIdentifier = 'no-matter-identifier';
79+
spyOn(localStorage, 'getItem').and.returnValue(undefined);
80+
81+
const projects = getProjectsOnStorage(projectsIdentifier);
82+
83+
expect(projects).toBeUndefined();
84+
});
85+
86+
it('If variables not exists on localStorage, getProjectsOnStorage should return an array of Projects', () => {
87+
const storageProjectsString = JSON.stringify(storageProjects);
88+
spyOn(localStorage, 'getItem').and.returnValue(storageProjectsString);
89+
90+
const projects = getProjectsOnStorage(projectsIdentifier);
91+
92+
expect(projects).toEqual(JSON.parse(storageProjectsString));
93+
});
94+
});
95+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { Project } from '../models/project.model';
2+
import { PROJECTS_KEY_FOR_LOCAL_STORAGE } from '../../../../environments/environment';
3+
4+
const projectsKey = PROJECTS_KEY_FOR_LOCAL_STORAGE;
5+
6+
export function updateProjectStorage(serverProjects: Project[]): void {
7+
const storageProjects: Project[] = getProjectsOnStorage(projectsKey);
8+
const isServerProjectsNotEmpty = serverProjects && serverProjects.length !== 0;
9+
const updatedStorageProjects: Project[] = [];
10+
11+
if (serverProjects && isServerProjectsNotEmpty && storageProjects) {
12+
storageProjects.forEach((storageProject: Project) => {
13+
const project = serverProjects.find((serverProject) => serverProject.id === storageProject.id);
14+
15+
if (project) {
16+
updatedStorageProjects.push(project);
17+
}
18+
});
19+
20+
const projectsForLocalStorage = JSON.stringify(updatedStorageProjects);
21+
localStorage.setItem(projectsKey, projectsForLocalStorage);
22+
}
23+
}
24+
25+
export function getProjectsOnStorage(projectsIdentifier: string): Project[] {
26+
const projectsInsideLocalStorage: string = localStorage.getItem(projectsIdentifier);
27+
return projectsInsideLocalStorage && JSON.parse(projectsInsideLocalStorage);
28+
}
29+
30+

src/app/modules/time-clock/components/project-list-hover/project-list-hover.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
[data]="listProjects"
88
[searchKeyword]="keyword"
99
[initialValue]=""
10-
historyIdentifier="projectsSelected"
10+
[historyIdentifier]="projectKeyForLocalStorage"
1111
notFoundText="No projects found"
1212
placeHolder="Enter the project name"
1313
[itemTemplate]="itemTemplate"

src/app/modules/time-clock/components/project-list-hover/project-list-hover.component.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import { Activity, } from '../../../shared/models';
1818
import { LoadActivities } from './../../../activities-management/store/activity-management.actions';
1919
import { allActivities } from 'src/app/modules/activities-management/store/activity-management.selectors';
2020
import { head } from 'lodash';
21+
import { updateProjectStorage } from '../../../shared/utils/project-storage.util';
22+
import { PROJECTS_KEY_FOR_LOCAL_STORAGE } from '../../../../../environments/environment';
2123

2224
@Component({
2325
selector: 'app-project-list-hover',
@@ -36,6 +38,7 @@ export class ProjectListHoverComponent implements OnInit, OnDestroy {
3638
projectsSubscription: Subscription;
3739
activeEntrySubscription: Subscription;
3840
loadActivitiesSubscription: Subscription;
41+
projectKeyForLocalStorage = PROJECTS_KEY_FOR_LOCAL_STORAGE;
3942

4043
constructor(
4144
private formBuilder: FormBuilder,
@@ -57,6 +60,8 @@ export class ProjectListHoverComponent implements OnInit, OnDestroy {
5760
projectWithSearchField.search_field = `${project.customer.name} - ${project.name}`;
5861
this.listProjects.push(projectWithSearchField);
5962
});
63+
64+
updateProjectStorage(projects);
6065
this.loadActiveTimeEntry();
6166
});
6267
this.store.dispatch(new LoadActivities());

src/environments/environment.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ export const ROLES = {
3333
value: 'time-tracker-tester',
3434
},
3535
};
36+
37+
/*
38+
This variable is used by the ng-autocomplete component used in these files:
39+
- details-fielfs.component.ts
40+
- project-list-hover.component.ts
41+
The purpose is to store the latest projects in the Local Storage with the next key.
42+
*/
43+
export const PROJECTS_KEY_FOR_LOCAL_STORAGE = 'projectsSelected';
3644
/*
3745
* For easier debugging in development mode, you can import the following file
3846
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.

0 commit comments

Comments
 (0)