Просмотр исходного кода

feat(admin-ui): Customer detail includes orders list

Relates to #52
Michael Bromley 7 лет назад
Родитель
Сommit
8a3e29876d

+ 6 - 2
admin-ui/src/app/common/base-detail.component.ts

@@ -1,7 +1,7 @@
 import { FormGroup } from '@angular/forms';
 import { ActivatedRoute, Router } from '@angular/router';
 import { combineLatest, Observable, of, Subject } from 'rxjs';
-import { map, shareReplay, switchMap, takeUntil } from 'rxjs/operators';
+import { map, shareReplay, switchMap, takeUntil, tap } from 'rxjs/operators';
 import { LanguageCode } from 'shared/generated-types';
 import { CustomFieldConfig, CustomFields } from 'shared/shared-types';
 
@@ -14,6 +14,7 @@ export abstract class BaseDetailComponent<Entity extends { id: string }> {
     availableLanguages$: Observable<LanguageCode[]>;
     languageCode$: Observable<LanguageCode>;
     isNew$: Observable<boolean>;
+    id: string;
     abstract detailForm: FormGroup;
     protected destroy$ = new Subject<void>();
 
@@ -24,7 +25,10 @@ export abstract class BaseDetailComponent<Entity extends { id: string }> {
     ) {}
 
     init() {
-        this.entity$ = this.route.data.pipe(switchMap(data => data.entity));
+        this.entity$ = this.route.data.pipe(
+            switchMap(data => data.entity),
+            tap<any>(entity => (this.id = entity.id)),
+        );
         this.isNew$ = this.entity$.pipe(
             map(entity => entity.id === ''),
             shareReplay(1),

+ 28 - 0
admin-ui/src/app/customer/components/customer-detail/customer-detail.component.html

@@ -90,5 +90,33 @@
     </div>
     <div class="clr-col-md-8">
         <h3>{{ 'customer.orders' | translate }}</h3>
+        <vdr-data-table
+            [items]="orders$ | async"
+            [itemsPerPage]="ordersPerPage"
+            [totalItems]="ordersCount$ | async"
+            [currentPage]="currentOrdersPage"
+            [emptyStateLabel]="'customer.no-orders-placed' | translate"
+            (itemsPerPageChange)="setOrderItemsPerPage($event)"
+            (pageChange)="setOrderCurrentPage($event)"
+        >
+            <vdr-dt-column>{{ 'common.code' | translate }}</vdr-dt-column>
+            <vdr-dt-column>{{ 'order.state' | translate }}</vdr-dt-column>
+            <vdr-dt-column>{{ 'order.total' | translate }}</vdr-dt-column>
+            <vdr-dt-column>{{ 'common.updated-at' | translate }}</vdr-dt-column>
+            <vdr-dt-column></vdr-dt-column>
+            <ng-template let-order="item">
+                <td class="left">{{ order.code }}</td>
+                <td class="left">{{ order.state }}</td>
+                <td class="left">{{ order.total / 100 | currency: order.currencyCode }}</td>
+                <td class="left">{{ order.updatedAt | date: 'medium' }}</td>
+                <td class="right">
+                    <vdr-table-row-action
+                        iconShape="shopping-cart"
+                        [label]="'common.open' | translate"
+                        [linkTo]="['/orders/', order.id]"
+                    ></vdr-table-row-action>
+                </td>
+            </ng-template>
+        </vdr-data-table>
     </div>
 </div>

+ 42 - 5
admin-ui/src/app/customer/components/customer-detail/customer-detail.component.ts

@@ -1,17 +1,18 @@
 import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
 import { FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
 import { ActivatedRoute, Router } from '@angular/router';
-import { forkJoin, Observable } from 'rxjs';
-import { mergeMap, publishBehavior, publishReplay, shareReplay, take } from 'rxjs/operators';
+import { forkJoin, Observable, Subject } from 'rxjs';
+import { filter, map, merge, mergeMap, shareReplay, take } from 'rxjs/operators';
 import {
     CreateAddressInput,
     CreateCustomerInput,
     Customer,
     GetAvailableCountries,
-    UpdateAddressInput,
+    GetCustomer,
     UpdateCustomerInput,
 } from 'shared/generated-types';
 import { CustomFieldConfig } from 'shared/shared-types';
+import { notNullOrUndefined } from 'shared/shared-utils';
 
 import { BaseDetailComponent } from '../../../common/base-detail.component';
 import { _ } from '../../../core/providers/i18n/mark-for-extraction';
@@ -25,14 +26,19 @@ import { ServerConfigService } from '../../../data/server-config';
     styleUrls: ['./customer-detail.component.scss'],
     changeDetection: ChangeDetectionStrategy.OnPush,
 })
-export class CustomerDetailComponent extends BaseDetailComponent<Customer.Fragment>
+export class CustomerDetailComponent extends BaseDetailComponent<GetCustomer.Customer>
     implements OnInit, OnDestroy {
     detailForm: FormGroup;
     customFields: CustomFieldConfig[];
     availableCountries$: Observable<GetAvailableCountries.AvailableCountries[]>;
+    orders$: Observable<GetCustomer.Items[]>;
+    ordersCount$: Observable<number>;
     defaultShippingAddressId: string;
     defaultBillingAddressId: string;
     addressDefaultsUpdated = false;
+    ordersPerPage = 1;
+    currentOrdersPage = 1;
+    private orderListUpdates$ = new Subject<GetCustomer.Customer>();
 
     constructor(
         route: ActivatedRoute,
@@ -63,15 +69,20 @@ export class CustomerDetailComponent extends BaseDetailComponent<Customer.Fragme
     }
 
     ngOnInit() {
+        this.init();
         this.availableCountries$ = this.dataService.settings
             .getAvailableCountries()
             .mapSingle(result => result.availableCountries)
             .pipe(shareReplay(1));
-        this.init();
+
+        const customerWithUpdates$ = this.entity$.pipe(merge(this.orderListUpdates$));
+        this.orders$ = customerWithUpdates$.pipe(map(customer => customer.orders.items));
+        this.ordersCount$ = this.entity$.pipe(map(customer => customer.orders.totalItems));
     }
 
     ngOnDestroy() {
         this.destroy();
+        this.orderListUpdates$.complete();
     }
 
     customFieldIsSet(name: string): boolean {
@@ -111,6 +122,16 @@ export class CustomerDetailComponent extends BaseDetailComponent<Customer.Fragme
         addressFormArray.push(newAddress);
     }
 
+    setOrderItemsPerPage(itemsPerPage: number) {
+        this.ordersPerPage = +itemsPerPage;
+        this.fetchOrdersList();
+    }
+
+    setOrderCurrentPage(page: number) {
+        this.currentOrdersPage = +page;
+        this.fetchOrdersList();
+    }
+
     create() {
         const customerForm = this.detailForm.get('customer');
         if (!customerForm) {
@@ -251,4 +272,20 @@ export class CustomerDetailComponent extends BaseDetailComponent<Customer.Fragme
             }
         }
     }
+
+    /**
+     * Refetch the customer with the current order list settings.
+     */
+    private fetchOrdersList() {
+        this.dataService.customer
+            .getCustomer(this.id, {
+                take: this.ordersPerPage,
+                skip: (this.currentOrdersPage - 1) * this.ordersPerPage,
+            })
+            .single$.pipe(
+                map(data => data.customer),
+                filter(notNullOrUndefined),
+            )
+            .subscribe(result => this.orderListUpdates$.next(result));
+    }
 }

+ 12 - 1
admin-ui/src/app/data/definitions/customer-definitions.ts

@@ -59,9 +59,20 @@ export const GET_CUSTOMER_LIST = gql`
 `;
 
 export const GET_CUSTOMER = gql`
-    query GetCustomer($id: ID!) {
+    query GetCustomer($id: ID!, $orderListOptions: OrderListOptions) {
         customer(id: $id) {
             ...Customer
+            orders(options: $orderListOptions) {
+                items {
+                    id
+                    code
+                    state
+                    total
+                    currencyCode
+                    updatedAt
+                }
+                totalItems
+            }
         }
     }
     ${CUSTOMER_FRAGMENT}

+ 6 - 2
admin-ui/src/app/data/providers/customer-data.service.ts

@@ -5,6 +5,7 @@ import {
     CreateCustomerInput,
     GetCustomer,
     GetCustomerList,
+    OrderListOptions,
     UpdateAddressInput,
     UpdateCustomer,
     UpdateCustomerAddress,
@@ -37,8 +38,11 @@ export class CustomerDataService {
         );
     }
 
-    getCustomer(id: string) {
-        return this.baseDataService.query<GetCustomer.Query, GetCustomer.Variables>(GET_CUSTOMER, { id });
+    getCustomer(id: string, orderListOptions?: OrderListOptions) {
+        return this.baseDataService.query<GetCustomer.Query, GetCustomer.Variables>(GET_CUSTOMER, {
+            id,
+            orderListOptions,
+        });
     }
 
     createCustomer(input: CreateCustomerInput, password?: string) {

+ 1 - 1
admin-ui/src/app/order/components/order-detail/order-detail.component.ts

@@ -14,7 +14,7 @@ import { ServerConfigService } from '../../../data/server-config';
 })
 export class OrderDetailComponent extends BaseDetailComponent<OrderWithLines.Fragment>
     implements OnInit, OnDestroy {
-    detailForm: FormGroup;
+    detailForm = new FormGroup({});
     constructor(router: Router, route: ActivatedRoute, serverConfigService: ServerConfigService) {
         super(route, router, serverConfigService);
     }

+ 8 - 12
admin-ui/src/app/order/components/order-list/order-list.component.html

@@ -1,13 +1,9 @@
-<vdr-action-bar>
-    <vdr-ab-right>
-        <!--
-            <a class="btn btn-primary" [routerLink]="['./create']">
-                <clr-icon shape="plus"></clr-icon>
-                {{ 'order.create-new-order' | translate }}
-            </a>
-        -->
-    </vdr-ab-right>
-</vdr-action-bar>
+<!--
+    <vdr-action-bar>
+        <vdr-ab-right>
+        </vdr-ab-right>
+    </vdr-action-bar>
+-->
 
 <vdr-data-table
     [items]="items$ | async"
@@ -33,8 +29,8 @@
         <td class="left">{{ order.updatedAt | date: 'medium' }}</td>
         <td class="right">
             <vdr-table-row-action
-                iconShape="edit"
-                [label]="'common.edit' | translate"
+                iconShape="shopping-cart"
+                [label]="'common.open' | translate"
                 [linkTo]="['./', order.id]"
             ></vdr-table-row-action>
         </td>

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

@@ -105,12 +105,14 @@
     "more": "More...",
     "name": "Name",
     "next": "Next",
+    "no-results": "No results",
     "notify-create-error": "An error occurred, could not create { entity }",
     "notify-create-success": "Created new { entity }",
     "notify-save-changes-error": "An error occurred, could not save changes",
     "notify-saved-changes": "Saved changes",
     "notify-update-error": "An error occurred, could not update { entity }",
     "notify-update-success": "Updated { entity }",
+    "open": "Open",
     "password": "Password",
     "remember-me": "Remember me",
     "remove": "Remove",
@@ -136,6 +138,7 @@
     "guest": "Guest",
     "last-name": "Last name",
     "name": "Name",
+    "no-orders-placed": "No orders placed",
     "orders": "Orders",
     "password": "Password",
     "phone-number": "Phone number",
@@ -372,7 +375,6 @@
   },
   "order": {
     "amount": "Amount",
-    "create-new-order": "Create new order",
     "customer": "Customer",
     "order-code": "Order code",
     "payment-metadata": "Payment metadata",

+ 21 - 1
shared/generated-types.ts

@@ -5438,6 +5438,7 @@ export namespace GetCustomerList {
 export namespace GetCustomer {
     export type Variables = {
         id: string;
+        orderListOptions?: OrderListOptions | null;
     };
 
     export type Query = {
@@ -5445,7 +5446,26 @@ export namespace GetCustomer {
         customer?: Customer | null;
     };
 
-    export type Customer = Customer.Fragment;
+    export type Customer = {
+        __typename?: 'Customer';
+        orders: Orders;
+    } & Customer.Fragment;
+
+    export type Orders = {
+        __typename?: 'OrderList';
+        items: Items[];
+        totalItems: number;
+    };
+
+    export type Items = {
+        __typename?: 'Order';
+        id: string;
+        code: string;
+        state: string;
+        total: number;
+        currencyCode: CurrencyCode;
+        updatedAt: DateTime;
+    };
 }
 
 export namespace CreateCustomer {