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

feat(admin-ui): Handle Fulfillments state from Order detail view

Relates to #426
Michael Bromley 5 лет назад
Родитель
Сommit
7883a7ac31
28 измененных файлов с 350 добавлено и 77 удалено
  1. 22 22
      packages/admin-ui/i18n-coverage.json
  2. 21 1
      packages/admin-ui/src/lib/core/src/common/generated-types.ts
  3. 11 0
      packages/admin-ui/src/lib/core/src/data/definitions/order-definitions.ts
  4. 12 0
      packages/admin-ui/src/lib/core/src/data/providers/order-data.service.ts
  5. 2 0
      packages/admin-ui/src/lib/core/src/shared/components/order-state-label/order-state-label.component.ts
  6. 2 0
      packages/admin-ui/src/lib/core/src/shared/pipes/order-state-i18n-token.pipe.ts
  7. 46 0
      packages/admin-ui/src/lib/order/src/components/fulfillment-card/fulfillment-card.component.html
  8. 12 0
      packages/admin-ui/src/lib/order/src/components/fulfillment-card/fulfillment-card.component.scss
  9. 39 0
      packages/admin-ui/src/lib/order/src/components/fulfillment-card/fulfillment-card.component.ts
  10. 0 1
      packages/admin-ui/src/lib/order/src/components/fulfillment-detail/fulfillment-detail.component.ts
  11. 4 0
      packages/admin-ui/src/lib/order/src/components/fulfillment-state-label/fulfillment-state-label.component.html
  12. 3 0
      packages/admin-ui/src/lib/order/src/components/fulfillment-state-label/fulfillment-state-label.component.scss
  13. 23 0
      packages/admin-ui/src/lib/order/src/components/fulfillment-state-label/fulfillment-state-label.component.ts
  14. 14 15
      packages/admin-ui/src/lib/order/src/components/order-detail/order-detail.component.html
  15. 0 5
      packages/admin-ui/src/lib/order/src/components/order-detail/order-detail.component.scss
  16. 9 0
      packages/admin-ui/src/lib/order/src/components/order-detail/order-detail.component.ts
  17. 33 6
      packages/admin-ui/src/lib/order/src/components/order-history/order-history.component.html
  18. 19 4
      packages/admin-ui/src/lib/order/src/components/order-history/order-history.component.ts
  19. 1 2
      packages/admin-ui/src/lib/order/src/components/order-payment-card/order-payment-card.component.ts
  20. 4 0
      packages/admin-ui/src/lib/order/src/order.module.ts
  21. 10 2
      packages/admin-ui/src/lib/static/i18n-messages/de.json
  22. 12 4
      packages/admin-ui/src/lib/static/i18n-messages/en.json
  23. 10 2
      packages/admin-ui/src/lib/static/i18n-messages/es.json
  24. 10 3
      packages/admin-ui/src/lib/static/i18n-messages/pl.json
  25. 10 3
      packages/admin-ui/src/lib/static/i18n-messages/pt_BR.json
  26. 10 3
      packages/admin-ui/src/lib/static/i18n-messages/zh_Hans.json
  27. 10 3
      packages/admin-ui/src/lib/static/i18n-messages/zh_Hant.json
  28. 1 1
      packages/dev-server/dev-config.ts

+ 22 - 22
packages/admin-ui/i18n-coverage.json

@@ -1,41 +1,41 @@
 {
-  "generatedOn": "2020-08-26T08:49:50.552Z",
-  "lastCommit": "8881aee83b25f57edba537fbdcb85c77bf5f3c4e",
+  "generatedOn": "2020-09-15T15:16:07.529Z",
+  "lastCommit": "8232ddce0b03f8f1b30e8d02563d5fe2d5f5ebe8",
   "translationStatus": {
     "de": {
-      "tokenCount": 662,
-      "translatedCount": 610,
-      "percentage": 92
+      "tokenCount": 670,
+      "translatedCount": 609,
+      "percentage": 91
     },
     "en": {
-      "tokenCount": 662,
-      "translatedCount": 662,
+      "tokenCount": 670,
+      "translatedCount": 669,
       "percentage": 100
     },
     "es": {
-      "tokenCount": 662,
-      "translatedCount": 467,
-      "percentage": 71
+      "tokenCount": 670,
+      "translatedCount": 466,
+      "percentage": 70
     },
     "pl": {
-      "tokenCount": 662,
-      "translatedCount": 566,
-      "percentage": 85
+      "tokenCount": 669,
+      "translatedCount": 564,
+      "percentage": 84
     },
     "pt_BR": {
-      "tokenCount": 662,
-      "translatedCount": 657,
-      "percentage": 99
+      "tokenCount": 669,
+      "translatedCount": 655,
+      "percentage": 98
     },
     "zh_Hans": {
-      "tokenCount": 662,
-      "translatedCount": 550,
-      "percentage": 83
+      "tokenCount": 669,
+      "translatedCount": 548,
+      "percentage": 82
     },
     "zh_Hant": {
-      "tokenCount": 662,
-      "translatedCount": 550,
-      "percentage": 83
+      "tokenCount": 669,
+      "translatedCount": 548,
+      "percentage": 82
     }
   }
 }

+ 21 - 1
packages/admin-ui/src/lib/core/src/common/generated-types.ts

@@ -4841,7 +4841,7 @@ export type OrderFragment = (
 
 export type FulfillmentFragment = (
   { __typename?: 'Fulfillment' }
-  & Pick<Fulfillment, 'id' | 'createdAt' | 'updatedAt' | 'method' | 'trackingCode'>
+  & Pick<Fulfillment, 'id' | 'state' | 'nextStates' | 'createdAt' | 'updatedAt' | 'method' | 'trackingCode'>
 );
 
 export type OrderLineFragment = (
@@ -5094,6 +5094,20 @@ export type UpdateOrderCustomFieldsMutation = (
   )> }
 );
 
+export type TransitionFulfillmentToStateMutationVariables = {
+  id: Scalars['ID'];
+  state: Scalars['String'];
+};
+
+
+export type TransitionFulfillmentToStateMutation = (
+  { __typename?: 'Mutation' }
+  & { transitionFulfillmentToState: (
+    { __typename?: 'Fulfillment' }
+    & FulfillmentFragment
+  ) }
+);
+
 export type AssetFragment = (
   { __typename?: 'Asset' }
   & Pick<Asset, 'id' | 'createdAt' | 'updatedAt' | 'name' | 'fileSize' | 'mimeType' | 'type' | 'preview' | 'source' | 'width' | 'height'>
@@ -7452,6 +7466,12 @@ export namespace UpdateOrderCustomFields {
   export type SetOrderCustomFields = OrderFragment;
 }
 
+export namespace TransitionFulfillmentToState {
+  export type Variables = TransitionFulfillmentToStateMutationVariables;
+  export type Mutation = TransitionFulfillmentToStateMutation;
+  export type TransitionFulfillmentToState = FulfillmentFragment;
+}
+
 export namespace Asset {
   export type Fragment = AssetFragment;
   export type FocalPoint = (NonNullable<AssetFragment['focalPoint']>);

+ 11 - 0
packages/admin-ui/src/lib/core/src/data/definitions/order-definitions.ts

@@ -56,6 +56,8 @@ export const ORDER_FRAGMENT = gql`
 export const FULFILLMENT_FRAGMENT = gql`
     fragment Fulfillment on Fulfillment {
         id
+        state
+        nextStates
         createdAt
         updatedAt
         method
@@ -310,3 +312,12 @@ export const UPDATE_ORDER_CUSTOM_FIELDS = gql`
     }
     ${ORDER_FRAGMENT}
 `;
+
+export const TRANSITION_FULFILLMENT_TO_STATE = gql`
+    mutation TransitionFulfillmentToState($id: ID!, $state: String!) {
+        transitionFulfillmentToState(id: $id, state: $state) {
+            ...Fulfillment
+        }
+    }
+    ${FULFILLMENT_FRAGMENT}
+`;

+ 12 - 0
packages/admin-ui/src/lib/core/src/data/providers/order-data.service.ts

@@ -15,6 +15,7 @@ import {
     SettlePayment,
     SettleRefund,
     SettleRefundInput,
+    TransitionFulfillmentToState,
     TransitionOrderToState,
     UpdateOrderCustomFields,
     UpdateOrderInput,
@@ -32,6 +33,7 @@ import {
     REFUND_ORDER,
     SETTLE_PAYMENT,
     SETTLE_REFUND,
+    TRANSITION_FULFILLMENT_TO_STATE,
     TRANSITION_ORDER_TO_STATE,
     UPDATE_ORDER_CUSTOM_FIELDS,
     UPDATE_ORDER_NOTE,
@@ -80,6 +82,16 @@ export class OrderDataService {
         );
     }
 
+    transitionFulfillmentToState(id: string, state: string) {
+        return this.baseDataService.mutate<
+            TransitionFulfillmentToState.Mutation,
+            TransitionFulfillmentToState.Variables
+        >(TRANSITION_FULFILLMENT_TO_STATE, {
+            id,
+            state,
+        });
+    }
+
     cancelOrder(input: CancelOrderInput) {
         return this.baseDataService.mutate<CancelOrder.Mutation, CancelOrder.Variables>(CANCEL_ORDER, {
             input,

+ 2 - 0
packages/admin-ui/src/lib/core/src/shared/components/order-state-label/order-state-label.component.ts

@@ -14,6 +14,8 @@ export class OrderStateLabelComponent {
             case 'PaymentAuthorized':
             case 'PaymentSettled':
             case 'PartiallyDelivered':
+            case 'PartiallyShipped':
+            case 'Shipped':
                 return 'warning';
             case 'Delivered':
                 return 'success';

+ 2 - 0
packages/admin-ui/src/lib/core/src/shared/pipes/order-state-i18n-token.pipe.ts

@@ -10,6 +10,8 @@ export class OrderStateI18nTokenPipe implements PipeTransform {
         ArrangingPayment: _('order.state-arranging-payment'),
         PaymentAuthorized: _('order.state-payment-authorized'),
         PaymentSettled: _('order.state-payment-settled'),
+        PartiallyShipped: _('order.state-partially-shipped'),
+        Shipped: _('order.state-shipped'),
         PartiallyDelivered: _('order.state-partially-delivered'),
         Delivered: _('order.state-delivered'),
         Cancelled: _('order.state-cancelled'),

+ 46 - 0
packages/admin-ui/src/lib/order/src/components/fulfillment-card/fulfillment-card.component.html

@@ -0,0 +1,46 @@
+<div class="card">
+    <div class="card-header fulfillment-header">
+        <div>{{ 'order.fulfillment' | translate }}</div>
+        <div class="fulfillment-state">
+            <vdr-fulfillment-state-label [state]="fulfillment?.state"></vdr-fulfillment-state-label>
+        </div>
+    </div>
+    <div class="card-block">
+        <vdr-fulfillment-detail
+            *ngIf="!!fulfillment"
+            [fulfillmentId]="fulfillment?.id"
+            [order]="order"
+        ></vdr-fulfillment-detail>
+    </div>
+    <div class="card-footer">
+        <ng-container *ngIf="nextSuggestedState() as suggestedState">
+            <button class="btn btn-sm btn-primary" (click)="transitionState.emit(suggestedState)">
+                {{ 'order.set-fulfillment-state' | translate: { state: suggestedState } }}
+            </button>
+        </ng-container>
+        <vdr-dropdown>
+            <button class="icon-button" vdrDropdownTrigger>
+                <clr-icon shape="ellipsis-vertical"></clr-icon>
+            </button>
+            <vdr-dropdown-menu vdrPosition="bottom-right">
+                <ng-container *ngFor="let nextState of nextOtherStates()">
+                    <button
+                        type="button"
+                        class="btn"
+                        vdrDropdownItem
+                        (click)="transitionState.emit(nextState)"
+                    >
+                        <ng-container *ngIf="nextState !== 'Cancelled'; else cancel">
+                            <clr-icon shape="step-forward-2"></clr-icon>
+                            {{ 'order.transition-to-state' | translate: { state: nextState } }}
+                        </ng-container>
+                        <ng-template #cancel>
+                            <clr-icon shape="error-standard" class="is-error"></clr-icon>
+                            {{ 'order.cancel-fulfillment' | translate }}
+                        </ng-template>
+                    </button>
+                </ng-container>
+            </vdr-dropdown-menu>
+        </vdr-dropdown>
+    </div>
+</div>

+ 12 - 0
packages/admin-ui/src/lib/order/src/components/fulfillment-card/fulfillment-card.component.scss

@@ -0,0 +1,12 @@
+
+.fulfillment-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+}
+
+.card-footer {
+    display: flex;
+    align-items: center;
+    justify-content: flex-end;
+}

+ 39 - 0
packages/admin-ui/src/lib/order/src/components/fulfillment-card/fulfillment-card.component.ts

@@ -0,0 +1,39 @@
+import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
+import { Fulfillment, OrderDetail } from '@vendure/admin-ui/core';
+
+@Component({
+    selector: 'vdr-fulfillment-card',
+    templateUrl: './fulfillment-card.component.html',
+    styleUrls: ['./fulfillment-card.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class FulfillmentCardComponent {
+    @Input() fulfillment: Fulfillment.Fragment | undefined;
+    @Input() order: OrderDetail.Fragment;
+    @Output() transitionState = new EventEmitter<string>();
+
+    nextSuggestedState(): string | undefined {
+        if (!this.fulfillment) {
+            return;
+        }
+        const { nextStates } = this.fulfillment;
+        const namedStateOrDefault = (targetState: string) =>
+            nextStates.includes(targetState) ? targetState : nextStates[0];
+        switch (this.fulfillment?.state) {
+            case 'Pending':
+                return namedStateOrDefault('Shipped');
+            case 'Shipped':
+                return namedStateOrDefault('Delivered');
+            default:
+                return nextStates.find(s => s !== 'Cancelled');
+        }
+    }
+
+    nextOtherStates(): string[] {
+        if (!this.fulfillment) {
+            return [];
+        }
+        const suggested = this.nextSuggestedState();
+        return this.fulfillment.nextStates.filter(s => s !== suggested);
+    }
+}

+ 0 - 1
packages/admin-ui/src/lib/order/src/components/fulfillment-detail/fulfillment-detail.component.ts

@@ -1,5 +1,4 @@
 import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
-
 import { OrderDetail } from '@vendure/admin-ui/core';
 
 @Component({

+ 4 - 0
packages/admin-ui/src/lib/order/src/components/fulfillment-state-label/fulfillment-state-label.component.html

@@ -0,0 +1,4 @@
+<vdr-chip [title]="'order.payment-state' | translate" [colorType]="chipColorType">
+    <clr-icon shape="check-circle" *ngIf="state === 'Delivered'"></clr-icon>
+    {{ state }}
+</vdr-chip>

+ 3 - 0
packages/admin-ui/src/lib/order/src/components/fulfillment-state-label/fulfillment-state-label.component.scss

@@ -0,0 +1,3 @@
+:host {
+    font-size: 14px;
+}

+ 23 - 0
packages/admin-ui/src/lib/order/src/components/fulfillment-state-label/fulfillment-state-label.component.ts

@@ -0,0 +1,23 @@
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
+
+@Component({
+    selector: 'vdr-fulfillment-state-label',
+    templateUrl: './fulfillment-state-label.component.html',
+    styleUrls: ['./fulfillment-state-label.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class FulfillmentStateLabelComponent {
+    @Input() state: string;
+
+    get chipColorType() {
+        switch (this.state) {
+            case 'Pending':
+            case 'Shipped':
+                return 'warning';
+            case 'Delivered':
+                return 'success';
+            case 'Cancelled':
+                return 'error';
+        }
+    }
+}

+ 14 - 15
packages/admin-ui/src/lib/order/src/components/order-detail/order-detail.component.html

@@ -3,8 +3,12 @@
         <div class="flex clr-align-items-center">
             <vdr-entity-info [entity]="entity$ | async"></vdr-entity-info>
             <vdr-order-state-label [state]="order.state">
-                <button class="icon-button" (click)="openStateDiagram()" [title]="'order.order-state-diagram' | translate">
-                <clr-icon shape="list"></clr-icon>
+                <button
+                    class="icon-button"
+                    (click)="openStateDiagram()"
+                    [title]="'order.order-state-diagram' | translate"
+                >
+                    <clr-icon shape="list"></clr-icon>
                 </button>
             </vdr-order-state-label>
         </div>
@@ -258,25 +262,20 @@
                 </div>
             </div>
             <ng-container *ngIf="order.payments && order.payments.length">
-                <vdr-order-payment-detail
+                <vdr-order-payment-card
                     *ngFor="let payment of order.payments"
                     [currencyCode]="order.currencyCode"
                     [payment]="payment"
                     (settlePayment)="settlePayment($event)"
                     (settleRefund)="settleRefund($event)"
-                ></vdr-order-payment-detail>
+                ></vdr-order-payment-card>
             </ng-container>
-            <ng-container *ngIf="order.fulfillments && order.fulfillments.length">
-                <div class="card">
-                    <div class="card-header">
-                        {{ 'order.fulfillment' | translate }}
-                    </div>
-                    <div class="card-block">
-                        <div class="fulfillment-detail" *ngFor="let fulfillment of order.fulfillments">
-                            <vdr-fulfillment-detail [fulfillmentId]="fulfillment.id" [order]="order"></vdr-fulfillment-detail>
-                        </div>
-                    </div>
-                </div>
+            <ng-container *ngFor="let fulfillment of order.fulfillments">
+                <vdr-fulfillment-card
+                    [fulfillment]="fulfillment"
+                    [order]="order"
+                    (transitionState)="transitionFulfillment(fulfillment.id, $event)"
+                ></vdr-fulfillment-card>
             </ng-container>
         </div>
     </div>

+ 0 - 5
packages/admin-ui/src/lib/order/src/components/order-detail/order-detail.component.scss

@@ -67,8 +67,3 @@
         color: $color-grey-500;
     }
 }
-
-.fulfillment-detail:not(:last-of-type) {
-    border-bottom: 1px dashed $color-grey-300;
-    margin-bottom: 12px;
-}

+ 9 - 0
packages/admin-ui/src/lib/order/src/components/order-detail/order-detail.component.ts

@@ -211,6 +211,15 @@ export class OrderDetailComponent extends BaseDetailComponent<OrderDetail.Fragme
             });
     }
 
+    transitionFulfillment(id: string, state: string) {
+        this.dataService.order
+            .transitionFulfillmentToState(id, state)
+            .pipe(switchMap(result => this.refetchOrder(result)))
+            .subscribe(() => {
+                this.notificationService.success(_('order.successfully-updated-fulfillment'));
+            });
+    }
+
     cancelOrRefund(order: OrderDetail.Fragment) {
         if (order.state === 'PaymentAuthorized' || order.active === true) {
             this.cancelOrder(order);

+ 33 - 6
packages/admin-ui/src/lib/order/src/components/order-history/order-history.component.html

@@ -60,7 +60,7 @@
                     {{
                         'order.history-payment-transition'
                             | translate
-                                : { from: entry.data.from, to: entry.data.to, id: entry.data.paymentId }
+                                : { from: entry.data.from, to: entry.data.to, id: getPayment(entry)?.transactionId }
                     }}
                 </ng-template>
             </ng-container>
@@ -82,10 +82,33 @@
                 </vdr-history-entry-detail>
             </ng-container>
             <ng-container *ngSwitchCase="type.ORDER_FULFILLMENT">
-                <div class="title">
-                    {{ 'order.history-fulfillment-created' | translate }}
-                </div>
-                {{ 'order.tracking-code' | translate }}: {{ getFulfillment(entry)?.trackingCode }}
+                {{ 'order.history-fulfillment-created' | translate }}
+                <vdr-history-entry-detail *ngIf="getFulfillment(entry) as fulfillment">
+                    <vdr-fulfillment-detail
+                        [fulfillmentId]="fulfillment.id"
+                        [order]="order"
+                    ></vdr-fulfillment-detail>
+                </vdr-history-entry-detail>
+            </ng-container>
+            <ng-container *ngSwitchCase="type.ORDER_FULFILLMENT_TRANSITION">
+                <ng-container *ngIf="entry.data.to === 'Delivered'">
+                    <div class="title">
+                        {{ 'order.history-fulfillment-delivered' | translate }}
+                    </div>
+                    {{ 'order.tracking-code' | translate }}: {{ getFulfillment(entry)?.trackingCode }}
+                </ng-container>
+                <ng-container *ngIf="entry.data.to === 'Shipped'">
+                    <div class="title">
+                        {{ 'order.history-fulfillment-shipped' | translate }}
+                    </div>
+                    {{ 'order.tracking-code' | translate }}: {{ getFulfillment(entry)?.trackingCode }}
+                </ng-container>
+                <ng-container *ngIf="entry.data.to !== 'Delivered' && entry.data.to !== 'Shipped'">
+                    {{
+                        'order.history-fulfillment-transition'
+                            | translate: { from: entry.data.from, to: entry.data.to }
+                    }}
+                </ng-container>
                 <vdr-history-entry-detail *ngIf="getFulfillment(entry) as fulfillment">
                     <vdr-fulfillment-detail
                         [fulfillmentId]="fulfillment.id"
@@ -149,5 +172,9 @@
             </ng-container>
         </ng-container>
     </vdr-timeline-entry>
-    <vdr-timeline-entry [isLast]="true"></vdr-timeline-entry>
+    <vdr-timeline-entry [isLast]="true" [createdAt]="order.createdAt" [featured]="true">
+        <div class="title">
+            {{ 'order.history-order-created' | translate }}
+        </div>
+    </vdr-timeline-entry>
 </div>

+ 19 - 4
packages/admin-ui/src/lib/order/src/components/order-history/order-history.component.ts

@@ -33,6 +33,11 @@ export class OrderHistoryComponent {
                 return 'error';
             }
         }
+        if (entry.type === HistoryEntryType.ORDER_FULFILLMENT_TRANSITION) {
+            if (entry.data.to === 'Delivered') {
+                return 'success';
+            }
+        }
         if (entry.type === HistoryEntryType.ORDER_PAYMENT_TRANSITION) {
             if (entry.data.to === 'Declined') {
                 return 'error';
@@ -62,8 +67,13 @@ export class OrderHistoryComponent {
         if (entry.type === HistoryEntryType.ORDER_NOTE) {
             return 'note';
         }
-        if (entry.type === HistoryEntryType.ORDER_FULFILLMENT) {
-            return 'truck';
+        if (entry.type === HistoryEntryType.ORDER_FULFILLMENT_TRANSITION) {
+            if (entry.data.to === 'Shipped') {
+                return 'truck';
+            }
+            if (entry.data.to === 'Delivered') {
+                return 'truck';
+            }
         }
     }
 
@@ -78,7 +88,8 @@ export class OrderHistoryComponent {
             }
             case HistoryEntryType.ORDER_PAYMENT_TRANSITION:
                 return entry.data.to === 'Settled';
-            case HistoryEntryType.ORDER_FULFILLMENT:
+            case HistoryEntryType.ORDER_FULFILLMENT_TRANSITION:
+                return entry.data.to === 'Delivered' || entry.data.to === 'Shipped';
             case HistoryEntryType.ORDER_NOTE:
                 return true;
             default:
@@ -87,7 +98,11 @@ export class OrderHistoryComponent {
     }
 
     getFulfillment(entry: GetOrderHistory.Items): OrderDetail.Fulfillments | undefined {
-        if (entry.type === HistoryEntryType.ORDER_FULFILLMENT && this.order.fulfillments) {
+        if (
+            (entry.type === HistoryEntryType.ORDER_FULFILLMENT ||
+                entry.type === HistoryEntryType.ORDER_FULFILLMENT_TRANSITION) &&
+            this.order.fulfillments
+        ) {
             return this.order.fulfillments.find(f => f.id === entry.data.fulfillmentId);
         }
     }

+ 1 - 2
packages/admin-ui/src/lib/order/src/components/order-payment-card/order-payment-card.component.ts

@@ -1,10 +1,9 @@
 import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
 import { CurrencyCode } from '@vendure/admin-ui/core';
-
 import { OrderDetail } from '@vendure/admin-ui/core';
 
 @Component({
-    selector: 'vdr-order-payment-detail',
+    selector: 'vdr-order-payment-card',
     templateUrl: './order-payment-card.component.html',
     styleUrls: ['./order-payment-card.component.scss'],
     changeDetection: ChangeDetectionStrategy.OnPush,

+ 4 - 0
packages/admin-ui/src/lib/order/src/order.module.ts

@@ -4,7 +4,9 @@ import { SharedModule } from '@vendure/admin-ui/core';
 
 import { CancelOrderDialogComponent } from './components/cancel-order-dialog/cancel-order-dialog.component';
 import { FulfillOrderDialogComponent } from './components/fulfill-order-dialog/fulfill-order-dialog.component';
+import { FulfillmentCardComponent } from './components/fulfillment-card/fulfillment-card.component';
 import { FulfillmentDetailComponent } from './components/fulfillment-detail/fulfillment-detail.component';
+import { FulfillmentStateLabelComponent } from './components/fulfillment-state-label/fulfillment-state-label.component';
 import { LineFulfillmentComponent } from './components/line-fulfillment/line-fulfillment.component';
 import { LineRefundsComponent } from './components/line-refunds/line-refunds.component';
 import { OrderCustomFieldsCardComponent } from './components/order-custom-fields-card/order-custom-fields-card.component';
@@ -47,6 +49,8 @@ import { orderRoutes } from './order.routes';
         OrderProcessNodeComponent,
         OrderProcessEdgeComponent,
         OrderProcessGraphDialogComponent,
+        FulfillmentStateLabelComponent,
+        FulfillmentCardComponent,
     ],
 })
 export class OrderModule {}

+ 10 - 2
packages/admin-ui/src/lib/static/i18n-messages/de.json

@@ -158,7 +158,6 @@
     "disabled": "Deaktiviert",
     "discard-changes": "Änderungen verwerfen",
     "display-custom-fields": "Benutzerdefinierte Felder anzeigen",
-    "done": "Fertig",
     "edit": "Bearbeiten",
     "edit-field": "Feld bearbeiten",
     "edit-note": "",
@@ -522,6 +521,7 @@
     "amount": "Betrag",
     "billing-address": "",
     "cancel": "Abbrechen",
+    "cancel-fulfillment": "",
     "cancel-order": "Bestellung stornieren",
     "cancel-reason-customer-request": "Kundenanfrage",
     "cancel-reason-not-available": "Nicht verfügbar",
@@ -539,8 +539,12 @@
     "history-coupon-code-applied": "Gutscheincode aktiviert",
     "history-coupon-code-removed": "Gutscheincode entfernt",
     "history-fulfillment-created": "Auftrag ausgeführt",
+    "history-fulfillment-delivered": "",
+    "history-fulfillment-shipped": "",
+    "history-fulfillment-transition": "",
     "history-items-cancelled": "{count} {count, plural, one {Artikel} other {Artikel}} gestrichen",
     "history-order-cancelled": "Bestellung storniert",
+    "history-order-created": "",
     "history-order-fulfilled": "Auftrag ausgeführt",
     "history-order-transition": "Auftragsstatus von {from} nach {to}",
     "history-payment-settled": "Bezahlt",
@@ -583,6 +587,7 @@
     "refunded-count": "{count} {count, plural, one {Artikel} other {Artikel}} erstattet",
     "return-to-stock": "Zum Lagerbestand hinzufügen",
     "search-by-order-code": "Suche nach Bestellcode",
+    "set-fulfillment-state": "",
     "settle-payment": "Zahlung durchführen",
     "settle-payment-error": "Die Zahlung konnte nicht durchgeführt werden",
     "settle-payment-success": "Zahlung erfolgreich durchgeführt",
@@ -599,9 +604,12 @@
     "state-cancelled": "Storniert",
     "state-delivered": "Ausgeführt",
     "state-partially-delivered": "Teilweise ausgeführt",
+    "state-partially-shipped": "",
     "state-payment-authorized": "Zahlung autorisiert",
     "state-payment-settled": "Bezahlt",
+    "state-shipped": "",
     "sub-total": "Zwischensumme",
+    "successfully-updated-fulfillment": "",
     "total": "Gesamtsumme",
     "tracking-code": "Sendungsverfolgungscode",
     "transaction-id": "Transaktions-ID",
@@ -691,4 +699,4 @@
     "job-result": "Job-Ergebnis",
     "job-state": "Job-Status"
   }
-}
+}

+ 12 - 4
packages/admin-ui/src/lib/static/i18n-messages/en.json

@@ -158,7 +158,6 @@
     "disabled": "Disabled",
     "discard-changes": "Discard changes",
     "display-custom-fields": "Display custom fields",
-    "done": "Done",
     "edit": "Edit",
     "edit-field": "Edit field",
     "edit-note": "Edit note",
@@ -522,7 +521,8 @@
     "amount": "Amount",
     "billing-address": "Billing address",
     "cancel": "Cancel",
-    "cancel-order": "Cancel Order",
+    "cancel-fulfillment": "Cancel fulfillment",
+    "cancel-order": "Cancel order",
     "cancel-reason-customer-request": "Customer request",
     "cancel-reason-not-available": "Not available",
     "cancel-selected-items": "Cancel selected items",
@@ -539,8 +539,12 @@
     "history-coupon-code-applied": "Coupon code applied",
     "history-coupon-code-removed": "Coupon code removed",
     "history-fulfillment-created": "Fulfillment created",
+    "history-fulfillment-delivered": "Fulfillment delivered",
+    "history-fulfillment-shipped": "Fulfillment shipped",
+    "history-fulfillment-transition": "Fulfillment transitioned from {from} to {to}",
     "history-items-cancelled": "{count} {count, plural, one {item} other {items}} cancelled",
     "history-order-cancelled": "Order cancelled",
+    "history-order-created": "Order created",
     "history-order-fulfilled": "Order fulfilled",
     "history-order-transition": "Order transitioned from {from} to {to}",
     "history-payment-settled": "Payment settled",
@@ -583,9 +587,10 @@
     "refunded-count": "{count} {count, plural, one {item} other {items}} refunded",
     "return-to-stock": "Return to stock",
     "search-by-order-code": "Search by order code",
+    "set-fulfillment-state": "Mark as {state}",
     "settle-payment": "Settle payment",
     "settle-payment-error": "Could not settle payment",
-    "settle-payment-success": "Sucessfully settled payment",
+    "settle-payment-success": "Successfully settled payment",
     "settle-refund": "Settle refund",
     "settle-refund-manual-instructions": "After manually refunding via your payment provider ({method}), enter the transaction ID here.",
     "settle-refund-success": "Successfully settled refund",
@@ -598,10 +603,13 @@
     "state-arranging-payment": "Arranging payment",
     "state-cancelled": "Cancelled",
     "state-delivered": "Delivered",
-    "state-partially-delivered": "Partially fulfilled",
+    "state-partially-delivered": "Partially delivered",
+    "state-partially-shipped": "Partially shipped",
     "state-payment-authorized": "Payment authorized",
     "state-payment-settled": "Payment settled",
+    "state-shipped": "Shipped",
     "sub-total": "Sub total",
+    "successfully-updated-fulfillment": "Successfully updated fulfillment",
     "total": "Total",
     "tracking-code": "Tracking code",
     "transaction-id": "Transaction ID",

+ 10 - 2
packages/admin-ui/src/lib/static/i18n-messages/es.json

@@ -158,7 +158,6 @@
     "disabled": "Deshabilitado",
     "discard-changes": "Descartar cambios",
     "display-custom-fields": "Mostrar campos personalizados",
-    "done": "Hecho",
     "edit": "Editar",
     "edit-field": "Editar campo",
     "edit-note": "Editar nota",
@@ -522,6 +521,7 @@
     "amount": "Precio",
     "billing-address": "",
     "cancel": "",
+    "cancel-fulfillment": "",
     "cancel-order": "",
     "cancel-reason-customer-request": "",
     "cancel-reason-not-available": "",
@@ -539,8 +539,12 @@
     "history-coupon-code-applied": "",
     "history-coupon-code-removed": "",
     "history-fulfillment-created": "",
+    "history-fulfillment-delivered": "",
+    "history-fulfillment-shipped": "",
+    "history-fulfillment-transition": "",
     "history-items-cancelled": "",
     "history-order-cancelled": "",
+    "history-order-created": "",
     "history-order-fulfilled": "",
     "history-order-transition": "",
     "history-payment-settled": "",
@@ -583,6 +587,7 @@
     "refunded-count": "",
     "return-to-stock": "",
     "search-by-order-code": "",
+    "set-fulfillment-state": "",
     "settle-payment": "",
     "settle-payment-error": "",
     "settle-payment-success": "",
@@ -599,9 +604,12 @@
     "state-cancelled": "",
     "state-delivered": "",
     "state-partially-delivered": "",
+    "state-partially-shipped": "",
     "state-payment-authorized": "",
     "state-payment-settled": "",
+    "state-shipped": "",
     "sub-total": "Sub total",
+    "successfully-updated-fulfillment": "",
     "total": "Total",
     "tracking-code": "",
     "transaction-id": "ID de transacción",
@@ -691,4 +699,4 @@
     "job-result": "Resultado",
     "job-state": "Estado"
   }
-}
+}

+ 10 - 3
packages/admin-ui/src/lib/static/i18n-messages/pl.json

@@ -158,7 +158,6 @@
     "disabled": "Wyłączony",
     "discard-changes": "Odrzuć zmiany",
     "display-custom-fields": "Wyświetl pola dodatkowe",
-    "done": "Skończone",
     "edit": "Edytuj",
     "edit-field": "Edytuj pole",
     "edit-note": "",
@@ -522,6 +521,7 @@
     "amount": "Ilość",
     "billing-address": "",
     "cancel": "Anuluj",
+    "cancel-fulfillment": "",
     "cancel-order": "Anuluj zamówienie",
     "cancel-reason-customer-request": "Prośba klienta",
     "cancel-reason-not-available": "Niedostępny",
@@ -538,9 +538,12 @@
     "fulfillment-method": "Metoda realizacji",
     "history-coupon-code-applied": "Użyto kodu rabatowego",
     "history-coupon-code-removed": "Usunięto kod rabatowy",
-    "history-fulfillment-created": "Utworzono wypełnienie",
+    "history-fulfillment-delivered": "",
+    "history-fulfillment-shipped": "",
+    "history-fulfillment-transition": "",
     "history-items-cancelled": "{count} {count, plural, one {element} other {elementów}} anulowano",
     "history-order-cancelled": "Zamówienie anulowane",
+    "history-order-created": "",
     "history-order-fulfilled": "Zamówienie zrealizowane",
     "history-order-transition": "Zamówienie wysłane z {from} do {to}",
     "history-payment-settled": "Opłacono",
@@ -583,6 +586,7 @@
     "refunded-count": "{count} {count, plural, one {zamówienie} other {zamówień}} zwrócono",
     "return-to-stock": "Zwróć do magazynu",
     "search-by-order-code": "Szukaj po numerze zamówienia",
+    "set-fulfillment-state": "",
     "settle-payment": "Rozlicz płatność",
     "settle-payment-error": "Nie można rozliczyć płatności",
     "settle-payment-success": "Płatność rozliczona pomyślnie",
@@ -599,9 +603,12 @@
     "state-cancelled": "Anulowano",
     "state-delivered": "Zrealizowano",
     "state-partially-delivered": "Częściowo zrealizowano",
+    "state-partially-shipped": "",
     "state-payment-authorized": "Płatność zaakceptowana",
     "state-payment-settled": "Płatność rozliczona",
+    "state-shipped": "",
     "sub-total": "Sub total",
+    "successfully-updated-fulfillment": "",
     "total": "Total",
     "tracking-code": "Numer przesyłki",
     "transaction-id": "Numer transakcji",
@@ -691,4 +698,4 @@
     "job-result": "Rezultat zlecenia",
     "job-state": "Status zlecenia"
   }
-}
+}

+ 10 - 3
packages/admin-ui/src/lib/static/i18n-messages/pt_BR.json

@@ -158,7 +158,6 @@
     "disabled": "Desabilitado",
     "discard-changes": "Descartar modificações",
     "display-custom-fields": "Exibir campos personalizados",
-    "done": "Pronto",
     "edit": "Editar",
     "edit-field": "Editar campo",
     "edit-note": "Editar nota",
@@ -522,6 +521,7 @@
     "amount": "Total",
     "billing-address": "Endereço de cobrança",
     "cancel": "Cancelar",
+    "cancel-fulfillment": "",
     "cancel-order": "Cancelar Pedido",
     "cancel-reason-customer-request": "Pedido do cliente",
     "cancel-reason-not-available": "Não disponível",
@@ -538,9 +538,12 @@
     "fulfillment-method": "Método de execução",
     "history-coupon-code-applied": "Código de cupom aplicado",
     "history-coupon-code-removed": "Código de cupom excluído",
-    "history-fulfillment-created": "Execução criada",
+    "history-fulfillment-delivered": "",
+    "history-fulfillment-shipped": "",
+    "history-fulfillment-transition": "",
     "history-items-cancelled": "{count} {count, plural, one {item} other {items}} cancelado",
     "history-order-cancelled": "Pedido cancelado",
+    "history-order-created": "",
     "history-order-fulfilled": "Pedido realizado",
     "history-order-transition": "Pedido transferido de {from} para {to}",
     "history-payment-settled": "Pagamento concluído",
@@ -583,6 +586,7 @@
     "refunded-count": "{count} {count, plural, one {item} other {items}} reembolsado",
     "return-to-stock": "Retorno ao estoque",
     "search-by-order-code": "Buscar por código do pedido",
+    "set-fulfillment-state": "",
     "settle-payment": "Liquidar pagamento",
     "settle-payment-error": "Não posso liquidar pagamento",
     "settle-payment-success": "Pagamento liquidado com sucesso",
@@ -599,9 +603,12 @@
     "state-cancelled": "Cancelado",
     "state-delivered": "Realizado",
     "state-partially-delivered": "Parcialmente realizado",
+    "state-partially-shipped": "",
     "state-payment-authorized": "Pagamento autorizado",
     "state-payment-settled": "Pagamento liquidado",
+    "state-shipped": "",
     "sub-total": "Subtotal",
+    "successfully-updated-fulfillment": "",
     "total": "Total",
     "tracking-code": "Código de rastreio",
     "transaction-id": "Código ID da transação",
@@ -691,4 +698,4 @@
     "job-result": "Resultado do trabalho",
     "job-state": "Estado do trabalho"
   }
-}
+}

+ 10 - 3
packages/admin-ui/src/lib/static/i18n-messages/zh_Hans.json

@@ -158,7 +158,6 @@
     "disabled": "禁用",
     "discard-changes": "放弃修改",
     "display-custom-fields": "显示客户化字段",
-    "done": "完成",
     "edit": "编辑",
     "edit-field": "编辑域",
     "edit-note": "",
@@ -522,6 +521,7 @@
     "amount": "金额",
     "billing-address": "",
     "cancel": "取消",
+    "cancel-fulfillment": "",
     "cancel-order": "取消订单",
     "cancel-reason-customer-request": "客户要求",
     "cancel-reason-not-available": "产品无库存",
@@ -538,9 +538,12 @@
     "fulfillment-method": "配货方式",
     "history-coupon-code-applied": "优惠卷已使用",
     "history-coupon-code-removed": "优惠卷已移除",
-    "history-fulfillment-created": "配货记录创建成功",
+    "history-fulfillment-delivered": "",
+    "history-fulfillment-shipped": "",
+    "history-fulfillment-transition": "",
     "history-items-cancelled": "{count}个已取消",
     "history-order-cancelled": "订单已取消",
+    "history-order-created": "",
     "history-order-fulfilled": "订单已配货",
     "history-order-transition": "订单状态从{from}更新至{to}",
     "history-payment-settled": "已结算付款",
@@ -583,6 +586,7 @@
     "refunded-count": "{count}个商品已退款",
     "return-to-stock": "返回商品至库存",
     "search-by-order-code": "输入要搜索的订单编号",
+    "set-fulfillment-state": "",
     "settle-payment": "结算付款",
     "settle-payment-error": "结算付款失败",
     "settle-payment-success": "结算付款成功",
@@ -599,9 +603,12 @@
     "state-cancelled": "已取消",
     "state-delivered": "已完成",
     "state-partially-delivered": "部分配货",
+    "state-partially-shipped": "",
     "state-payment-authorized": "已授权支付",
     "state-payment-settled": "已结算",
+    "state-shipped": "",
     "sub-total": "小计金额",
+    "successfully-updated-fulfillment": "",
     "total": "总计金额",
     "tracking-code": "物流码",
     "transaction-id": "交易ID",
@@ -691,4 +698,4 @@
     "job-result": "",
     "job-state": ""
   }
-}
+}

+ 10 - 3
packages/admin-ui/src/lib/static/i18n-messages/zh_Hant.json

@@ -158,7 +158,6 @@
     "disabled": "禁用",
     "discard-changes": "放弃修改",
     "display-custom-fields": "顯示客戶自訂欄位",
-    "done": "完成",
     "edit": "編辑",
     "edit-field": "編辑域",
     "edit-note": "",
@@ -522,6 +521,7 @@
     "amount": "金額",
     "billing-address": "",
     "cancel": "取消",
+    "cancel-fulfillment": "",
     "cancel-order": "取消訂單",
     "cancel-reason-customer-request": "客户要求",
     "cancel-reason-not-available": "產品無庫存",
@@ -538,9 +538,12 @@
     "fulfillment-method": "配貨方式",
     "history-coupon-code-applied": "優惠卷已使用",
     "history-coupon-code-removed": "優惠卷已移除",
-    "history-fulfillment-created": "配貨記錄建立成功",
+    "history-fulfillment-delivered": "",
+    "history-fulfillment-shipped": "",
+    "history-fulfillment-transition": "",
     "history-items-cancelled": "{count}個已取消",
     "history-order-cancelled": "訂單已取消",
+    "history-order-created": "",
     "history-order-fulfilled": "訂單已配貨",
     "history-order-transition": "訂單狀態從{from}更新至{to}",
     "history-payment-settled": "已結算付款",
@@ -583,6 +586,7 @@
     "refunded-count": "{count}個商品已退款",
     "return-to-stock": "返回商品至庫存",
     "search-by-order-code": "輸入要搜索的訂單編號",
+    "set-fulfillment-state": "",
     "settle-payment": "結算付款",
     "settle-payment-error": "結算付款失敗",
     "settle-payment-success": "結算付款成功",
@@ -599,9 +603,12 @@
     "state-cancelled": "已取消",
     "state-delivered": "已完成",
     "state-partially-delivered": "部分配貨",
+    "state-partially-shipped": "",
     "state-payment-authorized": "已授權支付",
     "state-payment-settled": "已結算",
+    "state-shipped": "",
     "sub-total": "小計金額",
+    "successfully-updated-fulfillment": "",
     "total": "總計金額",
     "tracking-code": "物流碼",
     "transaction-id": "交易編號",
@@ -691,4 +698,4 @@
     "job-result": "",
     "job-state": ""
   }
-}
+}

+ 1 - 1
packages/dev-server/dev-config.ts

@@ -120,7 +120,7 @@ function getDbConfig(): ConnectionOptions {
         default:
             console.log('Using mysql connection');
             return {
-                synchronize: false,
+                synchronize: true,
                 type: 'mysql',
                 host: '127.0.0.1',
                 port: 3306,