Skip to content

Commit e50d0a2

Browse files
committed
feat: TT-218-Dont-allow-deleting-projects
1 parent 03ea798 commit e50d0a2

File tree

7 files changed

+160
-20
lines changed

7 files changed

+160
-20
lines changed

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

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@
22
<table class="table table-sm table-bordered table-striped">
33
<thead class="thead-blue">
44
<tr class="d-flex">
5-
<th class="col-4">Project ID</th>
6-
<th class="col-5">Project</th>
7-
<th class="col-3 text-center"></th>
5+
<th class="col-4 text-center">Project ID</th>
6+
<th class="col-4 text-center">Project</th>
7+
<th class="col-2 text-center">Options</th>
8+
<th class="col-2 text-center">Visibility</th>
89
</tr>
910
</thead>
1011
<tbody>
1112
<tr class="d-flex" *ngFor="let project of projects">
1213
<td class="col-sm-4">{{ project.id }}</td>
13-
<td class="col-sm-5">{{ project.name }}</td>
14-
<td class="col-sm-3 text-center">
14+
<td class="col-sm-4">{{ project.name }}</td>
15+
<td class="col-sm-2 text-center">
1516
<button type="button" class="btn btn-sm btn-primary" (click)="updateProject(project)">
1617
<i class="fa fa-pencil fa-xs"></i>
1718
</button>
@@ -25,6 +26,18 @@
2526
<i class="fa fa-trash-alt fa-xs"></i>
2627
</button>
2728
</td>
29+
<td class="col-sm-2 text-center">
30+
<button
31+
class="btn btn-sm"
32+
data-toggle="modal"
33+
data-target="#deleteModal"
34+
[ngClass]="project.btnColor"
35+
(click)="switchStatus(project)"
36+
>
37+
<i class="fa" [ngClass]="project.btnIcon" ></i>
38+
{{project.btnName}}
39+
</button>
40+
</td>
2841
</tr>
2942
</tbody>
3043
</table>
@@ -37,7 +50,7 @@
3750
tabindex="-1"
3851
role="dialog"
3952
aria-hidden="true"
40-
[title]="'Delete Project'"
53+
[title]="'Archive Project'"
4154
[body]="message"
4255
(closeModalEvent)="deleteProject()"
4356
>

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

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { Project } from 'src/app/modules/shared/models';
66
import { ProjectState } from '../store/project.reducer';
77
import { getCustomerProjects } from '../store/project.selectors';
88
import * as actions from '../store/project.actions';
9+
import { ProjectUI } from '../../../../../shared/models/project.model';
10+
import { UnarchiveProject } from '../store/project.actions';
911

1012
@Component({
1113
selector: 'app-project-list',
@@ -16,7 +18,7 @@ export class ProjectListComponent implements OnInit, OnDestroy {
1618
initPage3 = 1;
1719
itemsPerPage = ITEMS_PER_PAGE;
1820
isLoading = false;
19-
projects: Project[] = [];
21+
projects: ProjectUI[] = [];
2022
filterProjects = '';
2123
showModal = false;
2224
idToDelete: string;
@@ -27,10 +29,30 @@ export class ProjectListComponent implements OnInit, OnDestroy {
2729
constructor(private store: Store<ProjectState>) {}
2830

2931
ngOnInit(): void {
32+
const btnProps = [
33+
{
34+
key: 'active',
35+
_status: false,
36+
btnColor: 'btn-danger',
37+
btnIcon: 'fa-arrow-circle-down',
38+
btnName: 'Archive',
39+
},
40+
{
41+
key: 'inactive',
42+
_status: true,
43+
btnColor: 'btn-primary',
44+
btnIcon: 'fa-arrow-circle-up',
45+
btnName: 'Active',
46+
},
47+
];
48+
3049
const projects$ = this.store.pipe(select(getCustomerProjects));
3150
this.projectsSubscription = projects$.subscribe((response) => {
3251
this.isLoading = response.isLoading;
33-
this.projects = response.customerProjects;
52+
this.projects = response.customerProjects.map((project: ProjectUI) => {
53+
const addProps = btnProps.find((prop) => prop.key === this.setActive(project.status));
54+
return { ...project, ...addProps };
55+
});
3456
});
3557
}
3658

@@ -47,9 +69,22 @@ export class ProjectListComponent implements OnInit, OnDestroy {
4769
this.showModal = false;
4870
}
4971

50-
openModal(item: Project) {
72+
openModal(item: ProjectUI) {
5173
this.idToDelete = item.id;
52-
this.message = `Are you sure you want to delete ${item.name}?`;
74+
this.message = `Are you sure you want archive ${item.name}?`;
5375
this.showModal = true;
5476
}
77+
78+
switchStatus(item: ProjectUI): void {
79+
if (item.key !== 'inactive') {
80+
this.openModal(item);
81+
} else {
82+
this.showModal = false;
83+
this.store.dispatch(new UnarchiveProject(item.id));
84+
}
85+
}
86+
87+
setActive(status: any): string {
88+
return status === 'inactive' ? 'inactive' : 'active';
89+
}
5590
}

src/app/modules/customer-management/components/projects/components/store/project.actions.ts

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

44
export enum ProjectActionTypes {
55
LOAD_PROJECTS = '[Projects] LOAD_PROJECTS',
@@ -20,6 +20,9 @@ export enum ProjectActionTypes {
2020
DELETE_PROJECT_SUCCESS = '[Projects] DELETE_PROJECT_SUCESS',
2121
DELETE_PROJECT_FAIL = '[Projects] DELETE_PROJECT_FAIL',
2222
CLEAN_CUSTOMER_PROJECTS = '[Projects] CLEAN_CUSTOMER_PROJECTS',
23+
UNARCHIVE_PROJECT = '[Projects] UNARCHIVE_PROJECT',
24+
UNARCHIVE_PROJECT_SUCCESS = '[Projects] UNARCHIVE_PROJECT_SUCCESS',
25+
UNARCHIVE_PROJECT_FAIL = '[Projects] UNARCHIVE_PROJECT_FAIL',
2326
}
2427

2528
export class CleanCustomerProjects implements Action {
@@ -42,7 +45,6 @@ export class LoadProjectsFail implements Action {
4245
constructor(public error: string) {}
4346
}
4447

45-
4648
export class LoadCustomerProjects implements Action {
4749
public readonly type = ProjectActionTypes.LOAD_CUSTOMER_PROJECTS;
4850
constructor(public customerId: string) {}
@@ -122,6 +124,24 @@ export class DeleteProjectFail implements Action {
122124
constructor(public error: string) {}
123125
}
124126

127+
export class UnarchiveProject implements Action {
128+
public readonly type = ProjectActionTypes.UNARCHIVE_PROJECT;
129+
130+
constructor(public payload: string) {}
131+
}
132+
133+
export class UnarchiveProjectSuccess implements Action {
134+
public readonly type = ProjectActionTypes.UNARCHIVE_PROJECT_SUCCESS;
135+
136+
constructor(public payload: Status) {}
137+
}
138+
139+
export class UnarchiveProjectFail implements Action {
140+
public readonly type = ProjectActionTypes.UNARCHIVE_PROJECT_FAIL;
141+
142+
constructor(public error: string) {}
143+
}
144+
125145
export type ProjectActions =
126146
| CleanCustomerProjects
127147
| LoadProjects
@@ -140,4 +160,7 @@ export type ProjectActions =
140160
| ResetProjectToEdit
141161
| DeleteProject
142162
| DeleteProjectSuccess
143-
| DeleteProjectFail;
163+
| DeleteProjectFail
164+
| UnarchiveProject
165+
| UnarchiveProjectSuccess
166+
| UnarchiveProjectFail;

src/app/modules/customer-management/components/projects/components/store/project.effects.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { ofType, Actions, Effect } from '@ngrx/effects';
77
import { ProjectService } from '../services/project.service';
88
import * as actions from './project.actions';
99
import { ToastrService } from 'ngx-toastr';
10+
import { Status } from 'src/app/modules/shared/models';
1011

1112
@Injectable()
1213
export class ProjectEffects {
@@ -101,4 +102,25 @@ export class ProjectEffects {
101102
)
102103
)
103104
);
105+
106+
@Effect()
107+
unarchiveProject$: Observable<Action> = this.actions$.pipe(
108+
ofType(actions.ProjectActionTypes.UNARCHIVE_PROJECT),
109+
map((action: actions.UnarchiveProject) => ({
110+
id: action.payload,
111+
status: 'active',
112+
})),
113+
mergeMap((project: Status) =>
114+
this.projectService.updateProject(project).pipe(
115+
map((projectData) => {
116+
this.toastrService.success(INFO_SAVED_SUCCESSFULLY);
117+
return new actions.UpdateProjectSuccess(projectData);
118+
}),
119+
catchError((error) => {
120+
this.toastrService.error(error.error.message);
121+
return of(new actions.UpdateProjectFail(error));
122+
})
123+
)
124+
)
125+
);
104126
}

src/app/modules/customer-management/components/projects/components/store/project.reducer.ts

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ export const initialState = {
2020
export const projectReducer = (state: ProjectState = initialState, action: ProjectActions) => {
2121
const projects = [...state.customerProjects];
2222
switch (action.type) {
23-
2423
case ProjectActionTypes.LOAD_PROJECTS: {
2524
return {
2625
...state,
@@ -139,25 +138,27 @@ export const projectReducer = (state: ProjectState = initialState, action: Proje
139138
return {
140139
...state,
141140
isLoading: true,
142-
message: 'Loading delete project',
141+
message: 'Loading archive project',
143142
};
144143
}
145144

146145
case ProjectActionTypes.DELETE_PROJECT_SUCCESS: {
147-
const newProjects = state.customerProjects.filter((project) => project.id !== action.projectId);
146+
const index = projects.findIndex((project) => project.id === action.projectId);
147+
const archived = { ...projects[index], ...{ status: 'inactive' } };
148+
projects[index] = archived;
148149
return {
149150
...state,
150-
customerProjects: newProjects,
151+
customerProjects: projects,
151152
isLoading: false,
152-
message: 'Project removed successfully!',
153+
message: 'Project archived successfully!',
153154
};
154155
}
155156

156157
case ProjectActionTypes.DELETE_PROJECT_FAIL: {
157158
return {
158159
customerProjects: [],
159160
isLoading: false,
160-
message: 'Something went wrong deleting the project!',
161+
message: 'Something went wrong archiving the project!',
161162
projectToEdit: undefined,
162163
};
163164
}
@@ -168,6 +169,34 @@ export const projectReducer = (state: ProjectState = initialState, action: Proje
168169
};
169170
}
170171

172+
case ProjectActionTypes.UNARCHIVE_PROJECT: {
173+
return {
174+
...state,
175+
isLoading: true,
176+
message: 'Loading unarchive project',
177+
};
178+
}
179+
180+
case ProjectActionTypes.UNARCHIVE_PROJECT_SUCCESS: {
181+
const index = projects.findIndex((project) => project.id === action.payload.id);
182+
projects[index] = { ...projects[index], ...action.payload };
183+
return {
184+
...state,
185+
customerProjects: projects,
186+
isLoading: false,
187+
message: 'Data unarchived successfully!',
188+
projectToEdit: undefined,
189+
};
190+
}
191+
192+
case ProjectActionTypes.UNARCHIVE_PROJECT_FAIL: {
193+
return {
194+
isLoading: false,
195+
message: 'Something went wrong unarchiving projects!',
196+
projectToEdit: undefined,
197+
};
198+
}
199+
171200
default:
172201
return state;
173202
}

src/app/modules/customer-management/components/projects/components/store/project.selectors.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ const getProjectState = createFeatureSelector<ProjectState>('projects');
55

66
export const getCustomerProjects = createSelector(getProjectState, (state: ProjectState) => state);
77

8-
export const getProjects = createSelector(getProjectState, (state: ProjectState) => state?.projects);
8+
export const getProjects = createSelector(getProjectState, (state: ProjectState) =>
9+
state?.projects.filter((item) => item.status !== 'inactive')
10+
);
911

1012
export const getProjectToEdit = createSelector(getProjectState, (state: ProjectState) => state?.projectToEdit);
1113

src/app/modules/shared/models/project.model.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,20 @@ export interface Project {
66
description?: string;
77
project_type_id?: string;
88
search_field?: string;
9+
status?: any;
10+
}
11+
12+
export interface ProjectUI {
13+
id?: string;
14+
customer_id?: string;
15+
customer_name?: string;
16+
name?: string;
17+
description?: string;
18+
project_type_id?: string;
19+
search_field?: string;
20+
status?: any;
21+
key?: string;
22+
btnColor?: string;
23+
btnIcon?: string;
24+
btnName?: string;
925
}

0 commit comments

Comments
 (0)