Skip to content

Commit 4070736

Browse files
authored
Merge branch 'master' into 320-update-summaries-data
2 parents 07d5f9d + 5ef5e28 commit 4070736

File tree

7 files changed

+97
-36
lines changed

7 files changed

+97
-36
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "time-tracker",
3-
"version": "1.9.1",
3+
"version": "1.9.2",
44
"scripts": {
55
"ng": "ng",
66
"start": "ng serve",

src/app/modules/time-clock/components/entry-fields/entry-fields.component.html

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,39 @@
33
<div class="input-group-prepend">
44
<span class="input-group-text span-width">Activity</span>
55
</div>
6-
<select id="activitiesSelect" (blur)="onSubmit()" class="form-control" formControlName="activity_id">
6+
<select
7+
id="activitiesSelect"
8+
(blur)="onSubmit()"
9+
class="form-control"
10+
formControlName="activity_id"
11+
[class.is-invalid]="activity_id.invalid && activity_id.touched"
12+
required
13+
>
714
<option value="-1"></option>
8-
<option *ngFor="let activity of activities" value="{{activity.id}}">{{ activity.name }}</option>
15+
<option *ngFor="let activity of activities" value="{{ activity.id }}">{{ activity.name }}</option>
916
</select>
1017
</div>
1118
<div class="input-group input-group-sm mb-3">
1219
<div class="input-group-prepend">
1320
<span class="input-group-text span-width">Ticket URI</span>
1421
</div>
15-
<input (blur)="onSubmit()" type="text" id="uri" formControlName="uri" class="form-control" aria-label="Small"
16-
aria-describedby="inputGroup-sizing-sm"/>
22+
<input
23+
(blur)="onSubmit()"
24+
type="text"
25+
id="uri"
26+
formControlName="uri"
27+
class="form-control"
28+
aria-label="Small"
29+
aria-describedby="inputGroup-sizing-sm"
30+
/>
1731
</div>
18-
<app-technologies (technologyAdded)="onTechnologyAdded($event)"
19-
(technologyRemoved)="onTechnologyRemoved($event)"
20-
[selectedTechnologies]="selectedTechnologies">
32+
<app-technologies
33+
(technologyAdded)="onTechnologyAdded($event)"
34+
(technologyRemoved)="onTechnologyRemoved($event)"
35+
[selectedTechnologies]="selectedTechnologies"
36+
>
2137
</app-technologies>
2238

23-
2439
<div class="input-group input-group-sm mb-3">
2540
<div class="input-group-prepend">
2641
<span class="input-group-text span-width">Description</span>
@@ -31,8 +46,8 @@
3146
formControlName="description"
3247
class="form-control"
3348
id="NotesTextarea"
34-
rows="2">
49+
rows="2"
50+
>
3551
</textarea>
3652
</div>
37-
3853
</form>

src/app/modules/time-clock/components/entry-fields/entry-fields.component.ts

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import {getActiveTimeEntry} from './../../store/entry.selectors';
2-
import {Component, OnInit} from '@angular/core';
3-
import {FormBuilder, FormGroup} from '@angular/forms';
4-
import {select, Store} from '@ngrx/store';
1+
import { getActiveTimeEntry } from './../../store/entry.selectors';
2+
import { Component, OnInit } from '@angular/core';
3+
import { FormBuilder, FormGroup } from '@angular/forms';
4+
import { select, Store } from '@ngrx/store';
55

6-
import {Activity, NewEntry} from '../../../shared/models';
7-
import {ProjectState} from '../../../customer-management/components/projects/components/store/project.reducer';
8-
import {TechnologyState} from '../../../shared/store/technology.reducers';
9-
import {ActivityState, allActivities, LoadActivities} from '../../../activities-management/store';
6+
import { Activity, NewEntry } from '../../../shared/models';
7+
import { ProjectState } from '../../../customer-management/components/projects/components/store/project.reducer';
8+
import { TechnologyState } from '../../../shared/store/technology.reducers';
9+
import { ActivityState, allActivities, LoadActivities } from '../../../activities-management/store';
1010

1111
import * as entryActions from '../../store/entry.actions';
1212

@@ -18,7 +18,6 @@ type Merged = TechnologyState & ProjectState & ActivityState;
1818
styleUrls: ['./entry-fields.component.scss'],
1919
})
2020
export class EntryFieldsComponent implements OnInit {
21-
2221
entryForm: FormGroup;
2322
selectedTechnologies: string[] = [];
2423
activities: Activity[] = [];
@@ -29,7 +28,7 @@ export class EntryFieldsComponent implements OnInit {
2928
this.entryForm = this.formBuilder.group({
3029
description: '',
3130
uri: '',
32-
activity_id: '-1'
31+
activity_id: '-1',
3332
});
3433
}
3534

@@ -58,6 +57,10 @@ export class EntryFieldsComponent implements OnInit {
5857
});
5958
}
6059

60+
get activity_id() {
61+
return this.entryForm.get('activity_id');
62+
}
63+
6164
setDataToUpdate(entryData: NewEntry) {
6265
if (entryData) {
6366
this.entryForm.patchValue({
@@ -73,17 +76,19 @@ export class EntryFieldsComponent implements OnInit {
7376
}
7477
}
7578

79+
entryFormIsValidate() {
80+
return this.entryForm.valid;
81+
}
82+
7683
onSubmit() {
77-
this.store.dispatch(new entryActions.UpdateEntryRunning({...this.newData, ...this.entryForm.value}));
84+
this.store.dispatch(new entryActions.UpdateEntryRunning({ ...this.newData, ...this.entryForm.value }));
7885
}
7986

8087
onTechnologyAdded($event: string[]) {
81-
this.store.dispatch(new entryActions.UpdateEntryRunning({...this.newData, technologies: $event})
82-
);
88+
this.store.dispatch(new entryActions.UpdateEntryRunning({ ...this.newData, technologies: $event }));
8389
}
8490

8591
onTechnologyRemoved($event: string[]) {
86-
this.store.dispatch(new entryActions.UpdateEntryRunning({...this.newData, technologies: $event}));
92+
this.store.dispatch(new entryActions.UpdateEntryRunning({ ...this.newData, technologies: $event }));
8793
}
88-
8994
}

src/app/modules/time-clock/pages/time-clock.component.spec.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { ProjectListHoverComponent } from '../components';
99
import { FilterProjectPipe } from '../../shared/pipes';
1010
import { AzureAdB2CService } from '../../login/services/azure.ad.b2c.service';
1111
import { ActionsSubject } from '@ngrx/store';
12+
import { EntryFieldsComponent } from '../components/entry-fields/entry-fields.component';
13+
import { ToastrService } from 'ngx-toastr';
1214

1315
describe('TimeClockComponent', () => {
1416
let component: TimeClockComponent;
@@ -17,6 +19,11 @@ describe('TimeClockComponent', () => {
1719
let azureAdB2CService: AzureAdB2CService;
1820
const actionSub: ActionsSubject = new ActionsSubject();
1921

22+
let injectedToastrService;
23+
const toastrService = {
24+
error: () => {},
25+
};
26+
2027
const state = {
2128
projects: {
2229
projects: [{ id: 'id', name: 'name', project_type_id: '' }],
@@ -44,12 +51,13 @@ describe('TimeClockComponent', () => {
4451
beforeEach(async(() => {
4552
TestBed.configureTestingModule({
4653
imports: [HttpClientTestingModule],
47-
declarations: [TimeClockComponent, ProjectListHoverComponent, FilterProjectPipe],
54+
declarations: [TimeClockComponent, ProjectListHoverComponent, FilterProjectPipe, EntryFieldsComponent],
4855
providers: [
4956
FormBuilder,
5057
AzureAdB2CService,
5158
provideMockStore({ initialState: state }),
5259
{ provide: ActionsSubject, useValue: actionSub },
60+
{ provide: ToastrService, useValue: toastrService },
5361
],
5462
}).compileComponents();
5563
store = TestBed.inject(MockStore);
@@ -60,6 +68,7 @@ describe('TimeClockComponent', () => {
6068
component = fixture.componentInstance;
6169
fixture.detectChanges();
6270
azureAdB2CService = TestBed.inject(AzureAdB2CService);
71+
injectedToastrService = TestBed.inject(ToastrService);
6372
});
6473

6574
it('should be created', () => {
@@ -110,11 +119,27 @@ describe('TimeClockComponent', () => {
110119
expect(azureAdB2CService.getName).toHaveBeenCalledTimes(0);
111120
});
112121

113-
it('clockOut dispatch a StopTimeEntryRunning action', () => {
122+
it('stopEntry dispatch a StopTimeEntryRunning action', () => {
114123
spyOn(store, 'dispatch');
115124

125+
component.stopEntry();
126+
expect(store.dispatch).toHaveBeenCalledWith(new StopTimeEntryRunning('id'));
127+
});
128+
129+
it('clockOut dispatch a StopTimeEntryRunning action', () => {
130+
spyOn(store, 'dispatch');
131+
spyOn(component.entryFieldsComponent, 'entryFormIsValidate').and.returnValue(true);
116132
component.clockOut();
117133

118134
expect(store.dispatch).toHaveBeenCalledWith(new StopTimeEntryRunning('id'));
119135
});
136+
137+
it('clockOut set error Activity is required', () => {
138+
spyOn(store, 'dispatch');
139+
spyOn(injectedToastrService, 'error');
140+
spyOn(component.entryFieldsComponent, 'entryFormIsValidate').and.returnValue(false);
141+
component.clockOut();
142+
143+
expect(injectedToastrService.error).toHaveBeenCalled();
144+
});
120145
});

src/app/modules/time-clock/pages/time-clock.component.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,31 @@ import { getActiveTimeEntry } from './../store/entry.selectors';
33
import { StopTimeEntryRunning, EntryActionTypes, LoadEntriesSummary } from './../store/entry.actions';
44
import { Entry } from './../../shared/models/entry.model';
55
import { Store, select, ActionsSubject } from '@ngrx/store';
6-
import { Component, OnInit, OnDestroy } from '@angular/core';
6+
import { Component, OnInit, ViewChild, OnDestroy } from '@angular/core';
77
import { AzureAdB2CService } from '../../login/services/azure.ad.b2c.service';
88
import { Subscription } from 'rxjs';
9-
9+
import { EntryFieldsComponent } from '../components/entry-fields/entry-fields.component';
10+
import { ToastrService } from 'ngx-toastr';
1011
@Component({
1112
selector: 'app-time-clock',
1213
templateUrl: './time-clock.component.html',
1314
styleUrls: ['./time-clock.component.scss'],
1415
})
1516
export class TimeClockComponent implements OnInit, OnDestroy {
17+
@ViewChild(EntryFieldsComponent)
18+
entryFieldsComponent: EntryFieldsComponent;
1619
username: string;
1720
areFieldsVisible = false;
1821
activeTimeEntry: Entry;
1922
clockOutSubscription: Subscription;
2023

21-
constructor(private azureAdB2CService: AzureAdB2CService, private store: Store<Entry>,
22-
private actionsSubject$: ActionsSubject) {
23-
}
24+
25+
constructor(
26+
private azureAdB2CService: AzureAdB2CService,
27+
private store: Store<Entry>,
28+
private toastrService: ToastrService,
29+
private actionsSubject$: ActionsSubject
30+
) {}
2431

2532
ngOnDestroy(): void {
2633
this.clockOutSubscription.unsubscribe();
@@ -52,8 +59,17 @@ export class TimeClockComponent implements OnInit, OnDestroy {
5259
});
5360
}
5461

55-
clockOut() {
62+
stopEntry() {
5663
this.store.dispatch(new StopTimeEntryRunning(this.activeTimeEntry.id));
5764
this.areFieldsVisible = false;
5865
}
66+
67+
clockOut() {
68+
if (this.entryFieldsComponent.entryFormIsValidate()) {
69+
this.stopEntry();
70+
} else {
71+
this.entryFieldsComponent.entryForm.get('activity_id').markAsTouched();
72+
this.toastrService.error('Activity is required');
73+
}
74+
}
5975
}

src/app/modules/time-entries/pages/time-entries.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ export class TimeEntriesComponent implements OnInit {
9797

9898
openModal(item: any) {
9999
this.idToDelete = item.id;
100-
this.message = `Are you sure you want to delete ${item.activity_name}`;
100+
this.message = `Are you sure you want to delete ${item.activity_name}?`;
101101
this.showModal = true;
102102
}
103103
}

0 commit comments

Comments
 (0)