diff --git a/src/app/modules/time-clock/components/entry-fields/entry-fields.component.html b/src/app/modules/time-clock/components/entry-fields/entry-fields.component.html
index 37764383b..fc29022fa 100644
--- a/src/app/modules/time-clock/components/entry-fields/entry-fields.component.html
+++ b/src/app/modules/time-clock/components/entry-fields/entry-fields.component.html
@@ -23,6 +23,25 @@
/>
+
+
+
+
+
{
type Merged = TechnologyState & ProjectState;
@@ -20,6 +23,10 @@ describe('EntryFieldsComponent', () => {
let mockProjectsSelector;
let entryForm;
const actionSub: ActionsSubject = new ActionsSubject();
+ const toastrServiceStub = {
+ error: (message?: string, title?: string, override?: Partial) => { },
+ warning: (message?: string, title?: string, override?: Partial) => { }
+ };
const state = {
projects: {
@@ -59,12 +66,18 @@ describe('EntryFieldsComponent', () => {
project_id: 'project-id-15',
description: 'description for active entry',
uri: 'abc',
+ start_date : moment().format('YYYY-MM-DD'),
+ start_hour : moment().format('HH:mm:ss'),
};
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [EntryFieldsComponent],
- providers: [provideMockStore({initialState: state}), { provide: ActionsSubject, useValue: actionSub }],
+ providers: [
+ provideMockStore({initialState: state}),
+ { provide: ActionsSubject, useValue: actionSub },
+ { provide: ToastrService, useValue: toastrServiceStub }
+ ],
imports: [FormsModule, ReactiveFormsModule],
}).compileComponents();
store = TestBed.inject(MockStore);
@@ -97,10 +110,56 @@ describe('EntryFieldsComponent', () => {
expect(component.entryForm.patchValue).toHaveBeenCalledTimes(1);
expect(component.entryForm.patchValue).toHaveBeenCalledWith(
- { description: entryDataForm.description, uri: entryDataForm.uri, activity_id: entryDataForm.activity_id });
+ {
+ description: entryDataForm.description,
+ uri: entryDataForm.uri,
+ activity_id: entryDataForm.activity_id,
+ start_hour: formatDate(entry.start_date, 'HH:mm:ss', 'en')
+ }
+ );
expect(component.selectedTechnologies).toEqual([]);
});
+ it('displays error message when the date selected is in the future', () => {
+ component.newData = entry;
+ component.activeEntry = entry ;
+ component.setDataToUpdate(entry);
+ spyOn(toastrServiceStub, 'error');
+
+ const hourInTheFuture = moment().add(1, 'hours').format('HH:mm:ss');
+ component.entryForm.patchValue({ start_hour : hourInTheFuture});
+ component.onUpdateStartHour();
+
+ expect(toastrServiceStub.error).toHaveBeenCalled();
+ });
+
+ it('If start hour is in the future, reset to initial start_date in form', () => {
+ component.newData = entry;
+ component.activeEntry = entry ;
+ component.setDataToUpdate(entry);
+
+ const hourInTheFuture = moment().add(1, 'hours').format('HH:mm:ss');
+ component.entryForm.patchValue({ start_hour : hourInTheFuture});
+
+ spyOn(component.entryForm, 'patchValue');
+ component.onUpdateStartHour();
+
+ expect(component.entryForm.patchValue).toHaveBeenCalledWith(
+ {
+ start_hour: component.newData.start_hour
+ }
+ );
+ });
+
+ it('when a start hour is updated, then dispatch UpdateActiveEntry', () => {
+ component.activeEntry = entry ;
+ component.setDataToUpdate(entry);
+ spyOn(store, 'dispatch');
+
+ component.onUpdateStartHour();
+ expect(store.dispatch).toHaveBeenCalled();
+ });
+
it('when a technology is added, then dispatch UpdateActiveEntry', () => {
const addedTechnologies = ['react'];
spyOn(store, 'dispatch');
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 f5d1206ed..c2aab444b 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
@@ -12,6 +12,10 @@ import { ActivityState, LoadActivities } from '../../../activities-management/st
import * as entryActions from '../../store/entry.actions';
+import * as moment from 'moment';
+import { ToastrService } from 'ngx-toastr';
+import { formatDate } from '@angular/common';
+
type Merged = TechnologyState & ProjectState & ActivityState;
@Component({
@@ -26,56 +30,75 @@ export class EntryFieldsComponent implements OnInit {
activeEntry;
newData;
- constructor(private formBuilder: FormBuilder, private store: Store, private actionsSubject$: ActionsSubject) {
+ constructor(
+ private formBuilder: FormBuilder,
+ private store: Store,
+ private actionsSubject$: ActionsSubject,
+ private toastrService: ToastrService
+ ) {
this.entryForm = this.formBuilder.group({
description: '',
uri: '',
activity_id: '',
+ start_hour: '',
+ start_date: '',
});
}
ngOnInit(): void {
this.store.dispatch(new LoadActivities());
- 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$.pipe(
- filter((action: any) => (action.type === EntryActionTypes.CREATE_ENTRY_SUCCESS))
- ).subscribe((action) => {
- if (!action.payload.end_date) {
+ 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$.pipe(
- filter((action: any) => ( action.type === EntryActionTypes.LOAD_ACTIVE_ENTRY_SUCCESS ))
- ).subscribe((action) => {
- this.activeEntry = action.payload;
- this.setDataToUpdate(this.activeEntry);
- this.newData = {
- id: this.activeEntry.id,
- project_id: this.activeEntry.project_id,
- uri: this.activeEntry.uri,
- activity_id: this.activeEntry.activity_id,
- };
- });
+ this.actionsSubject$
+ .pipe(
+ filter((action: any) => (
+ action.type === EntryActionTypes.CREATE_ENTRY_SUCCESS ||
+ action.type === EntryActionTypes.UPDATE_ENTRY_SUCCESS
+ ))
+ ).subscribe((action) => {
+ if (!action.payload.end_date) {
+ this.store.dispatch(new LoadActiveEntry());
+ this.store.dispatch(new entryActions.LoadEntriesSummary());
+ }
+ });
+
+ this.actionsSubject$
+ .pipe(filter((action: any) => action.type === EntryActionTypes.LOAD_ACTIVE_ENTRY_SUCCESS))
+ .subscribe((action) => {
+ this.activeEntry = action.payload;
+ this.setDataToUpdate(this.activeEntry);
+ this.newData = {
+ id: this.activeEntry.id,
+ project_id: this.activeEntry.project_id,
+ uri: this.activeEntry.uri,
+ activity_id: this.activeEntry.activity_id,
+ start_date: this.activeEntry.start_date,
+ start_hour: formatDate(this.activeEntry.start_date, 'HH:mm:ss', '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({
description: entryData.description,
uri: entryData.uri,
activity_id: entryData.activity_id,
+ start_hour: formatDate(entryData.start_date, 'HH:mm:ss', 'en'),
});
if (entryData.technologies) {
this.selectedTechnologies = entryData.technologies;
@@ -93,6 +116,19 @@ export class EntryFieldsComponent implements OnInit {
this.store.dispatch(new entryActions.UpdateEntryRunning({ ...this.newData, ...this.entryForm.value }));
}
+ onUpdateStartHour() {
+ const startDate = formatDate(this.activeEntry.start_date, 'yyyy-MM-dd', 'en');
+ const newHourEntered = new Date(`${startDate}T${this.entryForm.value.start_hour.trim()}`).toISOString();
+ const isEntryDateInTheFuture = moment(newHourEntered).isAfter(moment());
+ if (isEntryDateInTheFuture) {
+ this.toastrService.error('You cannot start a time-entry in the future');
+ this.entryForm.patchValue({ start_hour: this.newData.start_hour });
+ return;
+ }
+ this.entryForm.patchValue({ start_date: newHourEntered });
+ this.store.dispatch(new entryActions.UpdateEntryRunning({ ...this.newData, ...this.entryForm.value }));
+ }
+
onTechnologyAdded($event: string[]) {
this.store.dispatch(new entryActions.UpdateEntryRunning({ ...this.newData, technologies: $event }));
}