Skip to content

Commit ca162c5

Browse files
DiegoTinitanaenriquezrene
authored andcommitted
fix: #238 Add_error_message_next_to_fields
1 parent 0b6fb39 commit ca162c5

File tree

5 files changed

+98
-65
lines changed

5 files changed

+98
-65
lines changed

src/app/modules/shared/components/details-fields/details-fields.component.html

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
11
<form [formGroup]="entryForm" (ngSubmit)="onSubmit()">
22
<div class="input-group input-group-sm mb-3">
33
<div class="input-group-prepend">
4-
<span class="input-group-text span-width" id="inputGroup-sizing-sm">Project</span>
4+
<span class="input-group-text span-width">Project</span>
55
</div>
66
<select
77
[class.is-invalid]="project_id.invalid && project_id.touched"
88
required
99
id="project_id"
10-
class="form-control"
10+
class="custom-select"
1111
formControlName="project_id"
1212
>
13-
<option *ngFor="let project of listProjects" value="{{project.id}}">{{ project.name }}</option>
13+
<option *ngFor="let project of listProjects" value="{{ project.id }}">{{ project.name }}</option>
1414
</select>
15+
<div
16+
class="invalid-feedback"
17+
*ngIf="(project_id.dirty || project_id.touched) && project_id.invalid && project_id.errors.required"
18+
>
19+
Project is required.
20+
</div>
1521
</div>
22+
1623
<div class="input-group input-group-sm mb-3">
1724
<div class="input-group-prepend">
1825
<span class="input-group-text span-width" id="inputGroup-sizing-sm">Activity</span>
@@ -21,11 +28,17 @@
2128
[class.is-invalid]="activity_id.invalid && activity_id.touched"
2229
required
2330
id="activity_id"
24-
class="form-control"
31+
class="custom-select"
2532
formControlName="activity_id"
2633
>
27-
<option *ngFor="let activity of activities" value="{{activity.id}}">{{ activity.name }}</option>
34+
<option *ngFor="let activity of activities" value="{{ activity.id }}">{{ activity.name }}</option>
2835
</select>
36+
<div
37+
class="invalid-feedback"
38+
*ngIf="(activity_id.dirty || activity_id.touched) && activity_id.invalid && activity_id.errors.required"
39+
>
40+
Activity is required.
41+
</div>
2942
</div>
3043

3144
<div class="input-group input-group-sm mb-3">
@@ -53,9 +66,15 @@
5366
class="form-control"
5467
aria-label="Small"
5568
aria-describedby="inputGroup-sizing-sm"
56-
[class.is-invalid]="start_date.invalid && start_date.touched || hoursValidation"
69+
[class.is-invalid]="(start_date.invalid && start_date.touched)"
5770
required
5871
/>
72+
<div
73+
class="invalid-feedback"
74+
*ngIf="(start_date.dirty || start_date.touched) && start_date.invalid && start_date.errors.required"
75+
>
76+
Start date is required.
77+
</div>
5978
<div class="input-group-prepend">
6079
<span class="input-group-text span-width" id="inputGroup-sizing-sm">Start/Hour</span>
6180
</div>
@@ -65,10 +84,16 @@
6584
type="text"
6685
class="form-control"
6786
aria-label="Small"
68-
[class.is-invalid]="start_hour.invalid && start_hour.touched || hoursValidation"
87+
[class.is-invalid]="(start_hour.invalid && start_hour.touched)"
6988
required
7089
aria-describedby="inputGroup-sizing-sm"
7190
/>
91+
<div
92+
class="invalid-feedback"
93+
*ngIf="(start_hour.dirty || start_hour.touched) && start_hour.invalid && start_hour.errors.required"
94+
>
95+
Start hour is required.
96+
</div>
7297
</div>
7398
<div class="input-group input-group-sm mb-3">
7499
<div class="input-group-prepend">
@@ -84,6 +109,12 @@
84109
required
85110
aria-describedby="inputGroup-sizing-sm"
86111
/>
112+
<div
113+
class="invalid-feedback"
114+
*ngIf="(end_date.dirty || end_date.touched) && end_date.invalid && end_date.errors.required"
115+
>
116+
End date is required.
117+
</div>
87118
<div class="input-group-prepend">
88119
<span class="input-group-text span-width" id="inputGroup-sizing-sm">End/Hour</span>
89120
</div>
@@ -93,24 +124,32 @@
93124
id="end_hour"
94125
class="form-control"
95126
aria-label="Small"
96-
[class.is-invalid]="end_hour.invalid && end_hour.touched"
127+
[class.is-invalid]="(end_hour.invalid && end_hour.touched) || errorDate"
97128
required
98129
aria-describedby="inputGroup-sizing-sm"
99130
/>
131+
<div
132+
class="invalid-feedback"
133+
*ngIf="((end_hour.dirty || end_hour.touched) && end_hour.invalid && end_hour.errors.required) || errorDate"
134+
>
135+
End hour is required.
136+
</div>
100137
</div>
101138

102-
<app-technologies (technologyAdded)="onTechnologiesUpdated($event)"
103-
(technologyRemoved)="onTechnologiesUpdated($event)"
104-
[selectedTechnologies]="selectedTechnologies">
139+
<app-technologies
140+
(technologyAdded)="onTechnologiesUpdated($event)"
141+
(technologyRemoved)="onTechnologiesUpdated($event)"
142+
[selectedTechnologies]="selectedTechnologies"
143+
>
105144
</app-technologies>
106145

107146
<div class="form-group text-left">
108147
<label for="NotesTextarea">Description</label>
109148
<textarea formControlName="description" class="form-control" id="NotesTextarea" rows="3"></textarea>
110149
</div>
111150
<div class="modal-footer">
112-
<button type="submit" class="btn btn-primary">Save</button>
113-
<button #closeModal type="button" class="btn btn-secondary" data-dismiss="modal">
151+
<button type="submit" class="btn btn-primary" [disabled]="!entryForm.valid">Save</button>
152+
<button #closeModal type="button" class="btn btn-secondary" data-dismiss="modal" (click)="close()">
114153
Close
115154
</button>
116155
</div>

src/app/modules/shared/components/details-fields/details-fields.component.spec.ts

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ describe('DetailsFieldsComponent', () => {
140140
});
141141

142142
it('should emit ngOnChange with new data', () => {
143+
const childComponent = jasmine.createSpyObj('ChildComponent', ['closeModal']);
144+
component.closeModal = childComponent;
143145
const formValue = {
144146
project_id: '',
145147
activity_id: '',
@@ -157,13 +159,17 @@ describe('DetailsFieldsComponent', () => {
157159
});
158160

159161
it('should call createError ', () => {
162+
const childComponent = jasmine.createSpyObj('ChildComponent', ['closeModal']);
163+
component.closeModal = childComponent;
160164
mockEntriesCreateErrorSelector = store.overrideSelector(getCreateError, false);
161165
spyOn(store, 'dispatch');
162166
component.ngOnInit();
163167
expect(store.dispatch).toHaveBeenCalledWith(new entryActions.CleanEntryCreateError(null));
164168
});
165169

166170
it('should call updateError ', () => {
171+
const childComponent = jasmine.createSpyObj('ChildComponent', ['closeModal']);
172+
component.closeModal = childComponent;
167173
mockEntriesUpdateErrorSelector = store.overrideSelector(getUpdateError, false);
168174
spyOn(store, 'dispatch');
169175
component.ngOnInit();
@@ -172,44 +178,25 @@ describe('DetailsFieldsComponent', () => {
172178

173179
it('should emit saveEntry event', () => {
174180
spyOn(component.saveEntry, 'emit');
175-
component.onSubmit();
176-
const data = {
181+
component.entryForm.setValue({
177182
project_id: '',
178183
activity_id: '',
179-
technologies: [],
180-
description: '',
181-
start_date: 'T00:00',
182-
end_date: 'T00:00',
183184
uri: '',
184-
};
185-
expect(component.saveEntry.emit).toHaveBeenCalledWith(data);
186-
});
187-
188-
it('should emit saveEntry without project and activite fields event', () => {
189-
spyOn(component.saveEntry, 'emit');
190-
component.entryForm.setValue({
191-
project_id: 'id',
192-
activity_id: 'fc5fab41-a21e-4155-9d05-511b956ebd05',
193-
uri: '',
194-
start_date: '',
195-
end_date: '',
185+
start_date: '2020-02-05',
186+
end_date: '2020-02-05',
196187
start_hour: '00:00',
197-
end_hour: '00:00',
188+
end_hour: '00:01',
198189
description: '',
199190
technology: '',
200191
});
201-
component.activities = [
202-
{ id: 'fc5fab41-a21e-4155-9d05-511b956ebd05', tenant_id: 'ioet', name: 'activity1', description: '' },
203-
];
204192
component.onSubmit();
205-
fixture.detectChanges();
206193
const data = {
207-
project_id: 'id',
208-
activity_id: 'fc5fab41-a21e-4155-9d05-511b956ebd05',
194+
project_id: '',
195+
activity_id: '',
209196
technologies: [],
210197
description: '',
211-
start_date: 'T00:00',
212-
end_date: 'T00:00',
198+
start_date: '2020-02-05T00:00',
199+
end_date: '2020-02-05T00:01',
213200
uri: '',
214201
};
215202
expect(component.saveEntry.emit).toHaveBeenCalledWith(data);

src/app/modules/shared/components/details-fields/details-fields.component.ts

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -35,22 +35,16 @@ export class DetailsFieldsComponent implements OnChanges, OnInit {
3535
@Input() formType: string;
3636
@Output() saveEntry = new EventEmitter();
3737
@ViewChild('closeModal') closeModal: ElementRef;
38-
@ViewChild('list') list: ElementRef;
3938
entryForm: FormGroup;
4039
selectedTechnologies: string[] = [];
4140
isLoading = false;
4241
listProjects: Project[] = [];
4342
activities: Activity[] = [];
4443
keyword = 'name';
4544
showlist: boolean;
46-
hoursValidation: boolean;
45+
errorDate: boolean;
4746

4847
constructor(private formBuilder: FormBuilder, private store: Store<Merged>, private renderer: Renderer2) {
49-
this.renderer.listen('window', 'click', (e: Event) => {
50-
if (this.showlist && !this.list.nativeElement.contains(e.target)) {
51-
this.showlist = false;
52-
}
53-
});
5448
this.entryForm = this.formBuilder.group({
5549
project_id: '',
5650
activity_id: '',
@@ -65,7 +59,6 @@ export class DetailsFieldsComponent implements OnChanges, OnInit {
6559
}
6660

6761
ngOnInit(): void {
68-
6962
this.store.dispatch(new projectActions.LoadProjects());
7063
const projects$ = this.store.pipe(select(getProjects));
7164
projects$.subscribe((response) => {
@@ -95,7 +88,6 @@ export class DetailsFieldsComponent implements OnChanges, OnInit {
9588
}
9689

9790
ngOnChanges(): void {
98-
this.hoursValidation = false;
9991
if (this.entryToEdit) {
10092
this.selectedTechnologies = this.entryToEdit.technologies;
10193
this.entryForm.setValue({
@@ -110,21 +102,25 @@ export class DetailsFieldsComponent implements OnChanges, OnInit {
110102
technology: '',
111103
});
112104
} else {
113-
this.selectedTechnologies = [];
114-
this.entryForm.setValue({
115-
project_id: '',
116-
activity_id: '',
117-
description: '',
118-
start_date: formatDate(new Date(), 'yyyy-MM-dd', 'en'),
119-
start_hour: '00:00',
120-
end_date: formatDate(new Date(), 'yyyy-MM-dd', 'en'),
121-
end_hour: '00:00',
122-
uri: '',
123-
technology: '',
124-
});
105+
this.cleanForm();
125106
}
126107
}
127108

109+
cleanForm() {
110+
this.selectedTechnologies = [];
111+
this.entryForm.setValue({
112+
project_id: '',
113+
activity_id: '',
114+
description: '',
115+
start_date: formatDate(new Date(), 'yyyy-MM-dd', 'en'),
116+
start_hour: '00:00',
117+
end_date: formatDate(new Date(), 'yyyy-MM-dd', 'en'),
118+
end_hour: '00:00',
119+
uri: '',
120+
technology: '',
121+
});
122+
}
123+
128124
onTechnologiesUpdated($event: string[]) {
129125
this.selectedTechnologies = $event;
130126
}
@@ -153,8 +149,13 @@ export class DetailsFieldsComponent implements OnChanges, OnInit {
153149
}
154150

155151
closeEntryModal() {
152+
this.close();
153+
this.closeModal.nativeElement.click();
154+
}
155+
156+
close() {
156157
this.entryForm.reset();
157-
this.closeModal?.nativeElement?.click();
158+
this.cleanForm();
158159
}
159160

160161
onSubmit() {
@@ -167,6 +168,12 @@ export class DetailsFieldsComponent implements OnChanges, OnInit {
167168
end_date: `${this.entryForm.value.end_date}T${this.entryForm.value.end_hour}`,
168169
uri: this.entryForm.value.uri,
169170
};
170-
this.saveEntry.emit(entry);
171+
172+
if (new Date(entry.start_date) < new Date(entry.end_date)) {
173+
this.errorDate = false;
174+
this.saveEntry.emit(entry);
175+
} else {
176+
this.errorDate = true;
177+
}
171178
}
172179
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ describe('TimeEntriesComponent', () => {
127127
};
128128
mockEntriesSelector = store.overrideSelector(allEntries, [newEntry]);
129129
component.ngOnInit();
130-
expect(component.dataByMonth.length).toEqual(1);
130+
expect(component.dataByMonth.length).toEqual(0);
131131
}));
132132

133133
it('should call dataByMonth without new date in ngOnInit()', async(() => {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export class TimeEntriesComponent implements OnInit {
2626
dataByMonth$.subscribe((response) => {
2727
this.entryList = response;
2828
this.dataByMonth = this.entryList.reduce((acc: any, entry: any) => {
29-
if (new Date(entry.start_date).getMonth() === new Date().getMonth()) {
29+
if (new Date(entry.start_date).getMonth() === new Date().getMonth() && entry.end_date) {
3030
const item = { ...entry };
3131
return [...acc, item];
3232
}

0 commit comments

Comments
 (0)