Browse Source

feat(admin-ui): List/detail view for tax category adjustments

Michael Bromley 7 years ago
parent
commit
85f788f546
19 changed files with 414 additions and 17 deletions
  1. 4 0
      admin-ui/src/app/app.routes.ts
  2. 13 0
      admin-ui/src/app/core/components/main-nav/main-nav.component.html
  3. 45 13
      admin-ui/src/app/data/providers/adjustment-source-data.service.ts
  4. 4 0
      admin-ui/src/app/data/providers/data.service.mock.ts
  5. 1 2
      admin-ui/src/app/marketing/marketing.module.ts
  6. 29 0
      admin-ui/src/app/settings/components/tax-category-detail/tax-category-detail.component.html
  7. 0 0
      admin-ui/src/app/settings/components/tax-category-detail/tax-category-detail.component.scss
  8. 169 0
      admin-ui/src/app/settings/components/tax-category-detail/tax-category-detail.component.ts
  9. 31 0
      admin-ui/src/app/settings/components/tax-category-list/tax-category-list.component.html
  10. 0 0
      admin-ui/src/app/settings/components/tax-category-list/tax-category-list.component.scss
  11. 25 0
      admin-ui/src/app/settings/components/tax-category-list/tax-category-list.component.ts
  12. 28 0
      admin-ui/src/app/settings/providers/routing/tax-category-resolver.ts
  13. 16 0
      admin-ui/src/app/settings/settings.module.ts
  14. 38 0
      admin-ui/src/app/settings/settings.routes.ts
  15. 0 0
      admin-ui/src/app/shared/components/adjustment-operation-input/adjustment-operation-input.component.html
  16. 0 0
      admin-ui/src/app/shared/components/adjustment-operation-input/adjustment-operation-input.component.scss
  17. 0 0
      admin-ui/src/app/shared/components/adjustment-operation-input/adjustment-operation-input.component.ts
  18. 2 0
      admin-ui/src/app/shared/shared.module.ts
  19. 9 2
      admin-ui/src/i18n-messages/en.json

+ 4 - 0
admin-ui/src/app/app.routes.ts

@@ -35,6 +35,10 @@ export const routes: Route[] = [
                 path: 'marketing',
                 loadChildren: './marketing/marketing.module#MarketingModule',
             },
+            {
+                path: 'settings',
+                loadChildren: './settings/settings.module#SettingsModule',
+            },
         ],
     },
 ];

+ 13 - 0
admin-ui/src/app/core/components/main-nav/main-nav.component.html

@@ -77,5 +77,18 @@
                 </li>
             </ul>
         </section>
+        <section class="nav-group">
+            <input id="tabexample2" type="checkbox">
+            <label for="tabexample2">{{ 'nav.settings' | translate }}</label>
+            <ul class="nav-list">
+                <li>
+                    <a class="nav-link"
+                       [routerLink]="['/settings', 'tax-categories']"
+                       routerLinkActive="active">
+                        <clr-icon shape="calculator" size="20"></clr-icon>{{ 'nav.tax-categories' | translate }}
+                    </a>
+                </li>
+            </ul>
+        </section>
     </section>
 </nav>

+ 45 - 13
admin-ui/src/app/data/providers/adjustment-source-data.service.ts

@@ -23,10 +23,51 @@ export class AdjustmentSourceDataService {
     constructor(private baseDataService: BaseDataService) {}
 
     getPromotions(take: number = 10, skip: number = 0) {
+        return this.getAdjustmentSourceList(AdjustmentType.PROMOTION, take, skip);
+    }
+
+    getPromotion(id: string) {
+        return this.getAdjustmentSource(AdjustmentType.PROMOTION, id);
+    }
+
+    getTaxCategories(take: number = 10, skip: number = 0) {
+        return this.getAdjustmentSourceList(AdjustmentType.TAX, take, skip);
+    }
+
+    getTaxCategory(id: string) {
+        return this.getAdjustmentSource(AdjustmentType.TAX, id);
+    }
+
+    getAdjustmentOperations(type: AdjustmentType) {
+        return this.baseDataService.query<GetAdjustmentOperations.Query, GetAdjustmentOperations.Variables>(
+            GET_ADJUSTMENT_OPERATIONS,
+            {
+                type,
+            },
+        );
+    }
+
+    createPromotion(input: CreateAdjustmentSourceInput) {
+        return this.createAdjustmentSource(input);
+    }
+
+    updatePromotion(input: UpdateAdjustmentSourceInput) {
+        return this.updateAdjustmentSource(input);
+    }
+
+    createTaxCategory(input: CreateAdjustmentSourceInput) {
+        return this.createAdjustmentSource(input);
+    }
+
+    updateTaxCategory(input: UpdateAdjustmentSourceInput) {
+        return this.updateAdjustmentSource(input);
+    }
+
+    private getAdjustmentSourceList(type: AdjustmentType, take: number, skip: number) {
         return this.baseDataService.query<GetAdjustmentSourceList.Query, GetAdjustmentSourceList.Variables>(
             GET_ADJUSTMENT_SOURCE_LIST,
             {
-                type: AdjustmentType.PROMOTION,
+                type,
                 options: {
                     take,
                     skip,
@@ -35,7 +76,7 @@ export class AdjustmentSourceDataService {
         );
     }
 
-    getPromotion(id: string) {
+    private getAdjustmentSource(type: AdjustmentType, id: string) {
         return this.baseDataService.query<GetAdjustmentSource.Query, GetAdjustmentSource.Variables>(
             GET_ADJUSTMENT_SOURCE,
             {
@@ -44,16 +85,7 @@ export class AdjustmentSourceDataService {
         );
     }
 
-    getAdjustmentOperations(type: AdjustmentType) {
-        return this.baseDataService.query<GetAdjustmentOperations.Query, GetAdjustmentOperations.Variables>(
-            GET_ADJUSTMENT_OPERATIONS,
-            {
-                type,
-            },
-        );
-    }
-
-    createPromotion(input: CreateAdjustmentSourceInput) {
+    private createAdjustmentSource(input: CreateAdjustmentSourceInput) {
         return this.baseDataService.mutate<CreateAdjustmentSource.Mutation, CreateAdjustmentSource.Variables>(
             CREATE_ADJUSTMENT_SOURCE,
             {
@@ -62,7 +94,7 @@ export class AdjustmentSourceDataService {
         );
     }
 
-    updatePromotion(input: UpdateAdjustmentSourceInput) {
+    private updateAdjustmentSource(input: UpdateAdjustmentSourceInput) {
         return this.baseDataService.mutate<UpdateAdjustmentSource.Mutation, UpdateAdjustmentSource.Variables>(
             UPDATE_ADJUSTMENT_SOURCE,
             {

+ 4 - 0
admin-ui/src/app/data/providers/data.service.mock.ts

@@ -33,9 +33,13 @@ export class MockDataService implements DataServiceMock {
     adjustmentSource = {
         getPromotions: spyQueryResult('getPromotions'),
         getPromotion: spyQueryResult('getPromotion'),
+        getTaxCategories: spyQueryResult('getTaxCategories'),
+        getTaxCategory: spyQueryResult('getTaxCategory'),
         getAdjustmentOperations: spyQueryResult('getAdjustmentOperations'),
         createPromotion: spyObservable('createPromotion'),
         updatePromotion: spyObservable('updatePromotion'),
+        createTaxCategory: spyObservable('createTaxCategory'),
+        updateTaxCategory: spyObservable('updateTaxCategory'),
     };
     administrator = {
         getAdministrators: spyQueryResult('getAdministrators'),

+ 1 - 2
admin-ui/src/app/marketing/marketing.module.ts

@@ -3,7 +3,6 @@ import { RouterModule } from '@angular/router';
 
 import { SharedModule } from '../shared/shared.module';
 
-import { AdjustmentOperationInputComponent } from './components/adjustment-operation-input/adjustment-operation-input.component';
 import { PromotionDetailComponent } from './components/promotion-detail/promotion-detail.component';
 import { PromotionListComponent } from './components/promotion-list/promotion-list.component';
 import { marketingRoutes } from './marketing.routes';
@@ -11,7 +10,7 @@ import { PromotionResolver } from './providers/routing/promotion-resolver';
 
 @NgModule({
     imports: [SharedModule, RouterModule.forChild(marketingRoutes)],
-    declarations: [PromotionListComponent, PromotionDetailComponent, AdjustmentOperationInputComponent],
+    declarations: [PromotionListComponent, PromotionDetailComponent],
     providers: [PromotionResolver],
 })
 export class MarketingModule {}

+ 29 - 0
admin-ui/src/app/settings/components/tax-category-detail/tax-category-detail.component.html

@@ -0,0 +1,29 @@
+<vdr-action-bar>
+    <vdr-ab-left>
+    </vdr-ab-left>
+
+    <vdr-ab-right>
+        <button class="btn btn-primary"
+                *ngIf="isNew$ | async; else updateButton"
+                (click)="create()"
+                [disabled]="!saveButtonEnabled()">{{ 'common.create' | translate }}</button>
+        <ng-template #updateButton>
+            <button class="btn btn-primary"
+                    (click)="save()"
+                    [disabled]="!saveButtonEnabled()">{{ 'common.update' | translate }}</button>
+        </ng-template>
+    </vdr-ab-right>
+</vdr-action-bar>
+
+<form class="form" [formGroup]="taxCategoryForm" >
+    <section class="form-block">
+        <vdr-form-field [label]="'common.name' | translate" for="name">
+            <input id="name" type="text" formControlName="name">
+        </vdr-form-field>
+        <vdr-form-field [label]="'settings.tax-rate' | translate" for="name">
+            <vdr-affixed-input suffix="%">
+                <input id="name" type="number" step="0.1" min="0" max="100" formControlName="taxRate">
+            </vdr-affixed-input>
+        </vdr-form-field>
+    </section>
+</form>

+ 0 - 0
admin-ui/src/app/settings/components/tax-category-detail/tax-category-detail.component.scss


+ 169 - 0
admin-ui/src/app/settings/components/tax-category-detail/tax-category-detail.component.ts

@@ -0,0 +1,169 @@
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { ActivatedRoute, Router } from '@angular/router';
+import { Observable } from 'rxjs';
+import { mergeMap, take } from 'rxjs/operators';
+import {
+    AdjustmentOperation,
+    AdjustmentSource,
+    AdjustmentType,
+    CreateAdjustmentSourceInput,
+    LanguageCode,
+    UpdateAdjustmentSourceInput,
+} from 'shared/generated-types';
+
+import { BaseDetailComponent } from '../../../common/base-detail.component';
+import { _ } from '../../../core/providers/i18n/mark-for-extraction';
+import { NotificationService } from '../../../core/providers/notification/notification.service';
+import { DataService } from '../../../data/providers/data.service';
+import { ServerConfigService } from '../../../data/server-config';
+
+@Component({
+    selector: 'vdr-tax-detail',
+    templateUrl: './tax-category-detail.component.html',
+    styleUrls: ['./tax-category-detail.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class TaxCategoryDetailComponent extends BaseDetailComponent<AdjustmentSource.Fragment>
+    implements OnInit, OnDestroy {
+    taxCategory$: Observable<AdjustmentSource.Fragment>;
+    taxCategoryForm: FormGroup;
+
+    private taxCondition: AdjustmentOperation;
+    private taxAction: AdjustmentOperation;
+
+    constructor(
+        router: Router,
+        route: ActivatedRoute,
+        serverConfigService: ServerConfigService,
+        private changeDetector: ChangeDetectorRef,
+        private dataService: DataService,
+        private formBuilder: FormBuilder,
+        private notificationService: NotificationService,
+    ) {
+        super(route, router, serverConfigService);
+        this.taxCategoryForm = this.formBuilder.group({
+            name: ['', Validators.required],
+            taxRate: [0, Validators.required],
+        });
+    }
+
+    ngOnInit() {
+        this.init();
+        this.taxCategory$ = this.entity$;
+        const allOperations$ = this.dataService.adjustmentSource
+            .getAdjustmentOperations(AdjustmentType.TAX)
+            .single$.subscribe(data => {
+                this.taxCondition = data.adjustmentOperations.conditions[0];
+                this.taxAction = data.adjustmentOperations.actions[0];
+            });
+    }
+
+    ngOnDestroy() {
+        this.destroy();
+    }
+
+    saveButtonEnabled(): boolean {
+        return this.taxCategoryForm.dirty && this.taxCategoryForm.valid;
+    }
+
+    create() {
+        if (!this.taxCategoryForm.dirty) {
+            return;
+        }
+        const formValue = this.taxCategoryForm.value;
+        const input = this.createAdjustmentSourceInput(formValue.name, formValue.taxRate);
+        this.dataService.adjustmentSource.createTaxCategory(input).subscribe(
+            data => {
+                this.notificationService.success(_('common.notify-create-success'), {
+                    entity: 'TaxCategory',
+                });
+                this.taxCategoryForm.markAsPristine();
+                this.changeDetector.markForCheck();
+                this.router.navigate(['../', data.createAdjustmentSource.id], { relativeTo: this.route });
+            },
+            err => {
+                this.notificationService.error(_('common.notify-create-error'), {
+                    entity: 'TaxCategory',
+                });
+            },
+        );
+    }
+
+    save() {
+        if (!this.taxCategoryForm.dirty) {
+            return;
+        }
+        const formValue = this.taxCategoryForm.value;
+        this.taxCategory$
+            .pipe(
+                take(1),
+                mergeMap(taxCategory => {
+                    const input = this.createAdjustmentSourceInput(
+                        formValue.name,
+                        formValue.taxRate,
+                        taxCategory.id,
+                    );
+                    return this.dataService.adjustmentSource.updatePromotion(input);
+                }),
+            )
+            .subscribe(
+                data => {
+                    this.notificationService.success(_('common.notify-update-success'), {
+                        entity: 'TaxCategory',
+                    });
+                    this.taxCategoryForm.markAsPristine();
+                    this.changeDetector.markForCheck();
+                },
+                err => {
+                    this.notificationService.error(_('common.notify-update-error'), {
+                        entity: 'TaxCategory',
+                    });
+                },
+            );
+    }
+
+    private createAdjustmentSourceInput(name: string, taxRate: number): CreateAdjustmentSourceInput;
+    private createAdjustmentSourceInput(
+        name: string,
+        taxRate: number,
+        id: string,
+    ): UpdateAdjustmentSourceInput;
+    private createAdjustmentSourceInput(
+        name: string,
+        taxRate: number,
+        id?: string,
+    ): CreateAdjustmentSourceInput | UpdateAdjustmentSourceInput {
+        const input = {
+            name,
+            conditions: [
+                {
+                    code: this.taxCondition.code,
+                    arguments: [],
+                },
+            ],
+            actions: [
+                {
+                    code: this.taxAction.code,
+                    arguments: [taxRate.toString()],
+                },
+            ],
+        };
+        if (id !== undefined) {
+            return { ...input, id };
+        } else {
+            return { ...input, type: AdjustmentType.TAX, enabled: true } as CreateAdjustmentSourceInput;
+        }
+    }
+
+    /**
+     * Update the form values when the entity changes.
+     */
+    protected setFormValues(entity: AdjustmentSource.Fragment, languageCode: LanguageCode): void {
+        const action = entity.actions[0];
+        this.taxCategoryForm.patchValue({
+            name: entity.name,
+            taxRate: action ? action.args[0].value : 0,
+        });
+    }
+}

+ 31 - 0
admin-ui/src/app/settings/components/tax-category-list/tax-category-list.component.html

@@ -0,0 +1,31 @@
+<vdr-action-bar>
+    <vdr-ab-right>
+        <a class="btn btn-primary" [routerLink]="['./create']">
+            <clr-icon shape="plus"></clr-icon>
+            {{ 'settings.create-new-tax-category' | translate }}
+        </a>
+    </vdr-ab-right>
+</vdr-action-bar>
+
+<vdr-data-table [items]="items$ | async"
+                [itemsPerPage]="itemsPerPage$ | async"
+                [totalItems]="totalItems$ | async"
+                [currentPage]="currentPage$ | async"
+                (pageChange)="setPageNumber($event)"
+                (itemsPerPageChange)="setItemsPerPage($event)">
+    <vdr-dt-column>{{ 'common.ID' | translate }}</vdr-dt-column>
+    <vdr-dt-column>{{ 'common.name' | translate }}</vdr-dt-column>
+    <vdr-dt-column>{{ 'settings.tax-rate' | translate }}</vdr-dt-column>
+    <vdr-dt-column></vdr-dt-column>
+    <ng-template let-taxCategory="item">
+        <td class="left">{{ taxCategory.id }}</td>
+        <td class="left">{{ taxCategory.name }}</td>
+        <td class="left">{{ taxCategory.actions[0].args[0].value }}%</td>
+        <td class="right">
+            <vdr-table-row-action iconShape="edit"
+                                  [label]="'common.edit' | translate"
+                                  [linkTo]="['./', taxCategory.id]">
+            </vdr-table-row-action>
+        </td>
+    </ng-template>
+</vdr-data-table>

+ 0 - 0
admin-ui/src/app/settings/components/tax-category-list/tax-category-list.component.scss


+ 25 - 0
admin-ui/src/app/settings/components/tax-category-list/tax-category-list.component.ts

@@ -0,0 +1,25 @@
+import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { GetAdjustmentSourceList } from 'shared/generated-types';
+
+import { BaseListComponent } from '../../../common/base-list.component';
+import { DataService } from '../../../data/providers/data.service';
+
+@Component({
+    selector: 'vdr-tax-list',
+    templateUrl: './tax-category-list.component.html',
+    styleUrls: ['./tax-category-list.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class TaxCategoryListComponent extends BaseListComponent<
+    GetAdjustmentSourceList.Query,
+    GetAdjustmentSourceList.Items
+> {
+    constructor(private dataService: DataService, router: Router, route: ActivatedRoute) {
+        super(router, route);
+        super.setQueryFn(
+            (...args: any[]) => this.dataService.adjustmentSource.getTaxCategories(...args),
+            data => data.adjustmentSources,
+        );
+    }
+}

+ 28 - 0
admin-ui/src/app/settings/providers/routing/tax-category-resolver.ts

@@ -0,0 +1,28 @@
+import { Injectable } from '@angular/core';
+import { AdjustmentSource, AdjustmentType } from 'shared/generated-types';
+
+import { BaseEntityResolver } from '../../../common/base-entity-resolver';
+import { DataService } from '../../../data/providers/data.service';
+
+/**
+ * Resolves the id from the path into a Customer entity.
+ */
+@Injectable()
+export class TaxCategoryResolver extends BaseEntityResolver<AdjustmentSource.Fragment> {
+    constructor(private dataService: DataService) {
+        super(
+            {
+                __typename: 'AdjustmentSource',
+                id: '',
+                createdAt: '',
+                updatedAt: '',
+                type: AdjustmentType.TAX,
+                name: '',
+                enabled: false,
+                conditions: [],
+                actions: [],
+            },
+            id => this.dataService.adjustmentSource.getPromotion(id).mapStream(data => data.adjustmentSource),
+        );
+    }
+}

+ 16 - 0
admin-ui/src/app/settings/settings.module.ts

@@ -0,0 +1,16 @@
+import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
+
+import { SharedModule } from '../shared/shared.module';
+
+import { TaxCategoryDetailComponent } from './components/tax-category-detail/tax-category-detail.component';
+import { TaxCategoryListComponent } from './components/tax-category-list/tax-category-list.component';
+import { TaxCategoryResolver } from './providers/routing/tax-category-resolver';
+import { settingsRoutes } from './settings.routes';
+
+@NgModule({
+    imports: [SharedModule, RouterModule.forChild(settingsRoutes)],
+    declarations: [TaxCategoryListComponent, TaxCategoryDetailComponent],
+    providers: [TaxCategoryResolver],
+})
+export class SettingsModule {}

+ 38 - 0
admin-ui/src/app/settings/settings.routes.ts

@@ -0,0 +1,38 @@
+import { Route } from '@angular/router';
+import { AdjustmentSource } from 'shared/generated-types';
+
+import { createResolveData } from '../common/base-entity-resolver';
+import { detailBreadcrumb } from '../common/detail-breadcrumb';
+import { _ } from '../core/providers/i18n/mark-for-extraction';
+
+import { TaxCategoryDetailComponent } from './components/tax-category-detail/tax-category-detail.component';
+import { TaxCategoryListComponent } from './components/tax-category-list/tax-category-list.component';
+import { TaxCategoryResolver } from './providers/routing/tax-category-resolver';
+
+export const settingsRoutes: Route[] = [
+    {
+        path: 'tax-categories',
+        component: TaxCategoryListComponent,
+        data: {
+            breadcrumb: _('breadcrumb.tax-categories'),
+        },
+    },
+    {
+        path: 'tax-categories/:id',
+        component: TaxCategoryDetailComponent,
+        resolve: createResolveData(TaxCategoryResolver),
+        data: {
+            breadcrumb: taxCategoryBreadcrumb,
+        },
+    },
+];
+
+export function taxCategoryBreadcrumb(data: any, params: any) {
+    return detailBreadcrumb<AdjustmentSource.Fragment>({
+        entity: data.entity,
+        id: params.id,
+        breadcrumbKey: 'breadcrumb.tax-categories',
+        getName: promotion => promotion.name,
+        route: 'tax-categories',
+    });
+}

+ 0 - 0
admin-ui/src/app/marketing/components/adjustment-operation-input/adjustment-operation-input.component.html → admin-ui/src/app/shared/components/adjustment-operation-input/adjustment-operation-input.component.html


+ 0 - 0
admin-ui/src/app/marketing/components/adjustment-operation-input/adjustment-operation-input.component.scss → admin-ui/src/app/shared/components/adjustment-operation-input/adjustment-operation-input.component.scss


+ 0 - 0
admin-ui/src/app/marketing/components/adjustment-operation-input/adjustment-operation-input.component.ts → admin-ui/src/app/shared/components/adjustment-operation-input/adjustment-operation-input.component.ts


+ 2 - 0
admin-ui/src/app/shared/shared.module.ts

@@ -12,6 +12,7 @@ import {
     ActionBarLeftComponent,
     ActionBarRightComponent,
 } from './components/action-bar/action-bar.component';
+import { AdjustmentOperationInputComponent } from './components/adjustment-operation-input/adjustment-operation-input.component';
 import { AffixedInputComponent } from './components/affixed-input/affixed-input.component';
 import { PercentageSuffixInputComponent } from './components/affixed-input/percentage-suffix-input.component';
 import { ChipComponent } from './components/chip/chip.component';
@@ -50,6 +51,7 @@ const DECLARATIONS = [
     ActionBarComponent,
     ActionBarLeftComponent,
     ActionBarRightComponent,
+    AdjustmentOperationInputComponent,
     AffixedInputComponent,
     ChipComponent,
     CurrencyInputComponent,

+ 9 - 2
admin-ui/src/i18n-messages/en.json

@@ -28,7 +28,8 @@
     "orders": "Orders",
     "products": "Products",
     "promotions": "Promotions",
-    "roles": "Roles"
+    "roles": "Roles",
+    "tax-categories": "Tax categories"
   },
   "catalog": {
     "add-asset": "Add asset",
@@ -133,9 +134,15 @@
     "products": "Products",
     "promotions": "Promotions",
     "roles": "Roles",
-    "sales": "Sales"
+    "sales": "Sales",
+    "settings": "Settings",
+    "tax-categories": "Tax categories"
   },
   "order": {
     "create-new-order": "Create new order"
+  },
+  "settings": {
+    "create-new-tax-category": "Create tax category",
+    "tax-rate": "Tax rate"
   }
 }