- Field technology is requiered. Enter this field for clock out.
-
+ Field technology is requiered. Enter this field for clock out.
+
-
diff --git a/src/app/components/options-sidebar/time-clock/time-clock.component.spec.ts b/src/app/components/options-sidebar/time-clock/time-clock.component.spec.ts
index 4927e4433..0d045af13 100644
--- a/src/app/components/options-sidebar/time-clock/time-clock.component.spec.ts
+++ b/src/app/components/options-sidebar/time-clock/time-clock.component.spec.ts
@@ -1,6 +1,6 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
-import { DebugElement } from '@angular/core';
+import { DebugElement, Component } from '@angular/core';
import { TimeClockComponent } from './time-clock.component';
import { ProjectListHoverComponent } from '../../shared/project-list-hover/project-list-hover.component';
@@ -10,7 +10,6 @@ describe('TimeClockComponent', () => {
let de: DebugElement;
function setup() {
- // tslint:disable-next-line: no-shadowed-variable
const fixture = TestBed.createComponent(TimeClockComponent);
const app = fixture.debugElement.componentInstance;
return { fixture, app };
@@ -33,13 +32,12 @@ describe('TimeClockComponent', () => {
expect(component).toBeTruthy();
});
- it('should have p tag as \'Dario clocked out at hh:mm:ss\'', async(() => {
- // tslint:disable-next-line: no-shadowed-variable
- const { app, fixture } = setup();
+ it('should have p tag as \'Dario clocked out at 00:00:00\'', async(() => {
+ const { fixture } = setup();
fixture.detectChanges();
const compile = fixture.debugElement.nativeElement;
const ptag = compile.querySelector('p');
- expect(ptag.textContent).toBe('Dario clocked out at hh:mm:ss');
+ expect(ptag.textContent).toBe('Dario clocked out at 00:00:00');
}));
it('should set showfileds as true', () => {
@@ -63,4 +61,45 @@ describe('TimeClockComponent', () => {
li.nativeElement.click();
expect(component.setShowFields).toHaveBeenCalledWith(true);
});
+
+ it('should have button text as Options', async(() => {
+ const { fixture } = setup();
+ fixture.detectChanges();
+ const x = document.getElementById('optionsContainer');
+ const ptag = x.querySelector('button');
+ expect(ptag.textContent).toBe(' Options ');
+ }));
+
+ it('should set Clock In', () => {
+ const { fixture } = setup();
+ fixture.detectChanges();
+ const x = document.getElementById('clockInOutContainer');
+ const ptag = x.querySelector('button');
+ expect(ptag.textContent).toBe('Clock In');
+ });
+
+ it('should setVartToEmpty called', () => {
+ spyOn(component, 'setDefaultValuesToFields');
+ component.setDefaultValuesToFields();
+ expect(component.setDefaultValuesToFields).toHaveBeenCalled();
+ });
+
+ it('should employeClockIn called', () => {
+ spyOn(component, 'employeClockIn');
+ component.employeClockIn();
+ expect(component.employeClockIn).toHaveBeenCalled();
+ });
+
+ it('should employeClockOut called', () => {
+ spyOn(component, 'employeClockOut');
+ component.employeClockOut();
+ expect(component.employeClockOut).toHaveBeenCalled();
+ });
+
+ it('should enterTechnolofy called', () => {
+ spyOn(component, 'enterTechnology');
+ component.enterTechnology('');
+ expect(component.enterTechnology).toHaveBeenCalled();
+ });
+
});
diff --git a/src/app/components/options-sidebar/time-clock/time-clock.component.ts b/src/app/components/options-sidebar/time-clock/time-clock.component.ts
index 674c774e8..a38b5aa42 100644
--- a/src/app/components/options-sidebar/time-clock/time-clock.component.ts
+++ b/src/app/components/options-sidebar/time-clock/time-clock.component.ts
@@ -15,29 +15,42 @@ export class TimeClockComponent implements OnInit {
{ id: 'P4', name: 'Project 4' }
];
+ currentDate: Date = new Date();
username = 'Dario';
- clockInUsername = 'hh:mm:ss';
- clockOutUsername = 'hh:mm:ss';
isClockIn: boolean;
isEnterTechnology: boolean;
showAlertEnterTecnology: boolean;
-
+ showFields: boolean;
+ hourCounterRealTime: number;
+ minuteCounterRealTime: number;
+ secondsCounterRealTime: number;
hour: number;
minute: number;
seconds: number;
-
- showFields: boolean;
+ interval;
+ dataTechnology: string[] = new Array();
+ execOnlyOneTimeCounter = false;
+ execOnlyOneTimeClockIn = false;
+ isClockInEnable = false;
+ isHidenForm = true;
constructor() {
this.isClockIn = true;
this.isEnterTechnology = false;
+ this.hourCounterRealTime = 0;
+ this.minuteCounterRealTime = 0;
+ this.secondsCounterRealTime = 0;
this.hour = 0;
this.minute = 0;
this.seconds = 0;
}
employeClockIn(): boolean {
+ this.isClockInEnable = true;
this.isClockIn = !this.isClockIn;
+ this.isHidenForm = false;
+ this.startTimer();
+ this.setArrivalAndDepartureTimes();
return this.isClockIn;
}
@@ -46,9 +59,9 @@ export class TimeClockComponent implements OnInit {
this.isClockIn = false;
this.showAlertEnterTecnology = true;
} else {
- this.isClockIn = true;
- this.isEnterTechnology = false;
- this.showAlertEnterTecnology = false;
+ this.setDefaultValuesToFields();
+ this.pauseTimer();
+ this.setArrivalAndDepartureTimes();
}
}
@@ -61,10 +74,62 @@ export class TimeClockComponent implements OnInit {
}
setShowFields(show: boolean) {
- this.isClockIn = false;
- this.showFields = show;
+ this.isHidenForm = false;
+ if ( this.isClockInEnable !== true ) {
+ this.isClockIn = false;
+ this.showFields = show;
+ if ( !this.execOnlyOneTimeCounter ) {
+ this.startTimer();
+ this.execOnlyOneTimeCounter = true;
+ }
+ this.setArrivalAndDepartureTimes();
+ }
+ }
+
+ startTimer() {
+ this.interval = setInterval(() => {
+ this.timer();
+ }, 1000 );
+ }
+
+ pauseTimer() {
+ clearInterval(this.interval);
+ }
+
+ timer() {
+ this.secondsCounterRealTime += 1;
+ if ( this.secondsCounterRealTime === 59 ) {
+ this.minuteCounterRealTime += 1;
+ this.secondsCounterRealTime = 0;
+ if ( this.minuteCounterRealTime === 59 ) {
+ this.hourCounterRealTime += 1;
+ this.minuteCounterRealTime = 0;
+ }
+ }
+ }
+
+ setArrivalAndDepartureTimes() {
+ if ( !this.execOnlyOneTimeClockIn ) {
+ this.currentDate = new Date();
+ this.hour = this.currentDate.getHours();
+ this.minute = this.currentDate.getMinutes();
+ this.seconds = this.currentDate.getSeconds();
+ this.execOnlyOneTimeClockIn = true;
+ }
+
}
- ngOnInit(): void {}
+ setDefaultValuesToFields() {
+ this.isHidenForm = true;
+ this.isClockIn = true;
+ this.isEnterTechnology = false;
+ this.showAlertEnterTecnology = false;
+ this.execOnlyOneTimeClockIn = false;
+ this.execOnlyOneTimeCounter = false;
+ this.isClockInEnable = false;
+ }
+
+ ngOnInit(): void {
+ }
}
diff --git a/src/app/components/options-sidebar/time-entries/time-entries.component.css b/src/app/components/options-sidebar/time-entries/time-entries.component.css
index e69de29bb..4b6e05eb5 100644
--- a/src/app/components/options-sidebar/time-entries/time-entries.component.css
+++ b/src/app/components/options-sidebar/time-entries/time-entries.component.css
@@ -0,0 +1,46 @@
+.container-time-entries {
+ padding: 1rem;
+}
+
+.header-entries {
+ background-color: rgba(0, 0, 0, 0.03);
+ border-top: 1px solid rgba(0, 0, 0, 0.125);
+}
+
+.accordion-container {
+ max-height: 25rem;
+ overflow-y: auto;
+}
+
+.accordion-container::-webkit-scrollbar {
+ display: none;
+}
+
+.date-header {
+ background-color: #e6e6e6;
+ border-radius: 0;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.125);
+ font-size: small;
+ padding: 0 0.9rem;
+}
+
+.date-header > a {
+ color: #000000;
+}
+
+.btn-small > i {
+ font-size: 0.8rem;
+ opacity: 0.7;
+}
+
+.entries {
+ align-items: center;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.125);
+ display: flex;
+ font-size: small;
+}
+
+.entries:hover {
+ background-color: #d5edf0;
+ cursor: pointer;
+}
diff --git a/src/app/components/options-sidebar/time-entries/time-entries.component.html b/src/app/components/options-sidebar/time-entries/time-entries.component.html
index f4ee1f815..401cb29e0 100644
--- a/src/app/components/options-sidebar/time-entries/time-entries.component.html
+++ b/src/app/components/options-sidebar/time-entries/time-entries.component.html
@@ -1,5 +1,66 @@
-
+
+
+
+
+
+
+
0; else emptyState" class="accordion accordion-container" role="tablist"
+ aria-multiselectable="true">
+
+
+
-
time-entries works!
-
-
\ No newline at end of file
+
+
+
+
+ {{ item.project }}
+
+
+ {{ item.duration }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/components/options-sidebar/time-entries/time-entries.component.spec.ts b/src/app/components/options-sidebar/time-entries/time-entries.component.spec.ts
index 304646888..fddcd9cac 100644
--- a/src/app/components/options-sidebar/time-entries/time-entries.component.spec.ts
+++ b/src/app/components/options-sidebar/time-entries/time-entries.component.spec.ts
@@ -1,10 +1,26 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { TimeEntriesComponent } from './time-entries.component';
+import { GroupByDatePipe } from '../../shared/pipes/group-by-date/group-by-date.pipe';
+import { MonthPickerComponent } from '../../shared/month-picker/month-picker.component';
+import { DetailsFieldsComponent } from '../../shared/details-fields/details-fields.component';
+import { EmptyStateComponent } from '../../shared/empty-state/empty-state.component';
+import { ModalComponent } from '../../shared/modal/modal.component';
describe('TimeEntriesComponent', () => {
let component: TimeEntriesComponent;
let fixture: ComponentFixture
;
+ const entry = {
+ id: 'entry_1',
+ project: 'Mido - 05 de Febrero',
+ startDate: '2020-02-05T15:36:15.887Z',
+ endDate: '2020-02-05T18:36:15.887Z',
+ activity: 'development',
+ technology: 'Angular, TypeScript',
+ comments: 'No comments',
+ ticket: 'EY-25'
+ };
function setup() {
// tslint:disable-next-line: no-shadowed-variable
@@ -15,7 +31,18 @@ describe('TimeEntriesComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
- declarations: [ TimeEntriesComponent ]
+ declarations: [
+ EmptyStateComponent,
+ DetailsFieldsComponent,
+ GroupByDatePipe,
+ ModalComponent,
+ MonthPickerComponent,
+ TimeEntriesComponent
+ ],
+ imports: [
+ FormsModule,
+ ReactiveFormsModule
+ ]
})
.compileComponents();
}));
@@ -39,4 +66,46 @@ describe('TimeEntriesComponent', () => {
expect(ptag.textContent).toBe('time-entries works!');
}));
-});
\ No newline at end of file
+ it('should call dataByMonth in ngOnInit()', async(() => {
+ component.ngOnInit();
+ expect(component.dataByMonth.length).toEqual(3);
+ }));
+
+ it('should open Delete Modal', () => {
+ component.openModal(entry);
+ expect(component.entryToDelete).toBe(entry);
+ expect(component.showModal).toBe(true);
+ });
+
+ it('should filter the Entry to edit', () => {
+ const entryId = "entry_1"
+ component.editEntry(entryId);
+ expect(component.entry.project).toBe(entry.project);
+ expect(component.entry.startDate).toBe(entry.startDate);
+ expect(component.entry.endDate).toBe(entry.endDate);
+ expect(component.entry.activity).toBe(entry.activity);
+ expect(component.entry.technology).toBe(entry.technology);
+ });
+
+ it('should save an Entry', () => {
+ component.entryId = 'entry_1';
+ component.saveEntry(entry);
+ expect(component.entryList[0].project).toBe('Mido - 05 de Febrero');
+ expect(component.entryList[0].startDate).toBe('2020-02-05T15:36:15.887Z');
+ expect(component.entryList[0].endDate).toBe('2020-02-05T18:36:15.887Z');
+ expect(component.entryList[0].activity).toBe('development');
+ expect(component.entryList[0].technology).toBe('Angular, TypeScript');
+ });
+
+ it('should delete a Entry', () => {
+ const entryId = "entry_2";
+ component.removeEntry(entryId);
+ expect(component.dataByMonth.length).toBe(2);
+ });
+
+ it('should get the entry List by Month', () => {
+ const month = 3;
+ component.getMonth(month);
+ expect(component.dataByMonth.length).toEqual(1);
+ });
+});
diff --git a/src/app/components/options-sidebar/time-entries/time-entries.component.ts b/src/app/components/options-sidebar/time-entries/time-entries.component.ts
index 17ea41840..55b231c1c 100644
--- a/src/app/components/options-sidebar/time-entries/time-entries.component.ts
+++ b/src/app/components/options-sidebar/time-entries/time-entries.component.ts
@@ -1,4 +1,5 @@
import { Component, OnInit } from '@angular/core';
+import { Entry } from '../../../interfaces';
@Component({
selector: 'app-time-entries',
@@ -7,9 +8,95 @@ import { Component, OnInit } from '@angular/core';
})
export class TimeEntriesComponent implements OnInit {
+ showModal: boolean = false;
+ entryId: string;
+ entry: Entry;
+ entryToDelete: Entry;
+ dataByMonth: Entry[];
+
+ entryList = [
+ {
+ id: 'entry_1',
+ project: 'Mido - 05 de Febrero',
+ startDate: '2020-02-05T15:36:15.887Z',
+ endDate: '2020-02-05T18:36:15.887Z',
+ activity: 'development',
+ technology: 'Angular, TypeScript',
+ comments: 'No comments',
+ ticket: 'EY-25'
+ },
+ {
+ id: 'entry_2',
+ project: 'Mido 15 de Marzo',
+ startDate: '2020-03-15T20:36:15.887Z',
+ endDate: '2020-03-15T23:36:15.887Z',
+ activity: 'development',
+ technology: 'Angular, TypeScript',
+ comments: 'No comments',
+ ticket: 'EY-38'
+ },
+ {
+ id: 'entry_3',
+ project: 'GoSpace 15 y 16 de Marzo',
+ startDate: '2020-03-15T23:36:15.887Z',
+ endDate: '2020-03-16T05:36:15.887Z',
+ activity: 'development',
+ technology: 'Angular, TypeScript',
+ comments: 'No comments',
+ ticket: 'EY-225'
+ },
+ {
+ id: 'entry_4',
+ project: 'Mido 16 de Marzo',
+ startDate: '2020-03-16T15:36:15.887Z',
+ endDate: '2020-03-16T18:36:15.887Z',
+ activity: 'development',
+ technology: 'Angular, TypeScript',
+ comments: 'No comments',
+ ticket: 'EY-89'
+ },
+ {
+ id: 'entry_5',
+ project: 'Ernst&Young 01 de Abril',
+ startDate: '2020-04-01T09:36:15.887Z',
+ endDate: '2020-04-01T15:36:15.887Z',
+ activity: 'development',
+ technology: 'Angular, TypeScript',
+ comments: 'No comments',
+ ticket: 'EY-59'
+ }
+ ];
+
constructor() { }
ngOnInit(): void {
+ this.dataByMonth = this.entryList.filter(entry => new Date(entry.startDate).getMonth() === new Date().getMonth());
+ }
+
+ openModal(itemToDelete: Entry) {
+ this.entryToDelete = itemToDelete;
+ this.showModal = true;
+ }
+
+ editEntry(entryId: string) {
+ this.entryId = entryId;
+ this.entry = this.entryList.find((entry) => entry.id === entryId);
}
+ saveEntry(newData): void {
+ const entryIndex = this.entryList.findIndex((entry => entry.id === this.entryId));
+ this.entryList[entryIndex].project = newData.project;
+ this.entryList[entryIndex].activity = newData.activity;
+ this.entryList[entryIndex].technology = newData.technology;
+ this.entryList[entryIndex].ticket = newData.jiraTicket;
+ this.entryList[entryIndex].comments = newData.notes;
+ }
+
+ removeEntry(entryId: string) {
+ this.dataByMonth = this.dataByMonth.filter((entry: Entry) => entry.id !== entryId);
+ }
+
+ getMonth(month: number) {
+ this.dataByMonth = this.entryList.filter(entry => new Date(entry.startDate).getMonth() === month);
+ }
}
diff --git a/src/app/components/options-sidebar/time-off/time-off.component.css b/src/app/components/options-sidebar/time-off/time-off.component.css
deleted file mode 100644
index e69de29bb..000000000
diff --git a/src/app/components/options-sidebar/time-off/time-off.component.html b/src/app/components/options-sidebar/time-off/time-off.component.html
deleted file mode 100644
index 50aa714a8..000000000
--- a/src/app/components/options-sidebar/time-off/time-off.component.html
+++ /dev/null
@@ -1,5 +0,0 @@
-
\ No newline at end of file
diff --git a/src/app/components/options-sidebar/time-off/time-off.component.spec.ts b/src/app/components/options-sidebar/time-off/time-off.component.spec.ts
deleted file mode 100644
index 8aa6ed544..000000000
--- a/src/app/components/options-sidebar/time-off/time-off.component.spec.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { TimeOffComponent } from './time-off.component';
-
-describe('TimeOffComponent', () => {
- let component: TimeOffComponent;
- let fixture: ComponentFixture;
-
- function setup() {
- // tslint:disable-next-line: no-shadowed-variable
- const fixture = TestBed.createComponent(TimeOffComponent);
- const app = fixture.debugElement.componentInstance;
- return { fixture, app };
- }
-
- beforeEach(async(() => {
- TestBed.configureTestingModule({
- declarations: [ TimeOffComponent ]
- })
- .compileComponents();
- }));
-
- beforeEach(() => {
- fixture = TestBed.createComponent(TimeOffComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should be created', () => {
- expect(component).toBeTruthy();
- });
-
- it('should have p tag as \'time-off works!\'', async(() => {
- // tslint:disable-next-line: no-shadowed-variable
- const { app, fixture } = setup();
- fixture.detectChanges();
- const compile = fixture.debugElement.nativeElement;
- const ptag = compile.querySelector('p');
- expect(ptag.textContent).toBe('time-off works!');
- }));
-});
diff --git a/src/app/components/options-sidebar/time-off/time-off.component.ts b/src/app/components/options-sidebar/time-off/time-off.component.ts
deleted file mode 100644
index e41c61bdc..000000000
--- a/src/app/components/options-sidebar/time-off/time-off.component.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-@Component({
- selector: 'app-time-off',
- templateUrl: './time-off.component.html',
- styleUrls: ['./time-off.component.css']
-})
-export class TimeOffComponent implements OnInit {
-
- constructor() { }
-
- ngOnInit(): void {
- }
-
-}
diff --git a/src/app/components/shared/clock/clock.component.spec.ts b/src/app/components/shared/clock/clock.component.spec.ts
index 761908de4..9566bfa6b 100644
--- a/src/app/components/shared/clock/clock.component.spec.ts
+++ b/src/app/components/shared/clock/clock.component.spec.ts
@@ -1,11 +1,18 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ClockComponent } from './clock.component';
+import { interval, timer } from 'rxjs';
describe('ClockComponent', () => {
let component: ClockComponent;
let fixture: ComponentFixture;
+ function setup() {
+ // tslint:disable-next-line: no-shadowed-variable
+ const fixture = TestBed.createComponent(ClockComponent);
+ const app = fixture.debugElement.componentInstance;
+ return { fixture, app };
+ }
beforeEach(async(() => {
TestBed.configureTestingModule({
@@ -33,5 +40,4 @@ describe('ClockComponent', () => {
const currenMinutes = 5;
expect(component.currentDate.getMinutes()).toEqual(currenMinutes);
});
-
});
diff --git a/src/app/components/shared/clock/clock.component.ts b/src/app/components/shared/clock/clock.component.ts
index 0eea6b977..ec3ddad30 100644
--- a/src/app/components/shared/clock/clock.component.ts
+++ b/src/app/components/shared/clock/clock.component.ts
@@ -15,14 +15,14 @@ export class ClockComponent implements OnInit {
displayTime: boolean;
constructor() {
- this.showClcok();
+ this.showClock();
this.displayTime = false;
setTimeout(() => {
this.displayTime = true;
}, 3000);
}
- showClcok() {
+ showClock() {
const timenInterval = interval(1000);
timenInterval.subscribe( (data) => {
this.currentDate = new Date();
diff --git a/src/app/components/shared/create-project/create-project.component.ts b/src/app/components/shared/create-project/create-project.component.ts
index c31de6dbc..389a9a41d 100644
--- a/src/app/components/shared/create-project/create-project.component.ts
+++ b/src/app/components/shared/create-project/create-project.component.ts
@@ -1,6 +1,6 @@
import { Component, OnInit, Input, SimpleChanges } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
-import { Project } from '../../../interfaces/project';
+import { Project } from '../../../interfaces';
import { Output, EventEmitter } from '@angular/core';
import { ProjectListComponent } from '../project-list/project-list.component';
@@ -34,8 +34,10 @@ export class CreateProjectComponent implements OnInit {
ngOnChanges(): void {
if (this.projectToEdit) {
this.editProjectId = this.projectToEdit.id;
- this.projectForm.setValue({name: this.projectToEdit.name, details: this.projectToEdit.details,
- status: this.projectToEdit.status, completed: this.projectToEdit.completed});
+ this.projectForm.setValue({
+ name: this.projectToEdit.name, details: this.projectToEdit.details,
+ status: this.projectToEdit.status, completed: this.projectToEdit.completed
+ });
}
}
diff --git a/src/app/components/shared/details-fields/details-fields.component.css b/src/app/components/shared/details-fields/details-fields.component.css
index 82869783c..06b6e01f5 100644
--- a/src/app/components/shared/details-fields/details-fields.component.css
+++ b/src/app/components/shared/details-fields/details-fields.component.css
@@ -1,3 +1,7 @@
.span-width {
width: 6rem;
}
+
+.hidden {
+ display: none;
+}
diff --git a/src/app/components/shared/details-fields/details-fields.component.html b/src/app/components/shared/details-fields/details-fields.component.html
index 7365bc192..fd89befb5 100644
--- a/src/app/components/shared/details-fields/details-fields.component.html
+++ b/src/app/components/shared/details-fields/details-fields.component.html
@@ -1,44 +1,40 @@
-
-
diff --git a/src/app/components/shared/details-fields/details-fields.component.spec.ts b/src/app/components/shared/details-fields/details-fields.component.spec.ts
index 18266ca45..7d7c2019d 100644
--- a/src/app/components/shared/details-fields/details-fields.component.spec.ts
+++ b/src/app/components/shared/details-fields/details-fields.component.spec.ts
@@ -1,16 +1,35 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { DetailsFieldsComponent } from './details-fields.component';
describe('DetailsFieldsComponent', () => {
let component: DetailsFieldsComponent;
let fixture: ComponentFixture;
+ const initialData = {
+ project: '',
+ activity: '',
+ ticket: '',
+ technology: '',
+ comments: ''
+ }
+
+ const newData = {
+ project: 'Ernst&Young',
+ activity: 'development',
+ ticket: 'WA-15',
+ technology: 'Angular',
+ comments: 'No notes'
+ }
beforeEach(async(() => {
TestBed.configureTestingModule({
- declarations: [ DetailsFieldsComponent ]
- })
- .compileComponents();
+ declarations: [DetailsFieldsComponent],
+ imports: [
+ FormsModule,
+ ReactiveFormsModule
+ ],
+ }).compileComponents();
}));
beforeEach(() => {
@@ -22,4 +41,22 @@ describe('DetailsFieldsComponent', () => {
it('should create', () => {
expect(component).toBeTruthy();
});
+
+ it('should emit saveEntry event', () => {
+ spyOn(component.saveEntry, 'emit');
+ component.onSubmit();
+ expect(component.saveEntry.emit).toHaveBeenCalledWith(initialData);
+ });
+
+ it('should emit ngOnChange without data', () => {
+ component.entryToEdit = null;
+ component.ngOnChanges();
+ expect(component.entryForm.value).toEqual(initialData);
+ });
+
+ it('should emit ngOnChange with new data', () => {
+ component.entryToEdit = newData;
+ component.ngOnChanges();
+ expect(component.entryForm.value).toEqual(newData);
+ });
});
diff --git a/src/app/components/shared/details-fields/details-fields.component.ts b/src/app/components/shared/details-fields/details-fields.component.ts
index eea5de70b..93547a015 100644
--- a/src/app/components/shared/details-fields/details-fields.component.ts
+++ b/src/app/components/shared/details-fields/details-fields.component.ts
@@ -1,4 +1,5 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, Input, Output, EventEmitter, ViewChild, ElementRef } from '@angular/core';
+import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector: 'app-details-fields',
@@ -6,10 +7,38 @@ import { Component, OnInit } from '@angular/core';
styleUrls: ['./details-fields.component.css']
})
export class DetailsFieldsComponent implements OnInit {
+ @Input() entryToEdit;
+ @Input() formType: string;
+ @Output() saveEntry = new EventEmitter();
+ @ViewChild('closeModal') closeModal: ElementRef;
+ entryForm: FormGroup;
- constructor() { }
+ constructor(private formBuilder: FormBuilder) {
+ this.entryForm = this.formBuilder.group({
+ project: '',
+ activity: '',
+ ticket: '',
+ technology: '',
+ comments: ''
+ });
+ }
+
+ ngOnInit(): void { }
- ngOnInit(): void {
+ ngOnChanges(): void {
+ if (this.entryToEdit) {
+ this.entryForm.setValue({
+ project: this.entryToEdit.project,
+ activity: this.entryToEdit.activity,
+ ticket: this.entryToEdit.ticket,
+ technology: this.entryToEdit.technology,
+ comments: this.entryToEdit.comments
+ });
+ }
}
+ onSubmit() {
+ this.saveEntry.emit(this.entryForm.value);
+ this.closeModal.nativeElement.click();
+ }
}
diff --git a/src/app/components/shared/empty-state/empty-state.component.css b/src/app/components/shared/empty-state/empty-state.component.css
new file mode 100644
index 000000000..597443547
--- /dev/null
+++ b/src/app/components/shared/empty-state/empty-state.component.css
@@ -0,0 +1,8 @@
+.container-empty-state {
+ opacity: 0.3;
+ padding: 5rem;
+}
+
+.container-empty-state i {
+ font-size: 5rem;
+}
diff --git a/src/app/components/shared/empty-state/empty-state.component.html b/src/app/components/shared/empty-state/empty-state.component.html
new file mode 100644
index 000000000..1c536d61e
--- /dev/null
+++ b/src/app/components/shared/empty-state/empty-state.component.html
@@ -0,0 +1,4 @@
+
+
+
no data to display
+
diff --git a/src/app/components/shared/empty-state/empty-state.component.spec.ts b/src/app/components/shared/empty-state/empty-state.component.spec.ts
new file mode 100644
index 000000000..c5cd9ed4e
--- /dev/null
+++ b/src/app/components/shared/empty-state/empty-state.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { EmptyStateComponent } from './empty-state.component';
+
+describe('EmptyStateComponent', () => {
+ let component: EmptyStateComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ EmptyStateComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(EmptyStateComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/components/shared/empty-state/empty-state.component.ts b/src/app/components/shared/empty-state/empty-state.component.ts
new file mode 100644
index 000000000..0e58e14a1
--- /dev/null
+++ b/src/app/components/shared/empty-state/empty-state.component.ts
@@ -0,0 +1,15 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'app-empty-state',
+ templateUrl: './empty-state.component.html',
+ styleUrls: ['./empty-state.component.css']
+})
+export class EmptyStateComponent implements OnInit {
+
+ constructor() { }
+
+ ngOnInit(): void {
+ }
+
+}
diff --git a/src/app/components/shared/modal/modal.component.html b/src/app/components/shared/modal/modal.component.html
index 8e07addaa..05e9a29e1 100644
--- a/src/app/components/shared/modal/modal.component.html
+++ b/src/app/components/shared/modal/modal.component.html
@@ -1,5 +1,5 @@
-
+
- Are you sure you want to delete {{project.name}} project?
+ Are you sure you want to delete {{ list.name || list.project }} entry?
diff --git a/src/app/components/shared/modal/modal.component.spec.ts b/src/app/components/shared/modal/modal.component.spec.ts
index 986c9587b..01154738d 100644
--- a/src/app/components/shared/modal/modal.component.spec.ts
+++ b/src/app/components/shared/modal/modal.component.spec.ts
@@ -24,19 +24,24 @@ describe('ModalComponent', () => {
});
it('should emit removeProject event #removedProject', () => {
- const project = {
- id: 1,
+ const merged = {
+ id: '1',
name: 'app 4',
details: 'It is a good app',
status: 'inactive',
- completed: true
+ completed: true,
+ project: 'ErnstYoung',
+ startDate: '2020-02-05T15:36:15.887Z',
+ endDate: '2020-02-05T18:36:15.887Z',
+ activity: 'development',
+ technology: 'Angular, TypeScript',
};
- spyOn(component.removeProject, 'emit');
- component.project = project;
+ spyOn(component.removeList, 'emit');
+ component.list = merged;
fixture.detectChanges();
- component.removedProject(project.id);
- expect(component.removeProject.emit).toHaveBeenCalled();
+ component.removeListById(merged.id);
+ expect(component.removeList.emit).toHaveBeenCalled();
component.cancelDeleteModal.nativeElement.click();
});
});
diff --git a/src/app/components/shared/modal/modal.component.ts b/src/app/components/shared/modal/modal.component.ts
index 1180b9572..e269fe5a5 100644
--- a/src/app/components/shared/modal/modal.component.ts
+++ b/src/app/components/shared/modal/modal.component.ts
@@ -1,7 +1,14 @@
-import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
-import { Output, EventEmitter } from '@angular/core';
-import { Input } from '@angular/core';
-import { Project } from '../../../interfaces/project'
+import {
+ Component,
+ OnInit,
+ ViewChild,
+ ElementRef,
+ EventEmitter,
+ Output,
+ Input
+} from '@angular/core';
+import { Project, Entry } from '../../../interfaces'
+
@Component({
selector: 'app-modal',
templateUrl: './modal.component.html',
@@ -9,18 +16,17 @@ import { Project } from '../../../interfaces/project'
})
export class ModalComponent implements OnInit {
- @Input() project: Project;
- @Output() removeProject = new EventEmitter();
+ @Input() list: Project & Entry;
+ @Output() removeList = new EventEmitter();
@ViewChild('cancelDeleteModal') cancelDeleteModal: ElementRef;
constructor() { }
- ngOnInit(): void {
- }
+ ngOnInit(): void { }
- removedProject(projectId) {
- this.removeProject.emit(projectId);
+ removeListById(projectId: string) {
+ this.removeList.emit(projectId);
this.cancelDeleteModal.nativeElement.click();
}
}
diff --git a/src/app/components/shared/month-picker/month-picker.component.css b/src/app/components/shared/month-picker/month-picker.component.css
new file mode 100644
index 000000000..3d6fb70b6
--- /dev/null
+++ b/src/app/components/shared/month-picker/month-picker.component.css
@@ -0,0 +1,16 @@
+.month {
+ background-color: #0093dc;
+ cursor: pointer;
+}
+
+.month:hover {
+ opacity: 0.7;
+}
+
+.month:last-child {
+ margin-right: 0;
+}
+
+.active {
+ background-color: #005ce0;
+}
diff --git a/src/app/components/shared/month-picker/month-picker.component.html b/src/app/components/shared/month-picker/month-picker.component.html
new file mode 100644
index 000000000..32c6d361a
--- /dev/null
+++ b/src/app/components/shared/month-picker/month-picker.component.html
@@ -0,0 +1,10 @@
+
diff --git a/src/app/components/shared/month-picker/month-picker.component.spec.ts b/src/app/components/shared/month-picker/month-picker.component.spec.ts
new file mode 100644
index 000000000..df067b6e1
--- /dev/null
+++ b/src/app/components/shared/month-picker/month-picker.component.spec.ts
@@ -0,0 +1,32 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MonthPickerComponent } from './month-picker.component';
+
+describe('MonthPickerComponent', () => {
+ let component: MonthPickerComponent;
+ let fixture: ComponentFixture
;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ MonthPickerComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(MonthPickerComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('should emit activeMonth event', () => {
+ const month = 1;
+ spyOn(component.monthSelected, 'emit');
+ component.getMonth(month);
+ expect(component.monthSelected.emit).toHaveBeenCalledWith(month);
+ });
+});
diff --git a/src/app/components/shared/month-picker/month-picker.component.ts b/src/app/components/shared/month-picker/month-picker.component.ts
new file mode 100644
index 000000000..bc43dbc9e
--- /dev/null
+++ b/src/app/components/shared/month-picker/month-picker.component.ts
@@ -0,0 +1,23 @@
+import { Component, OnInit, Output, EventEmitter } from '@angular/core';
+
+@Component({
+ selector: 'app-month-picker',
+ templateUrl: './month-picker.component.html',
+ styleUrls: ['./month-picker.component.css']
+})
+export class MonthPickerComponent implements OnInit {
+ @Output() monthSelected = new EventEmitter();
+ activeMonth: number;
+ months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
+
+ constructor() { }
+
+ ngOnInit(): void {
+ this.activeMonth = new Date().getMonth();
+ }
+
+ getMonth(month: number) {
+ this.monthSelected.emit(month);
+ this.activeMonth = month;
+ }
+}
diff --git a/src/app/components/shared/pipes/group-by-date/group-by-date.pipe.spec.ts b/src/app/components/shared/pipes/group-by-date/group-by-date.pipe.spec.ts
new file mode 100644
index 000000000..b8b4906ff
--- /dev/null
+++ b/src/app/components/shared/pipes/group-by-date/group-by-date.pipe.spec.ts
@@ -0,0 +1,8 @@
+import { GroupByDatePipe } from './group-by-date.pipe';
+
+describe('GroupByDatePipe', () => {
+ it('create an instance', () => {
+ const pipe = new GroupByDatePipe();
+ expect(pipe).toBeTruthy();
+ });
+});
diff --git a/src/app/components/shared/pipes/group-by-date/group-by-date.pipe.ts b/src/app/components/shared/pipes/group-by-date/group-by-date.pipe.ts
new file mode 100644
index 000000000..5c9346750
--- /dev/null
+++ b/src/app/components/shared/pipes/group-by-date/group-by-date.pipe.ts
@@ -0,0 +1,33 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+ name: 'groupByDate'
+})
+export class GroupByDatePipe implements PipeTransform {
+
+ getDate(date: string) {
+ return new Date(date).toLocaleDateString();
+ }
+
+ transform(groupDate: Array): Array {
+ const newGroup = groupDate.reduce((previous, current) => {
+
+ const endDate = this.getDate(current.endDate);
+ const startDate = this.getDate(current.startDate);
+
+ if (!previous[endDate]) previous[endDate] = [];
+ if (!previous[startDate]) previous[startDate] = []
+
+ if (startDate !== endDate) previous[startDate].push(current);
+ previous[endDate].push(current);
+
+ return previous;
+ }, {});
+
+ return Object.keys(newGroup).map(date => ({
+ date: date,
+ details: newGroup[date]
+ }));
+ }
+}
+
diff --git a/src/app/components/shared/project-list/project-list.component.html b/src/app/components/shared/project-list/project-list.component.html
index b7204311f..d0697893a 100644
--- a/src/app/components/shared/project-list/project-list.component.html
+++ b/src/app/components/shared/project-list/project-list.component.html
@@ -41,7 +41,7 @@ Please, create one.
diff --git a/src/app/components/shared/project-list/project-list.component.spec.ts b/src/app/components/shared/project-list/project-list.component.spec.ts
index 438257e77..71168eb8a 100644
--- a/src/app/components/shared/project-list/project-list.component.spec.ts
+++ b/src/app/components/shared/project-list/project-list.component.spec.ts
@@ -25,7 +25,7 @@ describe('ProjectListComponent', () => {
it('should emit deleteProject event #removeProject', () => {
const project = {
- id: 1,
+ id: '1',
name: 'app 4',
details: 'It is a good app',
status: 'inactive',
@@ -41,7 +41,7 @@ describe('ProjectListComponent', () => {
it('should open delete modal #openModal', () => {
const project = {
- id: 1,
+ id: '1',
name: 'app 4',
details: 'It is a good app',
status: 'inactive',
@@ -49,7 +49,7 @@ describe('ProjectListComponent', () => {
};
component.openModal(project);
- expect(component.projectToDelete.id).toBe(1);
+ expect(component.projectToDelete.id).toBe('1');
expect(component.projectToDelete.name).toBe('app 4');
expect(component.projectToDelete.details).toBe('It is a good app');
expect(component.projectToDelete.status).toBe('inactive');
diff --git a/src/app/components/shared/project-list/project-list.component.ts b/src/app/components/shared/project-list/project-list.component.ts
index 6fc799c95..3c696ccfe 100644
--- a/src/app/components/shared/project-list/project-list.component.ts
+++ b/src/app/components/shared/project-list/project-list.component.ts
@@ -1,7 +1,7 @@
import { Component, OnInit } from '@angular/core';
import { Input } from '@angular/core';
import { Output, EventEmitter } from '@angular/core';
-import { Project } from '../../../interfaces/project';
+import { Project } from '../../../interfaces';
@Component({
selector: 'app-project-list',
diff --git a/src/app/components/shared/sidebar/sidebar.component.html b/src/app/components/shared/sidebar/sidebar.component.html
index 1a75e8002..c99cc85af 100644
--- a/src/app/components/shared/sidebar/sidebar.component.html
+++ b/src/app/components/shared/sidebar/sidebar.component.html
@@ -2,12 +2,10 @@
diff --git a/src/app/interfaces/entry.ts b/src/app/interfaces/entry.ts
new file mode 100644
index 000000000..62904c0b3
--- /dev/null
+++ b/src/app/interfaces/entry.ts
@@ -0,0 +1,11 @@
+export interface Entry {
+ id: string,
+ project: string,
+ startDate: string,
+ endDate: string,
+ activity: string,
+ technology: string,
+ comments?: string,
+ ticket?: string
+}
+
diff --git a/src/app/interfaces/index.ts b/src/app/interfaces/index.ts
new file mode 100644
index 000000000..e41e3fb3d
--- /dev/null
+++ b/src/app/interfaces/index.ts
@@ -0,0 +1,2 @@
+export * from './project';
+export * from './entry';
diff --git a/src/app/interfaces/project.ts b/src/app/interfaces/project.ts
index 1c7f6c435..d5eb3b8ed 100644
--- a/src/app/interfaces/project.ts
+++ b/src/app/interfaces/project.ts
@@ -1,7 +1,8 @@
export interface Project {
- id: number;
+ id: string;
name: string;
details: string;
status: string;
completed: boolean;
}
+
diff --git a/src/app/services/project.service.spec.ts b/src/app/services/project.service.spec.ts
index 3648b5b41..6e36e632b 100644
--- a/src/app/services/project.service.spec.ts
+++ b/src/app/services/project.service.spec.ts
@@ -1,6 +1,6 @@
import { TestBed, inject, async } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
-import { Project } from '../interfaces/project';
+import { Project } from '../interfaces';
import { ProjectService } from './project.service';
describe('ProjectService', () => {
@@ -8,21 +8,21 @@ describe('ProjectService', () => {
let httpMock: HttpTestingController;
const projects: Project[] = [{
- id: 1,
+ id: '1',
name: 'app 1',
details: 'It is a good app',
status: 'inactive',
completed: true
},
{
- id: 2,
+ id: '2',
name: 'app 2',
details: 'It is a good app',
status: 'inactive',
completed: false
},
{
- id: 3,
+ id: '3',
name: 'app 3',
details: 'It is a good app',
status: 'active',
diff --git a/src/app/services/project.service.ts b/src/app/services/project.service.ts
index 33a2395b9..3e04f2862 100644
--- a/src/app/services/project.service.ts
+++ b/src/app/services/project.service.ts
@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
-import { Project } from '../interfaces/project';
+import { Project } from '../interfaces';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';