Browse Source

feat(admin-ui): Facets list

Michael Bromley 7 years ago
parent
commit
78e654803b

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

@@ -6,6 +6,7 @@ import { SharedModule } from '../shared/shared.module';
 import { catalogRoutes } from './catalog.routes';
 import { CreateOptionGroupDialogComponent } from './components/create-option-group-dialog/create-option-group-dialog.component';
 import { CreateOptionGroupFormComponent } from './components/create-option-group-form/create-option-group-form.component';
+import { FacetListComponent } from './components/facet-list/facet-list.component';
 import { ProductDetailComponent } from './components/product-detail/product-detail.component';
 import { ProductListComponent } from './components/product-list/product-list.component';
 import { ProductVariantsWizardComponent } from './components/product-variants-wizard/product-variants-wizard.component';
@@ -25,6 +26,7 @@ import { ProductResolver } from './providers/routing/product-resolver';
         SelectOptionGroupDialogComponent,
         CreateOptionGroupFormComponent,
         SelectOptionGroupComponent,
+        FacetListComponent,
     ],
     entryComponents: [CreateOptionGroupDialogComponent, SelectOptionGroupDialogComponent],
     providers: [ProductResolver, ProductUpdaterService],

+ 8 - 0
admin-ui/src/app/catalog/catalog.routes.ts

@@ -4,6 +4,7 @@ import { map } from 'rxjs/operators';
 import { _ } from '../core/providers/i18n/mark-for-extraction';
 import { DataService } from '../data/providers/data.service';
 
+import { FacetListComponent } from './components/facet-list/facet-list.component';
 import { ProductDetailComponent } from './components/product-detail/product-detail.component';
 import { ProductListComponent } from './components/product-list/product-list.component';
 import { ProductResolver } from './providers/routing/product-resolver';
@@ -26,6 +27,13 @@ export const catalogRoutes: Route[] = [
             breadcrumb: productBreadcrumb,
         },
     },
+    {
+        path: 'facets',
+        component: FacetListComponent,
+        data: {
+            breadcrumb: _('breadcrumb.facets'),
+        },
+    },
 ];
 
 export function productBreadcrumb(data: any, params: any, dataService: DataService) {

+ 31 - 0
admin-ui/src/app/catalog/components/facet-list/facet-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>
+            {{ 'catalog.create-new-facet' | translate }}
+        </a>
+    </vdr-ab-right>
+</vdr-action-bar>
+
+<vdr-data-table [items]="facets$ | async"
+                [itemsPerPage]="itemsPerPage$ | async"
+                [totalItems]="totalItems$ | async"
+                [currentPage]="currentPage$ | async"
+                (pageChange)="setPageNumber($event)"
+                (itemsPerPageChange)="setItemsPerPage($event)">
+    <vdr-dt-column>{{ 'catalog.ID' | translate }}</vdr-dt-column>
+    <vdr-dt-column>{{ 'catalog.code' | translate }}</vdr-dt-column>
+    <vdr-dt-column>{{ 'catalog.name' | translate }}</vdr-dt-column>
+    <vdr-dt-column></vdr-dt-column>
+    <ng-template let-facet="item">
+        <td class="left">{{ facet.id }}</td>
+        <td class="left">{{ facet.code }}</td>
+        <td class="left">{{ facet.name }}</td>
+        <td class="right">
+            <vdr-table-row-action iconShape="edit"
+                                  [label]="'common.edit' | translate"
+                                  [linkTo]="['./', facet.id]">
+            </vdr-table-row-action>
+        </td>
+    </ng-template>
+</vdr-data-table>

+ 0 - 0
admin-ui/src/app/catalog/components/facet-list/facet-list.component.scss


+ 68 - 0
admin-ui/src/app/catalog/components/facet-list/facet-list.component.ts

@@ -0,0 +1,68 @@
+import { Component, OnDestroy, OnInit } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { combineLatest, Observable, Subject } from 'rxjs';
+import { map, takeUntil } from 'rxjs/operators';
+
+import { DataService } from '../../../data/providers/data.service';
+import { GetFacetList_facets_items } from '../../../data/types/gql-generated-types';
+
+@Component({
+    selector: 'vdr-facet-list',
+    templateUrl: './facet-list.component.html',
+    styleUrls: ['./facet-list.component.scss'],
+})
+export class FacetListComponent implements OnInit, OnDestroy {
+    facets$: Observable<GetFacetList_facets_items[]>;
+    totalItems$: Observable<number>;
+    itemsPerPage$: Observable<number>;
+    currentPage$: Observable<number>;
+    private destroy$ = new Subject<void>();
+
+    constructor(private dataService: DataService, private router: Router, private route: ActivatedRoute) {}
+
+    ngOnInit() {
+        const facetsQuery = this.dataService.facet.getFacets(10, 0);
+
+        const fetchPage = ([currentPage, itemsPerPage]: [number, number]) => {
+            const take = itemsPerPage;
+            const skip = (currentPage - 1) * itemsPerPage;
+            facetsQuery.ref.refetch({ options: { skip, take } });
+        };
+
+        this.facets$ = facetsQuery.stream$.pipe(map(data => data.facets.items));
+        this.totalItems$ = facetsQuery.stream$.pipe(map(data => data.facets.totalItems));
+        this.currentPage$ = this.route.queryParamMap.pipe(
+            map(qpm => qpm.get('page')),
+            map(page => (!page ? 1 : +page)),
+        );
+        this.itemsPerPage$ = this.route.queryParamMap.pipe(
+            map(qpm => qpm.get('perPage')),
+            map(perPage => (!perPage ? 10 : +perPage)),
+        );
+
+        combineLatest(this.currentPage$, this.itemsPerPage$)
+            .pipe(takeUntil(this.destroy$))
+            .subscribe(fetchPage);
+    }
+
+    ngOnDestroy() {
+        this.destroy$.next();
+        this.destroy$.complete();
+    }
+
+    setPageNumber(page: number) {
+        this.setQueryParam('page', page);
+    }
+
+    setItemsPerPage(perPage: number) {
+        this.setQueryParam('perPage', perPage);
+    }
+
+    private setQueryParam(key: string, value: any) {
+        this.router.navigate(['./'], {
+            queryParams: { [key]: value },
+            relativeTo: this.route,
+            queryParamsHandling: 'merge',
+        });
+    }
+}

+ 0 - 25
admin-ui/src/app/catalog/components/product-list/product-list.component.spec.ts

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

+ 3 - 2
admin-ui/src/app/catalog/components/product-list/product-list.component.ts

@@ -1,9 +1,10 @@
 import { Component, OnDestroy, OnInit } from '@angular/core';
 import { ActivatedRoute, Router } from '@angular/router';
-import { BehaviorSubject, combineLatest, Observable, Subject } from 'rxjs';
+import { combineLatest, Observable, Subject } from 'rxjs';
 import { map, takeUntil } from 'rxjs/operators';
 
 import { DataService } from '../../../data/providers/data.service';
+import { GetProductList_products_items } from '../../../data/types/gql-generated-types';
 
 @Component({
     selector: 'vdr-products-list',
@@ -11,7 +12,7 @@ import { DataService } from '../../../data/providers/data.service';
     styleUrls: ['./product-list.component.scss'],
 })
 export class ProductListComponent implements OnInit, OnDestroy {
-    products$: Observable<any[]>;
+    products$: Observable<GetProductList_products_items[]>;
     totalItems$: Observable<number>;
     itemsPerPage$: Observable<number>;
     currentPage$: Observable<number>;

+ 3 - 1
admin-ui/src/app/core/components/main-nav/main-nav.component.html

@@ -11,7 +11,9 @@
                         <clr-icon shape="library" size="20"></clr-icon>{{ 'nav.products' | translate }}
                     </a>
                 </li>
-                <li><a class="nav-link">
+                <li><a class="nav-link"
+                       [routerLink]="['/catalog', 'facets']"
+                       routerLinkActive="active">
                     <clr-icon shape="tag" size="20"></clr-icon>{{ 'nav.facets' | translate }}
                 </a>
                 </li>

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

@@ -56,4 +56,7 @@ export class MockDataService implements DataServiceMock {
         checkLoggedIn: spyObservable('checkLoggedIn'),
         attemptLogin: spyObservable('attemptLogin'),
     };
+    facet = {
+        getFacets: spyQueryResult('getFacets'),
+    };
 }

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

@@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
 
 import { BaseDataService } from './base-data.service';
 import { ClientDataService } from './client-data.service';
+import { FacetDataService } from './facet-data.service';
 import { ProductDataService } from './product-data.service';
 import { UserDataService } from './user-data.service';
 
@@ -10,10 +11,12 @@ export class DataService {
     user: UserDataService;
     product: ProductDataService;
     client: ClientDataService;
+    facet: FacetDataService;
 
     constructor(baseDataService: BaseDataService) {
         this.user = new UserDataService(baseDataService);
         this.product = new ProductDataService(baseDataService);
         this.client = new ClientDataService(baseDataService);
+        this.facet = new FacetDataService(baseDataService);
     }
 }

+ 20 - 0
admin-ui/src/app/data/providers/facet-data.service.ts

@@ -0,0 +1,20 @@
+import { getDefaultLanguage } from '../../common/utilities/get-default-language';
+import { GET_FACET_LIST } from '../queries/facet-queries';
+import { GetFacetList, GetFacetListVariables } from '../types/gql-generated-types';
+import { QueryResult } from '../types/query-result';
+
+import { BaseDataService } from './base-data.service';
+
+export class FacetDataService {
+    constructor(private baseDataService: BaseDataService) {}
+
+    getFacets(take: number = 10, skip: number = 0): QueryResult<GetFacetList, GetFacetListVariables> {
+        return this.baseDataService.query<GetFacetList, GetFacetListVariables>(GET_FACET_LIST, {
+            options: {
+                take,
+                skip,
+            },
+            languageCode: getDefaultLanguage(),
+        });
+    }
+}

+ 15 - 0
admin-ui/src/app/data/queries/facet-queries.ts

@@ -0,0 +1,15 @@
+import gql from 'graphql-tag';
+
+export const GET_FACET_LIST = gql`
+    query GetFacetList($options: FacetListOptions, $languageCode: LanguageCode) {
+        facets(languageCode: $languageCode, options: $options) {
+            items {
+                id
+                languageCode
+                code
+                name
+            }
+            totalItems
+        }
+    }
+`;

+ 68 - 16
admin-ui/src/app/data/types/gql-generated-types.ts

@@ -471,6 +471,36 @@ export interface RemoveOptionGroupFromProductVariables {
 /* tslint:disable */
 // This file was automatically generated and should not be edited.
 
+// ====================================================
+// GraphQL query operation: GetFacetList
+// ====================================================
+
+export interface GetFacetList_facets_items {
+  __typename: "Facet";
+  id: string;
+  languageCode: LanguageCode;
+  code: string;
+  name: string;
+}
+
+export interface GetFacetList_facets {
+  __typename: "FacetList";
+  items: GetFacetList_facets_items[];
+  totalItems: number;
+}
+
+export interface GetFacetList {
+  facets: GetFacetList_facets;
+}
+
+export interface GetFacetListVariables {
+  options?: FacetListOptions | null;
+  languageCode?: LanguageCode | null;
+}
+
+/* tslint:disable */
+// This file was automatically generated and should not be edited.
+
 // ====================================================
 // GraphQL query operation: GetNetworkStatus
 // ====================================================
@@ -1063,35 +1093,26 @@ export interface CreateProductOptionInput {
   customFields?: any | null;
 }
 
-export interface ProductListOptions {
+export interface FacetListOptions {
   take?: number | null;
   skip?: number | null;
-  sort?: ProductSortParameter | null;
-  filter?: ProductFilterParameter | null;
+  sort?: FacetSortParameter | null;
+  filter?: FacetFilterParameter | null;
 }
 
-export interface ProductSortParameter {
+export interface FacetSortParameter {
   id?: SortOrder | null;
   createdAt?: SortOrder | null;
   updatedAt?: SortOrder | null;
   name?: SortOrder | null;
-  slug?: SortOrder | null;
-  description?: SortOrder | null;
-  image?: SortOrder | null;
-  infoUrl?: SortOrder | null;
-  downloadable?: SortOrder | null;
-  nickname?: SortOrder | null;
+  code?: SortOrder | null;
 }
 
-export interface ProductFilterParameter {
+export interface FacetFilterParameter {
   name?: StringOperators | null;
-  slug?: StringOperators | null;
-  description?: StringOperators | null;
+  code?: StringOperators | null;
   createdAt?: DateOperators | null;
   updatedAt?: DateOperators | null;
-  infoUrl?: StringOperators | null;
-  downloadable?: BooleanOperators | null;
-  nickname?: StringOperators | null;
 }
 
 export interface StringOperators {
@@ -1111,6 +1132,37 @@ export interface DateRange {
   end: any;
 }
 
+export interface ProductListOptions {
+  take?: number | null;
+  skip?: number | null;
+  sort?: ProductSortParameter | null;
+  filter?: ProductFilterParameter | null;
+}
+
+export interface ProductSortParameter {
+  id?: SortOrder | null;
+  createdAt?: SortOrder | null;
+  updatedAt?: SortOrder | null;
+  name?: SortOrder | null;
+  slug?: SortOrder | null;
+  description?: SortOrder | null;
+  image?: SortOrder | null;
+  infoUrl?: SortOrder | null;
+  downloadable?: SortOrder | null;
+  nickname?: SortOrder | null;
+}
+
+export interface ProductFilterParameter {
+  name?: StringOperators | null;
+  slug?: StringOperators | null;
+  description?: StringOperators | null;
+  createdAt?: DateOperators | null;
+  updatedAt?: DateOperators | null;
+  infoUrl?: StringOperators | null;
+  downloadable?: BooleanOperators | null;
+  nickname?: StringOperators | null;
+}
+
 export interface BooleanOperators {
   eq?: boolean | null;
 }

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

@@ -1,14 +1,15 @@
 {
   "breadcrumb": {
     "dashboard": "Dashboard",
+    "facets": "Facets",
     "products": "Products"
   },
   "catalog": {
     "ID": "ID",
-    "add-existing-option-group": "Add existing option group",
-    "add-option-group": "Add option group",
+    "code": "Code",
     "confirm-generate-product-variants": "Click 'Finish' to generate {count} product variants.",
     "create-group": "Create option group",
+    "create-new-facet": "Create new facet",
     "create-new-option-group": "Create new option group",
     "create-new-product": "Create new product",
     "description": "Description",