diff --git a/package-lock.json b/package-lock.json index 31f593150..920dec5ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "time-tracker", - "version": "1.31.16", + "version": "1.31.20", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index c286b54b7..5bf34e977 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "time-tracker", - "version": "1.31.16", + "version": "1.31.20", "scripts": { "preinstall": "npx npm-force-resolutions", "ng": "ng", diff --git a/src/app/modules/customer-management/components/customer-info/components/create-customer/create-customer.html b/src/app/modules/customer-management/components/customer-info/components/create-customer/create-customer.html index 8279155e5..a6fda73bf 100644 --- a/src/app/modules/customer-management/components/customer-info/components/create-customer/create-customer.html +++ b/src/app/modules/customer-management/components/customer-info/components/create-customer/create-customer.html @@ -1,5 +1,8 @@
-
+
@@ -23,10 +27,20 @@ rows="3" formControlName="description" placeholder="Customer description" + (keydown)="getStatusCreateCustomerForm()" >
- - +
diff --git a/src/app/modules/customer-management/components/customer-info/components/create-customer/create-customer.ts b/src/app/modules/customer-management/components/customer-info/components/create-customer/create-customer.ts index 9cb14ae8d..9274b7bc2 100644 --- a/src/app/modules/customer-management/components/customer-info/components/create-customer/create-customer.ts +++ b/src/app/modules/customer-management/components/customer-info/components/create-customer/create-customer.ts @@ -27,6 +27,7 @@ export class CreateCustomerComponent implements OnInit, OnDestroy { @Output() closeCustomerComponent = new EventEmitter(); customerToEdit: Customer; editSubscription: Subscription; + statusForm = false; constructor(private formBuilder: FormBuilder, private store: Store) { this.customerForm = this.formBuilder.group({ @@ -94,4 +95,10 @@ export class CreateCustomerComponent implements OnInit, OnDestroy { this.store.dispatch(new ResetCustomerToEdit()); this.closeCustomerComponent.emit(false); } + getStatusCreateCustomerForm(){ + if (this.customerForm.dirty){ + this.statusForm = true; + console.log(this.statusForm) + } + } } diff --git a/src/app/modules/customer-management/components/customer-info/components/customer-list/customer-list.component.html b/src/app/modules/customer-management/components/customer-info/components/customer-list/customer-list.component.html index 09b7a0b76..e0e08cc8c 100644 --- a/src/app/modules/customer-management/components/customer-info/components/customer-list/customer-list.component.html +++ b/src/app/modules/customer-management/components/customer-info/components/customer-list/customer-list.component.html @@ -4,8 +4,7 @@ class="table table-bordered table-striped mb-0" datatable [dtTrigger]="dtTrigger" - [dtOptions]="dtOptions" - > + [dtOptions]="dtOptions"> Customer ID @@ -20,10 +19,11 @@ {{ customer.name }} @@ -50,6 +49,16 @@ aria-hidden="true" [title]="'Delete Customer'" [body]="message" - (closeModalEvent)="deleteCustomer()" -> + (closeModalEvent)="deleteCustomer()"> + + diff --git a/src/app/modules/customer-management/components/customer-info/components/customer-list/customer-list.component.ts b/src/app/modules/customer-management/components/customer-info/components/customer-list/customer-list.component.ts index e5c9785ed..2a66ad54a 100644 --- a/src/app/modules/customer-management/components/customer-info/components/customer-list/customer-list.component.ts +++ b/src/app/modules/customer-management/components/customer-info/components/customer-list/customer-list.component.ts @@ -13,6 +13,7 @@ import { } from './../../../../store/customer-management.actions'; import { ResetProjectToEdit } from '../../../projects/components/store/project.actions'; import { ResetProjectTypeToEdit } from '../../../projects-type/store'; +import { EntryState } from 'src/app/modules/time-clock/store/entry.reducer'; @Component({ selector: 'app-customer-list', templateUrl: './customer-list.component.html', @@ -31,6 +32,7 @@ export class CustomerListComponent implements OnInit, OnDestroy, AfterViewInit { changeCustomerSubscription: Subscription; showModal = false; idToDelete: string; + idToEdit: string; message: string; isLoading$: Observable; @@ -76,10 +78,10 @@ export class CustomerListComponent implements OnInit, OnDestroy, AfterViewInit { this.dtTrigger.unsubscribe(); } - editCustomer(customerId: string) { + editCustomer() { this.showCustomerForm = true; this.changeValueShowCustomerForm.emit(this.showCustomerForm); - this.store.dispatch(new SetCustomerToEdit(customerId)); + this.store.dispatch(new SetCustomerToEdit(this.idToEdit)); this.resetProjectFieldsToEdit(); } @@ -110,4 +112,13 @@ export class CustomerListComponent implements OnInit, OnDestroy, AfterViewInit { this.showModal = true; } + openEditModal(item: Customer) { + this.idToEdit = item.id; + if (this.showCustomerForm === true) { + this.message = `The changes will lose. Are you sure you change to customer ${item.name}?`; + this.showModal = true; + } else { + this.editCustomer(); + } + } } diff --git a/src/app/modules/customer-management/components/management-customer-projects/management-customer-projects.component.ts b/src/app/modules/customer-management/components/management-customer-projects/management-customer-projects.component.ts index 83caacb26..725f9ac1f 100644 --- a/src/app/modules/customer-management/components/management-customer-projects/management-customer-projects.component.ts +++ b/src/app/modules/customer-management/components/management-customer-projects/management-customer-projects.component.ts @@ -2,6 +2,7 @@ import { getCustomerUnderEdition } from './../../store/customer-management.selec import { Customer } from 'src/app/modules/shared/models'; import { Store, select } from '@ngrx/store'; import { Component, Output, EventEmitter, OnInit } from '@angular/core'; +import { Form } from '@angular/forms'; @Component({ selector: 'app-management-customer-projects', @@ -41,5 +42,4 @@ export class ManagementCustomerProjectsComponent implements OnInit { showTab(activeTab: string) { this.activeTab = activeTab; } - } diff --git a/src/app/modules/customer-management/components/projects-type/components/create-project-type/create-project-type.component.html b/src/app/modules/customer-management/components/projects-type/components/create-project-type/create-project-type.component.html index 69ef61d4e..3752351e3 100644 --- a/src/app/modules/customer-management/components/projects-type/components/create-project-type/create-project-type.component.html +++ b/src/app/modules/customer-management/components/projects-type/components/create-project-type/create-project-type.component.html @@ -1,13 +1,26 @@ -
+
- +
Name is required.
-
diff --git a/src/app/modules/shared/components/details-fields/details-fields.component.html b/src/app/modules/shared/components/details-fields/details-fields.component.html index 5a04f4b87..48388e267 100644 --- a/src/app/modules/shared/components/details-fields/details-fields.component.html +++ b/src/app/modules/shared/components/details-fields/details-fields.component.html @@ -90,6 +90,9 @@ aria-describedby="inputGroup-sizing-sm" [class.is-invalid]="start_date.invalid && start_date.touched" required + (ngModelChange)="onStartDateChange($event)" + [max]="getCurrentDate()" + onkeydown="return false" />
@@ -99,8 +102,8 @@ [format]="24" formControlName="start_hour" id="start_hour" - [disabled]="!(project_name.value && project_id.value)" class="timepicker-input" + [class.timepicker-input--disabled]="!(project_id.value && project_name.value)" > @@ -117,6 +120,8 @@ aria-describedby="inputGroup-sizing-sm" [class.is-invalid]="end_date.invalid && end_date.touched" required + [max]="getCurrentDate()" + onkeydown="return false" /> @@ -126,8 +131,8 @@ [format]="24" formControlName="end_hour" id="end_hour" - [disabled]="!(project_name.value && project_id.value)" class="timepicker-input" + [class.timepicker-input--disabled]="!(project_id.value && project_name.value)" > 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 88b00d6e7..25dd0337a 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 @@ -103,3 +103,8 @@ input[type="date"]::-webkit-clear-button { border-radius: 0.25rem; } +.timepicker-input--disabled ::ng-deep div{ + pointer-events: none; + background-color: #e9ecef; + opacity: 1; +} 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 60139632f..39cb76bdc 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 @@ -443,6 +443,56 @@ describe('DetailsFieldsComponent', () => { expect(component.projectSelected.emit).toHaveBeenCalledWith(data); }); + it('on selected start_date should change end_date', () => { + const expectedStartDate = '2020-02-05'; + + component.onStartDateChange(expectedStartDate); + fixture.detectChanges(); + const endDateInput: HTMLInputElement = fixture.debugElement.nativeElement.querySelector('#end_date'); + + expect(endDateInput.value).toEqual(expectedStartDate); + }); + + it('on selected end_date should not change start_date', () => { + const expectedStartDate = '2020-02-05'; + const expectedEndDate = '2020-02-06'; + + component.ngOnInit(); + fixture.detectChanges(); + const startDateInput: HTMLInputElement = fixture.debugElement.nativeElement.querySelector('#start_date'); + const endDateInput: HTMLInputElement = fixture.debugElement.nativeElement.querySelector('#end_date'); + startDateInput.value = expectedStartDate; + endDateInput.value = expectedEndDate; + endDateInput.dispatchEvent(new Event('#end_date')); + + expect(endDateInput.value).not.toEqual(startDateInput.value); + expect(startDateInput.value).toEqual(expectedStartDate); + }); + + it('on get current date should return expected date', () => { + const expectedDate = new Date().toISOString().split('T')[0]; + + expect(component.getCurrentDate()).toEqual(expectedDate); + }); + + it('on the input with id #start_date we could get the id and max value', () => { + fixture.detectChanges(); + const expectedDate = new Date().toISOString().split('T')[0]; + const startDateInput: HTMLInputElement = fixture.debugElement. + nativeElement.querySelector(`input[id="start_date"],input[max="${component.getCurrentDate()}"]`); + + expect(startDateInput.id).toEqual('start_date'); + expect(startDateInput.max).toEqual(expectedDate); + }); + + it('on the input with id #end_date we could get the current Date ', () => { + fixture.detectChanges(); + const expectedDate = new Date().toISOString().split('T')[0]; + const endDateInput = fixture.debugElement.nativeElement.querySelector('[id=end_date]'); + + expect(endDateInput.id).toEqual('end_date'); + expect(endDateInput.max).toEqual(expectedDate); + }); /* 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 5a0434d8a..8ca3bad92 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 @@ -124,6 +124,10 @@ export class DetailsFieldsComponent implements OnChanges, OnInit { }); } + onStartDateChange($event: string) { + this.end_date.setValue($event); + } + ngOnChanges(): void { this.goingToWorkOnThis = this.entryToEdit ? this.entryToEdit.running : false; this.shouldRestartEntry = false; @@ -143,7 +147,7 @@ export class DetailsFieldsComponent implements OnChanges, OnInit { technology: '', }); } else { - this.cleanForm(); + this.cleanForm(); } } @@ -167,6 +171,10 @@ export class DetailsFieldsComponent implements OnChanges, OnInit { this.selectedTechnologies = $event; } + getCurrentDate(): string { + return new Date().toISOString().split('T')[0]; + } + get project_id() { return this.entryForm.get('project_id'); } 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 e64fc92f1..095d14156 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 @@ -148,7 +148,6 @@ describe('TimeEntriesComponent', () => { it('when creating a new entry, then entryId should be null', () => { component.newEntry(); - expect(component.entry).toBe(null); expect(component.entryId).toBe(null); }); @@ -427,4 +426,20 @@ describe('TimeEntriesComponent', () => { expect(entryData.customer_name).toContain('ioet Inc.'); }); })); + + it('Should the entry be null if the flag is true', () => { + component.wasEditingExistingTimeEntry = true; + const idEntry = '1'; + component.editEntry(idEntry); + component.newEntry(); + expect(component.entry).toBe(null); + }); + + it('Should the input persist the data if the flag is false', () => { + const newEntry = { start_date: new Date(), id: '1234', technologies: [], project_name: 'time-tracker' }; + component.entry = newEntry; + component.wasEditingExistingTimeEntry = false; + component.newEntry(); + expect(component.entry).toEqual(newEntry); + }); }); 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 d21f64303..21a89ccb8 100644 --- a/src/app/modules/time-entries/pages/time-entries.component.ts +++ b/src/app/modules/time-entries/pages/time-entries.component.ts @@ -25,24 +25,21 @@ export class TimeEntriesComponent implements OnInit, OnDestroy { message: string; idToDelete: string; entriesSubscription: Subscription; + wasEditingExistingTimeEntry = false; canMarkEntryAsWIP = true; timeEntriesDataSource$: Observable>; selectedYearAsText: string; selectedMonth: number; selectedYear: number; selectedMonthAsText: string; - constructor(private store: Store, private toastrService: ToastrService, private actionsSubject$: ActionsSubject) { this.timeEntriesDataSource$ = this.store.pipe(delay(0), select(getTimeEntriesDataSource)); } - ngOnDestroy(): void { this.entriesSubscription.unsubscribe(); } - ngOnInit(): void { this.loadActiveEntry(); - this.entriesSubscription = this.actionsSubject$.pipe( filter((action: any) => ( action.type === EntryActionTypes.CREATE_ENTRY_SUCCESS || @@ -55,24 +52,22 @@ export class TimeEntriesComponent implements OnInit, OnDestroy { this.store.dispatch(new entryActions.LoadEntries(this.selectedMonth, this.selectedYear)); }); } - newEntry() { - this.entry = null; + if (this.wasEditingExistingTimeEntry) { + this.entry = null; + } this.entryId = null; this.store.pipe(select(getTimeEntriesDataSource)).subscribe(ds => { this.canMarkEntryAsWIP = !this.isThereAnEntryRunning(ds.data); }); } - private getEntryRunning(entries: Entry[]) { const runningEntry: Entry = entries.find(entry => entry.running === true); return runningEntry; } - private isThereAnEntryRunning(entries: Entry[]) { return !!this.getEntryRunning(entries); } - editEntry(entryId: string) { this.entryId = entryId; this.store.pipe(select(getTimeEntriesDataSource)).subscribe(ds => { @@ -80,8 +75,8 @@ export class TimeEntriesComponent implements OnInit, OnDestroy { this.canMarkEntryAsWIP = this.isEntryRunningEqualsToEntryToEdit(this.getEntryRunning(ds.data), this.entry) || this.isTheEntryToEditTheLastOne(ds.data); }); + this.wasEditingExistingTimeEntry = true; } - private isEntryRunningEqualsToEntryToEdit(entryRunning: Entry, entryToEdit: Entry) { if (entryRunning && entryToEdit) { return entryRunning.id === entryToEdit.id; @@ -89,7 +84,6 @@ export class TimeEntriesComponent implements OnInit, OnDestroy { return false; } } - private isTheEntryToEditTheLastOne(entries: Entry[]) { if (entries && entries.length > 0) { const lastEntry = entries[0]; @@ -98,11 +92,9 @@ export class TimeEntriesComponent implements OnInit, OnDestroy { return false; } } - private isNewEntry() { return this.entryId === null; } - saveEntry(event: SaveEntryEvent): void { if (this.activeTimeEntry) { const startDateAsLocalDate = new Date(event.entry.start_date); @@ -121,8 +113,8 @@ export class TimeEntriesComponent implements OnInit, OnDestroy { this.doSave(event); } } - projectSelected(event: ProjectSelectedEvent): void { + this.wasEditingExistingTimeEntry = false; this.store.pipe(select(getTimeEntriesDataSource)).subscribe(ds => { const dataToUse = ds.data.find(item => item.project_id === event.projectId); if (dataToUse && this.isNewEntry()) { @@ -140,7 +132,6 @@ export class TimeEntriesComponent implements OnInit, OnDestroy { } }); } - doSave(event: SaveEntryEvent) { if (this.entryId) { event.entry.id = this.entryId; @@ -152,19 +143,16 @@ export class TimeEntriesComponent implements OnInit, OnDestroy { this.store.dispatch(new entryActions.CreateEntry(event.entry)); } } - loadActiveEntry() { this.store.dispatch(new entryActions.LoadActiveEntry()); this.store.pipe(select(getActiveTimeEntry)).subscribe((activeTimeEntry) => { this.activeTimeEntry = activeTimeEntry; }); } - removeEntry() { this.store.dispatch(new entryActions.DeleteEntry(this.idToDelete)); this.showModal = false; } - dateSelected(event: { monthIndex: number; year: number }) { this.selectedYear = event.year; this.selectedYearAsText = event.year.toString(); @@ -172,10 +160,9 @@ export class TimeEntriesComponent implements OnInit, OnDestroy { this.selectedMonthAsText = moment().month(event.monthIndex).format('MMMM'); this.store.dispatch(new entryActions.LoadEntries(this.selectedMonth, this.selectedYear)); } - openModal(item: any) { this.idToDelete = item.id; this.message = `Are you sure you want to delete ${item.activity_name}?`; this.showModal = true; } -} +} \ No newline at end of file