Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
feat: TT-314 Synchronize local projects with Server projects
  • Loading branch information
jcalarcon98 committed Aug 21, 2021
commit eb8d2525ff1ddb526f36ac14c127b024faeedc3e
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -49,6 +51,7 @@ export class DetailsFieldsComponent implements OnChanges, OnInit {
activities$: Observable<Activity[]>;
goingToWorkOnThis = false;
shouldRestartEntry = false;
projectKeyForLocalStorage = PROJECTS_KEY_FOR_LOCAL_STORAGE;

constructor(
private formBuilder: FormBuilder,
Expand Down Expand Up @@ -81,6 +84,8 @@ export class DetailsFieldsComponent implements OnChanges, OnInit {
projectWithSearchField.search_field = `${project.customer.name} - ${project.name}`;
this.listProjects.push(projectWithSearchField);
});

updateProjectStorage(projects);
}
});

Expand Down
95 changes: 95 additions & 0 deletions src/app/modules/shared/utils/project-storage.util.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { updateProjectStorage, getProjectsOnStorage } from './project-storage.util';
import { Project } from '../models/project.model';

describe('Project Storage', () => {

let storageProjects: Project[];
let projectsIdentifier: string;
let serverProjects: Project[];

beforeEach(() => {
projectsIdentifier = 'projectsSelected';

storageProjects = [
{
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',
}
];

serverProjects = [
{
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',
},
{
customer: {
name: 'No Matter Name',
},
id: 'no-matter-id',
name: 'Warby Parker',
customer_id: 'no-matter-id',
status: 'active',
}
];

});

it('If exists projects in localStorage and the server returns the same project, should keep the same localStorage variables', () => {
spyOn(localStorage, 'getItem').and.returnValue(JSON.stringify(storageProjects));
spyOn(localStorage, 'setItem');

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();
spyOn(localStorage, 'getItem').and.returnValue(JSON.stringify(storageProjects));
spyOn(localStorage, 'setItem');

updateProjectStorage(serverProjects);

expect(localStorage.setItem).toHaveBeenCalledWith(projectsIdentifier, JSON.stringify([]));
});

it('If Server projects is empty, should not update the localStorage', () => {
serverProjects = [];
spyOn(localStorage, 'getItem').and.returnValue(JSON.stringify(storageProjects));
spyOn(localStorage, 'setItem');

updateProjectStorage(serverProjects);

expect(localStorage.setItem).toHaveBeenCalledTimes(0);
});

it('If variables does not exists on localStorage, getProjectsOnStorage should return undefined', () => {
projectsIdentifier = 'no-matter-identifier';
spyOn(localStorage, 'getItem').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);
spyOn(localStorage, 'getItem').and.returnValue(storageProjectsString);

const projects = getProjectsOnStorage(projectsIdentifier);

expect(projects).toEqual(JSON.parse(storageProjectsString));
});
});

30 changes: 30 additions & 0 deletions src/app/modules/shared/utils/project-storage.util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Project } from '../models/project.model';
import { PROJECTS_KEY_FOR_LOCAL_STORAGE } from '../../../../environments/environment';

const projectsKey = PROJECTS_KEY_FOR_LOCAL_STORAGE;

export function updateProjectStorage(serverProjects: Project[]): void {
const storageProjects: Project[] = getProjectsOnStorage(projectsKey);
const isServerProjectsNotEmpty = serverProjects && serverProjects.length !== 0;
const updatedStorageProjects: Project[] = [];

if (serverProjects && isServerProjectsNotEmpty && 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);
}


Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
[data]="listProjects"
[searchKeyword]="keyword"
[initialValue]=""
historyIdentifier="projectsSelected"
[historyIdentifier]="projectKeyForLocalStorage"
notFoundText="No projects found"
placeHolder="Enter the project name"
[itemTemplate]="itemTemplate"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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,
Expand All @@ -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());
Expand Down
8 changes: 8 additions & 0 deletions src/environments/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ export const ROLES = {
value: 'time-tracker-tester',
},
};

/*
This variable is used by the ng-autocomplete component used in these files:
- details-fielfs.component.ts
- project-list-hover.component.ts
The purpose is to store the latest projects in the Local Storage with the next key.
*/
export const PROJECTS_KEY_FOR_LOCAL_STORAGE = 'projectsSelected';
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
Expand Down