Skip to content

Commit 4432065

Browse files
authored
feat: TT-223 Consume end point archive customers (#670)
* feat: TT-223 Consume end point archive customers * fix: TT-223-Consume-end-point-archive-customers
1 parent 04bebc3 commit 4432065

File tree

10 files changed

+321
-46
lines changed

10 files changed

+321
-46
lines changed

src/app/modules/customer-management/components/customer-info/components/customer-list/customer-list.component.html

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,36 @@
88
>
99
<thead class="thead-blue">
1010
<tr class="d-flex">
11-
<th class="col-5 text-center">Customer ID</th>
12-
<th class="col-3 text-center">Name</th>
13-
<th class="col-4 text-center">Options</th>
11+
<th class="col-4 text-center">Customer ID</th>
12+
<th class="col-4 text-center">Name</th>
13+
<th class="col-2 text-center">Options</th>
14+
<th class="col-2 text-center">Visibility</th>
1415
</tr>
1516
</thead>
1617
<app-loading-bar *ngIf="(isLoading$ | async)"></app-loading-bar>
1718
<tbody *ngIf="((isLoading$ | async) === false)">
1819
<tr class="d-flex" *ngFor="let customer of customers">
19-
<td class="col-5 text-break">{{ customer.id }}</td>
20-
<td class="col-3 text-break">{{ customer.name }}</td>
21-
<td class="col-4 text-center">
22-
<button
23-
data-toggle="modal"
24-
(click)="editCustomer(customer.id)"
25-
type="button"
26-
data-target="#editModal"
27-
class="btn btn-sm btn-primary"
28-
>
20+
<td class="col-4 text-break">{{ customer.id }}</td>
21+
<td class="col-4 text-break">{{ customer.name }}</td>
22+
<td class="col-2 text-center">
23+
<button
24+
data-toggle="modal"
25+
(click)="editCustomer(customer.id)"
26+
type="button" data-target="#editModal"
27+
class="btn btn-sm btn-primary">
2928
<i class="fa fa-pen fa-xs"></i>
3029
</button>
31-
32-
<button
33-
data-toggle="modal"
34-
data-target="#deleteModal"
35-
(click)="openModal(customer)"
36-
type="button"
37-
class="btn btn-sm btn-danger ml-2"
30+
</td>
31+
<td class="col-2 text-center">
32+
<button
33+
class="btn btn-sm"
34+
data-toggle="modal"
35+
data-target="#deleteModal"
36+
[ngClass]="customer.btnColor"
37+
(click)="switchStatus(customer)"
3838
>
39-
<i class="fa fa-trash-alt fa-xs"></i>
39+
<i class="fa" [ngClass]="customer.btnIcon" ></i>
40+
{{customer.btnName}}
4041
</button>
4142
</td>
4243
</tr>
@@ -68,4 +69,4 @@
6869
[body]="message"
6970
(closeModalEvent)="closeModal()"
7071
>
71-
</app-dialog>
72+
</app-dialog>

src/app/modules/customer-management/components/customer-info/components/customer-list/customer-list.component.spec.ts

Lines changed: 87 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,39 @@ describe('CustomerTableListComponent', () => {
2323
const actionSub: ActionsSubject = new ActionsSubject();
2424

2525
const state = {
26-
data: [{ tenant_id: 'id', name: 'name', description: 'description' }],
26+
data: [{ tenant_id: 'id', name: 'name', description: 'description', status: 'inactive' }],
2727
isLoading: false,
2828
message: '',
2929
customerIdToEdit: '',
3030
customerId: '',
3131
};
3232

33-
beforeEach(waitForAsync(() => {
34-
TestBed.configureTestingModule({
35-
imports: [NgxPaginationModule, DataTablesModule],
36-
declarations: [CustomerListComponent],
37-
providers: [provideMockStore({ initialState: state }), { provide: ActionsSubject, useValue: actionSub }],
38-
}).compileComponents();
39-
}));
33+
const btnProps = [
34+
{
35+
key: 'active',
36+
_status: false,
37+
btnColor: 'btn-danger',
38+
btnIcon: 'fa-arrow-circle-down',
39+
btnName: 'Archive',
40+
},
41+
{
42+
key: 'inactive',
43+
_status: true,
44+
btnColor: 'btn-primary',
45+
btnIcon: 'fa-arrow-circle-up',
46+
btnName: 'Active',
47+
},
48+
];
49+
50+
beforeEach(
51+
waitForAsync(() => {
52+
TestBed.configureTestingModule({
53+
imports: [NgxPaginationModule, DataTablesModule],
54+
declarations: [CustomerListComponent],
55+
providers: [provideMockStore({ initialState: state }), { provide: ActionsSubject, useValue: actionSub }],
56+
}).compileComponents();
57+
})
58+
);
4059

4160
beforeEach(() => {
4261
fixture = TestBed.createComponent(CustomerListComponent);
@@ -175,7 +194,12 @@ describe('CustomerTableListComponent', () => {
175194

176195
actionSubject.next(action);
177196

178-
expect(component.customers).toEqual(state.data);
197+
const StateWithBtnProperties = state.data.map((customer) => {
198+
const addProps = btnProps.find((prop) => prop.key === component.setActive(customer.status));
199+
return { ...customer, ...addProps };
200+
});
201+
202+
expect(component.customers).toEqual(StateWithBtnProperties);
179203
});
180204

181205
it('on success load customer, the datatable should be reloaded', async () => {
@@ -191,6 +215,60 @@ describe('CustomerTableListComponent', () => {
191215
expect(component.dtElement.dtInstance.then).toHaveBeenCalled();
192216
});
193217

218+
it('openModal should set on true and display "Are you sure you want to archive customer"', () => {
219+
const message = 'Are you sure you want to archive name?';
220+
const itemData = {
221+
id: '1',
222+
name: 'name',
223+
description: 'description',
224+
status: 'active',
225+
key: 'active',
226+
_status: false,
227+
btnColor: 'btn-danger',
228+
btnIcon: 'fa-arrow-circle-down',
229+
btnName: 'Archive',
230+
};
231+
232+
component.openModal(itemData);
233+
expect(component.showModal).toBeTrue();
234+
expect(component.message).toBe(message);
235+
});
236+
237+
it('switchStatus should call openModal() on item.status = activate', () => {
238+
const itemData = {
239+
id: '1',
240+
name: 'name',
241+
description: 'description',
242+
status: 'activate',
243+
key: 'activate',
244+
_status: false,
245+
btnColor: 'btn-danger',
246+
btnIcon: 'fa-arrow-circle-down',
247+
btnName: 'Archive',
248+
};
249+
250+
spyOn(component, 'openModal');
251+
component.switchStatus(itemData);
252+
expect(component.openModal).toHaveBeenCalled();
253+
});
254+
255+
it('switchStatus should set showModal false when item.status = inactive', () => {
256+
const itemData = {
257+
id: '1',
258+
name: 'name',
259+
description: 'description',
260+
status: 'inactive',
261+
key: 'inactive',
262+
_status: true,
263+
btnColor: 'btn-primary',
264+
btnIcon: 'fa-arrow-circle-up',
265+
btnName: 'Active',
266+
};
267+
268+
component.switchStatus(itemData);
269+
expect(component.showModal).toBeFalse();
270+
});
271+
194272
afterEach(() => {
195273
component.dtTrigger.unsubscribe();
196274
component.changeCustomerSubscription.unsubscribe();

src/app/modules/customer-management/components/customer-info/components/customer-list/customer-list.component.ts

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import { Observable, Subject, Subscription } from 'rxjs';
55
import { delay, filter } from 'rxjs/operators';
66
import {
77
customerIdtoEdit,
8-
getIsLoading
8+
getIsLoading,
99
} from 'src/app/modules/customer-management/store/customer-management.selectors';
10-
import { Customer } from './../../../../../shared/models/customer.model';
10+
import { Customer, CustomerUI } from './../../../../../shared/models/customer.model';
1111
import {
1212
CustomerManagementActionTypes,
1313
DeleteCustomer,
@@ -17,6 +17,7 @@ import {
1717
} from './../../../../store/customer-management.actions';
1818
import { ResetProjectToEdit, SetProjectToEdit } from '../../../projects/components/store/project.actions';
1919
import { ResetProjectTypeToEdit, SetProjectTypeToEdit } from '../../../projects-type/store';
20+
import { UnarchiveCustomer } from '../../../../store/customer-management.actions';
2021

2122
@Component({
2223
selector: 'app-customer-list',
@@ -28,7 +29,7 @@ export class CustomerListComponent implements OnInit, OnDestroy, AfterViewInit {
2829
@Input() hasChange: boolean;
2930
@Output() changeValueShowCustomerForm = new EventEmitter<boolean>();
3031
@Input()
31-
customers: Customer[] = [];
32+
customers: CustomerUI[] = [];
3233
dtOptions: any = {};
3334
dtTrigger: Subject<any> = new Subject();
3435
@ViewChild(DataTableDirective, { static: false })
@@ -48,6 +49,23 @@ export class CustomerListComponent implements OnInit, OnDestroy, AfterViewInit {
4849
}
4950

5051
ngOnInit(): void {
52+
const btnProps = [
53+
{
54+
key: 'active',
55+
_status: false,
56+
btnColor: 'btn-danger',
57+
btnIcon: 'fa-arrow-circle-down',
58+
btnName: 'Archive',
59+
},
60+
{
61+
key: 'inactive',
62+
_status: true,
63+
btnColor: 'btn-primary',
64+
btnIcon: 'fa-arrow-circle-up',
65+
btnName: 'Active',
66+
},
67+
];
68+
5169
this.dtOptions = {
5270
scrollY: '325px',
5371
paging: false,
@@ -62,7 +80,10 @@ export class CustomerListComponent implements OnInit, OnDestroy, AfterViewInit {
6280
this.loadCustomersSubscription = this.actionsSubject$
6381
.pipe(filter((action: any) => action.type === CustomerManagementActionTypes.LOAD_CUSTOMERS_SUCCESS))
6482
.subscribe((action) => {
65-
this.customers = action.payload;
83+
this.customers = action.payload.map((customer: CustomerUI) => {
84+
const addProps = btnProps.find((prop) => prop.key === this.setActive(customer.status));
85+
return { ...customer, ...addProps };
86+
});
6687
this.rerenderDataTable();
6788
});
6889
this.changeCustomerSubscription = this.actionsSubject$
@@ -151,7 +172,20 @@ export class CustomerListComponent implements OnInit, OnDestroy, AfterViewInit {
151172

152173
openModal(item: Customer) {
153174
this.idToDelete = item.id;
154-
this.message = `Are you sure you want to delete ${item.name}?`;
175+
this.message = `Are you sure you want to archive ${item.name}?`;
155176
this.showModal = true;
156177
}
178+
179+
switchStatus(item: CustomerUI): void {
180+
if (item.key !== 'inactive') {
181+
this.openModal(item);
182+
} else {
183+
this.showModal = false;
184+
this.store.dispatch(new UnarchiveCustomer(item.id));
185+
}
186+
}
187+
188+
setActive(status: any): string {
189+
return status === 'inactive' ? 'inactive' : 'active';
190+
}
157191
}

src/app/modules/customer-management/store/customer-management.actions.spec.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,22 @@ describe('CustomerManagmentActions', () => {
7777
const resetCustomerIdToEdit = new actions.ResetCustomerToEdit();
7878
expect(resetCustomerIdToEdit.type).toEqual(actions.CustomerManagementActionTypes.RESET_CUSTOMER_ID_TO_EDIT);
7979
});
80+
81+
it('UnarchiveCustomer type is CustomerManagementActionTypes.UNARCHIVE_CUSTOMER', () => {
82+
const unArchiveCustomer = new actions.UnarchiveCustomer('id_test');
83+
expect(unArchiveCustomer.type).toEqual(actions.CustomerManagementActionTypes.UNARCHIVE_CUSTOMER);
84+
});
85+
86+
it('UnarchiveCustomerSuccess type is CustomerManagementActionTypes.UNARCHIVE_CUSTOMER_SUCCESS', () => {
87+
const unArchiveCustomerSuccess = new actions.UnarchiveCustomerSuccess({
88+
id: 'id_test',
89+
status: 'active',
90+
});
91+
expect(unArchiveCustomerSuccess.type).toEqual(actions.CustomerManagementActionTypes.UNARCHIVE_CUSTOMER_SUCCESS);
92+
});
93+
94+
it('UnarchiveCustomerFail type is CustomerManagementActionTypes.UNARCHIVE_CUSTOMER_FAIL', () => {
95+
const unArchiveCustomerFail = new actions.UnarchiveCustomerFail('error');
96+
expect(unArchiveCustomerFail.type).toEqual(actions.CustomerManagementActionTypes.UNARCHIVE_CUSTOMER_FAIL);
97+
});
8098
});

src/app/modules/customer-management/store/customer-management.actions.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Action } from '@ngrx/store';
2-
import { Customer } from '../../shared/models/customer.model';
2+
import { Customer, Status } from '../../shared/models/customer.model';
33

44
export enum CustomerManagementActionTypes {
55
CREATE_CUSTOMER = '[CustomerManagement] CREATE_CUSTOMER',
@@ -16,6 +16,9 @@ export enum CustomerManagementActionTypes {
1616
UPDATE_CUSTOMER_FAIL = '[CustomerManagement] UPDATE_CUSTOMER_FAIL',
1717
SET_CUSTOMER_ID_TO_EDIT = '[CustomerManagement] SET_CUSTOMER_ID_TO_EDIT',
1818
RESET_CUSTOMER_ID_TO_EDIT = '[CustomerManagement] RESET_CUSTOMER_ID_TO_EDIT',
19+
UNARCHIVE_CUSTOMER = '[CustomerManagement] UNARCHIVE_CUSTOMER',
20+
UNARCHIVE_CUSTOMER_SUCCESS = '[CustomerManagement] UNARCHIVE_CUSTOMER_SUCCESS',
21+
UNARCHIVE_CUSTOMER_FAIL = '[CustomerManagement] UNARCHIVE_CUSTOMER_FAIL',
1922
}
2023

2124
export class LoadCustomers implements Action {
@@ -97,6 +100,24 @@ export class ResetCustomerToEdit implements Action {
97100
public readonly type = CustomerManagementActionTypes.RESET_CUSTOMER_ID_TO_EDIT;
98101
}
99102

103+
export class UnarchiveCustomer implements Action {
104+
public readonly type = CustomerManagementActionTypes.UNARCHIVE_CUSTOMER;
105+
106+
constructor(public payload: string) {}
107+
}
108+
109+
export class UnarchiveCustomerSuccess implements Action {
110+
public readonly type = CustomerManagementActionTypes.UNARCHIVE_CUSTOMER_SUCCESS;
111+
112+
constructor(public payload: Status) {}
113+
}
114+
115+
export class UnarchiveCustomerFail implements Action {
116+
public readonly type = CustomerManagementActionTypes.UNARCHIVE_CUSTOMER_FAIL;
117+
118+
constructor(public error: string) {}
119+
}
120+
100121
export type CustomerManagementActions =
101122
| CreateCustomer
102123
| CreateCustomerSuccess
@@ -111,4 +132,7 @@ export type CustomerManagementActions =
111132
| UpdateCustomerSuccess
112133
| UpdateCustomerFail
113134
| SetCustomerToEdit
114-
| ResetCustomerToEdit;
135+
| ResetCustomerToEdit
136+
| UnarchiveCustomer
137+
| UnarchiveCustomerSuccess
138+
| UnarchiveCustomerFail;

src/app/modules/customer-management/store/customer-management.effects.spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,4 +121,27 @@ describe('CustomerEffects', () => {
121121
expect(action.type).toEqual(CustomerManagementActionTypes.DELETE_CUSTOMER_FAIL);
122122
});
123123
});
124+
125+
it('action type is UNARCHIVE_CUSTOMER_SUCCESS when service is executed sucessfully', async () => {
126+
const customerId = 'customerId';
127+
actions$ = of({ type: CustomerManagementActionTypes.UNARCHIVE_CUSTOMER, customerId });
128+
spyOn(toastrService, 'success');
129+
spyOn(service, 'updateCustomer').and.returnValue(of(customer));
130+
131+
effects.updateCustomer$.subscribe((action) => {
132+
expect(toastrService.success).toHaveBeenCalledWith(INFO_SAVED_SUCCESSFULLY);
133+
expect(action.type).toEqual(CustomerManagementActionTypes.UNARCHIVE_CUSTOMER_SUCCESS);
134+
});
135+
});
136+
137+
it('action type is UNARCHIVE_CUSTOMER_FAIL when service fail in execution', async () => {
138+
actions$ = of({ type: CustomerManagementActionTypes.UNARCHIVE_CUSTOMER, customer });
139+
spyOn(toastrService, 'error');
140+
spyOn(service, 'updateCustomer').and.returnValue(throwError({ error: { message: 'fail!' } }));
141+
142+
effects.updateCustomer$.subscribe((action) => {
143+
expect(toastrService.error).toHaveBeenCalled();
144+
expect(action.type).toEqual(CustomerManagementActionTypes.UNARCHIVE_CUSTOMER_FAIL);
145+
});
146+
});
124147
});

0 commit comments

Comments
 (0)