Browse Source

feat(admin-ui): Create CatalogModule with basic product list

Michael Bromley 7 years ago
parent
commit
06498cc183

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

@@ -14,6 +14,10 @@ export const routes: Route[] = [
                 pathMatch: 'full',
                 loadChildren: './dashboard/dashboard.module#DashboardModule',
             },
+            {
+                path: 'catalog',
+                loadChildren: './catalog/catalog.module#CatalogModule',
+            },
         ],
     },
 ];

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

@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
+import { SharedModule } from '../shared/shared.module';
+import { catalogRoutes } from './catalog.routes';
+import { ProductListComponent } from './components/product-list/product-list.component';
+
+@NgModule({
+    imports: [
+        SharedModule,
+        RouterModule.forChild(catalogRoutes),
+    ],
+    exports: [],
+    declarations: [ProductListComponent],
+    providers: [],
+})
+export class CatalogModule {
+}

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

@@ -0,0 +1,9 @@
+import { Route } from '@angular/router';
+import { ProductListComponent } from './components/product-list/product-list.component';
+
+export const catalogRoutes: Route[] = [
+    {
+        path: 'products',
+        component: ProductListComponent,
+    },
+];

+ 38 - 0
admin-ui/src/app/catalog/components/product-list/product-list.component.html

@@ -0,0 +1,38 @@
+<clr-datagrid>
+    <clr-dg-column>Product ID</clr-dg-column>
+    <clr-dg-column>Name</clr-dg-column>
+    <clr-dg-column>Slug</clr-dg-column>
+    <clr-dg-column>Description</clr-dg-column>
+
+    <clr-dg-row *ngFor="let product of products$ | async">
+        <clr-dg-cell>{{ product.id }}</clr-dg-cell>
+        <clr-dg-cell>{{ product.name }}</clr-dg-cell>
+        <clr-dg-cell>{{ product.slug }}</clr-dg-cell>
+        <clr-dg-cell>{{ product.description }}</clr-dg-cell>
+    </clr-dg-row>
+
+    <clr-dg-footer>
+        <clr-dg-pagination #pagination
+                           (clrDgPageChange)="getPage($event)"
+                           [(clrDgPage)]="currentPage"
+                           [clrDgPageSize]="itemsPerPage"
+                           [clrDgTotalItems]="totalItems">
+            <div>
+                <div class="form-group">
+                    <div class="select">
+                        <select [(ngModel)]="itemsPerPage" (change)="getPage(currentPage)">
+                            <option [value]="10">10 per page</option>
+                            <option [value]="25">25 per page</option>
+                            <option [value]="50">50 per page</option>
+                            <option [value]="100">100 per page</option>
+                        </select>
+                    </div>
+                </div>
+                <div>
+                    {{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
+                    of {{pagination.totalItems}} products
+                </div>
+            </div>
+        </clr-dg-pagination>
+    </clr-dg-footer>
+</clr-datagrid>

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


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

@@ -0,0 +1,25 @@
+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();
+  });
+});

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

@@ -0,0 +1,32 @@
+import { Component, OnInit } from '@angular/core';
+import { DataService } from '../../../core/providers/data/data.service';
+import { Observable } from 'rxjs';
+import { map, tap } from 'rxjs/operators';
+
+@Component({
+    selector: 'vdr-products-list',
+    templateUrl: './product-list.component.html',
+    styleUrls: ['./product-list.component.scss']
+})
+export class ProductListComponent implements OnInit {
+
+    products$: Observable<any[]>;
+    totalItems: number;
+    itemsPerPage = 25;
+    currentPage = 1;
+
+    constructor(private dataService: DataService) { }
+
+    ngOnInit() {
+        this.getPage(1);
+    }
+
+    getPage(pageNumber: number): void {
+        const take = this.itemsPerPage;
+        const skip = (pageNumber - 1) * this.itemsPerPage;
+        this.products$ = this.dataService.product.getProducts(take, skip).pipe(
+            tap(val => { this.totalItems = val.totalItems; }),
+            map(val => val.items),
+        );
+    }
+}

+ 26 - 9
admin-ui/src/app/core/components/main-nav/main-nav.component.html

@@ -4,17 +4,34 @@
             <input id="tabexample2" type="checkbox">
             <label for="tabexample2">Catalog</label>
             <ul class="nav-list">
-                <li><a class="nav-link"><clr-icon shape="library" size="20"></clr-icon>Products</a></li>
-                <li><a class="nav-link"><clr-icon shape="tag" size="20"></clr-icon>Facets</a></li>
-                <li><a class="nav-link"><clr-icon shape="folder-open" size="20"></clr-icon>Categories</a></li>
+                <li>
+                    <a class="nav-link"
+                       [routerLink]="['/catalog', 'products']"
+                       routerLinkActive="active">
+                        <clr-icon shape="library" size="20"></clr-icon>Products
+                    </a>
+                </li>
+                <li><a class="nav-link">
+                    <clr-icon shape="tag" size="20"></clr-icon>Facets
+                </a>
+                </li>
+                <li>
+                    <a class="nav-link">
+                        <clr-icon shape="folder-open" size="20"></clr-icon>Categories
+                    </a>
+                </li>
             </ul>
         </section>
         <section class="nav-group">
-                    <input id="tabexample2" type="checkbox">
-                    <label for="tabexample2">Sales</label>
-                    <ul class="nav-list">
-                        <li><a class="nav-link"><clr-icon shape="shopping-cart" size="20"></clr-icon>Orders</a></li>
-                    </ul>
-                </section>
+            <input id="tabexample2" type="checkbox">
+            <label for="tabexample2">Sales</label>
+            <ul class="nav-list">
+                <li>
+                    <a class="nav-link">
+                        <clr-icon shape="shopping-cart" size="20"></clr-icon>Orders
+                    </a>
+                </li>
+            </ul>
+        </section>
     </section>
 </nav>

+ 8 - 8
admin-ui/src/app/core/providers/data/base-data.service.ts

@@ -1,12 +1,10 @@
+import { HttpClient } from '@angular/common/http';
 import { Injectable } from '@angular/core';
 import { Apollo } from 'apollo-angular';
 import { DocumentNode } from 'graphql/language/ast';
 import { Observable } from 'rxjs';
-import { ApolloQueryResult } from 'apollo-client/core/types';
-import { HttpClient } from '@angular/common/http';
-import { API_URL } from '../../../app.config';
 import { map } from 'rxjs/operators';
-import { StateStore } from '../../../state/state-store.service';
+import { API_URL } from '../../../app.config';
 import { LocalStorageService } from '../local-storage/local-storage.service';
 
 @Injectable()
@@ -20,7 +18,7 @@ export class BaseDataService {
      * Performs a GraphQL query
      */
     query<T, V = Record<string, any>>(query: DocumentNode, variables?: V): Observable<T> {
-        return this.apollo.query<T, V>({
+        return this.apollo.watchQuery<T, V>({
             query,
             variables,
             context: {
@@ -28,11 +26,13 @@ export class BaseDataService {
                     Authorization: this.getAuthHeader(),
                 },
             },
-        }).pipe(map(result => result.data));
+        }).valueChanges.pipe(
+            map(result => result.data),
+        );
     }
 
     /**
-     * Perform REST POST
+     * Perform REST-like POST
      */
     post(path: string, payload: Record<string, any>): Observable<any> {
         return this.httpClient.post(`${API_URL}/${path}`, payload, {
@@ -43,7 +43,7 @@ export class BaseDataService {
     }
 
     /**
-     * Perform REST GET
+     * Perform REST-like GET
      */
     get(path: string): Observable<any> {
         return this.httpClient.get(`${API_URL}/${path}`, {

+ 1 - 6
admin-ui/src/app/core/providers/data/product-data.service.ts

@@ -9,7 +9,7 @@ export class ProductDataService {
 
     getProducts(take: number = 10, skip: number = 0): Observable<any> {
         const query = gql`
-            query($take: Int, $skip: Int){
+            query ($take: Int, $skip: Int){
                 products(languageCode: en, take: $take, skip: $skip) {
                     items {
                         id
@@ -17,11 +17,6 @@ export class ProductDataService {
                         name
                         slug
                         description
-                        translations {
-                            id
-                            languageCode
-                            name
-                        }
                     }
                     totalItems
                 }

+ 0 - 21
admin-ui/src/app/dashboard/components/dashboard/dashboard.component.html

@@ -1,22 +1 @@
 <h1>Dashboard</h1>
-
-<clr-datagrid>
-    <clr-dg-column>Product ID</clr-dg-column>
-    <clr-dg-column>Name</clr-dg-column>
-    <clr-dg-column>Slug</clr-dg-column>
-    <clr-dg-column>Description</clr-dg-column>
-
-    <clr-dg-row *ngFor="let product of products$ | async">
-        <clr-dg-cell>{{ product.id }}</clr-dg-cell>
-        <clr-dg-cell>{{ product.name }}</clr-dg-cell>
-        <clr-dg-cell>{{ product.slug }}</clr-dg-cell>
-        <clr-dg-cell>{{ product.description }}</clr-dg-cell>
-    </clr-dg-row>
-
-    <clr-dg-footer>
-        <clr-dg-pagination (clrDgPageChange)="getPage($event)"
-                           [clrDgPage]="currentPage"
-                           [clrDgPageSize]="10"
-                           [clrDgTotalItems]="totalItems"></clr-dg-pagination>
-    </clr-dg-footer>
-</clr-datagrid>

+ 3 - 28
admin-ui/src/app/dashboard/components/dashboard/dashboard.component.ts

@@ -1,34 +1,9 @@
-import { Component, OnInit } from '@angular/core';
-import gql from 'graphql-tag';
-import { DataService } from '../../../core/providers/data/data.service';
-import { Observable } from 'rxjs';
-import { map, tap } from 'rxjs/operators';
+import { Component } from '@angular/core';
 
 @Component({
     selector: 'vdr-dashboard',
     templateUrl: './dashboard.component.html',
-    styleUrls: ['./dashboard.component.scss']
+    styleUrls: ['./dashboard.component.scss'],
 })
-export class DashboardComponent implements OnInit {
-
-    products$: Observable<any[]>;
-    currentPage = 1;
-    totalItems: number;
-
-    constructor(private dataService: DataService) { }
-
-    ngOnInit() {
-        this.getPage(1);
-    }
-
-    getPage(pageNumber: number): void {
-        const itemsPerPage = 10;
-        const take = itemsPerPage;
-        const skip = (pageNumber - 1) * itemsPerPage;
-        this.products$ = this.dataService.product.getProducts(take, skip).pipe(
-            tap(val => { this.totalItems = val.totalItems; }),
-            map(val => val.items),
-        );
-    }
-
+export class DashboardComponent {
 }

+ 0 - 2
admin-ui/src/app/state/state-store.service.ts

@@ -4,8 +4,6 @@ import { Action, Store } from '@ngrx/store';
 import { AppState } from './app-state';
 import { distinctUntilChanged, take } from 'rxjs/operators';
 
-console.log('foo');
-
 /**
  * Wrapper class which wraps the @ngrx/store Store object, and also provides additional
  * convenience methods for accessing data.