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
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
<div class="d-flex">
<div
class="month w-100 text-center align-self-center d-flex flex-column"
*ngFor="let month of months; let i = index"
(click)="getMonth(i)"
[ngClass]="{ active: activeMonth === i }"
>
<div class="p-2">{{ month }}</div>
<div class="card-header">
<div class="d-flex align-items-center">
<button class="btn mr-auto" (click)="decrement()">
<i class="fa fa-chevron-left"></i>
</button>
<p>
{{selectedYearText}}
</p>
<button class="btn ml-auto" (click)="increment()">
<i class="fa fa-chevron-right"></i>
</button>
</div>
</div>
<div class="row content-months">
<div class="spacing col-xl-1 col-lg-1 col-md-2 col-sm-2 col-xs-4" *ngFor="let month of months; let i = index">
<button class="btn btn-light btn-block"
[ngClass]="{'btn-primary': isSelectedMonth(i)}"
(click)="selectMonth(i)">
<small>{{ month.slice(0, 3) }}</small>
</button>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
line-height: 1.2em;
font-weight: bold;
}
.content-months {
margin: 0;
}

.month {
background: $primary;
Expand All @@ -21,5 +24,7 @@
@include highlight();
border-radius: 0.2em;
text-decoration: underline;

}
.spacing {
padding: 0.1em;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import * as moment from 'moment';
import { NEVER } from 'rxjs';

import { MonthPickerComponent } from './month-picker.component';

Expand All @@ -23,10 +25,46 @@ describe('MonthPickerComponent', () => {
expect(component).toBeTruthy();
});

it('should emit activeMonth event', () => {
const month = 2;
spyOn(component.monthSelected, 'emit');
component.getMonth(month);
expect(component.monthSelected.emit).toHaveBeenCalledWith(month + 1);
it('should emit monthIndex and year', () => {
const month = new Date().getMonth();
const year = new Date().getFullYear();

spyOn(component.dateSelected, 'emit');
expect(component.dateSelected.emit({ monthIndex: month, year: year }));
});


it('should add a year to current year', () => {
component.selectedYearMoment = moment();
const incrementYear = moment().add(1, 'year').format('Y');
spyOn(component, 'increment').and.callThrough();

component.increment();

expect(component.increment).toHaveBeenCalled();
expect(component.selectedYearText).toEqual(incrementYear);
expect(component.selectedYearMoment.format('Y')).toEqual(incrementYear);
});


it('should subtract a year to current year', () => {
component.selectedYearMoment = moment();
const decrementYear = moment().subtract(1, 'year').format('Y');
spyOn(component, 'decrement').and.callThrough();

component.decrement();

expect(component.decrement).toHaveBeenCalled();
expect(component.selectedYearMoment.format('Y')).toEqual(decrementYear);
});


it('selectMonth should call selectDates', () => {
spyOn(component, 'selectDate');

component.selectMonth(8);

expect(component.selectDate).toHaveBeenCalledWith(8, 2020);
});

});
Original file line number Diff line number Diff line change
@@ -1,23 +1,68 @@
import { Component, OnInit, Output, EventEmitter } from '@angular/core';
import * as moment from 'moment';

@Component({
selector: 'app-month-picker',
templateUrl: './month-picker.component.html',
styleUrls: ['./month-picker.component.scss']
})
export class MonthPickerComponent implements OnInit {
@Output() monthSelected = new EventEmitter();
activeMonth: number;
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
@Output() dateSelected = new EventEmitter<{
monthIndex: number;
year: number;
}>();

constructor() { }
selectedMonthMoment: moment.Moment;
selectedMonthIndex: number;
selectedYearMoment: moment.Moment;
selectedYear: number;

ngOnInit(): void {
this.activeMonth = new Date().getMonth();
selectedYearText: string;
months: Array<string> = [];
years: Array<number> = [];

constructor() {
this.selectedYearMoment = moment();
this.selectedMonthMoment = moment();
this.months = moment.months();
this.selectedMonthIndex = this.selectedMonthMoment.month();
this.selectedYear = this.selectedYearMoment.year();
this.updateYearText();
}

ngOnInit() {
this.selectDate(this.selectedMonthIndex, this.selectedYear);
}

updateYearText() {
this.selectedYearText = moment(this.selectedYearMoment).format('YYYY');
}

increment() {
this.selectedYearMoment = this.selectedYearMoment.add(1, 'year');
this.updateYearText();
}

getMonth(month: number) {
this.monthSelected.emit(month + 1);
this.activeMonth = month;
decrement() {
this.selectedYearMoment = this.selectedYearMoment.subtract(1, 'year');
this.updateYearText();
}

selectMonth(monthIndex: number) {
this.selectedMonthIndex = monthIndex;
this.selectedYear = this.selectedYearMoment.year();
this.selectDate(this.selectedMonthIndex, this.selectedYear);
}

isSelectedMonth(monthIndex: number) {
return (
this.selectedMonthIndex === monthIndex &&
this.selectedYear === this.selectedYearMoment.year()
);
}

selectDate(monthIndex: number, year: number) {
this.dateSelected.emit({ monthIndex, year });
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export class EntryFieldsComponent implements OnInit {

ngOnInit(): void {
this.store.dispatch(new LoadActivities());
this.store.dispatch(new entryActions.LoadEntries(new Date().getMonth() + 1));
this.store.dispatch(new entryActions.LoadEntries(new Date().getMonth() + 1, new Date().getFullYear()));
this.actionsSubject$
.pipe(filter((action: any) => action.type === ActivityManagementActionTypes.LOAD_ACTIVITIES_SUCCESS))
.subscribe((action) => {
Expand Down
6 changes: 4 additions & 2 deletions src/app/modules/time-clock/services/entry.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,13 @@ describe('EntryService', () => {
});

it('load all Entries', () => {
const year = new Date().getFullYear();
const month = new Date().getMonth();

const timezoneOffset = new Date().getTimezoneOffset();
service.loadEntries(month).subscribe();
service.loadEntries({ year, month }).subscribe();

const loadEntryRequest = httpMock.expectOne(`${service.baseUrl}?month=${month}&timezone_offset=${timezoneOffset}`);
const loadEntryRequest = httpMock.expectOne(`${service.baseUrl}?month=${month}&year=${year}&timezone_offset=${timezoneOffset}`);
expect(loadEntryRequest.request.method).toBe('GET');

});
Expand Down
4 changes: 2 additions & 2 deletions src/app/modules/time-clock/services/entry.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ export class EntryService {
return this.http.get(`${this.baseUrl}/running`);
}

loadEntries(month): Observable<any> {
loadEntries(date): Observable<any> {
const timezoneOffset = new Date().getTimezoneOffset();
return this.http.get(`${this.baseUrl}?month=${month}&timezone_offset=${timezoneOffset}`);
return this.http.get(`${this.baseUrl}?month=${date.month}&year=${date.year}&timezone_offset=${timezoneOffset}`);
}

createEntry(entryData): Observable<any> {
Expand Down
2 changes: 1 addition & 1 deletion src/app/modules/time-clock/store/entry.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export class LoadActiveEntryFail implements Action {
export class LoadEntries implements Action {
public readonly type = EntryActionTypes.LOAD_ENTRIES;

constructor(public month: number) {
constructor(public month: number, public year: number) {
}
}

Expand Down
11 changes: 7 additions & 4 deletions src/app/modules/time-clock/store/entry.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ export class EntryEffects {
@Effect()
loadEntries$: Observable<Action> = this.actions$.pipe(
ofType(actions.EntryActionTypes.LOAD_ENTRIES),
map((action: actions.LoadEntries) => action.month),
mergeMap((month) =>
this.entryService.loadEntries(month).pipe(
map((action: actions.LoadEntries) => action),
mergeMap((date) =>
this.entryService.loadEntries({ month: date.month, year: date.year }).pipe(
map((entries) => new actions.LoadEntriesSuccess(entries)),
catchError((error) => {
this.toastrService.warning(`The data could not be loaded`);
Expand Down Expand Up @@ -226,7 +226,10 @@ export class EntryEffects {
ofType(actions.EntryActionTypes.UPDATE_CURRENT_OR_LAST_ENTRY),
map((action: actions.UpdateCurrentOrLastEntry) => action.payload),
switchMap((entry) =>
this.entryService.loadEntries(new Date().getMonth() + 1).pipe(
this.entryService.loadEntries({
month : new Date().getMonth() + 1,
year: new Date().getFullYear()
}).pipe(
map((entries) => {
const lastEntry = entries[1];
const isStartTimeInLastEntry = moment(entry.start_date).isBefore(lastEntry.end_date);
Expand Down
4 changes: 3 additions & 1 deletion src/app/modules/time-clock/store/entry.reducer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,9 @@ describe('entryReducer', () => {
});

it('on LoadEntries, isLoading is true', () => {
const action = new actions.LoadEntries(new Date().getMonth() + 1);
const month = 12;
const year = 2020;
const action = new actions.LoadEntries(month, year);
const state = entryReducer(initialState, action);
expect(state.timeEntriesDataSource.isLoading).toEqual(true);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</div>
</div>
<div style="height: 15px;"></div>
<app-month-picker (monthSelected)="getMonth($event)"></app-month-picker>
<app-month-picker (dateSelected)="dateSelected($event)"></app-month-picker>
<div style="height: 15px;"></div>

<table class="table table-sm table-striped mb-0" *ngIf="(timeEntriesDataSource$ | async) as dataSource">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ describe('TimeEntriesComponent', () => {

it('when create time entries, the time entries should be queried', () => {
const currentMonth = new Date().getMonth() + 1;
const year = new Date().getFullYear();
const entryToSave = {
entry: {
project_id: 'project-id',
Expand All @@ -141,7 +142,7 @@ describe('TimeEntriesComponent', () => {

component.saveEntry(entryToSave);
expect(store.dispatch).toHaveBeenCalledWith(new entryActions.CreateEntry(entryToSave.entry));
expect(store.dispatch).toHaveBeenCalledWith(new entryActions.LoadEntries(currentMonth));
expect(store.dispatch).toHaveBeenCalledWith(new entryActions.LoadEntries(currentMonth, year));
});

it('when creating a new entry, then entryId should be null', () => {
Expand Down Expand Up @@ -322,11 +323,13 @@ describe('TimeEntriesComponent', () => {
expect(store.dispatch).toHaveBeenCalledWith(new entryActions.DeleteEntry('id'));
});

it('should get the entry List by Month', () => {
const month = 1;
it('should get the entry List by Month and year', () => {
const month = new Date().getMonth();
const year = new Date().getFullYear();

spyOn(store, 'dispatch');
component.getMonth(month);
expect(store.dispatch).toHaveBeenCalledWith(new entryActions.LoadEntries(month));
component.dateSelected({monthIndex: month, year: year});
expect(store.dispatch).toHaveBeenCalledWith(new entryActions.LoadEntries(month + 1, year));
});

it('doSave when activeTimeEntry === null', waitForAsync(() => {
Expand Down
15 changes: 11 additions & 4 deletions src/app/modules/time-entries/pages/time-entries.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { SaveEntryEvent } from '../../shared/components/details-fields/save-entr
import { Entry } from '../../shared/models';
import { DataSource } from '../../shared/models/data-source.model';
import * as entryActions from '../../time-clock/store/entry.actions';
import * as moment from 'moment';
import { EntryState } from '../../time-clock/store/entry.reducer';
import { EntryActionTypes } from './../../time-clock/store/entry.actions';
import { getActiveTimeEntry, getTimeEntriesDataSource } from './../../time-clock/store/entry.selectors';
Expand All @@ -26,6 +27,9 @@ export class TimeEntriesComponent implements OnInit, OnDestroy {
entriesSubscription: Subscription;
canMarkEntryAsWIP = true;
timeEntriesDataSource$: Observable<DataSource<Entry>>;
selectedYearAsText: string;
selectedMonthIndex: number;
selectedMonthAsText: string;

constructor(private store: Store<EntryState>, private toastrService: ToastrService, private actionsSubject$: ActionsSubject) {
this.timeEntriesDataSource$ = this.store.pipe(delay(0), select(getTimeEntriesDataSource));
Expand All @@ -36,7 +40,7 @@ export class TimeEntriesComponent implements OnInit, OnDestroy {
}

ngOnInit(): void {
this.store.dispatch(new entryActions.LoadEntries(new Date().getMonth() + 1));
this.store.dispatch(new entryActions.LoadEntries(new Date().getMonth() + 1, new Date().getFullYear()));
this.loadActiveEntry();

this.entriesSubscription = this.actionsSubject$.pipe(
Expand All @@ -48,7 +52,7 @@ export class TimeEntriesComponent implements OnInit, OnDestroy {
)
).subscribe((action) => {
this.loadActiveEntry();
this.store.dispatch(new entryActions.LoadEntries(new Date().getMonth() + 1));
this.store.dispatch(new entryActions.LoadEntries(this.selectedMonthIndex, new Date().getFullYear()));
});
}

Expand Down Expand Up @@ -161,8 +165,11 @@ export class TimeEntriesComponent implements OnInit, OnDestroy {
this.showModal = false;
}

getMonth(month: number) {
this.store.dispatch(new entryActions.LoadEntries(month));
dateSelected(event: { monthIndex: number; year: number }) {
this.selectedYearAsText = event.year.toString();
this.selectedMonthIndex = event.monthIndex + 1;
this.selectedMonthAsText = moment().month(event.monthIndex).format('MMMM');
this.store.dispatch(new entryActions.LoadEntries(this.selectedMonthIndex, event.year));
}

openModal(item: any) {
Expand Down