diff --git a/package-lock.json b/package-lock.json index 64e6105da..618f72b72 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "time-tracker", - "version": "1.31.13", + "version": "1.31.15", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -4589,15 +4589,6 @@ "integrity": "sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==", "dev": true }, - "better-assert": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", - "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", - "dev": true, - "requires": { - "callsite": "1.0.0" - } - }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -5032,12 +5023,6 @@ "caller-callsite": "^2.0.0" } }, - "callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", - "dev": true - }, "callsites": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", @@ -7118,64 +7103,6 @@ "once": "^1.4.0" } }, - "engine.io": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.2.tgz", - "integrity": "sha512-b4Q85dFkGw+TqgytGPrGgACRUhsdKc9S9ErRAXpPGy/CXKs4tYoHDkvIRdsseAF7NjfVwjRFIn6KTnbw7LwJZg==", - "dev": true, - "requires": { - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "0.3.1", - "debug": "~4.1.0", - "engine.io-parser": "~2.2.0", - "ws": "^7.1.2" - }, - "dependencies": { - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", - "dev": true - }, - "ws": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", - "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==", - "dev": true - } - } - }, - "engine.io-client": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.3.tgz", - "integrity": "sha512-0NGY+9hioejTEJCaSJZfWZLk4FPI9dN+1H1C4+wj2iuFba47UgZbJzfWs4aNFajnX/qAaYKbe2lLTfEEWzCmcw==", - "dev": true, - "requires": { - "component-emitter": "~1.3.0", - "component-inherit": "0.0.3", - "debug": "~4.1.0", - "engine.io-parser": "~2.2.0", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "ws": "~6.1.0", - "xmlhttprequest-ssl": "~1.5.4", - "yeast": "0.1.2" - }, - "dependencies": { - "ws": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", - "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } - } - } - }, "engine.io-parser": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz", @@ -15315,12 +15242,6 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, - "object-component": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", - "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", - "dev": true - }, "object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", @@ -15841,24 +15762,6 @@ "parse5": "^6.0.1" } }, - "parseqs": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", - "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", - "dev": true, - "requires": { - "better-assert": "~1.0.0" - } - }, - "parseuri": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", - "dev": true, - "requires": { - "better-assert": "~1.0.0" - } - }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -18930,53 +18833,69 @@ } }, "socket.io": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz", - "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.4.1.tgz", + "integrity": "sha512-Si18v0mMXGAqLqCVpTxBa8MGqriHGQh8ccEOhmsmNS3thNCGBwO8WGrwMibANsWtQQ5NStdZwHqZR3naJVFc3w==", "dev": true, "requires": { "debug": "~4.1.0", - "engine.io": "~3.4.0", + "engine.io": "~3.5.0", "has-binary2": "~1.0.2", "socket.io-adapter": "~1.1.0", - "socket.io-client": "2.3.0", + "socket.io-client": "2.4.0", "socket.io-parser": "~3.4.0" - } - }, - "socket.io-adapter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", - "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==", - "dev": true - }, - "socket.io-client": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz", - "integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==", - "dev": true, - "requires": { - "backo2": "1.0.2", - "base64-arraybuffer": "0.1.5", - "component-bind": "1.0.0", - "component-emitter": "1.2.1", - "debug": "~4.1.0", - "engine.io-client": "~3.4.0", - "has-binary2": "~1.0.2", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "object-component": "0.0.3", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "socket.io-parser": "~3.3.0", - "to-array": "0.1.4" }, "dependencies": { - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", "dev": true }, + "engine.io": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.5.0.tgz", + "integrity": "sha512-21HlvPUKaitDGE4GXNtQ7PLP0Sz4aWLddMPw2VTyFz1FVZqu/kZsJUO8WNpKuE/OCL7nkfRaOui2ZCJloGznGA==", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "debug": "~4.1.0", + "engine.io-parser": "~2.2.0", + "ws": "~7.4.2" + } + }, + "engine.io-client": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.0.tgz", + "integrity": "sha512-12wPRfMrugVw/DNyJk34GQ5vIVArEcVMXWugQGGuw2XxUSztFNmJggZmv8IZlLyEdnpO1QB9LkcjeWewO2vxtA==", + "dev": true, + "requires": { + "component-emitter": "~1.3.0", + "component-inherit": "0.0.3", + "debug": "~3.1.0", + "engine.io-parser": "~2.2.0", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parseqs": "0.0.6", + "parseuri": "0.0.6", + "ws": "~7.4.2", + "xmlhttprequest-ssl": "~1.5.4", + "yeast": "0.1.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, "isarray": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", @@ -18989,15 +18908,35 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, - "socket.io-parser": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz", - "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==", + "parseqs": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", + "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==", + "dev": true + }, + "parseuri": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", + "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==", + "dev": true + }, + "socket.io-client": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.4.0.tgz", + "integrity": "sha512-M6xhnKQHuuZd4Ba9vltCLT9oa+YvTsP8j9NcEiLElfIg8KeYPyhWOes6x4t+LTAC8enQbE/995AdTem2uNyKKQ==", "dev": true, "requires": { - "component-emitter": "1.2.1", + "backo2": "1.0.2", + "component-bind": "1.0.0", + "component-emitter": "~1.3.0", "debug": "~3.1.0", - "isarray": "2.0.1" + "engine.io-client": "~3.5.0", + "has-binary2": "~1.0.2", + "indexof": "0.0.1", + "parseqs": "0.0.6", + "parseuri": "0.0.6", + "socket.io-parser": "~3.3.0", + "to-array": "0.1.4" }, "dependencies": { "debug": { @@ -19008,11 +18947,34 @@ "requires": { "ms": "2.0.0" } + }, + "socket.io-parser": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.2.tgz", + "integrity": "sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg==", + "dev": true, + "requires": { + "component-emitter": "~1.3.0", + "debug": "~3.1.0", + "isarray": "2.0.1" + } } } + }, + "ws": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz", + "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==", + "dev": true } } }, + "socket.io-adapter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", + "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==", + "dev": true + }, "socket.io-parser": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.1.tgz", diff --git a/package.json b/package.json index 5f8cba041..e6e67605a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "time-tracker", - "version": "1.31.13", + "version": "1.31.15", "scripts": { "preinstall": "npx npm-force-resolutions", "ng": "ng", diff --git a/src/app/modules/activities-management/components/activity-list/activity-list.component.html b/src/app/modules/activities-management/components/activity-list/activity-list.component.html index 903587928..d366fd3d5 100644 --- a/src/app/modules/activities-management/components/activity-list/activity-list.component.html +++ b/src/app/modules/activities-management/components/activity-list/activity-list.component.html @@ -2,17 +2,17 @@ - - + + - - - + +
Activity IDActivityActivity IDActivity Options
{{ activity.id }}{{ activity.name }} + {{ activity.id }}{{ activity.name }} diff --git a/src/app/modules/shared/components/details-fields/details-fields.component.scss b/src/app/modules/shared/components/details-fields/details-fields.component.scss index 4ef9f488b..88b00d6e7 100644 --- a/src/app/modules/shared/components/details-fields/details-fields.component.scss +++ b/src/app/modules/shared/components/details-fields/details-fields.component.scss @@ -93,3 +93,13 @@ input[type="date"]::-webkit-clear-button { border: none; margin: 0em; } + +.valid ::ng-deep .autocomplete-container ::ng-deep .input-container ::ng-deep input { + border: 1px solid $primary; + border-radius: 0.25rem; +} + +.timepicker-input ::ng-deep div{ + border-radius: 0.25rem; +} + 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 5bc62652d..5a0434d8a 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 @@ -246,6 +246,12 @@ export class DetailsFieldsComponent implements OnChanges, OnInit { this.saveEntry.emit({ entry, shouldRestartEntry: this.shouldRestartEntry }); } + onclickFormAction(isProjectSelected: boolean){ + if (isProjectSelected){ + this.toastrService.warning('Please, first select a project'); + } + } + onGoingToWorkOnThisChange(event: any) { this.goingToWorkOnThis = event.currentTarget.checked; if (!this.goingToWorkOnThis) { diff --git a/src/app/modules/time-clock/components/entry-fields/entry-fields.component.spec.ts b/src/app/modules/time-clock/components/entry-fields/entry-fields.component.spec.ts index 4764ac6a8..aa588c670 100644 --- a/src/app/modules/time-clock/components/entry-fields/entry-fields.component.spec.ts +++ b/src/app/modules/time-clock/components/entry-fields/entry-fields.component.spec.ts @@ -1,9 +1,9 @@ +import { Subscription } from 'rxjs'; import { LoadActiveEntry, EntryActionTypes, UpdateEntry } from './../../store/entry.actions'; import { ActivityManagementActionTypes } from './../../../activities-management/store/activity-management.actions'; import {waitForAsync, ComponentFixture, TestBed} from '@angular/core/testing'; import {MockStore, provideMockStore} from '@ngrx/store/testing'; import { FormsModule, ReactiveFormsModule, FormBuilder } from '@angular/forms'; - import {TechnologyState} from '../../../shared/store/technology.reducers'; import {allTechnologies} from '../../../shared/store/technology.selectors'; import {EntryFieldsComponent} from './entry-fields.component'; @@ -135,9 +135,7 @@ describe('EntryFieldsComponent', () => { }; spyOn(component.entryForm, 'patchValue'); - component.setDataToUpdate(entry); - expect(component.entryForm.patchValue).toHaveBeenCalledTimes(1); expect(component.entryForm.patchValue).toHaveBeenCalledWith( { @@ -156,6 +154,7 @@ describe('EntryFieldsComponent', () => { start_date : moment().format(DATE_FORMAT_YEAR), start_hour : moment().format('HH:mm') }; + component.newData = mockEntry; component.activeEntry = mockEntry ; component.setDataToUpdate(mockEntry); @@ -412,4 +411,18 @@ describe('EntryFieldsComponent', () => { expect(component.selectedTechnologies).toBe(initialTechnologies); }); + it('calls unsubscribe on ngDestroy', () => { + component.loadActivitiesSubscription = new Subscription(); + component.loadActiveEntrySubscription = new Subscription(); + component.actionSetDateSubscription = new Subscription(); + spyOn(component.loadActivitiesSubscription, 'unsubscribe'); + spyOn(component.loadActiveEntrySubscription, 'unsubscribe'); + spyOn(component.actionSetDateSubscription, 'unsubscribe'); + + component.ngOnDestroy(); + + expect(component.loadActivitiesSubscription.unsubscribe).toHaveBeenCalled(); + expect(component.loadActiveEntrySubscription.unsubscribe).toHaveBeenCalled(); + expect(component.actionSetDateSubscription.unsubscribe).toHaveBeenCalled(); + }); }); diff --git a/src/app/modules/time-clock/components/entry-fields/entry-fields.component.ts b/src/app/modules/time-clock/components/entry-fields/entry-fields.component.ts index f6160a48f..a5481281e 100644 --- a/src/app/modules/time-clock/components/entry-fields/entry-fields.component.ts +++ b/src/app/modules/time-clock/components/entry-fields/entry-fields.component.ts @@ -1,14 +1,13 @@ import { ActivityManagementActionTypes } from './../../../activities-management/store/activity-management.actions'; import { EntryActionTypes, LoadActiveEntry } from './../../store/entry.actions'; import { filter } from 'rxjs/operators'; -import { Component, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; import { Store, ActionsSubject, select } from '@ngrx/store'; import { Activity, NewEntry } from '../../../shared/models'; import { ProjectState } from '../../../customer-management/components/projects/components/store/project.reducer'; import { TechnologyState } from '../../../shared/store/technology.reducers'; import { ActivityState, LoadActivities } from '../../../activities-management/store'; - import * as entryActions from '../../store/entry.actions'; import { get } from 'lodash'; import * as moment from 'moment'; @@ -16,6 +15,7 @@ import { ToastrService } from 'ngx-toastr'; import { formatDate } from '@angular/common'; import { getTimeEntriesDataSource } from '../../store/entry.selectors'; import { DATE_FORMAT } from 'src/environments/environment'; +import { Subscription } from 'rxjs'; type Merged = TechnologyState & ProjectState & ActivityState; @@ -24,7 +24,7 @@ type Merged = TechnologyState & ProjectState & ActivityState; templateUrl: './entry-fields.component.html', styleUrls: ['./entry-fields.component.scss'], }) -export class EntryFieldsComponent implements OnInit { +export class EntryFieldsComponent implements OnInit, OnDestroy { entryForm: FormGroup; selectedTechnologies: string[] = []; activities: Activity[] = []; @@ -32,6 +32,9 @@ export class EntryFieldsComponent implements OnInit { newData; lastEntry; showTimeInbuttons = false; + loadActivitiesSubscription: Subscription; + loadActiveEntrySubscription: Subscription; + actionSetDateSubscription: Subscription; constructor( private formBuilder: FormBuilder, @@ -48,17 +51,16 @@ export class EntryFieldsComponent implements OnInit { }); } - ngOnInit(): void { + ngOnInit(): void { this.store.dispatch(new LoadActivities()); this.store.dispatch(new entryActions.LoadEntries(new Date().getMonth() + 1, new Date().getFullYear())); - this.actionsSubject$ + this.loadActivitiesSubscription = this.actionsSubject$ .pipe(filter((action: any) => action.type === ActivityManagementActionTypes.LOAD_ACTIVITIES_SUCCESS)) .subscribe((action) => { this.activities = action.payload; this.store.dispatch(new LoadActiveEntry()); }); - - this.actionsSubject$ + this.loadActiveEntrySubscription = this.actionsSubject$ .pipe( filter( (action: any) => @@ -76,8 +78,7 @@ export class EntryFieldsComponent implements OnInit { this.store.dispatch(new entryActions.LoadEntriesSummary()); } }); - - this.actionsSubject$ + this.actionSetDateSubscription = this.actionsSubject$ .pipe(filter((action: any) => action.type === EntryActionTypes.LOAD_ACTIVE_ENTRY_SUCCESS)) .subscribe((action) => { this.activeEntry = action.payload; @@ -90,17 +91,14 @@ export class EntryFieldsComponent implements OnInit { start_date: this.activeEntry.start_date, start_hour: formatDate(this.activeEntry.start_date, 'HH:mm', 'en'), }; - }); + }); } - get activity_id() { return this.entryForm.get('activity_id'); } - get start_hour() { return this.entryForm.get('start_hour'); } - setDataToUpdate(entryData: NewEntry) { if (entryData) { this.entryForm.patchValue({ @@ -174,4 +172,10 @@ export class EntryFieldsComponent implements OnInit { onTechnologyRemoved($event: string[]) { this.store.dispatch(new entryActions.UpdateEntryRunning({ ...this.newData, technologies: $event })); } + + ngOnDestroy(): void { + this.loadActivitiesSubscription.unsubscribe(); + this.loadActiveEntrySubscription.unsubscribe(); + this.actionSetDateSubscription.unsubscribe(); + } } diff --git a/src/app/modules/time-clock/components/project-list-hover/project-list-hover.component.spec.ts b/src/app/modules/time-clock/components/project-list-hover/project-list-hover.component.spec.ts index c7dd88ef4..e81dd76dc 100644 --- a/src/app/modules/time-clock/components/project-list-hover/project-list-hover.component.spec.ts +++ b/src/app/modules/time-clock/components/project-list-hover/project-list-hover.component.spec.ts @@ -5,7 +5,7 @@ import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; import { provideMockStore, MockStore } from '@ngrx/store/testing'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { AutocompleteLibModule } from 'angular-ng-autocomplete'; -import { Subscription } from 'rxjs'; +import { Subscription, of } from 'rxjs'; import { ProjectState } from '../../../customer-management/components/projects/components/store/project.reducer'; import { getCustomerProjects } from '../../../customer-management/components/projects/components/store/project.selectors'; import { FilterProjectPipe } from '../../../shared/pipes'; @@ -100,11 +100,17 @@ describe('ProjectListHoverComponent', () => { it('calls unsubscribe on ngDestroy', () => { component.updateEntrySubscription = new Subscription(); + component.projectsSubscription = new Subscription(); + component.activeEntrySubscription = new Subscription(); spyOn(component.updateEntrySubscription, 'unsubscribe'); + spyOn(component.projectsSubscription, 'unsubscribe'); + spyOn(component.activeEntrySubscription, 'unsubscribe'); component.ngOnDestroy(); expect(component.updateEntrySubscription.unsubscribe).toHaveBeenCalled(); + expect(component.projectsSubscription.unsubscribe).toHaveBeenCalled(); + expect(component.activeEntrySubscription.unsubscribe).toHaveBeenCalled(); }); it('sets customer name and project name on setSelectedProject', () => { @@ -115,10 +121,9 @@ describe('ProjectListHoverComponent', () => { component.setSelectedProject(); expect(component.projectsForm.setValue) - .toHaveBeenCalledWith({ project_id: 'customer - xyz'}); + .toHaveBeenCalledWith({ project_id: 'customer - xyz'}); }); - // TODO Fix this test since it is throwing this error // Expected spy dispatch to have been called with: // [CreateEntry({ payload: Object({ project_id: '1', start_date: '2020-07-27T22:30:26.743Z', timezone_offset: 300 }), @@ -140,5 +145,4 @@ describe('ProjectListHoverComponent', () => { // }) // ); // }); - }); 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 6213e3ca7..c238d63bf 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 @@ -3,7 +3,7 @@ import { FormBuilder, FormGroup } from '@angular/forms'; import { ActionsSubject, select, Store } from '@ngrx/store'; import { ToastrService } from 'ngx-toastr'; import { Observable, Subscription } from 'rxjs'; -import { delay, filter } from 'rxjs/operators'; +import { delay, filter, map } from 'rxjs/operators'; import { Project } from 'src/app/modules/shared/models'; import * as actions from '../../../customer-management/components/projects/components/store/project.actions'; import { ProjectState } from '../../../customer-management/components/projects/components/store/project.reducer'; @@ -17,7 +17,6 @@ import { getActiveTimeEntry } from './../../store/entry.selectors'; styleUrls: ['./project-list-hover.component.scss'], }) export class ProjectListHoverComponent implements OnInit, OnDestroy { - keyword = 'search_field'; listProjects: Project[] = []; activeEntry; @@ -25,6 +24,8 @@ export class ProjectListHoverComponent implements OnInit, OnDestroy { showClockIn: boolean; updateEntrySubscription: Subscription; isLoading$: Observable; + projectsSubscription: Subscription; + activeEntrySubscription: Subscription; constructor(private formBuilder: FormBuilder, private store: Store, private actionsSubject$: ActionsSubject, private toastrService: ToastrService) { @@ -32,10 +33,10 @@ export class ProjectListHoverComponent implements OnInit, OnDestroy { this.isLoading$ = this.store.pipe(delay(0), select(getIsLoading)); } - ngOnInit(): void { + ngOnInit(): void { this.store.dispatch(new actions.LoadProjects()); const projects$ = this.store.pipe(select(getProjects)); - projects$.subscribe((projects) => { + this.projectsSubscription = projects$.subscribe((projects) => { this.listProjects = []; projects.forEach((project) => { const projectWithSearchField = {...project}; @@ -45,7 +46,6 @@ export class ProjectListHoverComponent implements OnInit, OnDestroy { ); this.loadActiveTimeEntry(); }); - this.updateEntrySubscription = this.actionsSubject$.pipe( filter((action: any) => ( action.type === EntryActionTypes.UPDATE_ENTRY_SUCCESS @@ -55,13 +55,12 @@ export class ProjectListHoverComponent implements OnInit, OnDestroy { this.activeEntry = action.payload; this.setSelectedProject(); }); - } loadActiveTimeEntry() { this.store.dispatch(new entryActions.LoadActiveEntry()); const activeEntry$ = this.store.pipe(select(getActiveTimeEntry)); - activeEntry$.subscribe((activeEntry) => { + this.activeEntrySubscription = activeEntry$.subscribe((activeEntry) => { this.activeEntry = activeEntry; if (activeEntry) { this.showClockIn = false; @@ -72,13 +71,12 @@ export class ProjectListHoverComponent implements OnInit, OnDestroy { } }); } - setSelectedProject() { this.listProjects.forEach( (project) => { if (project.id === this.activeEntry.project_id) { this.projectsForm.setValue( { project_id: `${project.customer_name} - ${project.name}`, } - ); + ); } }); } @@ -110,6 +108,8 @@ export class ProjectListHoverComponent implements OnInit, OnDestroy { } ngOnDestroy(): void { + this.projectsSubscription.unsubscribe(); + this.activeEntrySubscription.unsubscribe(); this.updateEntrySubscription.unsubscribe(); } } diff --git a/src/app/modules/time-clock/pages/time-clock.component.spec.ts b/src/app/modules/time-clock/pages/time-clock.component.spec.ts index 1fa2476ee..10a4b5959 100644 --- a/src/app/modules/time-clock/pages/time-clock.component.spec.ts +++ b/src/app/modules/time-clock/pages/time-clock.component.spec.ts @@ -1,3 +1,4 @@ +import { of } from 'rxjs'; import { FormBuilder } from '@angular/forms'; import { StopTimeEntryRunning, EntryActionTypes, LoadEntriesSummary } from './../store/entry.actions'; import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; @@ -95,12 +96,14 @@ describe('TimeClockComponent', () => { expect(component.reloadSummariesOnClockOut).toHaveBeenCalled(); }); - it('unsubscribe clockOutSubscription onDestroy', () => { + it('unsubscribe clockOutSubscription, storeSubscription onDestroy', () => { spyOn(component.clockOutSubscription, 'unsubscribe'); + spyOn(component.storeSubscription, 'unsubscribe'); component.ngOnDestroy(); expect(component.clockOutSubscription.unsubscribe).toHaveBeenCalled(); + expect(component.storeSubscription.unsubscribe).toHaveBeenCalled(); }); it('onInit checks if isLogin and gets the userName', () => { diff --git a/src/app/modules/time-clock/pages/time-clock.component.ts b/src/app/modules/time-clock/pages/time-clock.component.ts index ac57d88aa..30cbbb994 100644 --- a/src/app/modules/time-clock/pages/time-clock.component.ts +++ b/src/app/modules/time-clock/pages/time-clock.component.ts @@ -2,7 +2,9 @@ import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { ActionsSubject, select, Store } from '@ngrx/store'; import { ToastrService } from 'ngx-toastr'; import { Subscription } from 'rxjs'; -import { filter } from 'rxjs/operators'; + +import { filter, map } from 'rxjs/operators'; +import { threadId } from 'worker_threads'; import { AzureAdB2CService } from '../../login/services/azure.ad.b2c.service'; import { EntryFieldsComponent } from '../components/entry-fields/entry-fields.component'; import { Entry } from './../../shared/models/entry.model'; @@ -20,7 +22,7 @@ export class TimeClockComponent implements OnInit, OnDestroy { areFieldsVisible = false; activeTimeEntry: Entry; clockOutSubscription: Subscription; - + storeSubscription: Subscription; constructor( private azureAdB2CService: AzureAdB2CService, @@ -29,13 +31,9 @@ export class TimeClockComponent implements OnInit, OnDestroy { private actionsSubject$: ActionsSubject ) {} - ngOnDestroy(): void { - this.clockOutSubscription.unsubscribe(); - } - - ngOnInit() { + ngOnInit(): void { this.username = this.azureAdB2CService.isLogin() ? this.azureAdB2CService.getName() : ''; - this.store.pipe(select(getActiveTimeEntry)).subscribe((activeTimeEntry) => { + this.storeSubscription = this.store.pipe(select(getActiveTimeEntry)).subscribe((activeTimeEntry) => { this.activeTimeEntry = activeTimeEntry; if (this.activeTimeEntry) { this.areFieldsVisible = true; @@ -43,9 +41,7 @@ export class TimeClockComponent implements OnInit, OnDestroy { this.areFieldsVisible = false; } }); - this.reloadSummariesOnClockOut(); - } reloadSummariesOnClockOut() { @@ -72,4 +68,12 @@ export class TimeClockComponent implements OnInit, OnDestroy { this.toastrService.error('Activity is required'); } } + + ngOnDestroy(): void { + this.clockOutSubscription.unsubscribe(); + this.storeSubscription.unsubscribe(); + } + } + + diff --git a/src/app/modules/time-entries/pages/time-entries.component.ts b/src/app/modules/time-entries/pages/time-entries.component.ts index 3c2ef721f..d21f64303 100644 --- a/src/app/modules/time-entries/pages/time-entries.component.ts +++ b/src/app/modules/time-entries/pages/time-entries.component.ts @@ -128,13 +128,13 @@ export class TimeEntriesComponent implements OnInit, OnDestroy { if (dataToUse && this.isNewEntry()) { const startDate = new Date(new Date().setHours(0, 0, 0, 0)); const entry = { - description : dataToUse.description ? dataToUse.description : '', - technologies : dataToUse.technologies ? dataToUse.technologies : [], - uri : dataToUse.uri ? dataToUse.uri : '', - activity_id : dataToUse.activity_id, - project_id : dataToUse.project_id, - start_date : startDate, - end_date : startDate + description: dataToUse.description ? dataToUse.description : '', + technologies: dataToUse.technologies ? dataToUse.technologies : [], + uri: dataToUse.uri ? dataToUse.uri : '', + activity_id: dataToUse.activity_id, + project_id: dataToUse.project_id, + start_date: startDate, + end_date: startDate }; this.entry = entry; }