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 @@ -9,7 +9,7 @@ <h2 class="mb-0">
{{ activity.name }}
</a>
<div class="btn-group float-right" role="group">
<i class="far fa-edit btn btn-link text-white"></i>
<i class="far fa-edit btn btn-link text-white" (click)="updateActivity(activity.id)"></i>
<i (click)="deleteActivity(activity.id)" class="far fa-trash-alt btn btn-link text-white"></i>
</div>
</h2>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,31 @@ import { ActivityListComponent } from './activity-list.component';
describe('ActivityListComponent', () => {
let component: ActivityListComponent;
let fixture: ComponentFixture<ActivityListComponent>;
let store: MockStore<ActivityState>;
let mockActivitiesSelector;

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

let store: MockStore<ActivityState>;
const state = {
data: [{ id: 'id', name: 'name', description: 'description' }],
isLoading: false,
message: '',
activityIdToEdit: '',
};

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

store = TestBed.inject(MockStore);

mockActivitiesSelector = store.overrideSelector( allActivities, state );
declarations: [ActivityListComponent],
providers: [provideMockStore({ initialState: state })],
}).compileComponents();
}));

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

store = TestBed.inject(MockStore);
store.setState(state);
mockActivitiesSelector = store.overrideSelector(allActivities, state.data);
});

it('should create', () => {
Expand All @@ -50,6 +53,7 @@ describe('ActivityListComponent', () => {
expect(component.activities).toBe(state.data);
});

afterEach(() => { fixture.destroy(); });

afterEach(() => {
fixture.destroy();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { OnInit } from '@angular/core';
import { Component } from '@angular/core';
import { Store, select } from '@ngrx/store';

import { LoadActivities, DeleteActivity } from './../../store/activity-management.actions';
import { LoadActivities, DeleteActivity, SetActivityToEdit } from './../../store/activity-management.actions';
import { ActivityState } from './../../store/activity-management.reducers';
import { allActivities } from '../../store';
import { Activity } from '../../../shared/models';
Expand All @@ -14,7 +14,6 @@ import { Activity } from '../../../shared/models';
})
export class ActivityListComponent implements OnInit {
activities: Activity[] = [];
public isLoading: boolean;

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

Expand All @@ -23,12 +22,15 @@ export class ActivityListComponent implements OnInit {
const activities$ = this.store.pipe(select(allActivities));

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

deleteActivity(activityId: string) {
this.store.dispatch(new DeleteActivity(activityId));
}

updateActivity(activityId: string) {
this.store.dispatch(new SetActivityToEdit(activityId));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@
<h1 class="card-title">Activity</h1>
<div class="form-group">
<label for="name">Name:</label>
<input class="form-control" id="name" type="text" formControlName="name"
[class.is-invalid]="name.invalid && name.touched" required />
<input
class="form-control"
id="name"
type="text"
formControlName="name"
[class.is-invalid]="name.invalid && name.touched"
required
/>
<div class="text-danger" *ngIf="(name.dirty || name.touched) && name.invalid && name.errors.required">
Activity name is required.
</div>
Expand All @@ -16,7 +22,14 @@ <h1 class="card-title">Activity</h1>
</div>

<div class="btn-toolbar" role="toolbar">
<button class="btn btn-primary" type="submit" [disabled]="!activityForm.valid">Save</button>
<div class="btn-group mr-2" role="group">
<button class="btn btn-primary mb-2" type="submit" [disabled]="!activityForm.valid">Save</button>
</div>
<div class="btn-group mr-2" role="group">
<button class="btn btn-secondary mb-2" type="reset" [hidden]="!activityToEdit" (click)="cancelButton()">
Cancel
</button>
</div>
</div>
</form>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,11 +0,0 @@
@import '../../../../../styles/colors.scss';

.save-button-style {
background-image: $button-primary-color;
color: white;
}

.cancel-button-style {
background-color: $modal-button-secondary;
color: white;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,30 @@ 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 {
ActivityState,
CreateActivity,
UpdateActivity,
activityIdtoEdit,
allActivities,
ResetActivityToEdit,
} 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: Activity[] = [
{
id: '1',
name: 'Training',
description: 'It is good for learning',
},
];
let activityIdtoEditMock;
let allActivitiesMock;
let getActivityByIdMock;

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

const activity: Activity = {
id: '1',
Expand All @@ -43,16 +50,60 @@ describe('CreateActivityComponent', () => {
store.setState(state);
});

afterEach(() => {
fixture.destroy();
});

it('should create the component', () => {
expect(component).toBeTruthy();
});

it('should reset form onSubmit', () => {
it('should reset form onSubmit and dispatch UpdateActivity action', () => {
const currentState = {
data: [{ id: '1', name: 'xxx', description: 'xxxx' }],
isLoading: false,
message: '',
activityIdToEdit: '1',
};

activityIdtoEditMock = store.overrideSelector(activityIdtoEdit, currentState.activityIdToEdit);
allActivitiesMock = store.overrideSelector(allActivities, currentState.data);
getActivityByIdMock = store.overrideSelector(allActivitiesMock, activityIdtoEditMock);

component.activityToEdit = getActivityByIdMock;

const activityForm = {
name: 'Develop',
description: 'xxx',
};

const activityUpdated = {
id: component.activityToEdit.id,
name: 'Develop',
description: 'xxx',
};

spyOn(component.activityForm, 'reset');
spyOn(store, 'dispatch');

component.onSubmit(data);
component.onSubmit(activityForm);

expect(component.activityForm.reset).toHaveBeenCalled();
expect(store.dispatch).toHaveBeenCalledTimes(1);
expect(store.dispatch).toHaveBeenCalledWith(new UpdateActivity(activityUpdated));
});

it('should reset form onSubmit and dispatch CreateActivity action', () => {
component.activityToEdit = undefined;

spyOn(component.activityForm, 'reset');
spyOn(store, 'dispatch');

component.onSubmit(activity);

expect(component.activityForm.reset).toHaveBeenCalled();
expect(store.dispatch).toHaveBeenCalledTimes(1);
expect(store.dispatch).toHaveBeenCalledWith(new CreateActivity(activity));
});

it('should get name using activityForm', () => {
Expand All @@ -71,10 +122,25 @@ describe('CreateActivityComponent', () => {
expect(component.activityForm.get).toHaveBeenCalledWith('description');
});

it('should dispatch createActivity action #onSubmit', () => {
it('should set data in activityForm', () => {
const activityDataForm = {
name: 'Training',
description: 'It is good for learning',
};

spyOn(component.activityForm, 'setValue');

component.setDataToUpdate(activity);
expect(component.activityForm.setValue).toHaveBeenCalledTimes(1);
expect(component.activityForm.setValue).toHaveBeenCalledWith(activityDataForm);
});

it('should dispatch a ResetActivityToEdit action', () => {
spyOn(store, 'dispatch');
component.onSubmit(activity);

component.cancelButton();

expect(store.dispatch).toHaveBeenCalledTimes(1);
expect(store.dispatch).toHaveBeenCalledWith(new CreateActivity(activity));
expect(store.dispatch).toHaveBeenCalledWith(new ResetActivityToEdit());
});
});
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { FormBuilder, Validators, FormGroup } from '@angular/forms';
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { Component, OnInit } from '@angular/core';
import { Store, select } from '@ngrx/store';

import { Activity } from '../../../shared/models';
import { ActivityState } from './../../store/activity-management.reducers';
import { CreateActivity } from '../../store';
import { CreateActivity, UpdateActivity, getActivityById, ResetActivityToEdit } from '../../store';

@Component({
selector: 'app-create-activity',
templateUrl: './create-activity.component.html',
styleUrls: ['./create-activity.component.scss'],
})
export class CreateActivityComponent {
export class CreateActivityComponent implements OnInit {
activityForm: FormGroup;
isLoading: boolean;
activityToEdit: Activity;

constructor(private formBuilder: FormBuilder, private store: Store<ActivityState>) {
this.activityForm = this.formBuilder.group({
Expand All @@ -20,9 +22,12 @@ export class CreateActivityComponent {
});
}

onSubmit(activityData) {
this.activityForm.reset();
this.store.dispatch(new CreateActivity(activityData));
ngOnInit() {
const activities$ = this.store.pipe(select(getActivityById));
activities$.subscribe((activity) => {
this.activityToEdit = activity;
this.setDataToUpdate(this.activityToEdit);
});
}

get name() {
Expand All @@ -32,4 +37,31 @@ export class CreateActivityComponent {
get description() {
return this.activityForm.get('description');
}

setDataToUpdate(activityData: Activity) {
if (activityData) {
this.activityForm.setValue({
name: activityData.name,
description: activityData.description,
});
}
}

onSubmit(activityData) {
this.activityForm.reset();

if (this.activityToEdit) {
const activity = {
...activityData,
id: this.activityToEdit.id,
};
this.store.dispatch(new UpdateActivity(activity));
} else {
this.store.dispatch(new CreateActivity(activityData));
}
}

cancelButton() {
this.store.dispatch(new ResetActivityToEdit());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,17 @@ describe('Activity Service', () => {
expect(getActivitiesRequest.request.method).toBe('DELETE');
getActivitiesRequest.flush(activities);
});

it('update activity using PUT from baseUrl', () => {
const activity = { id: '1', name: 'aaa', description: 'bbb' };

service.baseUrl = 'activities' + '/' + activity.id;

service.updateActivity(activity).subscribe((response) => {
expect(response.name).toBe('aaa');
});
const updateActivitiesRequest = httpMock.expectOne(`${service.baseUrl}/${activity.id}`);
expect(updateActivitiesRequest.request.method).toBe('PUT');
updateActivitiesRequest.flush(activity);
});
});
11 changes: 11 additions & 0 deletions src/app/modules/activities-management/services/activity.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,15 @@ export class ActivityService {
const url = `${this.baseUrl}/${acitivityId}`;
return this.http.delete(url);
}

updateActivity(activityData): Observable<any> {
const url = `${this.baseUrl}/${activityData.id}`;

const body = {
...activityData,
tenant_id: '4225ab1e-1033-4a5f-8650-0dd4950f38c8',
};

return this.http.put(url, body);
}
}
Loading