Skip to content
This repository has been archived by the owner on Oct 24, 2023. It is now read-only.

Category Manager - list and view working; add+edit skeleton #190

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,15 @@
"@angular/platform-browser-dynamic": "^8.2.4",
"@angular/router": "^8.2.4",
"@angular/service-worker": "^8.2.4",
"@ng-bootstrap/ng-bootstrap": "5.0.0",
"@ngtools/webpack": "^8.3.2",
"@ngx-lib/multiselect": "^1.0.3",
"@ng-bootstrap/ng-bootstrap": "5.0.0",
"@types/lodash": "^4.14.179",
"bootstrap": "^4.3.1",
"core-js": "^3.2.1",
"custom-select-dropdown": "^1.0.11",
"font-awesome": "^4.7.0",
"lodash": "^4.17.21",
"moment": "^2.24.0",
"rxjs": "^6.5.3",
"zone.js": "~0.9.1"
Expand Down
36 changes: 24 additions & 12 deletions src/app/app.routing.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Routes, RouterModule } from '@angular/router';
import { RouterModule, Routes } from '@angular/router';

import { AuthenticationGuard, RoleGuard } from './core';
import { NgModule } from '@angular/core';
Expand All @@ -7,57 +7,69 @@ import { NotFoundComponent } from './pages/404';
const AppRoutes: Routes = [
{
path: '',
data: {dashboard: true},
data: { dashboard: true },
canActivate: [AuthenticationGuard],
children: [
{
path: 'volunteers',
loadChildren: './pages/volunteers/volunteers.module#VolunteersModule',
loadChildren:
'./pages/volunteers/volunteers.module#VolunteersModule',
canActivate: [RoleGuard],
data: {roles: ['DSU', 'NGO', 'INS']}
data: { roles: ['DSU', 'NGO', 'INS'] }
},
{
path: 'resources',
loadChildren: './pages/resources/resources.module#ResourcesModule',
loadChildren:
'./pages/resources/resources.module#ResourcesModule',
canActivate: [RoleGuard],
data: {roles: ['DSU', 'NGO']}
data: { roles: ['DSU', 'NGO'] }
},
{
path: 'categories',
loadChildren:
'./pages/categories/categories.module#CategoriesModule',
canActivate: [RoleGuard],
data: { roles: ['DSU'] }
},
{
path: 'organisations',
loadChildren: './pages/organisations/organisations.module#OrganisationsModule',
loadChildren:
'./pages/organisations/organisations.module#OrganisationsModule',
canActivate: [RoleGuard],
data: {roles: ['DSU', 'NGO']}
data: { roles: ['DSU', 'NGO'] }
},
{
path: 'map',
loadChildren: './pages/map/map.module#MapModule',
canActivate: [RoleGuard],
data: {roles: ['DSU']}
data: { roles: ['DSU'] }
},
{
path: 'info',
loadChildren: './pages/info/info.module#InfoModule',
canActivate: [RoleGuard],
data: {roles: ['DSU', 'NGO', 'INS']}
data: { roles: ['DSU', 'NGO', 'INS'] }
},
{
path: 'users',
loadChildren: './pages/users/users.module#UsersModule',
canActivate: [RoleGuard],
data: {roles: ['DSU', 'NGO', 'INS']}
data: { roles: ['DSU', 'NGO', 'INS'] }
}
]
},
{
path: '',
loadChildren: './pages/authentication/authentication.module#AuthenticationModule'
loadChildren:
'./pages/authentication/authentication.module#AuthenticationModule'
},
{
path: '404',
component: NotFoundComponent
},
{ path: '**', redirectTo: '/404' }
];

@NgModule({
imports: [RouterModule.forRoot(AppRoutes)],
exports: [RouterModule]
Expand Down
34 changes: 34 additions & 0 deletions src/app/pages/categories/categories.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CategoriesComponent } from './categories/categories.component';
import { CategoriesDashboardComponent } from './categories/components/categories-dashboard/categories-dashboard.component';
import { CategoryAddComponent } from './categories/components/category-add/category-add.component';
import { CategoryEditComponent } from './categories/components/category-edit/category-edit.component';
import { CategoryDetailsComponent } from './categories/components/category-details/category-details.component';
import { CategoriesRoutingModule } from '@app/pages/categories/categories.routing';
import { NgxMultiselectModule } from '@ngx-lib/multiselect';
import { SelectDropDownModule } from 'custom-select-dropdown';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { SharedModule } from '@app/shared';

@NgModule({
declarations: [
CategoriesComponent,
CategoryAddComponent,
CategoryEditComponent,
CategoryDetailsComponent,
CategoriesDashboardComponent
],
imports: [
NgxMultiselectModule,
SelectDropDownModule,
NgbModule,
CommonModule,
FormsModule,
ReactiveFormsModule,
SharedModule,
CategoriesRoutingModule
]
})
export class CategoriesModule {}
47 changes: 47 additions & 0 deletions src/app/pages/categories/categories.routing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { NgModule } from '@angular/core';
import { RoleGuard } from '@app/core';
import { RouterModule, Routes } from '@angular/router';
import { CategoriesComponent } from './categories/categories.component';
import { CategoriesDashboardComponent } from './categories/components/categories-dashboard/categories-dashboard.component';
import { CategoryAddComponent } from './categories/components/category-add/category-add.component';
import { CategoryEditComponent } from './categories/components/category-edit/category-edit.component';
import { CategoryDetailsComponent } from './categories/components/category-details/category-details.component';

const routes: Routes = [
{
path: '',
component: CategoriesComponent,
children: [
{
path: '',
component: CategoriesDashboardComponent,
canActivate: [RoleGuard],
data: { roles: ['DSU'] }
},
{
path: 'add',
component: CategoryAddComponent,
canActivate: [RoleGuard],
data: { roles: ['DSU'] }
},
{
path: 'edit/:id',
component: CategoryEditComponent,
canActivate: [RoleGuard],
data: { roles: ['DSU'] }
},
{
path: 'id/:id',
component: CategoryDetailsComponent,
canActivate: [RoleGuard],
data: { roles: ['DSU'] }
}
]
}
];

@NgModule({
exports: [RouterModule],
imports: [RouterModule.forChild(routes)]
})
export class CategoriesRoutingModule {}
12 changes: 12 additions & 0 deletions src/app/pages/categories/categories.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { TestBed } from '@angular/core/testing';

import { CategoriesService } from './categories.service';

describe('CategoriesService', () => {
beforeEach(() => TestBed.configureTestingModule({}));

it('should be created', () => {
const service: CategoriesService = TestBed.get(CategoriesService);
expect(service).toBeTruthy();
});
});
192 changes: 192 additions & 0 deletions src/app/pages/categories/categories.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/internal/Observable';
import { HttpClient } from '@angular/common/http';
import { map, take } from 'rxjs/operators';
import * as lodash from 'lodash';

export interface Category {
id: string;
slug: string;
name: string;
subCategories: SubCategory[];
}

export interface SubCategory {
id: string;
slug: string;
name: string;
parentCategory: Category;
}

interface ApiCategory {
_id: string;
parent_id: string | undefined;
slug: string;
name: string;
}

@Injectable({
providedIn: 'root'
})
export class CategoriesService {
constructor(private httpClient: HttpClient) {}

/**
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a bunch of code related to pagination copy-pasted from the Resources components, but they're not used since the RVM categories API doesn't currently support pagination.

I'm still considering whether I should just completely remove them, if we don't intend to add pagination, or keep them if we do :)

* pager for categories table
*/
pager: any = {
sort: 1,
method: 'ASC',
page: 1,
size: 15,
total: 0,
filters: {}
};

/**
* get the category pager
* @returns pager
*/
getPager() {
return { ...this.pager };
}

/**
* init pager with default values
*/
setPager() {
this.pager = {
sort: 1,
method: 'ASC',
page: 1,
size: 15,
total: 0,
filters: {}
};
}

getCategoriesBySlug(slug?: string): Observable<Category[]> {
const allCategories$: Observable<Category[]> = this.getAllCategories();
if (slug && slug !== '') {
return allCategories$.pipe(
map((categories: Category[]) =>
categories.filter((category: Category) =>
category.slug.toLowerCase().includes(slug.toLowerCase())
)
)
);
} else {
return allCategories$;
}
}

getCategoryById(id: string): Observable<Category | undefined> {
return this.getAllCategories().pipe(
map((categories: Category[]) => {
return categories.find(category => category.id === id);
})
);
}

getAllCategories(): Observable<Category[]> {
return this.httpClient.get<ApiCategory[]>('/resources/categories').pipe(
take(1),
map((apiCategories: ApiCategory[]) =>
this.groupSubCategoriesOnCategories(apiCategories)
)
);
}

groupSubCategoriesOnCategories(apiCategories: ApiCategory[]): Category[] {
const partition: ApiCategory[][] = lodash.partition(
apiCategories,
category =>
category.parent_id === undefined || category.parent_id === '0'
);
const parentCategories = partition[0];
const subCategories = partition[1];

return parentCategories.map(apiCategory =>
this.convertToCategory(
apiCategory,
subCategories.filter(
(subCategory: ApiCategory) =>
subCategory.parent_id === apiCategory._id
)
)
);
}

convertToCategory(
apiCategory: ApiCategory,
apiSubCategories: ApiCategory[]
) {
const category: Category = {
id: apiCategory._id,
name: apiCategory.name,
slug: apiCategory.slug,
subCategories: apiSubCategories.map((apiSubCategory: ApiCategory) =>
this.convertToSubCategory(apiSubCategory, category)
)
};
return category;
}

convertToSubCategory(
apiSubCategory: ApiCategory,
parentCategory: Category
): SubCategory {
return {
id: apiSubCategory._id,
name: apiSubCategory.name,
slug: apiSubCategory.slug,
parentCategory: parentCategory
};
}

getCategory(id: string, paginationObj?: any): Observable<any> {
let params: any = {};

params = { ...params, ...paginationObj };
if (params.filters) {
Object.keys(params.filters).forEach(key => {
if (params.filters[key]) {
params['filters[' + key + ']'] = params.filters[key];
}
});
delete params.filters;
}

return this.httpClient.get(`/resources/categories/${id}`, {
params: params
});
}

/**
* post a new category to website, auto add Header
* @param {any} payload the org data to be added
* @returns observable with response
*/
addCategory(payload: any) {
return this.httpClient.post('/resources/categories', payload);
}

/**
* delete category by id
* @param {string} id of the category to be deleted
* @returns observable with response
*/
deleteCategory(id: any) {
return this.httpClient.delete(`/resources/categories/${id}`);
}

/**
* edit a category
* @param {any} payload the category data to be modified
* @param {string} id of the category to be modified
* @returns observable with response
*/
editCategory(id: string, payload: any) {
return this.httpClient.put(`/resources/categories/${id}`, payload);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<router-outlet></router-outlet>
Loading