Browse Source

feat(admin-ui): Improve layout of OrderDetailComponent

Michael Bromley 6 years ago
parent
commit
c1d8664ad6

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

@@ -9,7 +9,7 @@ import { ServerConfigService } from '../data/server-config';
 import { LanguageCode } from './generated-types';
 import { getDefaultLanguage } from './utilities/get-default-language';
 
-export abstract class BaseDetailComponent<Entity extends { id: string }> {
+export abstract class BaseDetailComponent<Entity extends { id: string; updatedAt?: string }> {
     entity$: Observable<Entity>;
     availableLanguages$: Observable<LanguageCode[]>;
     languageCode$: Observable<LanguageCode>;
@@ -27,7 +27,9 @@ export abstract class BaseDetailComponent<Entity extends { id: string }> {
     init() {
         this.entity$ = this.route.data.pipe(
             switchMap(data => data.entity as Observable<Entity>),
-            distinctUntilChanged((a, b) => a.id === b.id),
+            distinctUntilChanged((a, b) => {
+                return a.id === b.id && a.updatedAt === b.updatedAt;
+            }),
             tap(entity => (this.id = entity.id)),
             shareReplay(1),
         );

+ 1 - 0
admin-ui/src/app/data/definitions/facet-definitions.ts

@@ -21,6 +21,7 @@ export const FACET_VALUE_FRAGMENT = gql`
 export const FACET_WITH_VALUES_FRAGMENT = gql`
     fragment FacetWithValues on Facet {
         id
+        updatedAt
         languageCode
         isPrivate
         code

+ 106 - 140
admin-ui/src/app/order/components/order-detail/order-detail.component.html

@@ -1,154 +1,120 @@
-<vdr-action-bar>
-    <vdr-ab-left></vdr-ab-left>
+<vdr-action-bar *ngIf="entity$ | async as order">
+    <vdr-ab-left>
+        <div class="clr-row clr-flex-column">
+            <vdr-order-state-label [state]="order.state"></vdr-order-state-label>
+            <div class="date-detail">
+                <clr-icon shape="calendar"></clr-icon>
+                {{ 'common.updated' | translate }}:
+                <strong>{{ order.updatedAt | date: 'medium' }}</strong>
+            </div>
+        </div>
+    </vdr-ab-left>
 
     <vdr-ab-right></vdr-ab-right>
 </vdr-action-bar>
 
 <div *ngIf="entity$ | async as order">
     <div class="clr-row">
-        <div class="clr-col-md-4">
-            <table class="table table-vertical">
-                <tbody>
-                    <tr>
-                        <th>{{ 'order.order-code' | translate }}</th>
-                        <td>{{ order.code }}</td>
-                    </tr>
-                    <tr>
-                        <th>{{ 'order.state' | translate }}</th>
-                        <td>{{ order.state }}</td>
-                    </tr>
-                    <tr>
-                        <th>{{ 'common.created' | translate }}</th>
-                        <td>{{ order.createdAt | date: 'medium' }}</td>
-                    </tr>
+        <div class="clr-col-lg-8">
+            <table class="order-lines table">
+                <thead>
                     <tr>
-                        <th>{{ 'common.updated' | translate }}</th>
-                        <td>{{ order.updatedAt | date: 'medium' }}</td>
+                        <th></th>
+                        <th>{{ 'order.product-name' | translate }}</th>
+                        <th>{{ 'order.product-sku' | translate }}</th>
+                        <th>{{ 'order.unit-price' | translate }}</th>
+                        <th>{{ 'order.quantity' | translate }}</th>
+                        <th>{{ 'order.total' | translate }}</th>
                     </tr>
-                </tbody>
+                </thead>
+                <tr *ngFor="let line of order.lines" class="order-line">
+                    <td class="thumb"><img [src]="line.featuredAsset.preview + '?preset=tiny'" /></td>
+                    <td class="name">{{ line.productVariant.name }}</td>
+                    <td class="sku">{{ line.productVariant.sku }}</td>
+                    <td class="unit-price">
+                        {{ line.unitPriceWithTax / 100 | currency: order.currencyCode }}
+                    </td>
+                    <td class="quantity">{{ line.quantity }}</td>
+                    <td class="total">{{ line.totalPrice / 100 | currency: order.currencyCode }}</td>
+                </tr>
+                <tr class="sub-total">
+                    <td class="left">{{ 'order.sub-total' | translate }}</td>
+                    <td></td>
+                    <td></td>
+                    <td></td>
+                    <td></td>
+                    <td>{{ order.subTotal / 100 | currency: order.currencyCode }}</td>
+                </tr>
+                <tr class="order-ajustment" *ngFor="let adjustment of order.adjustments">
+                    <td colspan="5" class="left">{{ adjustment.description }}</td>
+                    <td>{{ adjustment.amount / 100 | currency: order.currencyCode }}</td>
+                </tr>
+                <tr class="shipping">
+                    <td class="left">{{ 'order.shipping' | translate }}</td>
+                    <td>{{ order.shippingMethod?.description }}</td>
+                    <td colspan="3"></td>
+                    <td>{{ order.shipping / 100 | currency: order.currencyCode }}</td>
+                </tr>
+                <tr class="total">
+                    <td class="left">{{ 'order.total' | translate }}</td>
+                    <td></td>
+                    <td></td>
+                    <td></td>
+                    <td></td>
+                    <td>{{ order.total / 100 | currency: order.currencyCode }}</td>
+                </tr>
             </table>
         </div>
-        <div class="clr-col-md-4">
-            <table class="table table-vertical">
-                <tbody>
-                    <tr>
-                        <th>{{ 'order.customer' | translate }}</th>
-                        <td><vdr-customer-label [customer]="order.customer"></vdr-customer-label></td>
-                    </tr>
-                    <tr>
-                        <th>{{ 'order.shipping-address' | translate }}</th>
-                        <td>
-                            <div
-                                class="address-line"
-                                *ngFor="let line of getShippingAddressLines(order.shippingAddress)"
-                            >
+        <div class="clr-col-lg-4 order-cards">
+            <div class="card">
+                <div class="card-header">
+                    {{ 'order.customer' | translate }}
+                </div>
+                <div class="card-block">
+                    <div class="card-text">
+                        <vdr-customer-label [customer]="order.customer"></vdr-customer-label>
+                        <h6 *ngIf="getShippingAddressLines(order.shippingAddress).length">
+                            {{ 'order.shipping-address' | translate }}
+                        </h6>
+                        <ul class="shipping-address">
+                            <li *ngFor="let line of getShippingAddressLines(order.shippingAddress)">
                                 {{ line }}
-                            </div>
-                        </td>
-                    </tr>
-                </tbody>
-            </table>
-        </div>
-        <div class="clr-col-md-4" *ngIf="order.payments && order.payments.length">
-            <table class="table table-vertical">
-                <tbody>
-                    <ng-container *ngFor="let payment of order.payments">
-                        <tr>
-                            <th>{{ 'order.payment-state' | translate }}</th>
-                            <td>
-                                {{ payment.state }}
-                                <ng-container *ngIf="payment.state === 'Authorized'">
-                                    <vdr-dropdown>
-                                        <button class="icon-button" vdrDropdownTrigger>
-                                            <clr-icon shape="ellipsis-vertical"></clr-icon>
-                                        </button>
-                                        <vdr-dropdown-menu vdrPosition="bottom-right">
-                                            <button
-                                                type="button"
-                                                vdrDropdownItem
-                                                [disabled]="i === 0"
-                                                (click)="settlePayment(payment)"
-                                            >
-                                                {{ 'order.settle-payment' | translate }}
-                                            </button>
-                                        </vdr-dropdown-menu>
-                                    </vdr-dropdown>
-                                </ng-container>
-                            </td>
-                        </tr>
-                        <tr>
-                            <th>{{ 'order.payment-method' | translate }}</th>
-                            <td>{{ payment.method }}</td>
-                        </tr>
-                        <tr>
-                            <th>{{ 'order.amount' | translate }}</th>
-                            <td>{{ payment.amount / 100 | currency: order.currencyCode }}</td>
-                        </tr>
-                        <tr>
-                            <th>{{ 'order.transaction-id' | translate }}</th>
-                            <td>{{ payment.transactionId }}</td>
-                        </tr>
-                        <tr>
-                            <th>{{ 'order.payment-metadata' | translate }}</th>
-                            <td>
-                                <ul class="payment-metadata">
-                                    <li *ngFor="let entry of getPaymentMetadata(payment)">
-                                        <strong>{{ entry[0] }}</strong>
-                                        : {{ entry[1] }}
-                                    </li>
-                                </ul>
-                            </td>
-                        </tr>
-                    </ng-container>
-                </tbody>
-            </table>
+                            </li>
+                        </ul>
+                    </div>
+                </div>
+            </div>
+            <ng-container *ngIf="order.payments && order.payments.length">
+                <div class="card" *ngFor="let payment of order.payments">
+                    <div class="card-header">
+                        {{ 'order.payment' | translate }}
+                    </div>
+                    <div class="card-block">
+                        <h6>{{ 'order.payment-state' | translate }}</h6>
+                        {{ payment.state }}
+                        <h6>{{ 'order.payment-method' | translate }}</h6>
+                        {{ payment.method }}
+                        <h6>{{ 'order.amount' | translate }}</h6>
+                        {{ payment.amount / 100 | currency: order.currencyCode }}
+                        <h6>{{ 'order.transaction-id' | translate }}</h6>
+                        {{ payment.transactionId }}
+                        <h6>{{ 'order.payment-metadata' | translate }}</h6>
+                        <ul class="payment-metadata">
+                            <li *ngFor="let entry of getPaymentMetadata(payment)">
+                                <span class="metadata-prop">{{ entry[0] }}:</span>
+                                {{ entry[1] }}
+                            </li>
+                        </ul>
+                    </div>
+                    <div class="card-footer">
+                        <ng-container *ngIf="payment.state === 'Authorized'">
+                            <button class="btn btn-sm btn-link" (click)="settlePayment(payment)">
+                                {{ 'order.settle-payment' | translate }}
+                            </button>
+                        </ng-container>
+                    </div>
+                </div>
+            </ng-container>
         </div>
     </div>
-
-    <table class="order-lines table">
-        <thead>
-            <tr>
-                <th></th>
-                <th>{{ 'order.product-name' | translate }}</th>
-                <th>{{ 'order.product-sku' | translate }}</th>
-                <th>{{ 'order.unit-price' | translate }}</th>
-                <th>{{ 'order.quantity' | translate }}</th>
-                <th>{{ 'order.total' | translate }}</th>
-            </tr>
-        </thead>
-        <tr *ngFor="let line of order.lines" class="order-line">
-            <td class="thumb"><img [src]="line.featuredAsset.preview + '?preset=tiny'" /></td>
-            <td class="name">{{ line.productVariant.name }}</td>
-            <td class="sku">{{ line.productVariant.sku }}</td>
-            <td class="unit-price">{{ line.unitPriceWithTax / 100 | currency: order.currencyCode }}</td>
-            <td class="quantity">{{ line.quantity }}</td>
-            <td class="total">{{ line.totalPrice / 100 | currency: order.currencyCode }}</td>
-        </tr>
-        <tr class="sub-total">
-            <td class="left">{{ 'order.sub-total' | translate }}</td>
-            <td></td>
-            <td></td>
-            <td></td>
-            <td></td>
-            <td>{{ order.subTotal / 100 | currency: order.currencyCode }}</td>
-        </tr>
-        <tr class="order-ajustment" *ngFor="let adjustment of order.adjustments">
-            <td colspan="5" class="left">{{ adjustment.description }}</td>
-            <td>{{ adjustment.amount / 100 | currency: order.currencyCode }}</td>
-        </tr>
-        <tr class="shipping">
-            <td class="left">{{ 'order.shipping' | translate }}</td>
-            <td>{{ order.shippingMethod?.description }}</td>
-            <td colspan="3"></td>
-            <td>{{ order.shipping / 100 | currency: order.currencyCode }}</td>
-        </tr>
-        <tr class="total">
-            <td class="left">{{ 'order.total' | translate }}</td>
-            <td></td>
-            <td></td>
-            <td></td>
-            <td></td>
-            <td>{{ order.total / 100 | currency: order.currencyCode }}</td>
-        </tr>
-    </table>
 </div>

+ 20 - 0
admin-ui/src/app/order/components/order-detail/order-detail.component.scss

@@ -1,8 +1,21 @@
 @import "variables";
 
+.date-detail {
+    color: $color-grey-500;
+    padding-left: 6px;
+}
+
 ul.payment-metadata {
    list-style-type: none;
 }
+.metadata-prop {
+    color: $color-grey-500;
+}
+
+.shipping-address {
+    list-style-type: none;
+    line-height: 1.3em;
+}
 
 .order-lines {
 
@@ -15,3 +28,10 @@ ul.payment-metadata {
         border-top: 1px dashed $color-grey-300;
     }
 }
+
+.order-cards {
+    h6 {
+        margin-top: 6px;
+        color: $color-grey-500;
+    }
+}

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

@@ -41,7 +41,9 @@ export class OrderDetailComponent extends BaseDetailComponent<OrderWithLines.Fra
         if (!shippingAddress) {
             return [];
         }
-        return Object.values(shippingAddress).filter(val => val !== 'ShippingAddress');
+        return Object.values(shippingAddress)
+            .filter(val => val !== 'OrderAddress')
+            .filter(line => !!line);
     }
 
     getPaymentMetadata(payment: OrderWithLines.Payments) {
@@ -56,8 +58,7 @@ export class OrderDetailComponent extends BaseDetailComponent<OrderWithLines.Fra
                 } else {
                     this.notificationService.error(_('order.settle-payment-error'));
                 }
-                payment.state = settlePayment.state;
-                this.changeDetector.markForCheck();
+                this.dataService.order.getOrder(this.id).single$.subscribe();
             }
         });
     }

+ 11 - 7
admin-ui/src/app/order/components/order-list/order-list.component.html

@@ -21,13 +21,17 @@
     <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.id }}</td>
-        <td class="left">{{ order.code }}</td>
-        <td class="left"><vdr-customer-label [customer]="order.customer"></vdr-customer-label></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">
+        <td class="left align-middle">{{ order.id }}</td>
+        <td class="left align-middle">{{ order.code }}</td>
+        <td class="left align-middle">
+            <vdr-customer-label [customer]="order.customer"></vdr-customer-label>
+        </td>
+        <td class="left align-middle">
+            <vdr-order-state-label [state]="order.state"></vdr-order-state-label>
+        </td>
+        <td class="left align-middle">{{ order.total / 100 | currency: order.currencyCode }}</td>
+        <td class="left align-middle">{{ order.updatedAt | date: 'medium' }}</td>
+        <td class="right align-middle">
             <vdr-table-row-action
                 iconShape="shopping-cart"
                 [label]="'common.open' | translate"

+ 2 - 2
admin-ui/src/app/shared/components/action-bar/action-bar.component.ts

@@ -26,6 +26,6 @@ export class ActionBarRightComponent {
     styleUrls: ['./action-bar.component.scss'],
 })
 export class ActionBarComponent {
-    @ContentChild(ActionBarLeftComponent, { static: true }) left: ActionBarLeftComponent;
-    @ContentChild(ActionBarRightComponent, { static: true }) right: ActionBarRightComponent;
+    @ContentChild(ActionBarLeftComponent, { static: false }) left: ActionBarLeftComponent;
+    @ContentChild(ActionBarRightComponent, { static: false }) right: ActionBarRightComponent;
 }

+ 1 - 0
admin-ui/src/app/shared/components/order-state-label/order-state-label.component.html

@@ -0,0 +1 @@
+<vdr-chip [ngClass]="state">{{ stateToken | translate }}</vdr-chip>

+ 33 - 0
admin-ui/src/app/shared/components/order-state-label/order-state-label.component.scss

@@ -0,0 +1,33 @@
+@import "variables";
+
+:host {
+    ::ng-deep vdr-chip {
+        &.PaymentAuthorized, &.PaymentSettled {
+            .wrapper {
+                color: $color-warning-700;
+            }
+            .chip-label {
+                color: $color-warning-700;
+                background-color: $color-warning-400;
+            }
+        }
+        &.Fulfilled {
+            .wrapper {
+                color: $color-success-700;
+            }
+            .chip-label {
+                color: $color-success-700;
+                background-color: $color-success-400;
+            }
+        }
+        &.Cancelled {
+            .wrapper {
+                color: $color-error-700;
+            }
+            .chip-label {
+                color: $color-error-700;
+                background-color: $color-error-400;
+            }
+        }
+    }
+}

+ 25 - 0
admin-ui/src/app/shared/components/order-state-label/order-state-label.component.ts

@@ -0,0 +1,25 @@
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
+import { _ } from 'src/app/core/providers/i18n/mark-for-extraction';
+
+@Component({
+    selector: 'vdr-order-state-label',
+    templateUrl: './order-state-label.component.html',
+    styleUrls: ['./order-state-label.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class OrderStateLabelComponent {
+    @Input() state: string;
+    private readonly stateI18nTokens = {
+        AddingItems: _('order.state-adding-items'),
+        ArrangingPayment: _('order.state-arranging-payment'),
+        PaymentAuthorized: _('order.state-payment-authorized'),
+        PaymentSettled: _('order.state-payment-settled'),
+        PartiallyFulfilled: _('order.state-partially-fulfilled'),
+        Fulfilled: _('order.state-fulfilled'),
+        Cancelled: _('order.state-cancelled'),
+    };
+
+    get stateToken(): string {
+        return this.stateI18nTokens[this.state as any] || this.state;
+    }
+}

+ 3 - 1
admin-ui/src/app/shared/directives/background-color-from.directive.ts

@@ -15,6 +15,8 @@ export class BackgroundColorFromDirective implements OnChanges {
     @Input() private vdrBackgroundColorFrom: string;
 
     ngOnChanges(): void {
-        this.backgroundColor = stringToColor(this.vdrBackgroundColorFrom);
+        if (this.vdrBackgroundColorFrom !== '') {
+            this.backgroundColor = stringToColor(this.vdrBackgroundColorFrom);
+        }
     }
 }

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

@@ -37,6 +37,7 @@ import { DialogButtonsDirective } from './components/modal-dialog/dialog-buttons
 import { DialogComponentOutletComponent } from './components/modal-dialog/dialog-component-outlet.component';
 import { DialogTitleDirective } from './components/modal-dialog/dialog-title.directive';
 import { ModalDialogComponent } from './components/modal-dialog/modal-dialog.component';
+import { OrderStateLabelComponent } from './components/order-state-label/order-state-label.component';
 import { PaginationControlsComponent } from './components/pagination-controls/pagination-controls.component';
 import { RichTextEditorComponent } from './components/rich-text-editor/rich-text-editor.component';
 import { SelectToggleComponent } from './components/select-toggle/select-toggle.component';
@@ -100,6 +101,7 @@ const DECLARATIONS = [
     DropdownMenuComponent,
     DropdownTriggerDirective,
     DropdownItemDirective,
+    OrderStateLabelComponent,
 ];
 
 @NgModule({

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

@@ -110,7 +110,6 @@
     "confirm": "Confirm",
     "confirm-navigation": "Confirm navigation",
     "create": "Create",
-    "created": "Created",
     "created-at": "Created at",
     "custom-fields": "Custom fields",
     "delete": "Delete",
@@ -408,7 +407,7 @@
   "order": {
     "amount": "Amount",
     "customer": "Customer",
-    "order-code": "Order code",
+    "payment": "Payment",
     "payment-metadata": "Payment metadata",
     "payment-method": "Payment method",
     "payment-state": "State",
@@ -421,6 +420,13 @@
     "shipping": "Shipping",
     "shipping-address": "Shipping address",
     "state": "State",
+    "state-adding-items": "Adding items",
+    "state-arranging-payment": "Arranging payment",
+    "state-cancelled": "Cancelled",
+    "state-fulfilled": "Fulfilled",
+    "state-partially-fulfilled": "Partially fulfilled",
+    "state-payment-authorized": "Payment authorized",
+    "state-payment-settled": "Payment settled",
     "sub-total": "Sub total",
     "total": "Total",
     "transaction-id": "Transaction ID",