Skip to content

Commit 9faff9b

Browse files
authored
fix: TTA-29 Selecting multiple time entries and show total of hours o… (#896)
* fix: TTA-29 Selecting multiple time entries and show total of hours of selected entries * refactor test TimeEntriesTableComponent * Refactor sumHoursEntriesSelected * resolved conflicts
1 parent dfb2ae1 commit 9faff9b

File tree

10 files changed

+67
-11
lines changed

10 files changed

+67
-11
lines changed

src/app/app.module.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ 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 { MatIconModule } from '@angular/material/icon';
17-
import { MatCheckboxModule } from '@angular/material/checkbox';
1818
import { MatListModule } from '@angular/material/list';
1919
import { MatMomentDateModule } from '@angular/material-moment-adapter';
2020
import { NgxPaginationModule } from 'ngx-pagination';
@@ -159,6 +159,7 @@ const maskConfig: Partial<IConfig> = {
159159
],
160160
imports: [
161161
NgxMaskModule.forRoot(maskConfig),
162+
MatCheckboxModule,
162163
MatInputModule,
163164
MatDatepickerModule,
164165
MatMomentDateModule,
@@ -176,7 +177,6 @@ const maskConfig: Partial<IConfig> = {
176177
UiSwitchModule,
177178
DragDropModule,
178179
MatIconModule,
179-
MatCheckboxModule,
180180
MatListModule,
181181
StoreModule.forRoot(reducers, {
182182
metaReducers,

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 & 2 deletions
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';
@@ -61,6 +61,7 @@ describe('Reports Page', () => {
6161
const state: EntryState = {
6262
active: timeEntry,
6363
isLoading: false,
64+
resultSumEntriesSelected: new TotalHours(),
6465
message: '',
6566
createError: false,
6667
updateError: false,
@@ -94,7 +95,8 @@ describe('Reports Page', () => {
9495
component = fixture.componentInstance;
9596
store = TestBed.inject(MockStore);
9697
store.setState(state);
97-
getReportDataSourceSelectorMock = store.overrideSelector(getReportDataSource, state.reportDataSource);
98+
getReportDataSourceSelectorMock = (store.overrideSelector(getReportDataSource, state.reportDataSource),
99+
store.overrideSelector(getResultSumEntriesSelected, state.resultSumEntriesSelected));
98100
fixture.detectChanges();
99101
}
100102
);
@@ -207,6 +209,14 @@ describe('Reports Page', () => {
207209
expect({ hours, minutes, seconds }).toEqual({ hours: 3, minutes: 20, seconds: 0 });
208210
});
209211

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

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

Lines changed: 28 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,8 +62,8 @@ 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 }],
64-
order: [[1, 'asc'], [2, 'desc'], [4, 'desc']]
65+
columnDefs: [{ type: 'date', targets: 2}, {orderable: false, targets: [0]}],
66+
order: [[1,'asc'],[2,'desc'],[4,'desc']]
6567
};
6668
dtTrigger: Subject<any> = new Subject();
6769
@ViewChild(DataTableDirective, { static: false })
@@ -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

75-
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();
80+
81+
constructor(private store: Store<EntryState>, private actionsSubject$: ActionsSubject, private storeUser: Store<User> ) {
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
});
@@ -153,5 +165,15 @@ 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+
this.totalTimeSelected = checked ? this.totalTimeSelected.add(duration) : this.totalTimeSelected.subtract(duration);
172+
const daysTotalInHours = this.totalTimeSelected.days() * 24;
173+
this.resultSumEntriesSelected.hours = this.totalTimeSelected.hours() + daysTotalInHours;
174+
this.resultSumEntriesSelected.minutes = this.totalTimeSelected.minutes();
175+
this.resultSumEntriesSelected.seconds = this.totalTimeSelected.seconds();
176+
return this.resultSumEntriesSelected;
177+
}
156178
}
157179

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
});

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,5 @@ export const getStatusMessage = createSelector(getEntryState, (state: EntryState
1616
export const getReportDataSource = createSelector(getEntryState, (state: EntryState) => state?.reportDataSource);
1717

1818
export const getTimeEntriesDataSource = createSelector(getEntryState, (state: EntryState) => state?.timeEntriesDataSource);
19+
20+
export const getResultSumEntriesSelected = createSelector(getEntryState, (state: EntryState) => state?.resultSumEntriesSelected);

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { DebugElement } from '@angular/core';
2323
import { FeatureToggle } from './../../../../environments/enum';
2424
import { CalendarView } from 'angular-calendar';
2525
import * as moment from 'moment';
26+
import { TotalHours } from '../../reports/models/total-hours-report';
2627

2728
describe('TimeEntriesComponent', () => {
2829
type Merged = TechnologyState & ProjectState & EntryState;
@@ -74,6 +75,7 @@ describe('TimeEntriesComponent', () => {
7475
createError: false,
7576
updateError: false,
7677
isLoading: false,
78+
resultSumEntriesSelected: new TotalHours(),
7779
message: 'any-message',
7880
active: {
7981
start_date: new Date('2019-01-01T15:36:15.887Z'),

0 commit comments

Comments
 (0)