Modifier Category table added as a parent of modifiers

They are also linked to products / menu categroies as to which products they can be used on
The linking to menu categories is done in the frontend, database is linked directly to products
Treeview for menu categories yet to be done in modifier category detail form
This commit is contained in:
Amritanshu
2019-06-20 17:45:28 +05:30
parent 05f8058a15
commit e1c42d4470
34 changed files with 972 additions and 59 deletions

View File

@ -13,6 +13,10 @@ const routes: Routes = [
path: 'guest-book',
loadChildren: () => import('./guest-book/guest-book.module').then(mod => mod.GuestBookModule)
},
{
path: 'modifier-categories',
loadChildren: () => import('./modifier-categories/modifier-categories.module').then(mod => mod.ModifierCategoriesModule)
},
{
path: 'printers',
loadChildren: () => import('./printers/printers.module').then(mod => mod.PrintersModule)

View File

@ -1,3 +1,5 @@
import {Product} from "./product";
export class MenuCategory {
id: string;
name: string;
@ -6,4 +8,6 @@ export class MenuCategory {
isActive: boolean;
isFixture: boolean;
sortOrder: number;
products: Product[];
enabled?: boolean;
}

View File

@ -14,4 +14,6 @@ export class Product {
quantity: number;
isActive: boolean;
sortOrder: number;
enabled?: boolean;
}

View File

@ -19,7 +19,7 @@
</div>
</form>
<mat-table [dataSource]="dataSource" aria-label="Elements">
<!-- Id Column -->
<!-- SNo Column -->
<ng-container matColumnDef="sno">
<mat-header-cell *matHeaderCellDef>S. No</mat-header-cell>
<mat-cell *matCellDef="let row">{{row.serial}}</mat-cell>
@ -31,13 +31,13 @@
<mat-cell *matCellDef="let row">{{row.name}}</mat-cell>
</ng-container>
<!-- Name Column -->
<!-- Phone Column -->
<ng-container matColumnDef="phone">
<mat-header-cell *matHeaderCellDef>Phone</mat-header-cell>
<mat-cell *matCellDef="let row">{{row.phone}}</mat-cell>
</ng-container>
<!-- Name Column -->
<!-- Pax Column -->
<ng-container matColumnDef="pax">
<mat-header-cell *matHeaderCellDef>Pax</mat-header-cell>
<mat-cell *matCellDef="let row">{{row.pax}}</mat-cell>

View File

@ -19,6 +19,9 @@
<a mat-raised-button routerLink="/products">
Products
</a>
<a mat-raised-button routerLink="/modifier-categories">
Modifier Categories
</a>
<a mat-raised-button routerLink="/taxes">
Taxes
</a>

View File

@ -0,0 +1,13 @@
import { ModifierCategoriesRoutingModule } from './modifier-categories-routing.module';
describe('ModifierCategoriesRoutingModule', () => {
let rolesRoutingModule: ModifierCategoriesRoutingModule;
beforeEach(() => {
rolesRoutingModule = new ModifierCategoriesRoutingModule();
});
it('should create an instance', () => {
expect(rolesRoutingModule).toBeTruthy();
});
});

View File

@ -0,0 +1,61 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule, Routes } from '@angular/router';
import { ModifierCategoryListResolver } from './modifier-category-list-resolver.service';
import { ModifierCategoryResolver } from './modifier-category-resolver.service';
import { ModifierCategoryListComponent } from './modifier-category-list/modifier-category-list.component';
import { ModifierCategoryDetailComponent } from './modifier-category-detail/modifier-category-detail.component';
import { AuthGuard } from '../auth/auth-guard.service';
const roleRoutes: Routes = [
{
path: '',
component: ModifierCategoryListComponent,
canActivate: [AuthGuard],
data: {
permission: 'Users'
},
resolve: {
list: ModifierCategoryListResolver
}
},
{
path: 'new',
component: ModifierCategoryDetailComponent,
canActivate: [AuthGuard],
data: {
permission: 'Users'
},
resolve: {
item: ModifierCategoryResolver,
}
},
{
path: ':id',
component: ModifierCategoryDetailComponent,
canActivate: [AuthGuard],
data: {
permission: 'Users'
},
resolve: {
item: ModifierCategoryResolver
}
}
];
@NgModule({
imports: [
CommonModule,
RouterModule.forChild(roleRoutes)
],
exports: [
RouterModule
],
providers: [
ModifierCategoryListResolver,
ModifierCategoryResolver
]
})
export class ModifierCategoriesRoutingModule {
}

View File

@ -0,0 +1,13 @@
import { ModifierCategoriesModule } from './modifier-categories.module';
describe('ModifierCategoriesModule', () => {
let rolesModule: ModifierCategoriesModule;
beforeEach(() => {
rolesModule = new ModifierCategoriesModule();
});
it('should create an instance', () => {
expect(rolesModule).toBeTruthy();
});
});

View File

@ -0,0 +1,43 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {ModifierCategoryListComponent} from './modifier-category-list/modifier-category-list.component';
import {ModifierCategoryDetailComponent} from './modifier-category-detail/modifier-category-detail.component';
import {ModifierCategoriesRoutingModule} from './modifier-categories-routing.module';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatDividerModule } from '@angular/material/divider';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatTableModule } from '@angular/material/table';
import {CdkTableModule} from '@angular/cdk/table';
import {ReactiveFormsModule} from '@angular/forms';
import {SharedModule} from '../shared/shared.module';
import {FlexLayoutModule} from '@angular/flex-layout';
@NgModule({
imports: [
CommonModule,
CdkTableModule,
FlexLayoutModule,
MatButtonModule,
MatCardModule,
MatCheckboxModule,
MatDividerModule,
MatIconModule,
MatInputModule,
MatProgressSpinnerModule,
MatTableModule,
ReactiveFormsModule,
SharedModule,
ModifierCategoriesRoutingModule
],
declarations: [
ModifierCategoryListComponent,
ModifierCategoryDetailComponent
]
})
export class ModifierCategoriesModule {
}

View File

@ -0,0 +1,3 @@
.example-card {
max-width: 400px;
}

View File

@ -0,0 +1,46 @@
<div fxLayout="row" fxFlex="50%" fxLayoutAlign="space-around center" class="example-card">
<mat-card fxFlex>
<mat-card-title-group>
<mat-card-title>ModifierCategory</mat-card-title>
</mat-card-title-group>
<mat-card-content>
<form [formGroup]="form" fxLayout="column">
<div fxLayout="row" fxLayoutAlign="space-around start" fxLayout.lt-md="column" fxLayoutGap="20px"
fxLayoutGap.lt-md="0px">
<mat-form-field fxFlex>
<mat-label>Name</mat-label>
<input matInput #nameElement placeholder="Name" formControlName="name">
</mat-form-field>
</div>
<div fxLayout="row" fxLayoutAlign="space-around start" fxLayout.lt-md="column" fxLayoutGap="20px"
fxLayoutGap.lt-md="0px">
<mat-form-field fxFlex>
<mat-label>Minimum</mat-label>
<input matInput placeholder="Minimum" type="number" formControlName="minimum">
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>Maximum</mat-label>
<input matInput placeholder="Maximum" type="number" formControlName="maximum">
</mat-form-field>
</div>
<mat-divider></mat-divider>
<div formArrayName="menuCategories">
<div *ngFor="let mc of item.menuCategories; index as i" [formGroupName]="i">
<mat-checkbox formControlName="menuCategory" fxFlex>{{mc.name}}</mat-checkbox>
<div formArrayName="products">
<div fxLayout="row" *ngFor="let p of mc.products; index as j" [formGroupName]="j" fxLayout="row"
fxLayoutAlign="space-around start" fxLayout.lt-md="column" fxLayoutGap="20px"
fxLayoutGap.lt-md="0px">
<mat-checkbox formControlName="product" fxFlex>{{p.name}}</mat-checkbox>
</div>
</div>
</div>
</div>
</form>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button color="primary" (click)="save()">Save</button>
<button mat-raised-button color="warn" (click)="confirmDelete()">Delete</button>
</mat-card-actions>
</mat-card>
</div>

View File

@ -0,0 +1,25 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {ModifierCategoryDetailComponent} from './modifier-category-detail.component';
describe('ModifierCategoryDetailComponent', () => {
let component: ModifierCategoryDetailComponent;
let fixture: ComponentFixture<ModifierCategoryDetailComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ModifierCategoryDetailComponent]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ModifierCategoryDetailComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,146 @@
import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { ModifierCategoryService } from '../modifier-category.service';
import { ModifierCategory } from '../modifier-category';
import { ToasterService } from '../../core/toaster.service';
import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component';
import { MatDialog } from '@angular/material/dialog';
import { FormArray, FormBuilder, FormGroup } from '@angular/forms';
import {Product} from "../../core/product";
@Component({
selector: 'app-role-detail',
templateUrl: './modifier-category-detail.component.html',
styleUrls: ['./modifier-category-detail.component.css']
})
export class ModifierCategoryDetailComponent implements OnInit, AfterViewInit {
@ViewChild('nameElement', { static: true }) nameElement: ElementRef;
form: FormGroup;
item: ModifierCategory;
constructor(
private route: ActivatedRoute,
private router: Router,
private fb: FormBuilder,
private toaster: ToasterService,
private dialog: MatDialog,
private ser: ModifierCategoryService
) {
this.createForm();
}
createForm() {
this.form = this.fb.group({
name: '',
minimum: '',
maximum: '',
menuCategories: this.fb.array([])
});
// this.setMenuCategories();
}
setMenuCategories(){
this.form.setControl('menuCategories', this.fb.array(
this.item.menuCategories.map(
x => this.fb.group({
menuCategory: x.enabled,
products: new FormArray(
x.products.map(
y => this.fb.group({
product: y.enabled
})
)
)
})
)
));
}
setProducts(x: any[]){
return ;
}
ngOnInit() {
this.route.data
.subscribe((data: { item: ModifierCategory }) => {
this.showItem(data.item);
});
}
showItem(item: ModifierCategory) {
this.item = item;
console.log(item);
this.form.patchValue({
name: this.item.name || '',
minimum: '' + this.item.minimum,
maximum: '' + this.item.maximum
});
this.setMenuCategories();
// this.form.setControl('menuCategories', this.fb.array(
// this.item.menuCategories.map(
// x => this.fb.group({
// product: x.enabled
// })
// )
// ));
}
ngAfterViewInit() {
setTimeout(() => {
this.nameElement.nativeElement.focus();
}, 0);
}
save() {
this.ser.saveOrUpdate(this.getItem())
.subscribe(
(result) => {
this.toaster.show('Success', '');
this.router.navigateByUrl('/modifier-categories');
},
(error) => {
this.toaster.show('Danger', error.error);
}
);
}
delete() {
this.ser.delete(this.item.id)
.subscribe(
(result) => {
this.toaster.show('Success', '');
this.router.navigateByUrl('/modifier-categories');
},
(error) => {
this.toaster.show('Danger', error.error);
}
);
}
confirmDelete(): void {
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
width: '250px',
data: {title: 'Delete Modifier Category?', content: 'Are you sure? This cannot be undone.'}
});
dialogRef.afterClosed().subscribe((result: boolean) => {
if (result) {
this.delete();
}
});
}
getItem(): ModifierCategory {
const formModel = this.form.value;
this.item.name = formModel.name;
this.item.minimum = +formModel.minimum;
this.item.maximum = +formModel.maximum;
const mc = this.form.get('menuCategories') as FormArray;
this.item.menuCategories.forEach((item, i) => {
item.products.forEach((prod, j) => {
prod.enabled = mc.value[i].products[j].product;
});
});
return this.item;
}
}

View File

@ -0,0 +1,15 @@
import {inject, TestBed} from '@angular/core/testing';
import {ModifierCategoryListResolver} from './modifier-category-list-resolver.service';
describe('ModifierCategoryListResolver', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [ModifierCategoryListResolver]
});
});
it('should be created', inject([ModifierCategoryListResolver], (service: ModifierCategoryListResolver) => {
expect(service).toBeTruthy();
}));
});

View File

@ -0,0 +1,18 @@
import {Injectable} from '@angular/core';
import {ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot} from '@angular/router';
import {ModifierCategory} from './modifier-category';
import {Observable} from 'rxjs/internal/Observable';
import {ModifierCategoryService} from './modifier-category.service';
@Injectable({
providedIn: 'root'
})
export class ModifierCategoryListResolver implements Resolve<ModifierCategory[]> {
constructor(private ser: ModifierCategoryService, private router: Router) {
}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<ModifierCategory[]> {
return this.ser.list();
}
}

View File

@ -0,0 +1,17 @@
import { DataSource } from '@angular/cdk/collections';
import { Observable, of as observableOf } from 'rxjs';
import { ModifierCategory } from '../modifier-category';
export class ModifierCategoryListDatasource extends DataSource<ModifierCategory> {
constructor(public data: ModifierCategory[]) {
super();
}
connect(): Observable<ModifierCategory[]> {
return observableOf(this.data);
}
disconnect() {
}
}

View File

@ -0,0 +1,44 @@
<mat-card>
<mat-card-title-group>
<mat-card-title>Modifier Categories</mat-card-title>
<a mat-button [routerLink]="['/modifier-categories', 'new']">
<mat-icon>add_box</mat-icon>
Add
</a>
</mat-card-title-group>
<mat-card-content>
<mat-table #table [dataSource]="dataSource" aria-label="Elements">
<!-- Name Column -->
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef>Name</mat-header-cell>
<mat-cell *matCellDef="let row"><a [routerLink]="['/modifier-categories', row.id]">{{row.name}}</a></mat-cell>
</ng-container>
<!-- Minimum Column -->
<ng-container matColumnDef="minimum">
<mat-header-cell *matHeaderCellDef>Minimum</mat-header-cell>
<mat-cell *matCellDef="let row">{{row.minimum}}</mat-cell>
</ng-container>
<!-- Maximum Column -->
<ng-container matColumnDef="maximum">
<mat-header-cell *matHeaderCellDef>Maximum</mat-header-cell>
<mat-cell *matCellDef="let row">{{row.maximum}}</mat-cell>
</ng-container>
<!-- Permissions Column -->
<ng-container matColumnDef="products">
<mat-header-cell *matHeaderCellDef>Products</mat-header-cell>
<mat-cell *matCellDef="let row">
<ul>
<li *ngFor="let product of row.products">{{product}}</li>
</ul>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
</mat-table>
</mat-card-content>
</mat-card>

View File

@ -0,0 +1,23 @@
import {ComponentFixture, fakeAsync, TestBed} from '@angular/core/testing';
import {ModifierCategoryListComponent} from './modifier-category-list.component';
describe('ModifierCategoryListComponent', () => {
let component: ModifierCategoryListComponent;
let fixture: ComponentFixture<ModifierCategoryListComponent>;
beforeEach(fakeAsync(() => {
TestBed.configureTestingModule({
declarations: [ModifierCategoryListComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ModifierCategoryListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
}));
it('should compile', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,27 @@
import { Component, OnInit } from '@angular/core';
import { ModifierCategoryListDatasource } from './modifier-category-list-datasource';
import { ModifierCategory } from '../modifier-category';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-modifier-category-list',
templateUrl: './modifier-category-list.component.html',
styleUrls: ['./modifier-category-list.component.css']
})
export class ModifierCategoryListComponent implements OnInit {
dataSource: ModifierCategoryListDatasource;
list: ModifierCategory[];
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
displayedColumns = ['name', 'minimum', 'maximum', 'products'];
constructor(private route: ActivatedRoute) {
}
ngOnInit() {
this.route.data
.subscribe((data: { list: ModifierCategory[] }) => {
this.list = data.list;
});
this.dataSource = new ModifierCategoryListDatasource(this.list);
}
}

View File

@ -0,0 +1,15 @@
import {inject, TestBed} from '@angular/core/testing';
import {ModifierCategoryResolver} from './modifier-category-resolver.service';
describe('ModifierCategoryResolver', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [ModifierCategoryResolver]
});
});
it('should be created', inject([ModifierCategoryResolver], (service: ModifierCategoryResolver) => {
expect(service).toBeTruthy();
}));
});

View File

@ -0,0 +1,19 @@
import {Injectable} from '@angular/core';
import {ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot} from '@angular/router';
import {ModifierCategory} from './modifier-category';
import {Observable} from 'rxjs/internal/Observable';
import {ModifierCategoryService} from './modifier-category.service';
@Injectable({
providedIn: 'root'
})
export class ModifierCategoryResolver implements Resolve<ModifierCategory> {
constructor(private ser: ModifierCategoryService, private router: Router) {
}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<ModifierCategory> {
const id = route.paramMap.get('id');
return this.ser.get(id);
}
}

View File

@ -0,0 +1,15 @@
import {inject, TestBed} from '@angular/core/testing';
import {ModifierCategoryService} from './modifier-category.service';
describe('ModifierCategoryService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [ModifierCategoryService]
});
});
it('should be created', inject([ModifierCategoryService], (service: ModifierCategoryService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -0,0 +1,73 @@
import {Injectable} from '@angular/core';
import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http';
import {ErrorLoggerService} from '../core/error-logger.service';
import {catchError} from 'rxjs/operators';
import {Observable} from 'rxjs/internal/Observable';
import {ModifierCategory} from './modifier-category';
const httpOptions = {
headers: new HttpHeaders({'Content-Type': 'application/json'})
};
const url = '/v1/modifier-categories';
const serviceName = 'ModifierCategoryService';
@Injectable({
providedIn: 'root'
})
export class ModifierCategoryService {
constructor(private http: HttpClient, private log: ErrorLoggerService) {
}
get(id: string): Observable<ModifierCategory> {
const getUrl: string = (id === null) ? `${url}/new` : `${url}/${id}`;
return <Observable<ModifierCategory>>this.http.get<ModifierCategory>(getUrl)
.pipe(
catchError(this.log.handleError(serviceName, `get id=${id}`))
);
}
list(): Observable<ModifierCategory[]> {
const options = {params: new HttpParams().set('l', '')};
return <Observable<ModifierCategory[]>>this.http.get<ModifierCategory[]>(url, options)
.pipe(
catchError(this.log.handleError(serviceName, 'list'))
);
}
listOfNames(): Observable<string[]> {
const options = {params: new HttpParams().set('n', '')};
return <Observable<string[]>>this.http.get<string[]>(url, options)
.pipe(
catchError(this.log.handleError(serviceName, 'list'))
);
}
save(role: ModifierCategory): Observable<ModifierCategory> {
return <Observable<ModifierCategory>>this.http.post<ModifierCategory>(`${url}/new`, role, httpOptions)
.pipe(
catchError(this.log.handleError(serviceName, 'save'))
);
}
update(role: ModifierCategory): Observable<ModifierCategory> {
return <Observable<ModifierCategory>>this.http.put<ModifierCategory>(`${url}/${role.id}`, role, httpOptions)
.pipe(
catchError(this.log.handleError(serviceName, 'update'))
);
}
saveOrUpdate(role: ModifierCategory): Observable<ModifierCategory> {
if (!role.id) {
return this.save(role);
} else {
return this.update(role);
}
}
delete(id: string): Observable<ModifierCategory> {
return <Observable<ModifierCategory>>this.http.delete<ModifierCategory>(`${url}/${id}`, httpOptions)
.pipe(
catchError(this.log.handleError(serviceName, 'delete'))
);
}
}

View File

@ -0,0 +1,9 @@
import {MenuCategory} from "../core/menu-category";
export class ModifierCategory {
id: string;
name: string;
minimum: number;
maximum: number;
menuCategories: MenuCategory[];
}

View File

@ -52,7 +52,6 @@ export class ProductDetailComponent implements OnInit, AfterViewInit {
.subscribe((data: { item: Product, menuCategories: MenuCategory[], saleCategories: SaleCategory[] }) => {
this.menuCategories = data.menuCategories;
this.saleCategories = data.saleCategories;
console.log(this.saleCategories);
this.showItem(data.item);
});
}