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
Expand Up @@ -4,3 +4,8 @@
background-image: $background-gradient-items;
color: white;
}

.scroll {
max-height: 400px;
overflow-y: auto;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,29 @@ import { Input, OnInit } from '@angular/core';
import { Component } from '@angular/core';
import { Store, select } from '@ngrx/store';

import { allActivities } from '../../store';
import { LoadActivities } from './../../store/activity-management.actions';
import { ActivityState } from './../../store/activity-management.reducers';
import { allActivities } from '../../store';
import { Activity } from '../../../shared/models';

@Component({selector: 'app-activity-list', templateUrl: './activity-list.component.html', styleUrls: ['./activity-list.component.scss']})
@Component({
selector: 'app-activity-list',
templateUrl: './activity-list.component.html',
styleUrls: ['./activity-list.component.scss'],
})
export class ActivityListComponent implements OnInit {

@Input()activities: Activity[] = [];
activities: Activity[] = [];
public isLoading: boolean;

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

ngOnInit() {
this.store.dispatch(new LoadActivities());
const activities$ = this.store.pipe(select(allActivities));

activities$.subscribe(response => {
activities$.subscribe((response) => {
this.isLoading = response.isLoading;
this.activities = response.data;
});
}

}
Original file line number Diff line number Diff line change
@@ -1,25 +1,48 @@
import { FormBuilder } from '@angular/forms';
import { async, TestBed } from '@angular/core/testing';
import { async, TestBed, ComponentFixture } from '@angular/core/testing';
import { provideMockStore, MockStore } from '@ngrx/store/testing';

import { CreateActivityComponent } from './create-activity.component';
import { ActivityState, CreateActivity } from '../../store';
import { Activity } from 'src/app/modules/shared/models';

describe('CreateActivityComponent', () => {

let component: CreateActivityComponent;
let fixture: ComponentFixture<CreateActivityComponent>;
let store: MockStore<ActivityState>;

const state = { data: [{ id: 'id', name: 'name', description: 'description' }], isLoading: false, message: '' };

const data = {
name: '',
description: ''
const data: Activity[] = [
{
id: '1',
name: 'Training',
description: 'It is good for learning',
},
];

const activity: Activity = {
id: '1',
name: 'Training',
description: 'It is good for learning',
};

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CreateActivityComponent],
providers: [ FormBuilder ]
});
component = TestBed.createComponent(CreateActivityComponent).componentInstance;
declarations: [CreateActivityComponent],
providers: [FormBuilder, provideMockStore({ initialState: state })],
}).compileComponents();
}));

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

store = TestBed.inject(MockStore);
store.setState(state);
});

it('should create the component', () => {
expect(component).toBeTruthy();
});
Expand Down Expand Up @@ -48,4 +71,10 @@ describe('CreateActivityComponent', () => {
expect(component.activityForm.get).toHaveBeenCalledWith('description');
});

it('should dispatch createActivity action #onSubmit', () => {
spyOn(store, 'dispatch');
component.onSubmit(activity);
expect(store.dispatch).toHaveBeenCalledTimes(1);
expect(store.dispatch).toHaveBeenCalledWith(new CreateActivity(activity));
});
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { FormBuilder, Validators, FormGroup } from '@angular/forms';
import { Component, Input } from '@angular/core';
import { Component, Input, EventEmitter, Output } from '@angular/core';
import { Store, select } from '@ngrx/store';
import { Activity } from '../../../shared/models';
import { ActivityState } from './../../store/activity-management.reducers';
import { CreateActivity, allActivities } from '../../store';

@Component({
selector: 'app-create-activity',
Expand All @@ -9,22 +12,18 @@ import { Activity } from '../../../shared/models';
})
export class CreateActivityComponent {
activityForm: FormGroup;
isLoading: boolean;

@Input()
activityToEdit: Activity;

constructor(private formBuilder: FormBuilder) {
constructor(private formBuilder: FormBuilder, private store: Store<ActivityState>) {
this.activityForm = this.formBuilder.group({
name: ['', Validators.required],
description: [''],
});
}

onSubmit(activityData) {
// TODO: add proper interaction with API to save this info
// see https://github.com/ioet/time-tracker-ui/issues/44
console.log(activityData);
this.activityForm.reset();
this.store.dispatch(new CreateActivity(activityData));
}

get name() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
<div class="parent">
<app-create-activity class="item">
</app-create-activity>
<app-create-activity class="item"> </app-create-activity>

<app-activity-list [activities]="activities" class="item">
</app-activity-list>
<app-activity-list class="item"> </app-activity-list>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import { ActivitiesManagementComponent } from './activities-management.component
describe('ActivitiesManagementComponent', () => {
let component: ActivitiesManagementComponent;
let activityService: ActivityService;
const activitiesFromApi: Activity[] = [{id: '123', name: 'aaa', description: 'xxx'}];
const activitiesFromApi: Activity[] = [{ id: '123', name: 'aaa', description: 'xxx' }];

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ActivitiesManagementComponent],
providers: [ ActivityService, HttpClient, HttpHandler ]
declarations: [ActivitiesManagementComponent],
providers: [ActivityService, HttpClient, HttpHandler],
});
component = TestBed.createComponent(ActivitiesManagementComponent).componentInstance;
activityService = TestBed.inject(ActivityService);
Expand All @@ -23,9 +23,4 @@ describe('ActivitiesManagementComponent', () => {
it('should create the component', () => {
expect(component).toBeTruthy();
});

it('should load activities', () => {
component.ngOnInit();
expect(component.activities).toBe(activitiesFromApi);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,10 @@ import { Activity } from '../../shared/models';
@Component({
selector: 'app-activities-management',
templateUrl: './activities-management.component.html',
styleUrls: ['./activities-management.component.scss']
styleUrls: ['./activities-management.component.scss'],
})
export class ActivitiesManagementComponent implements OnInit {
constructor(private activityService: ActivityService) {}

constructor(private activityService: ActivityService) { }

activities: Activity[];

ngOnInit(): void {
this.activityService.getActivities().subscribe( data => this.activities = data );
}
ngOnInit(): void {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ describe('Activity Service', () => {
let httpMock: HttpTestingController;

const activities: Activity[] = [
{ id: '1', name: 'aaa', description: 'xxx' },
{ id: '2', name: 'bbb', description: 'yyy' }
{ id: '1', name: 'aaa', description: 'xxx' },
{ id: '2', name: 'bbb', description: 'yyy' },
];

beforeEach(() => {
TestBed.configureTestingModule({imports: [HttpClientTestingModule]});
TestBed.configureTestingModule({ imports: [HttpClientTestingModule] });
service = TestBed.inject(ActivityService);
httpMock = TestBed.inject(HttpTestingController);
});
Expand All @@ -23,22 +23,35 @@ describe('Activity Service', () => {
httpMock.verify();
});

it('services are ready to be used', inject([ HttpClientTestingModule, ActivityService ],
it('services are ready to be used', inject(
[HttpClientTestingModule, ActivityService],
(httpClient: HttpClientTestingModule, activityService: ActivityService) => {
expect(activityService).toBeTruthy();
expect(httpClient).toBeTruthy();
}));
expect(activityService).toBeTruthy();
expect(httpClient).toBeTruthy();
}
));

it('activities are read using GET from baseUrl', () => {
const activitiesFoundSize = activities.length;
service.baseUrl = 'foo';
service
.getActivities()
.subscribe(activitiesInResponse => {
expect(activitiesInResponse.length).toBe(activitiesFoundSize);
});
service.getActivities().subscribe((activitiesInResponse) => {
expect(activitiesInResponse.length).toBe(activitiesFoundSize);
});
const getActivitiesRequest = httpMock.expectOne(service.baseUrl);
expect(getActivitiesRequest.request.method).toBe('GET');
getActivitiesRequest.flush(activities);
});

it('create activity using POST from baseUrl', () => {
const activity: Activity[] = [{ id: '1', name: 'ccc', description: 'xxx' }];

service.baseUrl = 'activities';

service.createActivity(activity).subscribe((response) => {
expect(response.length).toBe(1);
});
const createActivitiesRequest = httpMock.expectOne(service.baseUrl);
expect(createActivitiesRequest.request.method).toBe('POST');
createActivitiesRequest.flush(activity);
});
});
14 changes: 11 additions & 3 deletions src/app/modules/activities-management/services/activity.service.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';

import { environment } from './../../../../environments/environment';
import { Activity } from '../../shared/models';

@Injectable({
providedIn: 'root'
providedIn: 'root',
})
export class ActivityService {

baseUrl = `${environment.timeTrackerApiUrl}/activities`;

constructor(private http: HttpClient) {}

getActivities(): Observable<Activity[]> {
return this.http.get<Activity[]>(this.baseUrl);
}

createActivity(activityData): Observable<any> {
const body = {
...activityData,
tenant_id: '4225ab1e-1033-4a5f-8650-0dd4950f38c8',
};

return this.http.post(this.baseUrl, body);
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
import { LoadActivitiesFail } from './activity-management.actions';
import { LoadActivitiesFail, CreateActivitySuccess, CreateActivityFail } from './activity-management.actions';
import { LoadActivitiesSuccess, ActivityManagementActionTypes } from './activity-management.actions';

describe('LoadActivitiesSuccess', () => {

it('LoadActivitiesSuccess type is ActivityManagementActionTypes.LoadActivitiesSuccess', () => {
it('LoadActivitiesSuccess type is ActivityManagementActionTypes.LOAD_ACTIVITIES_SUCCESS', () => {
const loadActivitiesSuccess = new LoadActivitiesSuccess([]);
expect(loadActivitiesSuccess.type).toEqual(ActivityManagementActionTypes.LoadActivitiesSuccess);
expect(loadActivitiesSuccess.type).toEqual(ActivityManagementActionTypes.LOAD_ACTIVITIES_SUCCESS);
});

it('LoadActivitiesFail type is ActivityManagementActionTypes.LoadActivitiesFail', () => {
it('LoadActivitiesFail type is ActivityManagementActionTypes.LOAD_ACTIVITIES_FAIL', () => {
const loadActivitiesFail = new LoadActivitiesFail('error');
expect(loadActivitiesFail.type).toEqual(ActivityManagementActionTypes.LoadActivitiesFail);
expect(loadActivitiesFail.type).toEqual(ActivityManagementActionTypes.LOAD_ACTIVITIES_FAIL);
});

it('CreateActivitySuccess type is ActivityManagementActionTypes.CREATE_ACTIVITY_SUCCESS', () => {
const createActivitySuccess = new CreateActivitySuccess({
id: '1',
name: 'Training',
description: 'It is good for learning',
});
expect(createActivitySuccess.type).toEqual(ActivityManagementActionTypes.CREATE_ACTIVITY_SUCCESS);
});

it('CreateActivityFail type is ActivityManagementActionTypes.CREATE_ACTIVITY_FAIL', () => {
const createActivityFail = new CreateActivityFail('error');
expect(createActivityFail.type).toEqual(ActivityManagementActionTypes.CREATE_ACTIVITY_FAIL);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,52 @@ import { Action } from '@ngrx/store';
import { Activity } from './../../shared/models/activity.model';

export enum ActivityManagementActionTypes {
LoadActivities = '[ActivityManagement] Load Activities',
LoadActivitiesSuccess = '[ActivityManagement] Load Activities Successs',
LoadActivitiesFail = '[ActivityManagement] Load Activities Fail',
LOAD_ACTIVITIES = '[ActivityManagement] LOAD_ACTIVITIES',
LOAD_ACTIVITIES_SUCCESS = '[ActivityManagement] LOAD_ACTIVITIES_SUCCESS',
LOAD_ACTIVITIES_FAIL = '[ActivityManagement] LOAD_ACTIVITIES_FAIL',
CREATE_ACTIVITY = '[ActivityManagement] CREATE_ACTIVITY',
CREATE_ACTIVITY_SUCCESS = '[ActivityManagement] CREATE_ACTIVITY_SUCCESS',
CREATE_ACTIVITY_FAIL = '[ActivityManagement] CREATE_ACTIVITY_FAIL',
}


export class LoadActivities implements Action {
public readonly type = ActivityManagementActionTypes.LoadActivities;
public readonly type = ActivityManagementActionTypes.LOAD_ACTIVITIES;
}

export class LoadActivitiesSuccess implements Action {
public readonly type = ActivityManagementActionTypes.LoadActivitiesSuccess;
public readonly type = ActivityManagementActionTypes.LOAD_ACTIVITIES_SUCCESS;

constructor(public payload: Activity[]) { }
constructor(public payload: Activity[]) {}
}

export class LoadActivitiesFail implements Action {
public readonly type = ActivityManagementActionTypes.LoadActivitiesFail;
public readonly type = ActivityManagementActionTypes.LOAD_ACTIVITIES_FAIL;

constructor(public error: string) {}
}

export class CreateActivity implements Action {
public readonly type = ActivityManagementActionTypes.CREATE_ACTIVITY;

constructor(public payload: Activity) {}
}

export class CreateActivitySuccess implements Action {
public readonly type = ActivityManagementActionTypes.CREATE_ACTIVITY_SUCCESS;

constructor(public payload: Activity) {}
}

export class CreateActivityFail implements Action {
public readonly type = ActivityManagementActionTypes.CREATE_ACTIVITY_FAIL;

constructor(public error) { }
constructor(public error: string) {}
}

export type ActivityManagementActions = LoadActivities | LoadActivitiesSuccess | LoadActivitiesFail;
export type ActivityManagementActions =
| LoadActivities
| LoadActivitiesSuccess
| LoadActivitiesFail
| CreateActivity
| CreateActivitySuccess
| CreateActivityFail;
Loading