@@ -88,11 +88,11 @@
required
aria-describedby="inputGroup-sizing-sm"
/>
-
+
Time out
{
type Merged = TechnologyState & ProjectState & EntryState;
@@ -30,18 +31,18 @@ describe('DetailsFieldsComponent', () => {
const state = {
projects: {
- projects: [{ id: 'id', name: 'name', project_type_id: '' }],
- customerProjects: [{ id: 'id', name: 'name', description: 'description', project_type_id: '123' }],
+ projects: [{id: 'id', name: 'name', project_type_id: ''}],
+ customerProjects: [{id: 'id', name: 'name', description: 'description', project_type_id: '123'}],
isLoading: false,
message: '',
projectToEdit: undefined,
},
technologies: {
- technologyList: { items: [{ name: 'java' }] },
+ technologyList: {items: [{name: 'java'}]},
isLoading: false,
},
activities: {
- data: [{ id: 'fc5fab41-a21e-4155-9d05-511b956ebd05', tenant_id: 'ioet', deleted: null, name: 'abc' }],
+ data: [{id: 'fc5fab41-a21e-4155-9d05-511b956ebd05', tenant_id: 'ioet', deleted: null, name: 'abc'}],
isLoading: false,
message: 'Data fetch successfully!',
activityIdToEdit: '',
@@ -66,7 +67,7 @@ describe('DetailsFieldsComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [DetailsFieldsComponent, TechnologiesComponent],
- providers: [provideMockStore({ initialState: state }), { provide: ActionsSubject, useValue: actionSub }],
+ providers: [provideMockStore({initialState: state}), {provide: ActionsSubject, useValue: actionSub}],
imports: [FormsModule, ReactiveFormsModule],
}).compileComponents();
store = TestBed.inject(MockStore);
@@ -106,8 +107,8 @@ describe('DetailsFieldsComponent', () => {
});
[
- { actionType: EntryActionTypes.CREATE_ENTRY_SUCCESS },
- { actionType: EntryActionTypes.UPDATE_ENTRY_SUCCESS },
+ {actionType: EntryActionTypes.CREATE_ENTRY_SUCCESS},
+ {actionType: EntryActionTypes.UPDATE_ENTRY_SUCCESS},
].map((param) => {
it(`cleanForm after an action type ${param.actionType} is received`, () => {
const actionSubject = TestBed.inject(ActionsSubject) as ActionsSubject;
@@ -217,20 +218,23 @@ describe('DetailsFieldsComponent', () => {
technology: '',
});
component.onSubmit();
- const data = {
- project_id: '',
- activity_id: '',
- technologies: [],
- description: '',
- start_date: '2020-02-05T00:00:11',
- end_date: '2020-02-05T00:01:01',
- uri: '',
+ const data: SaveEntryEvent = {
+ entry: {
+ project_id: '',
+ activity_id: '',
+ technologies: [],
+ description: '',
+ start_date: '2020-02-05T00:00:11',
+ end_date: '2020-02-05T00:01:01',
+ uri: ''
+ },
+ shouldRestartEntry: false
};
expect(component.saveEntry.emit).toHaveBeenCalledWith(data);
});
it('when the current entry is not running, then the end hour input should be rendered', () => {
- component.isEntryRunning = false;
+ component.goingToWorkOnThis = false;
fixture.detectChanges();
const endHourInput = fixture.debugElement.nativeElement.querySelector('#end_hour');
@@ -238,63 +242,67 @@ describe('DetailsFieldsComponent', () => {
});
it('when the current entry is running, then the end hour input should not be rendered', () => {
- component.isEntryRunning = true;
+ component.goingToWorkOnThis = true;
fixture.detectChanges();
const endHourInput = fixture.debugElement.nativeElement.querySelector('#end_hour');
expect(endHourInput).toBeNull();
});
- it('when creating a new entry, then the new entry should be marked as not running', () => {
+ it('when creating a new entry, then the new entry should be marked as not run', () => {
component.entryToEdit = null;
- expect(component.isEntryRunning).toBeFalse();
+ expect(component.goingToWorkOnThis).toBeFalse();
});
- it('when editing entry that is currently running, then the entry should be marked as running', () => {
- component.entryToEdit = { ...entryToEdit, running: true };
+ it('when editing entry that is currently running, then the entry should be marked as I am working on this', () => {
+ component.entryToEdit = {...entryToEdit, running: true};
fixture.componentInstance.ngOnChanges();
- expect(component.isEntryRunning).toBeTrue();
+ expect(component.goingToWorkOnThis).toBeTrue();
});
it('when editing entry that already finished, then the entry should not be marked as running', () => {
- component.entryToEdit = { ...entryToEdit, running: false };
+ component.entryToEdit = {...entryToEdit, running: false};
fixture.componentInstance.ngOnChanges();
- expect(component.isEntryRunning).toBeFalse();
+ expect(component.goingToWorkOnThis).toBeFalse();
});
it('when editing entry that already finished, then the entry should not be marked as running', () => {
- component.entryToEdit = { ...entryToEdit, running: false };
+ component.entryToEdit = {...entryToEdit, running: false};
fixture.componentInstance.ngOnChanges();
- expect(component.isEntryRunning).toBeFalse();
+ expect(component.goingToWorkOnThis).toBeFalse();
});
it('when submitting a entry that is currently running, the end date should not be sent ', () => {
- component.isEntryRunning = true;
+ component.goingToWorkOnThis = true;
spyOn(component, 'getElapsedSeconds').and.returnValue('10');
spyOn(component.saveEntry, 'emit');
- component.entryForm.setValue({ ...formValues, entry_date: '2020-06-11' });
+ component.entryForm.setValue({...formValues, entry_date: '2020-06-11'});
component.onSubmit();
- const data = {
- project_id: '',
- activity_id: '',
- technologies: [],
- description: '',
- start_date: '2020-06-11T00:00:10',
- uri: 'ticketUri',
+ const data: SaveEntryEvent = {
+ entry: {
+ project_id: '',
+ activity_id: '',
+ technologies: [],
+ description: '',
+ start_date: '2020-06-11T00:00:10',
+ uri: 'ticketUri',
+ },
+ shouldRestartEntry: false
};
- expect(component.saveEntry.emit).toHaveBeenCalledWith(data);
+ expect(component.saveEntry.emit
+ ).toHaveBeenCalledWith(data);
});
- it('when disabling current entry is running, then the end hour should be set to the current time', () => {
+ it('when disabling going to work on this, then the end hour should be set to the current time', () => {
const datePipe: DatePipe = new DatePipe('en');
const currentTime = datePipe.transform(new Date(), 'HH:mm');
@@ -305,4 +313,15 @@ describe('DetailsFieldsComponent', () => {
const endHourInput: HTMLInputElement = fixture.debugElement.nativeElement.querySelector('#end_hour');
expect(endHourInput.value).toEqual(currentTime);
});
+
+ it('given going to work on this and the entry is not currently running, when submitting form then the entry should be restarted', () => {
+ component.goingToWorkOnThis = false;
+ component.entryToEdit = {...entryToEdit, running: false};
+
+ const checkIsEntryRunning: HTMLInputElement = fixture.debugElement.nativeElement.querySelector('#isEntryRunning');
+ checkIsEntryRunning.click();
+ fixture.detectChanges();
+
+ expect(component.shouldRestartEntry).toBeTrue();
+ });
});
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 6632a47aa..b7160bdeb 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
@@ -3,7 +3,7 @@ import { filter } from 'rxjs/operators';
import { NumberFormatter } from './../../formatters/number.formatter';
import { Component, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, ViewChild, } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
-import { select, Store, ActionsSubject } from '@ngrx/store';
+import { ActionsSubject, select, Store } from '@ngrx/store';
import { formatDate } from '@angular/common';
import { Activity, Entry, Project } from '../../models';
@@ -15,6 +15,7 @@ import * as projectActions from '../../../customer-management/components/project
import { EntryState } from '../../../time-clock/store/entry.reducer';
import * as entryActions from '../../../time-clock/store/entry.actions';
import { getCreateError, getUpdateError } from 'src/app/modules/time-clock/store/entry.selectors';
+import { SaveEntryEvent } from './save-entry-event';
type Merged = TechnologyState & ProjectState & ActivityState & EntryState;
@@ -25,15 +26,15 @@ type Merged = TechnologyState & ProjectState & ActivityState & EntryState;
})
export class DetailsFieldsComponent implements OnChanges, OnInit {
@Input() entryToEdit: Entry;
- @Input() formType: string;
- @Output() saveEntry = new EventEmitter();
+ @Output() saveEntry = new EventEmitter
();
@ViewChild('closeModal') closeModal: ElementRef;
entryForm: FormGroup;
selectedTechnologies: string[] = [];
isLoading = false;
listProjects: Project[] = [];
activities: Activity[] = [];
- isEntryRunning = false;
+ goingToWorkOnThis = false;
+ shouldRestartEntry = false;
constructor(private formBuilder: FormBuilder, private store: Store, private actionsSubject$: ActionsSubject) {
this.entryForm = this.formBuilder.group({
@@ -87,7 +88,7 @@ export class DetailsFieldsComponent implements OnChanges, OnInit {
}
ngOnChanges(): void {
- this.isEntryRunning = this.entryToEdit ? this.entryToEdit.running : false;
+ this.goingToWorkOnThis = this.entryToEdit ? this.entryToEdit.running : false;
if (this.entryToEdit) {
this.selectedTechnologies = this.entryToEdit.technologies;
this.entryForm.setValue({
@@ -160,10 +161,10 @@ export class DetailsFieldsComponent implements OnChanges, OnInit {
end_date: `${entryDate}T${this.entryForm.value.end_hour.trim()}:01`,
uri: this.entryForm.value.uri,
};
- if (this.isEntryRunning) {
+ if (this.goingToWorkOnThis) {
delete entry.end_date;
}
- this.saveEntry.emit(entry);
+ this.saveEntry.emit({entry, shouldRestartEntry: this.shouldRestartEntry});
}
getElapsedSeconds(date: Date): string {
@@ -175,10 +176,11 @@ export class DetailsFieldsComponent implements OnChanges, OnInit {
}
}
- onIsRunningChange(event: any) {
- this.isEntryRunning = event.currentTarget.checked;
- if (!this.isEntryRunning) {
- this.entryForm.patchValue({ end_hour: formatDate(new Date(), 'HH:mm', 'en') });
+ onGoingToWorkOnThisChange(event: any) {
+ this.goingToWorkOnThis = event.currentTarget.checked;
+ if (!this.goingToWorkOnThis) {
+ this.entryForm.patchValue({end_hour: formatDate(new Date(), 'HH:mm', 'en')});
}
+ this.shouldRestartEntry = !this.entryToEdit?.running && this.goingToWorkOnThis;
}
}
diff --git a/src/app/modules/shared/components/details-fields/save-entry-event.ts b/src/app/modules/shared/components/details-fields/save-entry-event.ts
new file mode 100644
index 000000000..f41d13c23
--- /dev/null
+++ b/src/app/modules/shared/components/details-fields/save-entry-event.ts
@@ -0,0 +1,4 @@
+export interface SaveEntryEvent {
+ entry: any;
+ shouldRestartEntry: boolean;
+}
diff --git a/src/app/modules/shared/models/entry.model.ts b/src/app/modules/shared/models/entry.model.ts
index bfc4831f7..51f8eb926 100644
--- a/src/app/modules/shared/models/entry.model.ts
+++ b/src/app/modules/shared/models/entry.model.ts
@@ -2,7 +2,7 @@ export interface Entry {
running?: boolean;
id: string;
start_date: Date;
- end_date: Date;
+ end_date?: Date;
activity_id?: string;
technologies: string[];
uri?: string;
diff --git a/src/app/modules/time-clock/services/entry.service.spec.ts b/src/app/modules/time-clock/services/entry.service.spec.ts
index 6171a3d77..5247c54a1 100644
--- a/src/app/modules/time-clock/services/entry.service.spec.ts
+++ b/src/app/modules/time-clock/services/entry.service.spec.ts
@@ -104,4 +104,13 @@ describe('EntryService', () => {
expect(loadEntryRequest.request.params.get('end_date')).toBe(pipe.transform(today, EntryService.TIME_ENTRIES_DATE_TIME_FORMAT));
expect(loadEntryRequest.request.params.get('user_id')).toEqual('123');
});
+
+ it('when restarting entry, a POST is triggered', () => {
+ const entry = 'entryId';
+
+ service.restartEntry(entry).subscribe();
+
+ const restartEntryRequest = httpMock.expectOne( `${service.baseUrl}/${entry}/restart`);
+ expect(restartEntryRequest.request.method).toBe('POST');
+ });
});
diff --git a/src/app/modules/time-clock/services/entry.service.ts b/src/app/modules/time-clock/services/entry.service.ts
index fac4cef8b..bdca14832 100644
--- a/src/app/modules/time-clock/services/entry.service.ts
+++ b/src/app/modules/time-clock/services/entry.service.ts
@@ -6,6 +6,7 @@ import { Observable } from 'rxjs';
import { environment } from './../../../../environments/environment';
import { TimeEntriesTimeRange } from '../models/time-entries-time-range';
import { DatePipe } from '@angular/common';
+import { Entry } from '../../shared/models';
@Injectable({
providedIn: 'root',
@@ -45,6 +46,11 @@ export class EntryService {
return this.http.post(url, null);
}
+ restartEntry(idEntry: string): Observable {
+ const url = `${this.baseUrl}/${idEntry}/restart`;
+ return this.http.post(url, null);
+ }
+
summary(): Observable {
const timeOffset = new Date().getTimezoneOffset();
const summaryUrl = `${this.baseUrl}/summary?time_offset=${timeOffset}`;
diff --git a/src/app/modules/time-clock/store/entry.actions.spec.ts b/src/app/modules/time-clock/store/entry.actions.spec.ts
index 8debb2bb2..54f198bc2 100644
--- a/src/app/modules/time-clock/store/entry.actions.spec.ts
+++ b/src/app/modules/time-clock/store/entry.actions.spec.ts
@@ -1,8 +1,19 @@
import * as actions from './entry.actions';
import * as moment from 'moment';
import { TimeEntriesTimeRange } from '../models/time-entries-time-range';
+import { Entry } from '../../shared/models';
describe('Actions for Entries', () => {
+ let entry: Entry;
+ beforeEach(() => {
+ entry = {
+ id: '1',
+ start_date: new Date(),
+ end_date: new Date(),
+ activity_id: '',
+ technologies: ['abc', 'abc'],
+ };
+ });
it('SwitchTimeEntry type is EntryActionTypes.SWITCH_TIME_ENTRY', () => {
@@ -66,13 +77,7 @@ describe('Actions for Entries', () => {
});
it('UpdateEntrySuccess type is EntryActionTypes.UDPATE_ENTRY_SUCCESS', () => {
- const updateActiveEntrySuccess = new actions.UpdateEntrySuccess({
- id: '1',
- start_date: new Date(),
- end_date: new Date(),
- activity_id: '',
- technologies: ['abc', 'abc'],
- });
+ const updateActiveEntrySuccess = new actions.UpdateEntrySuccess(entry);
expect(updateActiveEntrySuccess.type).toEqual(actions.EntryActionTypes.UPDATE_ENTRY_SUCCESS);
});
@@ -82,13 +87,7 @@ describe('Actions for Entries', () => {
});
it('UpdateActiveEntry type is EntryActionTypes.UDPATE_ENTRY_FAIL', () => {
- const action = new actions.UpdateEntryRunning({
- id: '1',
- start_date: new Date(),
- end_date: new Date(),
- activity_id: '',
- technologies: ['abc', 'abc'],
- });
+ const action = new actions.UpdateEntryRunning(entry);
expect(action.type).toEqual(actions.EntryActionTypes.UPDATE_ENTRY_RUNNING);
});
@@ -111,4 +110,19 @@ describe('Actions for Entries', () => {
const action = new actions.LoadEntriesByTimeRangeFail();
expect(action.type).toEqual(actions.EntryActionTypes.LOAD_ENTRIES_BY_TIME_RANGE_FAIL);
});
+
+ it('RestartEntry type is EntryActionTypes.RESTART_ENTRY', () => {
+ const action = new actions.RestartEntry(entry);
+ expect(action.type).toEqual(actions.EntryActionTypes.RESTART_ENTRY);
+ });
+
+ it('RestartEntrySuccess type is EntryActionTypes.RESTART_ENTRY_SUCCESS', () => {
+ const action = new actions.RestartEntrySuccess(entry);
+ expect(action.type).toEqual(actions.EntryActionTypes.RESTART_ENTRY_SUCCESS);
+ });
+
+ it('RestartEntryFail type is EntryActionTypes.RESTART_ENTRY_FAIL', () => {
+ const action = new actions.RestartEntryFail('error');
+ expect(action.type).toEqual(actions.EntryActionTypes.RESTART_ENTRY_FAIL);
+ });
});
diff --git a/src/app/modules/time-clock/store/entry.actions.ts b/src/app/modules/time-clock/store/entry.actions.ts
index 741c43399..9b155bacc 100644
--- a/src/app/modules/time-clock/store/entry.actions.ts
+++ b/src/app/modules/time-clock/store/entry.actions.ts
@@ -1,7 +1,7 @@
import { TimeEntriesSummary } from '../models/time.entry.summary';
import { Action } from '@ngrx/store';
import { NewEntry, Entry } from '../../shared/models';
-import {TimeEntriesTimeRange} from '../models/time-entries-time-range';
+import { TimeEntriesTimeRange } from '../models/time-entries-time-range';
export enum EntryActionTypes {
LOAD_ENTRIES_SUMMARY = '[Entry] LOAD_ENTRIES_SUMMARY',
@@ -34,6 +34,9 @@ export enum EntryActionTypes {
LOAD_ENTRIES_BY_TIME_RANGE_FAIL = '[Entry] LOAD_ENTRIES_BY_TIME_RANGE_FAIL',
SWITCH_TIME_ENTRY = '[Entry] SWITCH_TIME_ENTRY',
SWITCH_TIME_ENTRY_FAIL = '[Entry] SWITCH_TIME_ENTRY_FAIL',
+ RESTART_ENTRY = '[Entry] RESTART_ENTRY',
+ RESTART_ENTRY_SUCCESS = '[Entry] RESTART_ENTRY_SUCCESS',
+ RESTART_ENTRY_FAIL = '[Entry] RESTART_ENTRY_FAIL',
}
export class SwitchTimeEntry implements Action {
@@ -52,7 +55,9 @@ export class LoadEntriesSummary implements Action {
export class LoadEntriesSummarySuccess implements Action {
readonly type = EntryActionTypes.LOAD_ENTRIES_SUMMARY_SUCCESS;
- constructor(readonly payload: TimeEntriesSummary) {}
+
+ constructor(readonly payload: TimeEntriesSummary) {
+ }
}
export class LoadEntriesSummaryFail implements Action {
@@ -65,133 +70,185 @@ export class LoadActiveEntry implements Action {
export class LoadActiveEntrySuccess implements Action {
readonly type = EntryActionTypes.LOAD_ACTIVE_ENTRY_SUCCESS;
- constructor(readonly payload) {}
+
+ constructor(readonly payload) {
+ }
}
export class LoadActiveEntryFail implements Action {
public readonly type = EntryActionTypes.LOAD_ACTIVE_ENTRY_FAIL;
- constructor(public error: string) {}
+ constructor(public error: string) {
+ }
}
export class LoadEntries implements Action {
public readonly type = EntryActionTypes.LOAD_ENTRIES;
- constructor(public month: number) {}
+
+ constructor(public month: number) {
+ }
}
export class LoadEntriesSuccess implements Action {
readonly type = EntryActionTypes.LOAD_ENTRIES_SUCCESS;
- constructor(readonly payload: Entry[]) {}
+
+ constructor(readonly payload: Entry[]) {
+ }
}
export class LoadEntriesFail implements Action {
public readonly type = EntryActionTypes.LOAD_ENTRIES_FAIL;
- constructor(public error: string) {}
+ constructor(public error: string) {
+ }
}
export class CreateEntry implements Action {
public readonly type = EntryActionTypes.CREATE_ENTRY;
- constructor(public payload: NewEntry) {}
+ constructor(public payload: NewEntry) {
+ }
}
export class CreateEntrySuccess implements Action {
public readonly type = EntryActionTypes.CREATE_ENTRY_SUCCESS;
-
constructor(public payload: Entry) {}
}
export class CreateEntryFail implements Action {
public readonly type = EntryActionTypes.CREATE_ENTRY_FAIL;
- constructor(public error: string) {}
+ constructor(public error: string) {
+ }
}
export class DeleteEntry implements Action {
public readonly type = EntryActionTypes.DELETE_ENTRY;
- constructor(public entryId: string) {}
+ constructor(public entryId: string) {
+ }
}
export class DeleteEntrySuccess implements Action {
public readonly type = EntryActionTypes.DELETE_ENTRY_SUCCESS;
- constructor(public entryId: string) {}
+ constructor(public entryId: string) {
+ }
}
export class DeleteEntryFail implements Action {
public readonly type = EntryActionTypes.DELETE_ENTRY_FAIL;
- constructor(public error: string) {}
+ constructor(public error: string) {
+ }
}
export class UpdateEntryRunning implements Action {
public readonly type = EntryActionTypes.UPDATE_ENTRY_RUNNING;
- constructor(public payload) {}
+
+ constructor(public payload) {
+ }
}
export class UpdateEntry implements Action {
public readonly type = EntryActionTypes.UPDATE_ENTRY;
- constructor(public payload) {}
+ constructor(public payload) {
+ }
}
export class UpdateEntrySuccess implements Action {
public readonly type = EntryActionTypes.UPDATE_ENTRY_SUCCESS;
- constructor(public payload: Entry) {}
+ constructor(public payload: Entry) {
+ }
}
export class UpdateEntryFail implements Action {
public readonly type = EntryActionTypes.UPDATE_ENTRY_FAIL;
- constructor(public error: string) {}
+ constructor(public error: string) {
+ }
}
export class StopTimeEntryRunning implements Action {
public readonly type = EntryActionTypes.STOP_TIME_ENTRY_RUNNING;
- constructor(readonly payload: string) {}
+
+ constructor(readonly payload: string) {
+ }
}
export class StopTimeEntryRunningSuccess implements Action {
public readonly type = EntryActionTypes.STOP_TIME_ENTRY_RUNNING_SUCCESS;
- constructor(readonly payload) {}
+
+ constructor(readonly payload) {
+ }
}
export class StopTimeEntryRunningFail implements Action {
public readonly type = EntryActionTypes.STOP_TIME_ENTRY_RUNNING_FAILED;
- constructor(public error: string) {}
+
+ constructor(public error: string) {
+ }
}
export class CleanEntryCreateError implements Action {
public readonly type = EntryActionTypes.CLEAN_ENTRY_CREATE_ERROR;
- constructor(public error: boolean) {}
+
+ constructor(public error: boolean) {
+ }
}
export class CleanEntryUpdateError implements Action {
public readonly type = EntryActionTypes.CLEAN_ENTRY_UPDATE_ERROR;
- constructor(public error: boolean) {}
+
+ constructor(public error: boolean) {
+ }
}
+
export class DefaultEntry implements Action {
public readonly type = EntryActionTypes.DEFAULT_ENTRY;
}
export class LoadEntriesByTimeRange implements Action {
public readonly type = EntryActionTypes.LOAD_ENTRIES_BY_TIME_RANGE;
- constructor(readonly timeRange: TimeEntriesTimeRange, readonly userId: string= '*') {
+
+ constructor(readonly timeRange: TimeEntriesTimeRange, readonly userId: string = '*') {
}
}
export class LoadEntriesByTimeRangeSuccess implements Action {
readonly type = EntryActionTypes.LOAD_ENTRIES_BY_TIME_RANGE_SUCCESS;
- constructor(readonly payload: Entry[]) {}
+
+ constructor(readonly payload: Entry[]) {
+ }
}
export class LoadEntriesByTimeRangeFail implements Action {
readonly type = EntryActionTypes.LOAD_ENTRIES_BY_TIME_RANGE_FAIL;
}
+export class RestartEntry implements Action {
+ readonly type = EntryActionTypes.RESTART_ENTRY;
+
+ constructor(readonly entry: Entry) {
+ }
+}
+
+export class RestartEntrySuccess implements Action {
+ readonly type = EntryActionTypes.RESTART_ENTRY_SUCCESS;
+
+ constructor(readonly payload: Entry) {
+ }
+}
+
+export class RestartEntryFail implements Action {
+ public readonly type = EntryActionTypes.RESTART_ENTRY_FAIL;
+
+ constructor(public error: string) {
+ }
+}
+
export type EntryActions =
| LoadEntriesSummary
| LoadEntriesSummarySuccess
@@ -222,4 +279,7 @@ export type EntryActions =
| LoadEntriesByTimeRangeSuccess
| LoadEntriesByTimeRangeFail
| SwitchTimeEntry
- | SwitchTimeEntryFail;
+ | SwitchTimeEntryFail
+ | RestartEntry
+ | RestartEntrySuccess
+ | RestartEntryFail;
diff --git a/src/app/modules/time-clock/store/entry.effects.spec.ts b/src/app/modules/time-clock/store/entry.effects.spec.ts
index 498a064d3..a062078f7 100644
--- a/src/app/modules/time-clock/store/entry.effects.spec.ts
+++ b/src/app/modules/time-clock/store/entry.effects.spec.ts
@@ -136,6 +136,29 @@ describe('TimeEntryActionEffects', () => {
});
});
+ it('When the service returns a value, then RESTART_ENTRY_SUCCESS should be triggered', () => {
+
+ const entryId = '123';
+ actions$ = of({type: EntryActionTypes.RESTART_ENTRY, entryId});
+ const serviceSpy = spyOn(service, 'restartEntry');
+ serviceSpy.and.returnValue(of({}));
+
+ effects.restartEntry$.subscribe(action => {
+ expect(action.type).toEqual(EntryActionTypes.RESTART_ENTRY_SUCCESS);
+ });
+
+ });
+
+ it('When the service fails, then RESTART_ENTRY_FAIL should be triggered', async () => {
+ const entryId = '123';
+ actions$ = of({type: EntryActionTypes.LOAD_ENTRIES_BY_TIME_RANGE, entryId});
+ spyOn(service, 'restartEntry').and.returnValue(throwError('any error'));
+
+ effects.restartEntry$.subscribe(action => {
+ expect(action.type).toEqual(EntryActionTypes.RESTART_ENTRY_FAIL);
+ });
+ });
+
// TODO Implement the remaining unit tests for the other effects.
});
diff --git a/src/app/modules/time-clock/store/entry.effects.ts b/src/app/modules/time-clock/store/entry.effects.ts
index abb9cda1c..beddc1f8f 100644
--- a/src/app/modules/time-clock/store/entry.effects.ts
+++ b/src/app/modules/time-clock/store/entry.effects.ts
@@ -146,6 +146,7 @@ export class EntryEffects {
)
);
+
@Effect()
updateEntryRunning$: Observable = this.actions$.pipe(
ofType(actions.EntryActionTypes.UPDATE_ENTRY_RUNNING),
@@ -196,4 +197,21 @@ export class EntryEffects {
)
)
);
+
+ @Effect()
+ restartEntry$: Observable = this.actions$.pipe(
+ ofType(actions.EntryActionTypes.RESTART_ENTRY),
+ map((action: actions.RestartEntry) => action.entry),
+ mergeMap((entry) =>
+ this.entryService.restartEntry(entry.id).pipe(
+ map((entryResponse) => {
+ return new actions.RestartEntrySuccess(entryResponse);
+ }),
+ catchError((error) => {
+ this.toastrService.error( error.error.message, 'This entry could not be restarted');
+ return of(new actions.RestartEntryFail(error));
+ })
+ )
+ )
+ );
}
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 932018fda..2750771f1 100644
--- a/src/app/modules/time-entries/pages/time-entries.component.html
+++ b/src/app/modules/time-entries/pages/time-entries.component.html
@@ -64,10 +64,10 @@ {{ entryId ? 'Edit Entry' : '