From eeff6343c5c0864171aa76b66de6a4fa21d4897e Mon Sep 17 00:00:00 2001 From: Santiago Pozo Ruiz <38196801+DrFreud1@users.noreply.github.com> Date: Tue, 10 Aug 2021 17:24:25 -0500 Subject: [PATCH 1/5] Tt 305 tags semantic release (#717) * fix: TT-299 removal of variables and tests which are no longer neccessary * Revert "fix: TT-299 removal of variables and tests which are no longer neccessary" This reverts commit a49eb5ef34177cdfaefbc4ad215dd3864bcb76d7. * refactor: TT-305 add code-smell tag for semantic release * code-smell: TT-305 test code smell tag --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 7ac6015d7..b76ca4f1d 100644 --- a/package.json +++ b/package.json @@ -106,8 +106,8 @@ }, "config": { "commit-message-validator": { - "pattern": "^(fix: TT-|feat: TT-|perf: TT-|build: TT-|ci: TT-|docs: TT-|refactor: TT-|style: TT-|test: TT-)[0-9].*", - "errorMessage": "Your commit message needs to start with fix: , feat:, or perf: followed by any commit message, e.g. fix: TT-43 any commit message" + "pattern": "^(fix: TT-|feat: TT-|perf: TT-|build: TT-|ci: TT-|docs: TT-|refactor: TT-|style: TT-|test: TT-|code-smell: TT-)[0-9].*", + "errorMessage": "Your commit message needs to start with fix: , feat:, or perf: followed by any commit message, e.g. fix: TT-43 any commit message." } }, "resolutions": { From 8fa17b27e04b46958933ca8c0725fead4dc30dea Mon Sep 17 00:00:00 2001 From: Jean Pierre Alvarado Date: Tue, 10 Aug 2021 17:37:45 -0500 Subject: [PATCH 2/5] feat: TT-296 add scroll to the current time in calendar component (#715) * feat: TT-296 add scroll to the current time in calendar component * feat: TT-296 The code was modified with the suggestions in the PR * feat: TT-296 The Code Smells were corrected --- .../calendar/calendar.component.html | 2 +- .../calendar/calendar.component.scss | 23 ++++ .../calendar/calendar.component.spec.ts | 86 ++++++++------- .../components/calendar/calendar.component.ts | 100 +++++++++++------- 4 files changed, 135 insertions(+), 76 deletions(-) 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 08164362c..f2364ddc3 100644 --- a/src/app/modules/time-entries/components/calendar/calendar.component.html +++ b/src/app/modules/time-entries/components/calendar/calendar.component.html @@ -128,7 +128,7 @@ -
+
{ let fakeEntry: Entry; let fakeEntryRunning: Entry; - beforeEach(waitForAsync( () => { - TestBed.configureTestingModule({ - declarations: [ CalendarComponent ] + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [CalendarComponent], + }).compileComponents(); + + currentDate = moment(); + fakeEntry = { + id: 'entry_1', + project_id: 'abc', + project_name: 'Time-tracker', + start_date: new Date('2020-02-05T15:36:15.887Z'), + end_date: new Date('2020-02-05T18:36:15.887Z'), + customer_name: 'ioet Inc.', + activity_id: 'development', + technologies: ['Angular', 'TypeScript'], + description: 'No comments', + uri: 'EY-25', + }; + fakeEntryRunning = { + id: 'entry_1', + project_id: 'abc', + project_name: 'Time-tracker', + start_date: new Date('2020-02-05T15:36:15.887Z'), + end_date: null, + customer_name: 'ioet Inc.', + activity_id: 'development', + technologies: ['Angular', 'TypeScript'], + description: 'No comments', + uri: 'EY-25', + }; + + jasmine.clock().mockDate(currentDate.toDate()); }) - .compileComponents(); - - currentDate = moment(); - fakeEntry = { - id: 'entry_1', - project_id: 'abc', - project_name: 'Time-tracker', - start_date: new Date('2020-02-05T15:36:15.887Z'), - end_date: new Date('2020-02-05T18:36:15.887Z'), - customer_name: 'ioet Inc.', - activity_id: 'development', - technologies: ['Angular', 'TypeScript'], - description: 'No comments', - uri: 'EY-25', - }; - fakeEntryRunning = { - id: 'entry_1', - project_id: 'abc', - project_name: 'Time-tracker', - start_date: new Date('2020-02-05T15:36:15.887Z'), - end_date: null, - customer_name: 'ioet Inc.', - activity_id: 'development', - technologies: ['Angular', 'TypeScript'], - description: 'No comments', - uri: 'EY-25', - }; - - jasmine.clock().mockDate(currentDate.toDate()); - })); + ); beforeEach(() => { fixture = TestBed.createComponent(CalendarComponent); @@ -83,11 +84,11 @@ describe('CalendarComponent', () => { end: fakeEntry.end_date, title: fakeEntry.description, id: fakeEntry.id, - meta: fakeEntry + meta: fakeEntry, }; const fakeDatasource = { isLoading: false, - data: [fakeEntry] + data: [fakeEntry], }; const fakeTimeEntries = of(fakeDatasource); const expectedtimeEntriesAsEvent = [fakeTimeEntryAsEvent]; @@ -104,11 +105,11 @@ describe('CalendarComponent', () => { end: fakeEntryRunning.end_date, title: fakeEntryRunning.description, id: fakeEntryRunning.id, - meta: fakeEntryRunning + meta: fakeEntryRunning, }; const fakeDatasource = { isLoading: false, - data: [fakeEntryRunning] + data: [fakeEntryRunning], }; const fakeTimeEntries = of(fakeDatasource); const expectedtimeEntriesAsEvent = [fakeTimeEntryAsEvent]; @@ -141,7 +142,7 @@ describe('CalendarComponent', () => { end: fakeEntry.end_date, title: fakeEntry.description, id: fakeEntry.id, - meta: fakeEntry + meta: fakeEntry, }; const fakeValueEmit = { id: fakeEntry.id, @@ -159,7 +160,7 @@ describe('CalendarComponent', () => { end: fakeEntry.end_date, title: fakeEntry.description, id: fakeEntry.id, - meta: fakeEntry + meta: fakeEntry, }; const fakeValueEmit = { timeEntry: fakeEntry, @@ -195,6 +196,13 @@ describe('CalendarComponent', () => { expect(component.calendarView).toEqual(fakeCalendarView); }); + it('set srcoll to current time marker in calendarView when is call scrollToCurrentTimeMarker', () => { + const fakeCalendarView: CalendarView = CalendarView.Week; + spyOn(component, 'scrollToCurrentTimeMarker'); + component.changeCalendarView(fakeCalendarView); + expect(component.scrollToCurrentTimeMarker).toHaveBeenCalled(); + }); + it('set false in nextDateDisabled when call navigationEnable and calendarView != Month and currentDate + 1 day is not greater to initialDate', () => { component.currentDate = moment().subtract(2, 'day').toDate(); component.initialDate = moment().toDate(); diff --git a/src/app/modules/time-entries/components/calendar/calendar.component.ts b/src/app/modules/time-entries/components/calendar/calendar.component.ts index 76c118fef..627b06b0b 100644 --- a/src/app/modules/time-entries/components/calendar/calendar.component.ts +++ b/src/app/modules/time-entries/components/calendar/calendar.component.ts @@ -1,14 +1,21 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { - CalendarEvent, - CalendarView, -} from 'angular-calendar'; + ChangeDetectorRef, + Component, + ElementRef, + EventEmitter, + Input, + OnInit, + Output, + ViewChild, +} from '@angular/core'; +import { CalendarEvent, CalendarView } from 'angular-calendar'; import { Observable } from 'rxjs'; import * as moment from 'moment'; import { DataSource } from '../../../shared/models/data-source.model'; import { Entry } from 'src/app/modules/shared/models'; import { map } from 'rxjs/operators'; import { SubstractDatePipe } from 'src/app/modules/shared/pipes/substract-date/substract-date.pipe'; +import { differenceInMinutes, startOfDay, startOfHour } from 'date-fns'; @Component({ selector: 'app-calendar', @@ -16,7 +23,12 @@ import { SubstractDatePipe } from 'src/app/modules/shared/pipes/substract-date/s styleUrls: ['./calendar.component.scss'], }) export class CalendarComponent implements OnInit { - @Input() set timeEntries$(timeEntries: Observable>){ + readonly DEFAULT_HEADER_HEIGHT = 52; + readonly VARIATION_HEIGHT: number = 2; + + @ViewChild('scrollContainer') scrollContainer: ElementRef; + + @Input() set timeEntries$(timeEntries: Observable>) { this.castEntryToCalendarEvent(timeEntries); } @Input() calendarView: CalendarView = CalendarView.Month; @@ -25,7 +37,7 @@ export class CalendarComponent implements OnInit { @Output() viewModal: EventEmitter = new EventEmitter(); @Output() deleteTimeEntry: EventEmitter = new EventEmitter(); @Output() changeDate: EventEmitter = new EventEmitter<{ - date: Date + date: Date; }>(); initialDate: Date; @@ -34,97 +46,113 @@ export class CalendarComponent implements OnInit { timeEntriesAsEvent: CalendarEvent[]; nextDateDisabled: boolean; - constructor() { + constructor(private referenceChangeDetector: ChangeDetectorRef) { this.initialDate = new Date(); this.previusDate = new Date(); this.isToday = false; this.timeEntriesAsEvent = []; this.nextDateDisabled = true; - } + } ngOnInit(): void { this.isToday = this.isVisibleForCurrentDate(); this.navigationEnable(this.calendarView); } - get CalendarViewEnum(): typeof CalendarView{ + get CalendarViewEnum(): typeof CalendarView { return CalendarView; } + scrollToCurrentTimeMarker() { + if (this.calendarView === CalendarView.Week || CalendarView.Day) { + const minutesSinceStartOfDay = differenceInMinutes(startOfHour(this.currentDate), startOfDay(this.currentDate)); + const headerHeight = this.calendarView === CalendarView.Week ? this.DEFAULT_HEADER_HEIGHT : 0; + this.scrollContainer.nativeElement.scrollTop = minutesSinceStartOfDay * this.VARIATION_HEIGHT + headerHeight; + } + } + castEntryToCalendarEvent(timeEntries$: Observable>) { - timeEntries$.pipe( - map((timeEntriesDatasorce) => timeEntriesDatasorce.data.map( - (timeEntries) => ({ - start: new Date(timeEntries.start_date), - end: timeEntries.end_date ? new Date(timeEntries.end_date) : null , - title: timeEntries.description, - id: timeEntries.id, - meta: timeEntries - } as CalendarEvent) + timeEntries$ + .pipe( + map((timeEntriesDatasorce) => + timeEntriesDatasorce.data.map( + (timeEntries) => + ({ + start: new Date(timeEntries.start_date), + end: timeEntries.end_date ? new Date(timeEntries.end_date) : null, + title: timeEntries.description, + id: timeEntries.id, + meta: timeEntries, + } as CalendarEvent) + ) ) ) - ) - .subscribe((timeEntriesAsEvent) => { + .subscribe((timeEntriesAsEvent) => { this.timeEntriesAsEvent = [...timeEntriesAsEvent].reverse(); - }); + }); } handleEditEvent(timeEntryAsEvent: CalendarEvent): void { - this.viewModal.emit( { - id: timeEntryAsEvent.id + this.viewModal.emit({ + id: timeEntryAsEvent.id, }); } handleDeleteEvent(timeEntryAsEvent: CalendarEvent): void { this.deleteTimeEntry.emit({ - timeEntry: timeEntryAsEvent.meta + timeEntry: timeEntryAsEvent.meta, }); } - handleChangeDateEvent(): void{ + handleChangeDateEvent(): void { const date = this.currentDate; this.isToday = this.isVisibleForCurrentDate(); this.navigationEnable(this.calendarView); - this.changeDate.emit({date}); + this.changeDate.emit({ date }); } - changeCalendarView(calendarView: CalendarView){ + changeCalendarView(calendarView: CalendarView) { this.calendarView = calendarView; + this.scrollContainer.nativeElement.scrollTop = 0; + if (this.calendarView !== CalendarView.Month) { + this.referenceChangeDetector.detectChanges(); + this.scrollToCurrentTimeMarker(); + } } - navigationEnable(calendarView: CalendarView){ + navigationEnable(calendarView: CalendarView) { let enable = false; const currentDate = moment(this.currentDate); const initialDate = moment(this.initialDate); - if (calendarView === CalendarView.Month){ + if (calendarView === CalendarView.Month) { if (currentDate.month() === initialDate.month() && currentDate.year() === initialDate.year()) { enable = true; } } currentDate.add(1, 'day'); - if (currentDate > initialDate){ + if (currentDate > initialDate) { enable = true; } this.nextDateDisabled = enable; } - getTimeWork(startDate: Date, endDate: Date): number{ - if (!endDate){ + getTimeWork(startDate: Date, endDate: Date): number { + if (!endDate) { return 30; } - return new SubstractDatePipe().transformInMinutes( endDate , startDate); + return new SubstractDatePipe().transformInMinutes(endDate, startDate); } - timeIsGreaterThan(startDate: Date, endDate: Date, timeRange: number ): boolean{ + timeIsGreaterThan(startDate: Date, endDate: Date, timeRange: number): boolean { const timeWorkInMinutes = this.getTimeWork(startDate, endDate); return timeWorkInMinutes > timeRange; } - isVisibleForCurrentView(currentCalendarView: CalendarView, desiredView: CalendarView ): boolean{ + isVisibleForCurrentView(currentCalendarView: CalendarView, desiredView: CalendarView): boolean { return currentCalendarView === desiredView; } - isVisibleForCurrentDate(): boolean{ + isVisibleForCurrentDate(): boolean { const currentDate: Date = new Date(this.currentDate); const initialDate: Date = new Date(this.initialDate); return currentDate.setHours(0, 0, 0, 0) === initialDate.setHours(0, 0, 0, 0); From 83e50c84bb11dfada67b89a842fe76aa4c215c14 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Tue, 10 Aug 2021 22:41:27 +0000 Subject: [PATCH 3/5] chore(release): 1.47.0 [skip ci]nn --- package-lock.json | 32 ++++++++++++++++---------------- package.json | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 336ab149c..46642683c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "time-tracker", - "version": "1.46.1", + "version": "1.47.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -6142,6 +6142,15 @@ "p-try": "^2.0.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==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, "ssri": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", @@ -18472,15 +18481,6 @@ } } }, - "serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, "serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", @@ -19902,9 +19902,9 @@ } }, "serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz", + "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -21132,9 +21132,9 @@ }, "dependencies": { "serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz", + "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==", "dev": true, "requires": { "randombytes": "^2.1.0" diff --git a/package.json b/package.json index b76ca4f1d..3d2589351 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "time-tracker", - "version": "1.46.1", + "version": "1.47.0", "scripts": { "preinstall": "npx npm-force-resolutions", "ng": "ng", From 8a89f8501bd416f6dc982656a03b172501c3432d Mon Sep 17 00:00:00 2001 From: Edgar Guaman Date: Fri, 6 Aug 2021 12:01:05 -0500 Subject: [PATCH 4/5] fix: TT-304 Handle message: the data could not be load --- .../pages/time-entries.component.html | 2 +- .../pages/time-entries.component.ts | 36 +++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) 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 1711ea0b8..df2e1869b 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.ts b/src/app/modules/time-entries/pages/time-entries.component.ts index 7ba274bbe..3fdc19aba 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 } from '@angular/core'; +import { Component, OnDestroy, OnInit, ViewChild, AfterViewInit } from '@angular/core'; import { ActionsSubject, select, Store } from '@ngrx/store'; import { ToastrService } from 'ngx-toastr'; -import { Observable, Subscription } from 'rxjs'; +import { Observable, Subscription, Subject } 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,12 +14,13 @@ 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 { +export class TimeEntriesComponent implements OnInit, OnDestroy, AfterViewInit { entryId: string; entry: Entry; activeTimeEntry: Entry; @@ -38,6 +39,11 @@ export class TimeEntriesComponent implements OnInit, OnDestroy { 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, @@ -49,8 +55,18 @@ export class TimeEntriesComponent implements OnInit, OnDestroy { } 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( @@ -65,6 +81,9 @@ export class TimeEntriesComponent implements OnInit, OnDestroy { this.store.dispatch(new entryActions.LoadEntries(this.selectedMonth, this.selectedYear)); }); } + ngAfterViewInit(): void { + this.rerenderDataTable(); + } newEntry() { if (this.wasEditingExistingTimeEntry) { this.entry = null; @@ -216,4 +235,15 @@ export class TimeEntriesComponent implements OnInit, OnDestroy { }); } } + + 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(); + } + } } From ae1eaee26f5fca35d48319625c520a42ee7432b9 Mon Sep 17 00:00:00 2001 From: Edgar Guaman Date: Wed, 11 Aug 2021 00:42:58 -0500 Subject: [PATCH 5/5] test: TT-304 Creating an unit test to coverage the new feature --- .../time-entries/pages/time-entries.component.spec.ts | 7 +++++++ 1 file changed, 7 insertions(+) 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 2406a1a59..4ff687dd6 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,4 +668,11 @@ 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(); + }); });
Date