Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
770 changes: 585 additions & 185 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,13 @@
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.2",
"karma-json-fixtures-preprocessor": "0.0.6",
"popper.js": "^1.16.0",
"prettier": "^2.0.2",
"protractor": "~5.4.3",
"protractor": "^7.0.0",
"semantic-release": "^17.0.4",
"ts-node": "~8.3.0",
"tslint": "~5.18.0",
"typescript": "~3.7.5",
"popper.js": "^1.16.0"
"typescript": "~3.7.5"
},
"husky": {
"hooks": {
Expand Down
4 changes: 4 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ import { EntryEffects } from './modules/time-clock/store/entry.effects';
import { InjectTokenInterceptor } from './modules/shared/interceptors/inject.token.interceptor';
import { SubstractDatePipe } from './modules/shared/pipes/substract-date/substract-date.pipe';
import {TechnologiesComponent} from './modules/shared/components/technologies/technologies.component';
import { TimeEntriesSummaryComponent } from './modules/time-clock/components/time-entries-summary/time-entries-summary.component';
import { TimeDetailsPipe } from './modules/time-clock/pipes/time-details.pipe';

@NgModule({
declarations: [
Expand Down Expand Up @@ -94,6 +96,8 @@ import {TechnologiesComponent} from './modules/shared/components/technologies/te
EntryFieldsComponent,
SubstractDatePipe,
TechnologiesComponent,
TimeEntriesSummaryComponent,
TimeDetailsPipe,
],
imports: [
CommonModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ describe('DetailsFieldsComponent', () => {
});

it('should emit ngOnChange with new data', () => {
const project = { id: 'id', name: 'name', project_type_id: '' };
const entryToEdit = {
project_id: 'id',
activity_id: 'fc5fab41-a21e-4155-9d05-511b956ebd05',
Expand Down Expand Up @@ -116,7 +115,6 @@ describe('DetailsFieldsComponent', () => {
});

it('should emit ngOnChange with new data', () => {
const project = { id: 'id', name: 'name', project_type_id: '' };
const entryToEdit = {
project_id: 'id',
activity_id: '',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {TechnologyState} from '../../store/technology.reducers';
import {allTechnologies} from '../../store/technology.selectors';
import {TechnologiesComponent} from './technologies.component';
import * as actions from '../../store/technology.actions';
import {ProjectState} from '../../../customer-management/components/projects/components/store/project.reducer';

describe('Technologies component', () => {
let component: TechnologiesComponent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,7 @@ export class TechnologiesComponent implements OnInit {
}

addTechnology(name: string) {
const index = this.selectedTechnologies.indexOf(name);
if (index > -1) {
this.removeTechnology(index);
} else if (this.selectedTechnologies.length < this.MAX_NUM_TECHNOLOGIES) {
if (this.selectedTechnologies.length < this.MAX_NUM_TECHNOLOGIES) {
this.selectedTechnologies = [...this.selectedTechnologies, name];
this.technologyAdded.emit(this.selectedTechnologies);
this.showList = false;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<h6 class="text-left"><strong>Summary</strong></h6>
<hr />
<div class="row pb-4">
<div class="col-4">
<h6>Day</h6>
<h3>{{ timeEntriesSummary.day | timeDetails }}</h3>
</div>
<div class="col-4">
<h6>Week</h6>
<h3>{{ timeEntriesSummary.week | timeDetails }}</h3>
</div>
<div class="col-4">
<h6>Month</h6>
<h3>{{ timeEntriesSummary.month | timeDetails }}</h3>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { TimeDetailsPipe } from './../../pipes/time-details.pipe';
import { provideMockStore } from '@ngrx/store/testing';
import { TimeEntriesSummary } from './../../models/time.entry.summary';
import { TimeDetails } from './../../models/time.entry.summary';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { TimeEntriesSummaryComponent } from './time-entries-summary.component';

describe('TimeEntriesSummaryComponent', () => {
let component: TimeEntriesSummaryComponent;
let fixture: ComponentFixture<TimeEntriesSummaryComponent>;


const emptyTimeDetails: TimeDetails = { hours: '--:--', minutes: '--:--', seconds: '--:--' };
const emptyTimeEntriesSummary: TimeEntriesSummary = { day: emptyTimeDetails, week: emptyTimeDetails, month: emptyTimeDetails };

const state = {
entries: {
timeEntriesSummary: emptyTimeEntriesSummary
},
};

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ TimeEntriesSummaryComponent, TimeDetailsPipe ],
providers: [provideMockStore({ initialState: state })
],
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(TimeEntriesSummaryComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { getEntriesSummary } from './../../store/entry.selectors';
import { TimeEntriesSummary } from '../../models/time.entry.summary';
import { LoadEntriesSummary } from './../../store/entry.actions';
import { EntryState } from './../../store/entry.reducer';
import { Store, select } from '@ngrx/store';
import { Component, OnInit } from '@angular/core';

@Component({
selector: 'app-time-entries-summary',
templateUrl: './time-entries-summary.component.html',
styleUrls: ['./time-entries-summary.component.css']
})
export class TimeEntriesSummaryComponent implements OnInit {

timeEntriesSummary: TimeEntriesSummary;

constructor(private store: Store<EntryState>) { }

ngOnInit(): void {
this.store.dispatch(new LoadEntriesSummary());
const timeEntriesSummary$ = this.store.pipe(select(getEntriesSummary));
timeEntriesSummary$.subscribe((response) => {
this.timeEntriesSummary = response;
});
}

}
18 changes: 2 additions & 16 deletions src/app/modules/time-clock/pages/time-clock.component.html
Original file line number Diff line number Diff line change
@@ -1,19 +1,5 @@
<h6 class="text-left"><strong>Summary</strong></h6>
<hr />
<div class="row pb-4">
<div class="col-4">
<h6>Day</h6>
<h3>4:22</h3>
</div>
<div class="col-4">
<h6>Week</h6>
<h3>14:25</h3>
</div>
<div class="col-4">
<h6>Month</h6>
<h3>49:32</h3>
</div>
</div>
<app-time-entries-summary></app-time-entries-summary>

<div style="width: 60%;">

<div class="row pb-4">
Expand Down
4 changes: 2 additions & 2 deletions src/app/modules/time-clock/pages/time-clock.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { EntryActionTypes, StopTimeEntryRunning } from './../store/entry.actions';
import { async, ComponentFixture, TestBed, inject } from '@angular/core/testing';
import { StopTimeEntryRunning } from './../store/entry.actions';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { provideMockStore, MockStore } from '@ngrx/store/testing';
import { ToastrService } from 'ngx-toastr';
Expand Down
13 changes: 13 additions & 0 deletions src/app/modules/time-clock/pipes/time-details.pipe.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { TimeDetailsPipe } from './time-details.pipe';

describe('TimeDetailsPipe', () => {
it('create an instance', () => {
const pipe = new TimeDetailsPipe();
expect(pipe).toBeTruthy();
});

it('adds a 0 if has one number', () => {
const pipe = new TimeDetailsPipe();
expect(pipe.formatAsTwoDigit('5')).toBe('05');
});
});
19 changes: 19 additions & 0 deletions src/app/modules/time-clock/pipes/time-details.pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { TimeDetails } from '../models/time.entry.summary';
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
name: 'timeDetails'
})
export class TimeDetailsPipe implements PipeTransform {

transform(value: TimeDetails): string {
return `${this.formatAsTwoDigit(value.hours)}:${this.formatAsTwoDigit(value.minutes)}` ;
}

formatAsTwoDigit(time: string): string {
// tslint:disable-next-line:no-construct
const stringLength = new String(time).length;
const formattedTime = (stringLength === 1) ? `0${time}` : time;
return formattedTime;
}
}
2 changes: 1 addition & 1 deletion src/app/modules/time-clock/services/entry.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TimeEntriesSummary } from './../models/time.entry.summary';
import { TimeEntriesSummary } from '../models/time.entry.summary';
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

Expand Down
11 changes: 11 additions & 0 deletions src/app/modules/time-clock/store/entry.actions.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
import * as actions from './entry.actions';

describe('Actions for Entries', () => {

it('LoadEntriesSummaryFail type is EntryActionTypes.LOAD_ENTRIES_SUMMARY_FAIL', () => {
const action = new actions.LoadEntriesSummaryFail();
expect(action.type).toEqual(actions.EntryActionTypes.LOAD_ENTRIES_SUMMARY_FAIL);
});

it('LoadEntriesSummarySuccess type is EntryActionTypes.LOAD_ENTRIES_SUMMARY_SUCCESS', () => {
const action = new actions.LoadEntriesSummarySuccess(null);
expect(action.type).toEqual(actions.EntryActionTypes.LOAD_ENTRIES_SUMMARY_SUCCESS);
});

it('LoadActiveEntrySuccess type is EntryActionTypes.LOAD_ACTIVE_ENTRY_SUCCESS', () => {
const loadActiveEntrySuccess = new actions.LoadActiveEntrySuccess([]);
expect(loadActiveEntrySuccess.type).toEqual(actions.EntryActionTypes.LOAD_ACTIVE_ENTRY_SUCCESS);
Expand Down
20 changes: 20 additions & 0 deletions src/app/modules/time-clock/store/entry.actions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { TimeEntriesSummary } from '../models/time.entry.summary';
import { Action } from '@ngrx/store';
import { NewEntry, Entry } from '../../shared/models';

export enum EntryActionTypes {
LOAD_ENTRIES_SUMMARY = '[Entry] LOAD_ENTRIES_SUMMARY',
LOAD_ENTRIES_SUMMARY_SUCCESS = '[Entry] LOAD_ENTRIES_SUMMARY_SUCCESS',
LOAD_ENTRIES_SUMMARY_FAIL = '[Entry] LOAD_ENTRIES_SUMMARY_FAIL',
LOAD_ACTIVE_ENTRY = '[Entry] LOAD_ACTIVE_ENTRY',
LOAD_ACTIVE_ENTRY_SUCCESS = '[Entry] LOAD_ACTIVE_ENTRY_SUCCESS',
LOAD_ACTIVE_ENTRY_FAIL = '[Entry] LOAD_ACTIVE_ENTRY_FAIL',
Expand All @@ -25,6 +29,19 @@ export enum EntryActionTypes {
CLEAN_ENTRY_UPDATE_ERROR = '[Entry] CLEAN_ENTRY_UPDATE_ERROR',
}

export class LoadEntriesSummary implements Action {
public readonly type = EntryActionTypes.LOAD_ENTRIES_SUMMARY;
}

export class LoadEntriesSummarySuccess implements Action {
readonly type = EntryActionTypes.LOAD_ENTRIES_SUMMARY_SUCCESS;
constructor(readonly payload: TimeEntriesSummary) {}
}

export class LoadEntriesSummaryFail implements Action {
readonly type = EntryActionTypes.LOAD_ENTRIES_SUMMARY_FAIL;
}

export class LoadActiveEntry implements Action {
public readonly type = EntryActionTypes.LOAD_ACTIVE_ENTRY;
}
Expand Down Expand Up @@ -137,6 +154,9 @@ export class DefaultEntry implements Action {
}

export type EntryActions =
| LoadEntriesSummary
| LoadEntriesSummarySuccess
| LoadEntriesSummaryFail
| LoadActiveEntry
| LoadActiveEntrySuccess
| LoadActiveEntryFail
Expand Down
13 changes: 13 additions & 0 deletions src/app/modules/time-clock/store/entry.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@ import * as actions from './entry.actions';
export class EntryEffects {
constructor(private actions$: Actions, private entryService: EntryService, private toastrService: ToastrService) {}

@Effect()
loadEntriesSummary$: Observable<Action> = this.actions$.pipe(
ofType(actions.EntryActionTypes.LOAD_ENTRIES_SUMMARY),
mergeMap(() =>
this.entryService.summary().pipe(
map((response) => {
return new actions.LoadEntriesSummarySuccess(response);
}),
catchError((error) => of(new actions.LoadEntriesSummaryFail()))
)
)
);

@Effect()
loadActiveEntry$: Observable<Action> = this.actions$.pipe(
ofType(actions.EntryActionTypes.LOAD_ACTIVE_ENTRY),
Expand Down
40 changes: 33 additions & 7 deletions src/app/modules/time-clock/store/entry.reducer.spec.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,54 @@
import { TimeEntriesSummary, TimeDetails } from '../models/time.entry.summary';
import { NewEntry, Entry } from './../../shared/models';
import * as actions from './entry.actions';
import { entryReducer, EntryState } from './entry.reducer';

describe('entryReducer', () => {

const emptyTimeDetails: TimeDetails = { hours: '--:--', minutes: '--:--', seconds: '--:--' };
const emptyTimeEntriesSummary: TimeEntriesSummary = { day: emptyTimeDetails, week: emptyTimeDetails, month: emptyTimeDetails };

const initialState: EntryState = {
active: null,
entryList: [],
isLoading: false,
message: '',
createError: null,
updateError: null,
timeEntriesSummary: emptyTimeEntriesSummary
};

const entry: NewEntry = {
start_date: 'start-date',
description: 'description',
project_id: '112',
technologies: ['angular', 'typescript'],
};

const newEntry: Entry = {
id: '1',
start_date: new Date(),
end_date: new Date(),
activity: '',
technologies: ['abc', 'abc'],
};
it('sets timeEntriesSummary from action on LOAD_ENTRIES_SUMMARY_SUCCESS', () => {
const payload = null;
const action = new actions.LoadEntriesSummarySuccess(payload);
const state = entryReducer(initialState, action);
expect(state.timeEntriesSummary).toBe(payload);
});

it('sets message on LOAD_ACTIVE_ENTRY_FAIL', () => {
const action = new actions.LoadActiveEntryFail('');
const state = entryReducer(initialState, action);
expect(state.message).toBe('Something went wrong fetching active entry!');
});

it('sets timeEntriesSummary as empty on LOAD_ENTRIES_SUMMARY_FAIL', () => {
const action = new actions.LoadEntriesSummaryFail();
const state = entryReducer(initialState, action);
expect(state.timeEntriesSummary).toEqual(emptyTimeEntriesSummary);
});

it('on LOAD_ENTRIES_SUMMARY, is Loading true', () => {
const action = new actions.LoadEntriesSummary();
const state = entryReducer(initialState, action);
expect(state.isLoading).toBe(true);
});

it('on Default, ', () => {
const action = new actions.DefaultEntry();
Expand Down Expand Up @@ -116,6 +140,7 @@ describe('entryReducer', () => {

it('on DeleteEntrySuccess', () => {
const currentState = {
timeEntriesSummary: emptyTimeEntriesSummary,
active: null,
entryList: [
{
Expand Down Expand Up @@ -171,6 +196,7 @@ describe('entryReducer', () => {
message: '',
createError: null,
updateError: null,
timeEntriesSummary: emptyTimeEntriesSummary
};
const entryUpdated: Entry = {
id: 'id',
Expand Down
Loading