diff --git a/package-lock.json b/package-lock.json index 3f4a0bc73..99fbf6250 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "time-tracker", - "version": "1.47.1", + "version": "1.47.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 1c5667726..1b72fd6fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "time-tracker", - "version": "1.47.1", + "version": "1.47.2", "scripts": { "preinstall": "npx npm-force-resolutions", "ng": "ng", diff --git a/src/app/modules/shared/components/details-fields/details-fields.component.spec.ts b/src/app/modules/shared/components/details-fields/details-fields.component.spec.ts index 2fa11b8af..0db557b09 100644 --- a/src/app/modules/shared/components/details-fields/details-fields.component.spec.ts +++ b/src/app/modules/shared/components/details-fields/details-fields.component.spec.ts @@ -141,8 +141,8 @@ describe('DetailsFieldsComponent', () => { uri: 'ticketUri', start_date: '', end_date: '', - start_hour: '00:00:10', - end_hour: '00:00:11', + start_hour: '00:00', + end_hour: '00:00', description: '', technology: '', }; @@ -176,22 +176,23 @@ describe('DetailsFieldsComponent', () => { expect(component.saveEntry.emit).toHaveBeenCalledTimes(0); }); - [{ actionType: EntryActionTypes.CREATE_ENTRY_SUCCESS }, { actionType: EntryActionTypes.UPDATE_ENTRY_SUCCESS }].forEach( - (param) => { - it(`cleanForm after an action type ${param.actionType} is received`, () => { - const actionSubject = TestBed.inject(ActionsSubject) as ActionsSubject; - const action = { - type: param.actionType, - }; - spyOn(component, 'cleanForm'); + [ + { actionType: EntryActionTypes.CREATE_ENTRY_SUCCESS }, + { actionType: EntryActionTypes.UPDATE_ENTRY_SUCCESS }, + ].forEach((param) => { + it(`cleanForm after an action type ${param.actionType} is received`, () => { + const actionSubject = TestBed.inject(ActionsSubject) as ActionsSubject; + const action = { + type: param.actionType, + }; + spyOn(component, 'cleanForm'); - component.ngOnInit(); - actionSubject.next(action); + component.ngOnInit(); + actionSubject.next(action); - expect(component.cleanForm).toHaveBeenCalled(); - }); - } - ); + expect(component.cleanForm).toHaveBeenCalled(); + }); + }); it('on cleanFieldsForm the project_id and project_name should be kept', () => { const entryFormValueExpected = { @@ -295,8 +296,8 @@ describe('DetailsFieldsComponent', () => { uri: '', start_date: '2020-02-05', end_date: '2020-02-05', - start_hour: '00:00:01', - end_hour: '00:01:01', + start_hour: '00:00', + end_hour: '00:01', description: '', technology: '', }); @@ -309,8 +310,8 @@ describe('DetailsFieldsComponent', () => { activity_id: 'a1', technologies: [], description: '', - start_date: new Date('2020-02-05T00:00:01').toISOString(), - end_date: new Date('2020-02-05T00:01:01').toISOString(), + start_date: new Date('2020-02-05T00:00:00').toISOString(), + end_date: new Date('2020-02-05T00:01:00').toISOString(), uri: '', timezone_offset: new Date().getTimezoneOffset(), }, @@ -408,7 +409,7 @@ describe('DetailsFieldsComponent', () => { activity_id: 'a1', technologies: [], description: '', - start_date: new Date('2020-06-11T00:00:10').toISOString(), + start_date: new Date('2020-06-11T00:00:00').toISOString(), uri: 'ticketUri', timezone_offset: new Date().getTimezoneOffset(), }, @@ -652,6 +653,58 @@ describe('DetailsFieldsComponent', () => { componentToTest.onSubmit(); expect(toastrServiceStub.error).not.toHaveBeenCalled(); }); + + it('Should return a date in ISO format given a date, hour, minute, second and milisecond', () => { + const fakeDates = [ + { + date: '2021-04-20', + hourAndMinutes: '10:00', + seconds: 20, + miliseconds: 100, + }, + { + date: '2020-09-21', + hourAndMinutes: '08:00', + seconds: 10, + miliseconds: 100, + }, + { + date: '2021-04-15', + hourAndMinutes: '23:00', + seconds: 0, + miliseconds: 0, + }, + { + date: '2019-03-29', + hourAndMinutes: '12:00', + seconds: 30, + miliseconds: 400, + }, + ]; + + const expectedISODates = [ + new Date('2021-04-20T10:00:20.100').toISOString(), + new Date('2020-09-21T08:00:10.100').toISOString(), + new Date('2021-04-15T23:00:00.000').toISOString(), + new Date('2019-03-29T12:00:30.400').toISOString(), + ]; + + fakeDates.forEach(({ date, hourAndMinutes, seconds, miliseconds }, fakeDateIndex) => { + const dateInISOFormat = component.getDateISOFormat(date, hourAndMinutes, seconds, miliseconds); + expect(dateInISOFormat).toBe(expectedISODates[fakeDateIndex]); + }); + }); + + it('should return a number in ISO format given a normal number', () => { + const numbersForTest = [1, 2, 3, 4, 20, 30, 40, 32, 45]; + + const expectedISOFormatNumbers = ['01', '02', '03', '04', '20', '30', '40', '32', '45']; + + numbersForTest.forEach((currentNumber, numberIndex) => { + const numberinISOFormat = component.getNumberInISOFormat(currentNumber); + expect(numberinISOFormat).toBe(expectedISOFormatNumbers[numberIndex]); + }); + }); /* TODO As part of https://github.com/ioet/time-tracker-ui/issues/424 a new parameter was added to the details-field-component, and now these couple of tests are failing. A solution to this error might be generate a Test Wrapper Component. More details here: 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 0a1b6f7da..59b07a5ae 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 @@ -264,12 +264,37 @@ export class DetailsFieldsComponent implements OnChanges, OnInit { return startDate >= endDate; } - dateToSubmit(date, hour) { - const entryFormDate = this.entryForm.value[date]; - const updatedHour = this.entryForm.value[hour]; - const updatedDate = new Date(`${entryFormDate}T${updatedHour.trim()}`).toISOString(); + getDateISOFormat(date: string, hourAndMinutes: string, seconds: number, miliseconds: number): string { + hourAndMinutes = hourAndMinutes.trim(); + const secondsInISOFormat: string = this.getNumberInISOFormat(seconds); + const milisecondsInISOFormat: string = this.getNumberInISOFormat(miliseconds); + const ISOFormat = `${date}T${hourAndMinutes}:${secondsInISOFormat}.${milisecondsInISOFormat}`; + return new Date(ISOFormat).toISOString(); + } + + getNumberInISOFormat(numberToFormat: number): string { + const limitSingleNumber = 9; + const isNumberGreaterThanLimitNumber = numberToFormat > limitSingleNumber; + return isNumberGreaterThanLimitNumber ? numberToFormat.toString() : `0${numberToFormat}`; + } + + dateToSubmit(date: string, hour: string) { + const timeEntryISODate: string = get(this.entryToEdit, date); + let seconds = 0; + let miliseconds = 0; + + if (timeEntryISODate) { + const timeEntryDate: Date = new Date(timeEntryISODate); + seconds = timeEntryDate.getSeconds(); + miliseconds = timeEntryDate.getMilliseconds(); + } + + const newEntryDate: string = this.entryForm.value[date]; + const newEntryHour: string = this.entryForm.value[hour]; + + const updatedDate: string = this.getDateISOFormat(newEntryDate, newEntryHour, seconds, miliseconds); const initialDate = get(this.entryToEdit, date, updatedDate); - const dateHasNotChanged = (initialDate === updatedDate); + const dateHasNotChanged = initialDate === updatedDate; const result = dateHasNotChanged ? initialDate : updatedDate; return result; } diff --git a/src/app/modules/time-entries/pages/time-entries.component.html b/src/app/modules/time-entries/pages/time-entries.component.html index df2e1869b..1711ea0b8 100644 --- a/src/app/modules/time-entries/pages/time-entries.component.html +++ b/src/app/modules/time-entries/pages/time-entries.component.html @@ -42,7 +42,7 @@
- +
diff --git a/src/app/modules/time-entries/pages/time-entries.component.spec.ts b/src/app/modules/time-entries/pages/time-entries.component.spec.ts index 4ff687dd6..2406a1a59 100644 --- a/src/app/modules/time-entries/pages/time-entries.component.spec.ts +++ b/src/app/modules/time-entries/pages/time-entries.component.spec.ts @@ -668,11 +668,4 @@ describe('TimeEntriesComponent', () => { expect(HTMLTimeEntriesView).not.toBeNull(); }); - - it('after the component is initialized it should initialize the table', () => { - spyOn(component.dtTrigger, 'next'); - component.ngAfterViewInit(); - - expect(component.dtTrigger.next).toHaveBeenCalled(); - }); }); 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 3fdc19aba..7ba274bbe 100644 --- a/src/app/modules/time-entries/pages/time-entries.component.ts +++ b/src/app/modules/time-entries/pages/time-entries.component.ts @@ -1,7 +1,7 @@ -import { Component, OnDestroy, OnInit, ViewChild, AfterViewInit } from '@angular/core'; +import { Component, OnDestroy, OnInit } from '@angular/core'; import { ActionsSubject, select, Store } from '@ngrx/store'; import { ToastrService } from 'ngx-toastr'; -import { Observable, Subscription, Subject } from 'rxjs'; +import { Observable, Subscription } from 'rxjs'; import { delay, filter } from 'rxjs/operators'; import { ProjectSelectedEvent } from '../../shared/components/details-fields/project-selected-event'; import { SaveEntryEvent } from '../../shared/components/details-fields/save-entry-event'; @@ -14,13 +14,12 @@ import { EntryActionTypes } from './../../time-clock/store/entry.actions'; import { getActiveTimeEntry, getTimeEntriesDataSource } from './../../time-clock/store/entry.selectors'; import { CookieService } from 'ngx-cookie-service'; import { FeatureToggle } from './../../../../environments/enum'; -import { DataTableDirective } from 'angular-datatables'; @Component({ selector: 'app-time-entries', templateUrl: './time-entries.component.html', styleUrls: ['./time-entries.component.scss'], }) -export class TimeEntriesComponent implements OnInit, OnDestroy, AfterViewInit { +export class TimeEntriesComponent implements OnInit, OnDestroy { entryId: string; entry: Entry; activeTimeEntry: Entry; @@ -39,11 +38,6 @@ export class TimeEntriesComponent implements OnInit, OnDestroy, AfterViewInit { selectedYear: number; selectedMonthAsText: string; isActiveEntryOverlapping = false; - dtOptions: any = {}; - dtTrigger: Subject = new Subject(); - @ViewChild(DataTableDirective, { static: false }) - dtElement: DataTableDirective; - rerenderTableSubscription: Subscription; constructor( private store: Store, private toastrService: ToastrService, @@ -55,18 +49,8 @@ export class TimeEntriesComponent implements OnInit, OnDestroy, AfterViewInit { } ngOnDestroy(): void { this.entriesSubscription.unsubscribe(); - this.rerenderTableSubscription.unsubscribe(); - this.dtTrigger.unsubscribe(); } ngOnInit(): void { - this.dtOptions = { - scrollY: '325px', - paging: false, - responsive: true, - }; - this.rerenderTableSubscription = this.timeEntriesDataSource$.subscribe((ds) => { - this.rerenderDataTable(); - }); this.loadActiveEntry(); this.isFeatureToggleCalendarActive = (this.cookiesService.get(FeatureToggle.TIME_TRACKER_CALENDAR) === 'true'); this.entriesSubscription = this.actionsSubject$.pipe( @@ -81,9 +65,6 @@ export class TimeEntriesComponent implements OnInit, OnDestroy, AfterViewInit { this.store.dispatch(new entryActions.LoadEntries(this.selectedMonth, this.selectedYear)); }); } - ngAfterViewInit(): void { - this.rerenderDataTable(); - } newEntry() { if (this.wasEditingExistingTimeEntry) { this.entry = null; @@ -235,15 +216,4 @@ export class TimeEntriesComponent implements OnInit, OnDestroy, AfterViewInit { }); } } - - private rerenderDataTable(): void { - if (this.dtElement && this.dtElement.dtInstance) { - this.dtElement.dtInstance.then((dtInstance: DataTables.Api) => { - dtInstance.destroy(); - this.dtTrigger.next(); - }); - } else { - this.dtTrigger.next(); - } - } }
Date