Skip to content

Commit 835d03c

Browse files
committed
fix: TTA-29 Selecting multiple time entries and show total of hours of selected entries
1 parent 358e576 commit 835d03c

File tree

12 files changed

+86
-8
lines changed

12 files changed

+86
-8
lines changed

.dev.env

2 Bytes
Binary file not shown.

src/app/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { StoreModule } from '@ngrx/store';
1111
import { EffectsModule } from '@ngrx/effects';
1212
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
1313
import { DragDropModule } from '@angular/cdk/drag-drop';
14+
import { MatCheckboxModule } from '@angular/material/checkbox';
1415
import { MatDatepickerModule } from '@angular/material/datepicker';
1516
import { MatInputModule } from '@angular/material/input';
1617
import { MatMomentDateModule } from '@angular/material-moment-adapter';
@@ -149,6 +150,7 @@ const maskConfig: Partial<IConfig> = {
149150
],
150151
imports: [
151152
NgxMaskModule.forRoot(maskConfig),
153+
MatCheckboxModule,
152154
MatInputModule,
153155
MatDatepickerModule,
154156
MatMomentDateModule,

src/app/modules/reports/components/time-entries-table/time-entries-table.component.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<table class="table table-striped mb-0" datatable [dtTrigger]="dtTrigger" [dtOptions]="dtOptions" *ngIf="(reportDataSource$ | async) as dataSource">
55
<thead class="thead-blue">
66
<tr class="d-flex">
7+
<th class="col md-col">Selected</th>
78
<th class="hidden-col">ID</th>
89
<th class="col md-col">User email</th>
910
<th class="col sm-col">Date</th>
@@ -23,6 +24,8 @@
2324
<app-loading-bar *ngIf="dataSource.isLoading"></app-loading-bar>
2425
<tbody *ngIf="!dataSource.isLoading">
2526
<tr class="d-flex col-height" *ngFor="let entry of dataSource.data">
27+
<td class="col md-col"><mat-checkbox
28+
(change)="sumHoursEntriesSelected(entry, $event.checked)"></mat-checkbox></td>
2629
<td class="hidden-col">{{ entry.id }}</td>
2730
<td class="col md-col">{{ entry.owner_email }}</td>
2831
<td class="col sm-col">
@@ -57,4 +60,5 @@
5760
</tbody>
5861
</table>
5962
</div>
60-
<div class="alert alert-dark mt-3">Total: {{this.resultSum.hours}} hours, {{this.resultSum.minutes}} minutes</div>
63+
<div class="alert alert-dark mt-3">Total: {{this.resultSum.hours}} hours, {{this.resultSum.minutes}} minutes,
64+
<br/> Total hours entries selected: {{resultSumEntriesSelected.hours}} hours, {{resultSumEntriesSelected.minutes}} minutes</div>

src/app/modules/reports/components/time-entries-table/time-entries-table.component.spec.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { DataTablesModule } from 'angular-datatables';
44
import { NgxPaginationModule } from 'ngx-pagination';
55
import { Entry } from 'src/app/modules/shared/models';
66
import { SubstractDatePipe } from 'src/app/modules/shared/pipes/substract-date/substract-date.pipe';
7-
import { getReportDataSource } from 'src/app/modules/time-clock/store/entry.selectors';
7+
import { getReportDataSource, getResultSumEntriesSelected } from 'src/app/modules/time-clock/store/entry.selectors';
88
import { EntryState } from '../../../time-clock/store/entry.reducer';
99
import { TimeEntriesTableComponent } from './time-entries-table.component';
1010
import { TotalHours } from '../../models/total-hours-report';
@@ -17,6 +17,7 @@ describe('Reports Page', () => {
1717
let fixture: ComponentFixture<TimeEntriesTableComponent>;
1818
let store: MockStore<EntryState>;
1919
let getReportDataSourceSelectorMock;
20+
let getResultSumEntriesSelectedMock;
2021
let durationTime: number;
2122
let row: number;
2223
let node: number;
@@ -61,6 +62,7 @@ describe('Reports Page', () => {
6162
const state: EntryState = {
6263
active: timeEntry,
6364
isLoading: false,
65+
resultSumEntriesSelected: new TotalHours(),
6466
message: '',
6567
createError: false,
6668
updateError: false,
@@ -95,6 +97,7 @@ describe('Reports Page', () => {
9597
store = TestBed.inject(MockStore);
9698
store.setState(state);
9799
getReportDataSourceSelectorMock = store.overrideSelector(getReportDataSource, state.reportDataSource);
100+
getResultSumEntriesSelectedMock = store.overrideSelector(getResultSumEntriesSelected, state.resultSumEntriesSelected);
98101
fixture.detectChanges();
99102
}
100103
);
@@ -207,6 +210,14 @@ describe('Reports Page', () => {
207210
expect({hours, minutes, seconds}).toEqual({hours:3,minutes:20,seconds:0});
208211
});
209212

213+
it('the sume of hours of entries selected is equal to {hours:0, minutes:0, seconds:0}', () => {
214+
let checked = true;
215+
let{hours, minutes, seconds}:TotalHours = component.sumHoursEntriesSelected(timeEntryList[0], checked);
216+
checked = false;
217+
({hours, minutes,seconds} = component.sumHoursEntriesSelected(timeEntryList[0], checked));
218+
expect({hours, minutes, seconds}).toEqual({hours:0, minutes:0, seconds:0});
219+
});
220+
210221
afterEach(() => {
211222
fixture.destroy();
212223
});

src/app/modules/reports/components/time-entries-table/time-entries-table.component.ts

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { filter } from 'rxjs/operators';
88
import { Entry } from 'src/app/modules/shared/models';
99
import { DataSource } from 'src/app/modules/shared/models/data-source.model';
1010
import { EntryState } from '../../../time-clock/store/entry.reducer';
11-
import { getReportDataSource } from '../../../time-clock/store/entry.selectors';
11+
import { getReportDataSource, getResultSumEntriesSelected } from '../../../time-clock/store/entry.selectors';
1212
import { TotalHours } from '../../models/total-hours-report';
1313
import { User } from 'src/app/modules/users/models/users';
1414
import { LoadUsers, UserActionTypes } from 'src/app/modules/users/store/user.actions';
@@ -24,7 +24,9 @@ export class TimeEntriesTableComponent implements OnInit, OnDestroy, AfterViewIn
2424

2525
selectOptionValues = [15, 30, 50, 100, -1];
2626
selectOptionNames = [15, 30, 50, 100, 'All'];
27+
totalTimeSelected: moment.Duration;
2728
users: User[] = [];
29+
2830
dtOptions: any = {
2931
scrollY: '590px',
3032
dom: '<"d-flex justify-content-between"B<"d-flex"<"mr-5"l>f>>rtip',
@@ -60,7 +62,7 @@ export class TimeEntriesTableComponent implements OnInit, OnDestroy, AfterViewIn
6062
filename: `time-entries-${formatDate(new Date(), 'MM_dd_yyyy-HH_mm', 'en')}`
6163
},
6264
],
63-
columnDefs: [{ type: 'date', targets: 2}],
65+
columnDefs: [{ type: 'date', targets: 2}, {orderable: false, targets: [0]}],
6466
order: [[1,'asc'],[2,'desc'],[4,'desc']]
6567
};
6668
dtTrigger: Subject<any> = new Subject();
@@ -70,11 +72,17 @@ export class TimeEntriesTableComponent implements OnInit, OnDestroy, AfterViewIn
7072
reportDataSource$: Observable<DataSource<Entry>>;
7173
rerenderTableSubscription: Subscription;
7274
resultSum: TotalHours;
75+
resultSumEntriesSelected: TotalHours;
76+
resultSumEntriesSelected$:Observable<TotalHours>;
77+
totalHoursSubscription: Subscription;
7378
dateTimeOffset: ParseDateTimeOffset;
7479

80+
7581
constructor(private store: Store<EntryState>, private actionsSubject$: ActionsSubject, private storeUser: Store<User> ) {
76-
this.reportDataSource$ = this.store.pipe(select(getReportDataSource));
77-
this.dateTimeOffset = new ParseDateTimeOffset();
82+
this.reportDataSource$ = this.store.pipe(select(getReportDataSource));
83+
this.resultSumEntriesSelected$ = this.store.pipe(select(getResultSumEntriesSelected));
84+
this.dateTimeOffset = new ParseDateTimeOffset();
85+
this.resultSumEntriesSelected = new TotalHours();
7886
}
7987

8088
uploadUsers(): void {
@@ -88,6 +96,10 @@ export class TimeEntriesTableComponent implements OnInit, OnDestroy, AfterViewIn
8896

8997
ngOnInit(): void {
9098
this.rerenderTableSubscription = this.reportDataSource$.subscribe((ds) => {
99+
this.totalHoursSubscription = this.resultSumEntriesSelected$.subscribe((actTotalHours) => {
100+
this.resultSumEntriesSelected = actTotalHours ;
101+
this.totalTimeSelected = moment.duration(0);
102+
});
91103
this.sumDates(ds.data);
92104
this.rerenderDataTable();
93105
});
@@ -128,15 +140,15 @@ export class TimeEntriesTableComponent implements OnInit, OnDestroy, AfterViewIn
128140
const durationColumnIndex = 3;
129141
return column === durationColumnIndex ? moment.duration(dataFormated).asHours().toFixed(2) : dataFormated;
130142
}
131-
143+
132144
sumDates(arrayData: Entry[]): TotalHours{
133145
this.resultSum = new TotalHours();
134146
let arrayDurations= new Array();
135147
arrayData.forEach(entry =>{
136148
let start = moment(entry.end_date).diff(moment(entry.start_date));
137149
arrayDurations.push(moment.utc(start).format("HH:mm:ss"));
138150
});
139-
151+
140152
let totalDurations = arrayDurations.slice(1)
141153
.reduce((prev, cur) => {
142154
return prev.add(cur);
@@ -153,5 +165,20 @@ export class TimeEntriesTableComponent implements OnInit, OnDestroy, AfterViewIn
153165
this.selectedUserId.emit(userId);
154166
}
155167

168+
sumHoursEntriesSelected(entry: Entry, checked: boolean){
169+
this.resultSumEntriesSelected = new TotalHours();
170+
const duration = moment.duration(moment(entry.end_date).diff(moment(entry.start_date)));
171+
if(checked){
172+
this.totalTimeSelected = this.totalTimeSelected.add(duration);
173+
}
174+
else{
175+
this.totalTimeSelected = this.totalTimeSelected.subtract(duration);
176+
}
177+
let daysTotalInHours = this.totalTimeSelected.days() * 24;
178+
this.resultSumEntriesSelected.hours = this.totalTimeSelected.hours() + daysTotalInHours;
179+
this.resultSumEntriesSelected.minutes = this.totalTimeSelected.minutes();
180+
this.resultSumEntriesSelected.seconds = this.totalTimeSelected.seconds();
181+
return this.resultSumEntriesSelected;
182+
}
156183
}
157184

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,20 @@ describe('DetailsFieldsComponent', () => {
715715
expect(component.entryForm.value.project_name).toBe('name');
716716
});
717717

718+
it('it calls the close method if opened equals true', () => {
719+
const datepicker: any = { opened : true, open : () => ({}), close : () => ({}) };
720+
spyOn(datepicker, 'close');
721+
component.openOrCloseDatePicker(datepicker);
722+
expect(datepicker.close).toHaveBeenCalled();
723+
});
724+
725+
it('it calls the open method if opened equals false', () => {
726+
const datepicker: any = { opened : false, open : () => ({}), close : () => ({}) };
727+
spyOn(datepicker, 'open');
728+
component.openOrCloseDatePicker(datepicker);
729+
expect(datepicker.open).toHaveBeenCalled();
730+
});
731+
718732
/*
719733
TODO As part of https://github.com/ioet/time-tracker-ui/issues/424 a new parameter was added to the details-field-component,
720734
and now these couple of tests are failing. A solution to this error might be generate a Test Wrapper Component. More details here:

src/app/modules/time-clock/components/time-entries-summary/time-entries-summary.component.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { waitForAsync, ComponentFixture, discardPeriodicTasks, fakeAsync, TestBe
22
import { ActionsSubject } from '@ngrx/store';
33
import { MockStore, provideMockStore } from '@ngrx/store/testing';
44
import { interval } from 'rxjs';
5+
import { TotalHours } from 'src/app/modules/reports/models/total-hours-report';
56
import { Entry } from 'src/app/modules/shared/models/entry.model';
67
import { TimeDetails, TimeEntriesSummary } from './../../models/time.entry.summary';
78
import { TimeDetailsPipe } from './../../pipes/time-details.pipe';
@@ -36,6 +37,7 @@ describe('TimeEntriesSummaryComponent', () => {
3637
const state: EntryState = {
3738
active: timeEntry,
3839
isLoading: false,
40+
resultSumEntriesSelected: new TotalHours(),
3941
message: '',
4042
createError: false,
4143
updateError: false,

src/app/modules/time-clock/store/entry.reducer.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { TotalHours } from '../../reports/models/total-hours-report';
12
import { TimeDetails, TimeEntriesSummary } from '../models/time.entry.summary';
23
import { Entry, NewEntry } from './../../shared/models';
34
import * as actions from './entry.actions';
@@ -15,6 +16,7 @@ describe('entryReducer', () => {
1516
message: '',
1617
createError: null,
1718
updateError: null,
19+
resultSumEntriesSelected: new TotalHours(),
1820
timeEntriesSummary: emptyTimeEntriesSummary,
1921
timeEntriesDataSource: {
2022
data: [],

src/app/modules/time-clock/store/entry.reducer.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ import { Entry } from '../../shared/models';
22
import { DataSource } from '../../shared/models/data-source.model';
33
import { TimeDetails, TimeEntriesSummary } from '../models/time.entry.summary';
44
import { EntryActions, EntryActionTypes } from './entry.actions';
5+
import { TotalHours } from '../../reports/models/total-hours-report';
56

67
export interface EntryState {
78
active: Entry;
89
isLoading: boolean;
10+
resultSumEntriesSelected: TotalHours;
911
message: string;
1012
createError: boolean;
1113
updateError: boolean;
@@ -20,6 +22,7 @@ const emptyTimeEntriesSummary: TimeEntriesSummary = { day: emptyTimeDetails, wee
2022
export const initialState = {
2123
active: null,
2224
isLoading: false,
25+
resultSumEntriesSelected: new TotalHours(),
2326
message: '',
2427
createError: null,
2528
updateError: null,
@@ -263,6 +266,7 @@ export const entryReducer = (state: EntryState = initialState, action: EntryActi
263266
return {
264267
...state,
265268
isLoading: true,
269+
resultSumEntriesSelected:{hours:0, minutes:0, seconds:0},
266270
reportDataSource: {
267271
data: [],
268272
isLoading: true

src/app/modules/time-clock/store/entry.selectors.spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { TotalHours } from '../../reports/models/total-hours-report';
12
import { Entry } from '../../shared/models';
23
import { TimeDetails, TimeEntriesSummary } from '../models/time.entry.summary';
34
import * as selectors from './entry.selectors';
@@ -52,4 +53,11 @@ describe('Entry selectors', () => {
5253

5354
expect(selectors.getUpdateError.projector(entryState)).toEqual(error);
5455
});
56+
57+
it('should select resultSumEntriesSelected', () => {
58+
const resultSumEntriesSelected:TotalHours = { hours:0, minutes:0, seconds:0 };
59+
const entryState = { resultSumEntriesSelected };
60+
61+
expect(selectors.getResultSumEntriesSelected.projector(entryState)).toEqual(resultSumEntriesSelected);
62+
});
5563
});

0 commit comments

Comments
 (0)