Skip to content

Commit fa0eb34

Browse files
authored
Merge pull request #152 from ioet/90_Manage_customer_project_types
fix: #90 Manage customer project types
2 parents 18b6aa5 + 5986a9f commit fa0eb34

26 files changed

+967
-45
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ yarn-error.log
4141
testem.log
4242
/typings
4343
.keys.json
44-
**keys.ts
44+
keys.ts
4545
src/environments/keys.ts
4646

4747
# System Files

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/app/app.module.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import { LoginComponent } from './modules/login/login.component';
3434
import { ActivityEffects } from './modules/activities-management/store/activity-management.effects';
3535
import { ProjectEffects } from './modules/project-management/store/project.effects';
3636
import { TechnologyEffects } from './modules/shared/store/technology.effects';
37+
import { ProjectTypeEffects } from './modules/customer-management/components/projects-type/store/project-type.effects';
3738
import { reducers, metaReducers } from './reducers';
3839
import { environment } from '../environments/environment';
3940
import { CustomerComponent } from './modules/customer-management/pages/customer.component';
@@ -102,7 +103,7 @@ import { InjectTokenInterceptor } from './modules/shared/interceptors/inject.tok
102103
maxAge: 15, // Retains last 15 states
103104
})
104105
: [],
105-
EffectsModule.forRoot([ProjectEffects, ActivityEffects, CustomerEffects, TechnologyEffects]),
106+
EffectsModule.forRoot([ProjectEffects, ActivityEffects, CustomerEffects, TechnologyEffects, ProjectTypeEffects]),
106107
],
107108
providers: [{
108109
provide: HTTP_INTERCEPTORS,

src/app/modules/customer-management/components/management-customer-projects/management-customer-projects.component.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@
5252
<app-create-project></app-create-project>
5353
</div>
5454
<div class="tab-pane fade mt-3" id="projectsType" role="tabpanel" aria-labelledby="projects-type-tab">
55-
<app-create-project-type></app-create-project-type>
55+
<div class="container">
56+
<app-create-project-type></app-create-project-type>
57+
<app-project-type-list></app-project-type-list>
58+
</div>
5659
</div>
5760
</div>
5861
</div>
Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,35 @@
1-
<div class="container">
2-
<form style="width: 600px;">
1+
2+
<form style="width: 600px;" [formGroup]="projectTypeForm" (ngSubmit)="onSubmit(projectTypeForm.value)">
33
<div class="form-group">
4-
<input type="text" class="form-control form-control-sm" id="" aria-describedby="" placeholder="Name" />
5-
<textarea class="form-control form-control-sm mt-2" id="descriptionTextArea" rows="3"
6-
placeholder="Description"></textarea>
7-
<button type="submit" class="btn btn-sm btn-primary">Save</button>
8-
<button type="submit" class="btn btn-sm btn-secondary mb-2 ml-2 mt-2">Cancel</button>
4+
<input
5+
type="text"
6+
class="form-control form-control-sm"
7+
id="name"
8+
formControlName="name"
9+
aria-describedby=""
10+
[class.is-invalid]="name.invalid && name.touched"
11+
required
12+
placeholder="Name"
13+
/>
14+
<div class="text-danger" *ngIf="(name.dirty || name.touched) && name.invalid && name.errors.required">
15+
Name is required.
16+
</div>
17+
<textarea
18+
class="form-control form-control-sm mt-2"
19+
id="descriptionTextArea"
20+
rows="3"
21+
placeholder="Description"
22+
formControlName="description"
23+
></textarea>
24+
<div class="btn-toolbar" role="toolbar">
25+
<div class="btn-group mr-2" role="group">
26+
<button type="submit" class="btn btn-sm btn-primary mb-2 ml-2 mt-2" [disabled]="!projectTypeForm.valid">Save</button>
27+
</div>
28+
<div class="btn-group mr-2" role="group">
29+
<button class="btn btn-sm btn-secondary mb-2 ml-2 mt-2" type="reset" [hidden]="!projectTypeToEdit" (click)="cancelButton()">Cancel</button>
30+
</div>
31+
</div>
932
</div>
1033
</form>
1134
<hr />
12-
<app-project-type-list></app-project-type-list>
13-
</div>
35+
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,146 @@
11
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2+
import { FormBuilder } from '@angular/forms';
3+
import { provideMockStore, MockStore } from '@ngrx/store/testing';
24

35
import { CreateProjectTypeComponent } from './create-project-type.component';
6+
import {
7+
ProjectTypeState,
8+
CreateProjectType,
9+
UpdateProjectType,
10+
projectTypeIdToEdit,
11+
allProjectTypes,
12+
ResetProjectTypeToEdit,
13+
} from '../../store';
14+
import { ProjectType } from '../../../../../shared/models/project-type.model';
415

516
describe('InputProjectTypeComponent', () => {
617
let component: CreateProjectTypeComponent;
718
let fixture: ComponentFixture<CreateProjectTypeComponent>;
19+
let store: MockStore<ProjectTypeState>;
20+
let projectTypeIdToEditMock;
21+
let allProjectTypesMock;
22+
let getProjectTypeByIdMock;
23+
24+
const state = {
25+
data: [{ id: '', name: '', description: '' }],
26+
isLoading: false,
27+
message: '',
28+
projectTypeIdToEdit: '',
29+
};
30+
31+
const projectType: ProjectType = {
32+
id: '1',
33+
name: 'Training',
34+
description: 'It is good for learning',
35+
};
836

937
beforeEach(async(() => {
1038
TestBed.configureTestingModule({
1139
declarations: [CreateProjectTypeComponent],
40+
providers: [FormBuilder, provideMockStore({ initialState: state })],
1241
}).compileComponents();
1342
}));
1443

1544
beforeEach(() => {
1645
fixture = TestBed.createComponent(CreateProjectTypeComponent);
1746
component = fixture.componentInstance;
1847
fixture.detectChanges();
48+
49+
store = TestBed.inject(MockStore);
50+
store.setState(state);
51+
});
52+
53+
afterEach(() => {
54+
fixture.destroy();
1955
});
2056

2157
it('component should be created', () => {
2258
expect(component).toBeTruthy();
2359
});
60+
61+
it('should reset form onSubmit and dispatch UpdateProjectType action', () => {
62+
const currentState = {
63+
data: [{ id: '1', name: 'xxx', description: 'xxxx' }],
64+
isLoading: false,
65+
message: '',
66+
projectTypeIdToEdit: '1',
67+
};
68+
69+
projectTypeIdToEditMock = store.overrideSelector(projectTypeIdToEdit, currentState.projectTypeIdToEdit);
70+
allProjectTypesMock = store.overrideSelector(allProjectTypes, currentState.data);
71+
getProjectTypeByIdMock = store.overrideSelector(allProjectTypesMock, projectTypeIdToEditMock);
72+
73+
component.projectTypeToEdit = getProjectTypeByIdMock;
74+
75+
const projectTypeForm = {
76+
name: 'Develop',
77+
description: 'xxx',
78+
};
79+
80+
const projectTypeUpdated = {
81+
id: component.projectTypeToEdit.id,
82+
name: 'Develop',
83+
description: 'xxx',
84+
};
85+
86+
spyOn(component.projectTypeForm, 'reset');
87+
spyOn(store, 'dispatch');
88+
89+
component.onSubmit(projectTypeForm);
90+
91+
expect(component.projectTypeForm.reset).toHaveBeenCalled();
92+
expect(store.dispatch).toHaveBeenCalledTimes(1);
93+
expect(store.dispatch).toHaveBeenCalledWith(new UpdateProjectType(projectTypeUpdated));
94+
});
95+
96+
it('should reset form onSubmit and dispatch CreateProjectType action', () => {
97+
component.projectTypeToEdit = undefined;
98+
99+
spyOn(component.projectTypeForm, 'reset');
100+
spyOn(store, 'dispatch');
101+
102+
component.onSubmit(projectType);
103+
104+
expect(component.projectTypeForm.reset).toHaveBeenCalled();
105+
expect(store.dispatch).toHaveBeenCalledTimes(1);
106+
expect(store.dispatch).toHaveBeenCalledWith(new CreateProjectType(projectType));
107+
});
108+
109+
it('should get name using projectTypeForm', () => {
110+
spyOn(component.projectTypeForm, 'get');
111+
// tslint:disable-next-line:no-unused-expression
112+
component.name;
113+
expect(component.projectTypeForm.get).toHaveBeenCalledWith('name');
114+
});
115+
116+
it('should get description using projectTypeForm', () => {
117+
spyOn(component.projectTypeForm, 'get');
118+
119+
// tslint:disable-next-line:no-unused-expression
120+
component.description;
121+
122+
expect(component.projectTypeForm.get).toHaveBeenCalledWith('description');
123+
});
124+
125+
it('should set data in projectTypeForm', () => {
126+
const projectTypeDataForm = {
127+
name: 'Training',
128+
description: 'It is good for learning',
129+
};
130+
131+
spyOn(component.projectTypeForm, 'setValue');
132+
133+
component.setDataToUpdate(projectType);
134+
expect(component.projectTypeForm.setValue).toHaveBeenCalledTimes(1);
135+
expect(component.projectTypeForm.setValue).toHaveBeenCalledWith(projectTypeDataForm);
136+
});
137+
138+
it('should dispatch a ResetProjectTypeToEdit action', () => {
139+
spyOn(store, 'dispatch');
140+
141+
component.cancelButton();
142+
143+
expect(store.dispatch).toHaveBeenCalledTimes(1);
144+
expect(store.dispatch).toHaveBeenCalledWith(new ResetProjectTypeToEdit());
145+
});
24146
});
Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,68 @@
1-
import { Component } from '@angular/core';
1+
import { Component, OnInit } from '@angular/core';
2+
import { FormBuilder, Validators, FormGroup } from '@angular/forms';
3+
import { Store, select } from '@ngrx/store';
4+
5+
import { ProjectType } from '../../../../../shared/models';
6+
import { ProjectTypeState } from '../../store';
7+
import { CreateProjectType, ResetProjectTypeToEdit, UpdateProjectType, getProjectTypeById } from '../../store';
28

39
@Component({
410
selector: 'app-create-project-type',
511
templateUrl: './create-project-type.component.html',
612
styleUrls: ['./create-project-type.component.scss'],
713
})
8-
export class CreateProjectTypeComponent {
9-
constructor() {}
14+
export class CreateProjectTypeComponent implements OnInit {
15+
projectTypeForm: FormGroup;
16+
projectTypeToEdit: ProjectType;
17+
18+
constructor(private formBuilder: FormBuilder, private store: Store<ProjectTypeState>) {
19+
this.projectTypeForm = this.formBuilder.group({
20+
name: ['', Validators.required],
21+
description: [''],
22+
});
23+
}
24+
25+
ngOnInit(): void {
26+
const projectType$ = this.store.pipe(select(getProjectTypeById));
27+
projectType$.subscribe((projectType) => {
28+
this.projectTypeToEdit = projectType;
29+
this.setDataToUpdate(this.projectTypeToEdit);
30+
});
31+
}
32+
33+
get name() {
34+
return this.projectTypeForm.get('name');
35+
}
36+
37+
get description() {
38+
return this.projectTypeForm.get('description');
39+
}
40+
41+
setDataToUpdate(projectTypeData: ProjectType) {
42+
if (projectTypeData) {
43+
this.projectTypeForm.setValue({
44+
name: projectTypeData.name,
45+
description: projectTypeData.description,
46+
});
47+
}
48+
}
49+
50+
onSubmit(projectTypeData) {
51+
this.projectTypeForm.reset();
52+
53+
if (this.projectTypeToEdit) {
54+
const projectType = {
55+
...projectTypeData,
56+
id: this.projectTypeToEdit.id,
57+
};
58+
this.store.dispatch(new UpdateProjectType(projectType));
59+
} else {
60+
this.store.dispatch(new CreateProjectType(projectTypeData));
61+
this.projectTypeForm.get('description').setValue('');
62+
}
63+
}
64+
65+
cancelButton() {
66+
this.store.dispatch(new ResetProjectTypeToEdit());
67+
}
1068
}

src/app/modules/customer-management/components/projects-type/components/project-type-list/project-type-list.component.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@
99
<tr
1010
class="d-flex"
1111
*ngFor="
12-
let projectType of projectsType | paginate: { itemsPerPage: itemsPerPage, currentPage: initPage2, id: 'second' }
12+
let projectType of projectTypes | paginate: { itemsPerPage: itemsPerPage, currentPage: initPage2, id: 'second' }
1313
"
1414
>
1515
<td class="col-sm-9">{{ projectType.name }}</td>
1616
<td class="col-sm-3 text-center">
17-
<button type="button" class="btn btn-sm btn-secondary"><i class="fa fa-pencil fa-xs"></i></button>
18-
<button type="button" class="btn btn-sm btn-danger ml-2"><i class="fas fa-trash-alt fa-xs"></i></button>
17+
<button type="button" class="btn btn-sm btn-secondary" (click)="updateProjectType(projectType.id)"><i class="fa fa-pencil fa-xs"></i></button>
18+
<button type="button" class="btn btn-sm btn-danger ml-2" (click)="deleteProjectType(projectType.id)"><i class="fas fa-trash-alt fa-xs"></i></button>
1919
</td>
2020
</tr>
2121
</tbody>

0 commit comments

Comments
 (0)