Ver Fonte

chore(admin-ui): Update order detail components

Michael Bromley há 2 anos atrás
pai
commit
fbc579c7d8
26 ficheiros alterados com 861 adições e 619 exclusões
  1. 1 0
      packages/admin-ui/src/lib/core/src/shared/components/data-table-2/data-table-column.component.ts
  2. 2 2
      packages/admin-ui/src/lib/core/src/shared/components/data-table-2/data-table-custom-field-column.component.ts
  3. 11 42
      packages/admin-ui/src/lib/core/src/shared/components/data-table-2/data-table2.component.scss
  4. 1 1
      packages/admin-ui/src/lib/core/src/shared/components/data-table-2/data-table2.component.ts
  5. 1 1
      packages/admin-ui/src/lib/core/src/shared/components/data-table-column-picker/data-table-column-picker.component.html
  6. 1 1
      packages/admin-ui/src/lib/core/src/shared/components/history-entry-detail/history-entry-detail.component.html
  7. 4 0
      packages/admin-ui/src/lib/core/src/shared/components/history-entry-detail/history-entry-detail.component.scss
  8. 4 4
      packages/admin-ui/src/lib/core/src/shared/components/timeline-entry/timeline-entry.component.scss
  9. 3 0
      packages/admin-ui/src/lib/order/src/components/fulfill-order-dialog/fulfill-order-dialog.component.scss
  10. 0 1
      packages/admin-ui/src/lib/order/src/components/fulfillment-card/fulfillment-card.component.html
  11. 3 3
      packages/admin-ui/src/lib/order/src/components/line-fulfillment/line-fulfillment.component.scss
  12. 185 0
      packages/admin-ui/src/lib/order/src/components/order-data-table/order-data-table.component.html
  13. 4 0
      packages/admin-ui/src/lib/order/src/components/order-data-table/order-data-table.component.scss
  14. 59 0
      packages/admin-ui/src/lib/order/src/components/order-data-table/order-data-table.component.ts
  15. 11 0
      packages/admin-ui/src/lib/order/src/components/order-data-table/order-total-column.component.ts
  16. 17 13
      packages/admin-ui/src/lib/order/src/components/order-detail/order-detail.component.html
  17. 15 17
      packages/admin-ui/src/lib/order/src/components/order-detail/order-detail.component.ts
  18. 377 345
      packages/admin-ui/src/lib/order/src/components/order-editor/order-editor.component.html
  19. 4 4
      packages/admin-ui/src/lib/order/src/components/order-history/order-history.component.scss
  20. 0 1
      packages/admin-ui/src/lib/order/src/components/order-payment-card/order-payment-card.component.html
  21. 1 0
      packages/admin-ui/src/lib/order/src/components/order-table/order-table-mixin.scss
  22. 91 150
      packages/admin-ui/src/lib/order/src/components/order-table/order-table.component.html
  23. 1 1
      packages/admin-ui/src/lib/order/src/components/order-table/order-table.component.scss
  24. 4 0
      packages/admin-ui/src/lib/order/src/order.module.ts
  25. 47 23
      packages/admin-ui/src/lib/static/styles/_mixins.scss
  26. 14 10
      packages/admin-ui/src/lib/static/styles/global/_overrides.scss

+ 1 - 0
packages/admin-ui/src/lib/core/src/shared/components/data-table-2/data-table-column.component.ts

@@ -17,6 +17,7 @@ export class DataTable2ColumnComponent<T> implements OnInit {
     @Input() sort?: DataTableSort<any>;
     @Input() sort?: DataTableSort<any>;
     @Input() optional = true;
     @Input() optional = true;
     @Input() hiddenByDefault = false;
     @Input() hiddenByDefault = false;
+    @Input() orderable = true;
     #visible = true;
     #visible = true;
     #onColumnChangeFns: Array<() => void> = [];
     #onColumnChangeFns: Array<() => void> = [];
     get id(): string {
     get id(): string {

+ 2 - 2
packages/admin-ui/src/lib/core/src/shared/components/data-table-2/data-table-custom-field-column.component.ts

@@ -14,7 +14,7 @@ import { DataTable2ColumnComponent } from './data-table-column.component';
 })
 })
 export class DataTableCustomFieldColumnComponent<T> extends DataTable2ColumnComponent<T> implements OnInit {
 export class DataTableCustomFieldColumnComponent<T> extends DataTable2ColumnComponent<T> implements OnInit {
     @Input() customField: CustomFieldConfig;
     @Input() customField: CustomFieldConfig;
-    @Input() sorts: DataTableSortCollection<any, any[]>;
+    @Input() sorts?: DataTableSortCollection<any, any[]>;
     @ViewChild(TemplateRef, { static: false }) template: TemplateRef<any>;
     @ViewChild(TemplateRef, { static: false }) template: TemplateRef<any>;
     protected uiLanguage$: Observable<LanguageCode>;
     protected uiLanguage$: Observable<LanguageCode>;
     constructor(protected dataService: DataService) {
     constructor(protected dataService: DataService) {
@@ -37,7 +37,7 @@ export class DataTableCustomFieldColumnComponent<T> extends DataTable2ColumnComp
                     : this.customField.name;
                     : this.customField.name;
         });
         });
         this.hiddenByDefault = true;
         this.hiddenByDefault = true;
-        this.sort = this.sorts.get(this.customField.name);
+        this.sort = this.sorts?.get(this.customField.name);
         super.ngOnInit();
         super.ngOnInit();
     }
     }
 }
 }

+ 11 - 42
packages/admin-ui/src/lib/core/src/shared/components/data-table-2/data-table2.component.scss

@@ -1,4 +1,5 @@
 @import 'variables';
 @import 'variables';
+@import "mixins";
 
 
 :host {
 :host {
     display: block;
     display: block;
@@ -8,6 +9,8 @@
     container-type: inline-size;
     container-type: inline-size;
 }
 }
 
 
+@include table-base-styles;
+
 .bulk-actions {
 .bulk-actions {
     margin-left: calc(var(--space-unit) * 5);
     margin-left: calc(var(--space-unit) * 5);
     @media screen and (min-width: $breakpoint-medium) {
     @media screen and (min-width: $breakpoint-medium) {
@@ -41,14 +44,14 @@ table.no-select {
     width: 24px;
     width: 24px;
 }
 }
 
 
-.heading-row th {
-    border-bottom: 1px solid var(--color-weight-200);
-    font-size: var(--font-size-xs);
-    font-weight: 600;
-    text-transform: uppercase;
-    position: relative;
-    white-space: nowrap;
-}
+//.heading-row th {
+//    border-bottom: 1px solid var(--color-weight-200);
+//    font-size: var(--font-size-xs);
+//    font-weight: 600;
+//    text-transform: uppercase;
+//    position: relative;
+//    white-space: nowrap;
+//}
 
 
 .sort-toggle {
 .sort-toggle {
     display: flex;
     display: flex;
@@ -126,40 +129,6 @@ th.filter-row {
     }
     }
 }
 }
 
 
-th,
-td {
-    padding: calc(var(--space-unit) * 1.5) calc(var(--space-unit) * 1);
-    font-size: var(--font-size-sm);
-}
-
-tr td:first-of-type,
-tr th:first-of-type {
-    text-align: center;
-    @media screen and (min-width: $breakpoint-medium) {
-        padding-left: var(--surface-margin-left);
-        text-align: left;
-    }
-}
-
-th:last-of-type,
-td:last-of-type {
-    border-right: 1px solid var(--color-weight-200);
-}
-
-tr:first-of-type th:last-of-type {
-    border-image: linear-gradient(0deg, var(--color-weight-200), transparent) 1;
-}
-tr:last-of-type td:last-of-type {
-    border-image: linear-gradient(180deg, var(--color-weight-200), transparent) 1;
-}
-
-tbody td {
-    border-bottom: 1px solid var(--color-table-row-separator);
-}
-
-tbody tr:hover {
-    background-color: var(--color-table-row-hover-bg);
-}
 
 
 .cell-link {
 .cell-link {
     display: block;
     display: block;

+ 1 - 1
packages/admin-ui/src/lib/core/src/shared/components/data-table-2/data-table2.component.ts

@@ -274,7 +274,7 @@ export class DataTable2Component<T> implements AfterContentInit, OnChanges, OnIn
         this.selectionManager?.toggleSelection(item, event);
         this.selectionManager?.toggleSelection(item, event);
     }
     }
 
 
-    private getDataTableConfig(): DataTableConfig {
+    protected getDataTableConfig(): DataTableConfig {
         const dataTableConfig = this.localStorageService.get('dataTableConfig') ?? {};
         const dataTableConfig = this.localStorageService.get('dataTableConfig') ?? {};
         if (!dataTableConfig[this.id]) {
         if (!dataTableConfig[this.id]) {
             dataTableConfig[this.id] = { visibility: [], order: {}, showSearchFilterRow: false };
             dataTableConfig[this.id] = { visibility: [], order: {}, showSearchFilterRow: false };

+ 1 - 1
packages/admin-ui/src/lib/core/src/shared/components/data-table-column-picker/data-table-column-picker.component.html

@@ -11,7 +11,7 @@
                 cdkDragLockAxis="y"
                 cdkDragLockAxis="y"
                 [cdkDragData]="column"
                 [cdkDragData]="column"
             >
             >
-                <div cdkDragHandle class="drag-handle">
+                <div cdkDragHandle class="drag-handle" [cdkDragHandleDisabled]="column.orderable === false">
                     <clr-icon shape="drag-handle"></clr-icon>
                     <clr-icon shape="drag-handle"></clr-icon>
                 </div>
                 </div>
                 <label class="flex">
                 <label class="flex">

+ 1 - 1
packages/admin-ui/src/lib/core/src/shared/components/history-entry-detail/history-entry-detail.component.html

@@ -1,5 +1,5 @@
 <vdr-dropdown>
 <vdr-dropdown>
-    <button class="btn btn-link btn-sm details-button" vdrDropdownTrigger>
+    <button class="button-small" vdrDropdownTrigger>
         <clr-icon shape="details" size="12"></clr-icon>
         <clr-icon shape="details" size="12"></clr-icon>
         {{ 'common.details' | translate }}
         {{ 'common.details' | translate }}
     </button>
     </button>

+ 4 - 0
packages/admin-ui/src/lib/core/src/shared/components/history-entry-detail/history-entry-detail.component.scss

@@ -1,3 +1,7 @@
+:host {
+    display: inline-block;
+}
+
 .entry-dropdown {
 .entry-dropdown {
     margin: 0 12px;
     margin: 0 12px;
 }
 }

+ 4 - 4
packages/admin-ui/src/lib/core/src/shared/components/timeline-entry/timeline-entry.component.scss

@@ -53,7 +53,7 @@
         align-items: center;
         align-items: center;
         justify-content: center;
         justify-content: center;
         border-radius: 50%;
         border-radius: 50%;
-        color: var(--color-primary-600);
+        color: var(--color-primary-700);
         background-color: var(--color-component-bg-100);
         background-color: var(--color-component-bg-100);
         border: 1px solid var(--color-component-border-200);
         border: 1px solid var(--color-component-border-200);
         display: none;
         display: none;
@@ -117,7 +117,7 @@
 
 
 .success {
 .success {
     .timeline, .timeline .custom-icon {
     .timeline, .timeline .custom-icon {
-        color: var(--color-success-400);
+        color: var(--color-success-700);
     }
     }
     .timeline:after {
     .timeline:after {
         background-color: var(--color-success-200);
         background-color: var(--color-success-200);
@@ -127,7 +127,7 @@
 
 
 .error {
 .error {
     .timeline, .timeline .custom-icon {
     .timeline, .timeline .custom-icon {
-        color: var(--color-error-400);
+        color: var(--color-error-700);
     }
     }
     .timeline:after {
     .timeline:after {
         background-color: var(--color-error-200);
         background-color: var(--color-error-200);
@@ -137,7 +137,7 @@
 
 
 .warning {
 .warning {
     .timeline, .timeline .custom-icon {
     .timeline, .timeline .custom-icon {
-        color: var(--color-warning-400);
+        color: var(--color-warning-700);
     }
     }
     .timeline:after {
     .timeline:after {
         background-color: var(--color-warning-200);
         background-color: var(--color-warning-200);

+ 3 - 0
packages/admin-ui/src/lib/order/src/components/fulfill-order-dialog/fulfill-order-dialog.component.scss

@@ -1,9 +1,12 @@
 @import "variables";
 @import "variables";
+@import "../order-table/order-table-mixin";
+
 
 
 :host {
 :host {
     height: 100%;
     height: 100%;
     display: flex;
     display: flex;
     min-height: 64vh;
     min-height: 64vh;
+    @include order-table;
 }
 }
 .fulfillment-wrapper {
 .fulfillment-wrapper {
     flex: 1;
     flex: 1;

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

@@ -26,7 +26,6 @@
                 <ng-container *ngFor="let nextState of nextOtherStates()">
                 <ng-container *ngFor="let nextState of nextOtherStates()">
                     <button
                     <button
                         type="button"
                         type="button"
-                        class="btn"
                         vdrDropdownItem
                         vdrDropdownItem
                         (click)="transitionState.emit(nextState)"
                         (click)="transitionState.emit(nextState)"
                     >
                     >

+ 3 - 3
packages/admin-ui/src/lib/order/src/components/line-fulfillment/line-fulfillment.component.scss

@@ -1,12 +1,12 @@
 
 
 .item-fulfilled {
 .item-fulfilled {
-    color: var(--color-success-500);
+    color: var(--color-success-700);
 }
 }
 .item-partially-fulfilled {
 .item-partially-fulfilled {
-    color: var(--color-warning-500);
+    color: var(--color-warning-700);
 }
 }
 .item-not-fulfilled {
 .item-not-fulfilled {
-    color: var(--color-error-500);
+    color: var(--color-error-700);
 }
 }
 
 
 .fulfillment-detail {
 .fulfillment-detail {

+ 185 - 0
packages/admin-ui/src/lib/order/src/components/order-data-table/order-data-table.component.html

@@ -0,0 +1,185 @@
+<div class="bulk-actions">
+    <ng-content select="vdr-bulk-action-menu"></ng-content>
+</div>
+<div class="table-wrapper">
+    <table
+        class=""
+        [class.no-select]="disableSelect"
+        [style.table-layout]="!items?.length ? 'fixed' : 'inherit'"
+    >
+        <thead [class.items-selected]="selectionManager?.selection.length">
+            <tr class="heading-row">
+                <th *ngIf="selectionManager" class="selection-col">
+                    <input
+                        type="checkbox"
+                        clrCheckbox
+                        [checked]="selectionManager?.areAllCurrentItemsSelected()"
+                        (change)="onToggleAllClick()"
+                    />
+                </th>
+                <th
+                    *ngFor="let column of visibleSortedColumns; last as isLast"
+                    [class.expand]="column.expand"
+                >
+                    <div class="cell-content" [ngClass]="column.align">
+                        <span>{{ column.heading }}</span>
+                        <div *ngIf="column.sort as sort" class="sort-toggle">
+                            <button (click)="sort.toggleSortOrder()" [class.active]="sort.sortOrder">
+                                <clr-icon *ngIf="!sort.sortOrder" shape="two-way-arrows left"></clr-icon>
+                                <clr-icon *ngIf="sort.sortOrder === 'ASC'" shape="arrow up"></clr-icon>
+                                <clr-icon *ngIf="sort.sortOrder === 'DESC'" shape="arrow down"></clr-icon>
+                            </button>
+                            <div class="sort-label" *ngIf="sort.sortOrder">{{ sort.sortOrder }}</div>
+                        </div>
+                    </div>
+                </th>
+                <th>
+                    <div class="column-picker">
+                        <vdr-data-table-colum-picker
+                            [uiLanguage]="uiLanguage$ | async"
+                            [columns]="sortedColumns"
+                            (reorder)="onColumnReorder($event)"
+                            (resetColumns)="onColumnsReset()"
+                        ></vdr-data-table-colum-picker>
+                    </div>
+                </th>
+            </tr>
+            <tr *ngIf="searchComponent || customSearchTemplate || filters?.length">
+                <th
+                    [attr.colspan]="visibleSortedColumns.length + (selectionManager ? 2 : 1)"
+                    class="filter-row"
+                    [class.active]="showSearchFilterRow"
+                >
+                    <button
+                        class="button-ghost toggle-search-filter-row"
+                        [class.active]="showSearchFilterRow"
+                        (click)="toggleSearchFilterRow()"
+                        [title]="'common.search-and-filter-list' | translate"
+                    >
+                        <clr-icon shape="search"></clr-icon>
+                    </button>
+                    <div class="filter-row-wrapper" [class.hidden]="!showSearchFilterRow">
+                        <ng-container *ngTemplateOutlet="searchComponent?.template"></ng-container>
+                        <ng-container *ngTemplateOutlet="customSearchTemplate"></ng-container>
+                        <ng-container *ngIf="filters">
+                            <div class="filters">
+                                <vdr-data-table-filters
+                                    *ngFor="let activeFilter of filters.activeFilters"
+                                    [filterWithValue]="activeFilter"
+                                    [filters]="filters"
+                                    class="mt-1"
+                                ></vdr-data-table-filters>
+                                <vdr-data-table-filters
+                                    *ngIf="filters.length"
+                                    [filters]="filters"
+                                    class="mt-1"
+                                ></vdr-data-table-filters>
+                            </div>
+                        </ng-container>
+                    </div>
+                </th>
+            </tr>
+        </thead>
+        <tbody>
+            <tr
+                *ngFor="
+                    let item of items
+                        | paginate
+                            : {
+                                  id: id,
+                                  itemsPerPage: itemsPerPage,
+                                  currentPage: currentPage,
+                                  totalItems: totalItems
+                              };
+                    index as i;
+                    trackBy: trackByFn
+                "
+            >
+                <td *ngIf="selectionManager" class="selection-col" [class.active]="activeIndex === i">
+                    <input
+                        type="checkbox"
+                        clrCheckbox
+                        [checked]="selectionManager?.isSelected(item)"
+                        (click)="onRowClick(item, $event)"
+                    />
+                </td>
+                <td *ngFor="let column of visibleSortedColumns" [class.active]="activeIndex === i">
+                    <div class="cell-content" [ngClass]="column.align">
+                        <ng-container
+                            *ngTemplateOutlet="column.template; context: { item: item, index: i }"
+                        ></ng-container>
+                    </div>
+                </td>
+                <td [class.active]="activeIndex === i"><!-- column select --></td>
+            </tr>
+            <tr class="surcharge" *ngFor="let surcharge of order.surcharges">
+                <td class="align-middle name left" colspan="2">{{ surcharge.description }}</td>
+                <td class="align-middle sku">{{ surcharge.sku }}</td>
+                <td class="align-middle" colspan="2"></td>
+                <td class="align-middle total">
+                    {{ surcharge.priceWithTax | localeCurrency : order.currencyCode }}
+                    <div class="net-price" [title]="'order.net-price' | translate">
+                        {{ surcharge.price | localeCurrency : order.currencyCode }}
+                    </div>
+                </td>
+            </tr>
+            <ng-container *ngFor="let discount of order.discounts">
+                <tr class="order-adjustment" *ngIf="discount.type !== 'OTHER'">
+                    <td  [attr.colspan]="visibleSortedColumns.length" class="">
+                        <a [routerLink]="getPromotionLink(discount)">{{ discount.description }}</a>
+                        <vdr-chip *ngIf="getCouponCodeForAdjustment(order, discount) as couponCode">{{
+                            couponCode
+                        }}</vdr-chip>
+                    </td>
+                    <td class="">
+                        {{ discount.amountWithTax | localeCurrency : order.currencyCode }}
+                        <div class="net-price" [title]="'order.net-price' | translate">
+                            {{ discount.amount | localeCurrency : order.currencyCode }}
+                        </div>
+                    </td>
+                    <td><!-- column select --></td>
+                </tr>
+            </ng-container>
+            <tr class="sub-total">
+                <td class="">{{ 'order.sub-total' | translate }}</td>
+                <td [attr.colspan]="visibleSortedColumns.length - 2"></td>
+                <td class="clr-align-middle">
+                    {{ order.subTotalWithTax | localeCurrency : order.currencyCode }}
+                    <div class="net-price" [title]="'order.net-price' | translate">
+                        {{ order.subTotal | localeCurrency : order.currencyCode }}
+                    </div>
+                </td>
+                <td><!-- column select --></td>
+            </tr>
+            <tr class="shipping">
+                <td class="">{{ 'order.shipping' | translate }}</td>
+                <td [attr.colspan]="visibleSortedColumns.length - 2">{{ getShippingNames(order) }}</td>
+                <td class="clr-align-middle">
+                    {{ order.shippingWithTax | localeCurrency : order.currencyCode }}
+                    <div class="net-price" [title]="'order.net-price' | translate">
+                        {{ order.shipping | localeCurrency : order.currencyCode }}
+                    </div>
+                </td>
+                <td><!-- column select --></td>
+            </tr>
+            <tr class="total">
+                <td class="">{{ 'order.total' | translate }}</td>
+                <td [attr.colspan]="visibleSortedColumns.length - 2"></td>
+                <td class="clr-align-middle">
+                    {{ order.totalWithTax | localeCurrency : order.currencyCode }}
+                    <div class="net-price" [title]="'order.net-price' | translate">
+                        {{ order.total | localeCurrency : order.currencyCode }}
+                    </div>
+                </td>
+                <td><!-- column select --></td>
+            </tr>
+            <ng-container>
+                <tr *ngIf="!items?.length">
+                    <td [attr.colspan]="visibleSortedColumns.length + (selectionManager ? 2 : 1)">
+                        <vdr-empty-placeholder [emptyStateLabel]="emptyStateLabel"></vdr-empty-placeholder>
+                    </td>
+                </tr>
+            </ng-container>
+        </tbody>
+    </table>
+</div>

+ 4 - 0
packages/admin-ui/src/lib/order/src/components/order-data-table/order-data-table.component.scss

@@ -0,0 +1,4 @@
+@import "../order-table/order-table-mixin";
+:host {
+    @include order-table;
+}

+ 59 - 0
packages/admin-ui/src/lib/order/src/components/order-data-table/order-data-table.component.ts

@@ -0,0 +1,59 @@
+import { ChangeDetectionStrategy, Component, ContentChildren, Input, QueryList } from '@angular/core';
+import { DataTable2ColumnComponent, DataTable2Component, OrderDetailFragment } from '@vendure/admin-ui/core';
+import { OrderTotalColumnComponent } from './order-total-column.component';
+
+@Component({
+    selector: 'vdr-order-data-table',
+    templateUrl: './order-data-table.component.html',
+    styleUrls: [
+        '../../../../core/src/shared/components/data-table-2/data-table2.component.scss',
+        './order-data-table.component.scss',
+    ],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class OrderDataTableComponent extends DataTable2Component<OrderDetailFragment> {
+    @ContentChildren(OrderTotalColumnComponent) totalColumns: QueryList<OrderTotalColumnComponent<any>>;
+    @Input() order: OrderDetailFragment;
+
+    get allColumns() {
+        return [...(this.columns ?? []), ...(this.customFieldColumns ?? []), ...(this.totalColumns ?? [])];
+    }
+
+    get sortedColumns() {
+        const columns = this.allColumns;
+        const dataTableConfig = this.getDataTableConfig();
+        for (const [id, index] of Object.entries(dataTableConfig[this.id].order)) {
+            const column = columns.find(c => c.id === id);
+            const currentIndex = columns.findIndex(c => c.id === id);
+            if (currentIndex !== -1 && column) {
+                columns.splice(currentIndex, 1);
+                columns.splice(index, 0, column);
+            }
+        }
+        return columns;
+    }
+
+    getPromotionLink(promotion: OrderDetailFragment['discounts'][number]): any[] {
+        const id = promotion.adjustmentSource.split(':')[1];
+        return ['/marketing', 'promotions', id];
+    }
+
+    getCouponCodeForAdjustment(
+        order: OrderDetailFragment,
+        promotionAdjustment: OrderDetailFragment['discounts'][number],
+    ): string | undefined {
+        const id = promotionAdjustment.adjustmentSource.split(':')[1];
+        const promotion = order.promotions.find(p => p.id === id);
+        if (promotion) {
+            return promotion.couponCode || undefined;
+        }
+    }
+
+    getShippingNames(order: OrderDetailFragment) {
+        if (order.shippingLines.length) {
+            return order.shippingLines.map(shippingLine => shippingLine.shippingMethod.name).join(', ');
+        } else {
+            return '';
+        }
+    }
+}

+ 11 - 0
packages/admin-ui/src/lib/order/src/components/order-data-table/order-total-column.component.ts

@@ -0,0 +1,11 @@
+import { Component } from '@angular/core';
+import { DataTable2ColumnComponent } from '@vendure/admin-ui/core';
+
+@Component({
+    selector: 'vdr-order-total-column',
+    template: ``,
+    exportAs: 'row',
+})
+export class OrderTotalColumnComponent<T> extends DataTable2ColumnComponent<T> {
+    orderable = false;
+}

+ 17 - 13
packages/admin-ui/src/lib/order/src/components/order-detail/order-detail.component.html

@@ -45,7 +45,7 @@
                     <ng-container
                     <ng-container
                         *ngIf="order.type !== 'Aggregate' && order.nextStates.includes('Modifying')"
                         *ngIf="order.type !== 'Aggregate' && order.nextStates.includes('Modifying')"
                     >
                     >
-                        <button type="button" class="btn" vdrDropdownItem (click)="transitionToModifying()">
+                        <button type="button" vdrDropdownItem (click)="transitionToModifying()">
                             <clr-icon shape="pencil"></clr-icon>
                             <clr-icon shape="pencil"></clr-icon>
                             {{ 'order.modify-order' | translate }}
                             {{ 'order.modify-order' | translate }}
                         </button>
                         </button>
@@ -53,7 +53,6 @@
                     </ng-container>
                     </ng-container>
                     <button
                     <button
                         type="button"
                         type="button"
-                        class="btn"
                         vdrDropdownItem
                         vdrDropdownItem
                         *ngIf="order.type !== 'Aggregate' && order.nextStates.includes('Cancelled')"
                         *ngIf="order.type !== 'Aggregate' && order.nextStates.includes('Cancelled')"
                         (click)="cancelOrRefund(order)"
                         (click)="cancelOrRefund(order)"
@@ -72,7 +71,6 @@
                         <button
                         <button
                             *ngFor="let nextState of nextStates$ | async"
                             *ngFor="let nextState of nextStates$ | async"
                             type="button"
                             type="button"
-                            class="btn"
                             vdrDropdownItem
                             vdrDropdownItem
                             (click)="transitionToState(nextState)"
                             (click)="transitionToState(nextState)"
                         >
                         >
@@ -84,12 +82,7 @@
                         </button>
                         </button>
                     </ng-container>
                     </ng-container>
                     <div class="dropdown-divider"></div>
                     <div class="dropdown-divider"></div>
-                    <button
-                        type="button"
-                        class="btn"
-                        vdrDropdownItem
-                        (click)="manuallyTransitionToState(order)"
-                    >
+                    <button type="button" vdrDropdownItem (click)="manuallyTransitionToState(order)">
                         <clr-icon shape="step-forward-2" class="is-warning"></clr-icon>
                         <clr-icon shape="step-forward-2" class="is-warning"></clr-icon>
                         {{ 'order.manually-transition-to-state' | translate }}
                         {{ 'order.manually-transition-to-state' | translate }}
                     </button>
                     </button>
@@ -155,14 +148,14 @@
             *ngIf="order.sellerOrders.length"
             *ngIf="order.sellerOrders.length"
             [orderId]="order.id"
             [orderId]="order.id"
         ></vdr-seller-orders-card>
         ></vdr-seller-orders-card>
-        <vdr-card [paddingX]="true">
+        <vdr-card [paddingX]="false">
             <vdr-order-table
             <vdr-order-table
                 class="card-span"
                 class="card-span"
                 [order]="order"
                 [order]="order"
                 [orderLineCustomFields]="orderLineCustomFields"
                 [orderLineCustomFields]="orderLineCustomFields"
             ></vdr-order-table>
             ></vdr-order-table>
         </vdr-card>
         </vdr-card>
-        <vdr-card [title]="'order.tax-summary' | translate" [paddingX]="true">
+        <vdr-card [title]="'order.tax-summary' | translate" [paddingX]="false">
             <table class="table card-span">
             <table class="table card-span">
                 <thead>
                 <thead>
                     <tr>
                     <tr>
@@ -184,11 +177,22 @@
         </vdr-card>
         </vdr-card>
         <vdr-card [title]="'common.custom-fields' | translate" *ngIf="customFields.length">
         <vdr-card [title]="'common.custom-fields' | translate" *ngIf="customFields.length">
             <vdr-tabbed-custom-fields
             <vdr-tabbed-custom-fields
-                entityName="Facet"
+                entityName="Order"
                 [customFields]="customFields"
                 [customFields]="customFields"
-                [customFieldsFormGroup]="detailForm.get(['facet', 'customFields'])"
+                [customFieldsFormGroup]="detailForm.get(['customFields'])"
                 [readonly]="!('UpdateOrder' | hasPermission)"
                 [readonly]="!('UpdateOrder' | hasPermission)"
             />
             />
+            <div class="card-span">
+                <button
+                    class="button primary"
+                    (click)="updateCustomFields()"
+                    [disabled]="
+                        detailForm.get('customFields')?.pristine || detailForm.get('customFields')?.invalid
+                    "
+                >
+                    {{ 'common.update' | translate }}
+                </button>
+            </div>
         </vdr-card>
         </vdr-card>
         <vdr-custom-detail-component-host
         <vdr-custom-detail-component-host
             locationId="order-detail"
             locationId="order-detail"

+ 15 - 17
packages/admin-ui/src/lib/order/src/components/order-detail/order-detail.component.ts

@@ -1,35 +1,28 @@
 import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
 import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
-import { UntypedFormGroup } from '@angular/forms';
+import { FormBuilder, FormGroup, UntypedFormGroup } from '@angular/forms';
 import { ActivatedRoute, Router } from '@angular/router';
 import { ActivatedRoute, Router } from '@angular/router';
 import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
 import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
 import {
 import {
-    BaseDetailComponent,
-    CancelOrderMutation,
-    CustomFieldConfig,
     DataService,
     DataService,
     EditNoteDialogComponent,
     EditNoteDialogComponent,
     FulfillmentFragment,
     FulfillmentFragment,
     GetOrderHistoryQuery,
     GetOrderHistoryQuery,
     GetOrderQuery,
     GetOrderQuery,
-    HistoryEntryType,
     ModalService,
     ModalService,
     NotificationService,
     NotificationService,
     Order,
     Order,
     ORDER_DETAIL_FRAGMENT,
     ORDER_DETAIL_FRAGMENT,
     OrderDetailFragment,
     OrderDetailFragment,
     OrderDetailQueryDocument,
     OrderDetailQueryDocument,
-    OrderLineFragment,
     Refund,
     Refund,
-    RefundOrderMutation,
     ServerConfigService,
     ServerConfigService,
     SortOrder,
     SortOrder,
     TimelineHistoryEntry,
     TimelineHistoryEntry,
     TypedBaseDetailComponent,
     TypedBaseDetailComponent,
 } from '@vendure/admin-ui/core';
 } from '@vendure/admin-ui/core';
-import { pick } from '@vendure/common/lib/pick';
 import { assertNever, summate } from '@vendure/common/lib/shared-utils';
 import { assertNever, summate } from '@vendure/common/lib/shared-utils';
 import { gql } from 'apollo-angular';
 import { gql } from 'apollo-angular';
-import { EMPTY, merge, Observable, of, Subject } from 'rxjs';
+import { EMPTY, Observable, of, Subject } from 'rxjs';
 import { map, mapTo, startWith, switchMap, take } from 'rxjs/operators';
 import { map, mapTo, startWith, switchMap, take } from 'rxjs/operators';
 
 
 import { OrderTransitionService } from '../../providers/order-transition.service';
 import { OrderTransitionService } from '../../providers/order-transition.service';
@@ -61,12 +54,16 @@ export class OrderDetailComponent
     extends TypedBaseDetailComponent<typeof OrderDetailQueryDocument, 'order'>
     extends TypedBaseDetailComponent<typeof OrderDetailQueryDocument, 'order'>
     implements OnInit, OnDestroy
     implements OnInit, OnDestroy
 {
 {
-    detailForm = new UntypedFormGroup({});
+    customFields = this.getCustomFieldConfig('Order');
+    orderLineCustomFields = this.getCustomFieldConfig('OrderLine');
+    detailForm = new FormGroup({
+        customFields: this.formBuilder.group(
+            this.customFields.reduce((hash, field) => ({ ...hash, [field.name]: '' }), {}),
+        ),
+    });
     history$: Observable<NonNullable<GetOrderHistoryQuery['order']>['history']['items'] | undefined>;
     history$: Observable<NonNullable<GetOrderHistoryQuery['order']>['history']['items'] | undefined>;
     nextStates$: Observable<string[]>;
     nextStates$: Observable<string[]>;
     fetchHistory = new Subject<void>();
     fetchHistory = new Subject<void>();
-    customFields: CustomFieldConfig[];
-    orderLineCustomFields: CustomFieldConfig[];
     private readonly defaultStates = [
     private readonly defaultStates = [
         'AddingItems',
         'AddingItems',
         'ArrangingPayment',
         'ArrangingPayment',
@@ -90,6 +87,7 @@ export class OrderDetailComponent
         private notificationService: NotificationService,
         private notificationService: NotificationService,
         private modalService: ModalService,
         private modalService: ModalService,
         private orderTransitionService: OrderTransitionService,
         private orderTransitionService: OrderTransitionService,
+        private formBuilder: FormBuilder,
     ) {
     ) {
         super(route, router, serverConfigService, dataService);
         super(route, router, serverConfigService, dataService);
     }
     }
@@ -101,8 +99,6 @@ export class OrderDetailComponent
                 this.router.navigate(['./', 'modify'], { relativeTo: this.route });
                 this.router.navigate(['./', 'modify'], { relativeTo: this.route });
             }
             }
         });
         });
-        this.customFields = this.getCustomFieldConfig('Order');
-        this.orderLineCustomFields = this.getCustomFieldConfig('OrderLine');
         this.history$ = this.fetchHistory.pipe(
         this.history$ = this.fetchHistory.pipe(
             startWith(null),
             startWith(null),
             switchMap(() =>
             switchMap(() =>
@@ -184,11 +180,11 @@ export class OrderDetailComponent
             });
             });
     }
     }
 
 
-    updateCustomFields(customFieldsValue: any) {
+    updateCustomFields() {
         this.dataService.order
         this.dataService.order
             .updateOrderCustomFields({
             .updateOrderCustomFields({
                 id: this.id,
                 id: this.id,
-                customFields: customFieldsValue,
+                customFields: this.detailForm.value.customFields,
             })
             })
             .subscribe(() => {
             .subscribe(() => {
                 this.notificationService.success(_('common.notify-update-success'), { entity: 'Order' });
                 this.notificationService.success(_('common.notify-update-success'), { entity: 'Order' });
@@ -643,6 +639,8 @@ export class OrderDetailComponent
     }
     }
 
 
     protected setFormValues(entity: OrderDetailFragment): void {
     protected setFormValues(entity: OrderDetailFragment): void {
-        // empty
+        if (this.customFields.length) {
+            this.setCustomFieldFormValues(this.customFields, this.detailForm.get(['customFields']), entity);
+        }
     }
     }
 }
 }

+ 377 - 345
packages/admin-ui/src/lib/order/src/components/order-editor/order-editor.component.html

@@ -1,365 +1,397 @@
-<vdr-action-bar *ngIf="entity$ | async as order">
-    <vdr-ab-left>
-        <div class="flex clr-align-items-center">
-            <vdr-entity-info [entity]="entity$ | async"></vdr-entity-info>
-            <vdr-order-state-label [state]="order.state"></vdr-order-state-label>
-        </div>
-    </vdr-ab-left>
+<vdr-page-block>
+    <vdr-action-bar *ngIf="entity$ | async as order">
+        <vdr-ab-left>
+            <div class="flex clr-align-items-center">
+                <vdr-order-state-label [state]="order.state"></vdr-order-state-label>
+            </div>
+        </vdr-ab-left>
 
 
-    <vdr-ab-right>
-        <button class="btn btn-secondary" (click)="transitionToPriorState(order)">
-            {{ 'order.cancel-modification' | translate }}
-        </button>
-    </vdr-ab-right>
-</vdr-action-bar>
+        <vdr-ab-right>
+            <button class="btn btn-secondary" (click)="transitionToPriorState(order)">
+                {{ 'order.cancel-modification' | translate }}
+            </button>
+        </vdr-ab-right>
+    </vdr-action-bar>
+</vdr-page-block>
 
 
-<div *ngIf="entity$ | async as order">
-    <div class="clr-row">
-        <div class="clr-col-lg-8">
-            <table class="order-table 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 *ngIf="orderLineCustomFields.length">{{ 'common.custom-fields' | translate }}</th>
-                        <th>{{ 'order.total' | translate }}</th>
-                    </tr>
-                </thead>
-                <tbody>
-                    <tr
-                        *ngFor="let line of order.lines; let i = index"
-                        class="order-line"
-                        [class.is-cancelled]="line.quantity === 0"
-                        [class.modified]="isLineModified(line)"
-                    >
-                        <td class="align-middle thumb">
-                            <img
-                                *ngIf="line.featuredAsset"
-                                [src]="line.featuredAsset | assetPreview: 'tiny'"
-                            />
-                        </td>
-                        <td class="align-middle name">{{ line.productVariant.name }}</td>
-                        <td class="align-middle sku">{{ line.productVariant.sku }}</td>
-                        <td class="align-middle unit-price">
-                            {{ line.unitPriceWithTax | localeCurrency: order.currencyCode }}
-                            <div class="net-price" [title]="'order.net-price' | translate">
-                                {{ line.unitPrice | localeCurrency: order.currencyCode }}
-                            </div>
-                        </td>
-                        <td class="align-middle quantity">
-                            <input
-                                type="number"
-                                min="0"
-                                [value]="line.quantity"
-                                (input)="updateLineQuantity(line, $event.target.value)"
-                            />
-                            <vdr-line-refunds [line]="line" [payments]="order.payments"></vdr-line-refunds>
-                            <vdr-line-fulfillment
-                                [line]="line"
-                                [allOrderFulfillments]="order.fulfillments"
-                                [orderState]="order.state"
-                            ></vdr-line-fulfillment>
-                        </td>
-                        <td *ngIf="orderLineCustomFields.length" class="order-line-custom-field align-middle">
-                            <vdr-tabbed-custom-fields
-                                entityName="OrderLine"
-                                [customFields]="orderLineCustomFields"
-                                [customFieldsFormGroup]="orderLineCustomFieldsFormArray.get([i])"
-                                [compact]="true"
-                            ></vdr-tabbed-custom-fields>
-                        </td>
-                        <td class="align-middle total">
-                            {{ line.linePriceWithTax | localeCurrency: order.currencyCode }}
-                            <div class="net-price" [title]="'order.net-price' | translate">
-                                {{ line.linePrice | localeCurrency: order.currencyCode }}
-                            </div>
-                        </td>
-                    </tr>
-                    <tr
-                        *ngFor="let addedLine of addedLines; trackBy: trackByProductVariantId; let i = index"
-                        class="modified"
-                    >
-                        <td class="align-middle thumb">
-                            <img
-                                *ngIf="addedLine.productAsset"
-                                [src]="addedLine.productAsset | assetPreview: 'tiny'"
-                            />
-                        </td>
-                        <td class="align-middle name">{{ addedLine.productVariantName }}</td>
-                        <td class="align-middle sku">{{ addedLine.sku }}</td>
-                        <td class="align-middle unit-price">
-                            {{ addedLine.priceWithTax | localeCurrency: order.currencyCode }}
-                            <div class="net-price" [title]="'order.net-price' | translate">
-                                {{ addedLine.price | localeCurrency: order.currencyCode }}
-                            </div>
-                        </td>
-                        <td class="align-middle quantity">
-                            <input
-                                type="number"
-                                min="0"
-                                [value]="addedLine.quantity"
-                                (input)="updateAddedItemQuantity(addedLine, $event.target.value)"
-                            />
-                            <button class="icon-button" (click)="removeAddedItem(i)">
-                                <clr-icon shape="trash"></clr-icon>
-                            </button>
-                        </td>
-                        <td *ngIf="orderLineCustomFields.length" class="order-line-custom-field align-middle">
-                            <ng-container *ngFor="let customField of orderLineCustomFields">
-                                <vdr-custom-field-control
-                                    [customField]="customField"
-                                    [customFieldsFormGroup]="addItemCustomFieldsFormArray.get([i])"
+<vdr-page-block>
+    <div *ngIf="entity$ | async as order">
+        <div class="clr-row">
+            <div class="clr-col-lg-8">
+                <table class="order-table 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 *ngIf="orderLineCustomFields.length">
+                                {{ 'common.custom-fields' | translate }}
+                            </th>
+                            <th>{{ 'order.total' | translate }}</th>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        <tr
+                            *ngFor="let line of order.lines; let i = index"
+                            class="order-line"
+                            [class.is-cancelled]="line.quantity === 0"
+                            [class.modified]="isLineModified(line)"
+                        >
+                            <td class="align-middle thumb">
+                                <img
+                                    *ngIf="line.featuredAsset"
+                                    [src]="line.featuredAsset | assetPreview : 'tiny'"
+                                />
+                            </td>
+                            <td class="align-middle name">{{ line.productVariant.name }}</td>
+                            <td class="align-middle sku">{{ line.productVariant.sku }}</td>
+                            <td class="align-middle unit-price">
+                                {{ line.unitPriceWithTax | localeCurrency : order.currencyCode }}
+                                <div class="net-price" [title]="'order.net-price' | translate">
+                                    {{ line.unitPrice | localeCurrency : order.currencyCode }}
+                                </div>
+                            </td>
+                            <td class="align-middle quantity">
+                                <input
+                                    type="number"
+                                    min="0"
+                                    [value]="line.quantity"
+                                    (input)="updateLineQuantity(line, $event.target.value)"
+                                />
+                                <vdr-line-refunds
+                                    [line]="line"
+                                    [payments]="order.payments"
+                                ></vdr-line-refunds>
+                                <vdr-line-fulfillment
+                                    [line]="line"
+                                    [allOrderFulfillments]="order.fulfillments"
+                                    [orderState]="order.state"
+                                ></vdr-line-fulfillment>
+                            </td>
+                            <td
+                                *ngIf="orderLineCustomFields.length"
+                                class="order-line-custom-field align-middle"
+                            >
+                                <vdr-tabbed-custom-fields
                                     entityName="OrderLine"
                                     entityName="OrderLine"
+                                    [customFields]="orderLineCustomFields"
+                                    [customFieldsFormGroup]="orderLineCustomFieldsFormArray.get([i])"
                                     [compact]="true"
                                     [compact]="true"
-                                ></vdr-custom-field-control>
-                            </ng-container>
-                        </td>
-                        <td class="align-middle total">
-                            {{
-                                (addedLine.priceWithTax * addedLine.quantity) / 100
-                                    | currency: order.currencyCode
-                            }}
-                            <div class="net-price" [title]="'order.net-price' | translate">
+                                ></vdr-tabbed-custom-fields>
+                            </td>
+                            <td class="align-middle total">
+                                {{ line.linePriceWithTax | localeCurrency : order.currencyCode }}
+                                <div class="net-price" [title]="'order.net-price' | translate">
+                                    {{ line.linePrice | localeCurrency : order.currencyCode }}
+                                </div>
+                            </td>
+                        </tr>
+                        <tr
+                            *ngFor="
+                                let addedLine of addedLines;
+                                trackBy: trackByProductVariantId;
+                                let i = index
+                            "
+                            class="modified"
+                        >
+                            <td class="align-middle thumb">
+                                <img
+                                    *ngIf="addedLine.productAsset"
+                                    [src]="addedLine.productAsset | assetPreview : 'tiny'"
+                                />
+                            </td>
+                            <td class="align-middle name">{{ addedLine.productVariantName }}</td>
+                            <td class="align-middle sku">{{ addedLine.sku }}</td>
+                            <td class="align-middle unit-price">
+                                {{ addedLine.priceWithTax | localeCurrency : order.currencyCode }}
+                                <div class="net-price" [title]="'order.net-price' | translate">
+                                    {{ addedLine.price | localeCurrency : order.currencyCode }}
+                                </div>
+                            </td>
+                            <td class="align-middle quantity">
+                                <input
+                                    type="number"
+                                    min="0"
+                                    [value]="addedLine.quantity"
+                                    (input)="updateAddedItemQuantity(addedLine, $event.target.value)"
+                                />
+                                <button class="icon-button" (click)="removeAddedItem(i)">
+                                    <clr-icon shape="trash"></clr-icon>
+                                </button>
+                            </td>
+                            <td
+                                *ngIf="orderLineCustomFields.length"
+                                class="order-line-custom-field align-middle"
+                            >
+                                <ng-container *ngFor="let customField of orderLineCustomFields">
+                                    <vdr-custom-field-control
+                                        [customField]="customField"
+                                        [customFieldsFormGroup]="addItemCustomFieldsFormArray.get([i])"
+                                        entityName="OrderLine"
+                                        [compact]="true"
+                                    ></vdr-custom-field-control>
+                                </ng-container>
+                            </td>
+                            <td class="align-middle total">
                                 {{
                                 {{
-                                    (addedLine.price * addedLine.quantity) / 100
-                                        | currency: order.currencyCode
+                                    (addedLine.priceWithTax * addedLine.quantity) / 100
+                                        | currency : order.currencyCode
                                 }}
                                 }}
-                            </div>
-                        </td>
-                    </tr>
-                    <tr class="surcharge" *ngFor="let surcharge of order.surcharges">
-                        <td class="align-middle name left" colspan="2">{{ surcharge.description }}</td>
-                        <td class="align-middle sku">{{ surcharge.sku }}</td>
-                        <td class="align-middle"></td>
-                        <td></td>
-                        <td *ngIf="orderLineCustomFields.length"></td>
-                        <td class="align-middle total">
-                            {{ surcharge.priceWithTax | localeCurrency: order.currencyCode }}
-                            <div class="net-price" [title]="'order.net-price' | translate">
-                                {{ surcharge.price | localeCurrency: order.currencyCode }}
-                            </div>
-                        </td>
-                    </tr>
-                    <tr
-                        class="surcharge modified"
-                        *ngFor="let surcharge of modifyOrderInput.surcharges; let i = index"
-                    >
-                        <td class="align-middle name left" colspan="2">
-                            {{ surcharge.description }}
-                            <button class="icon-button" (click)="removeSurcharge(i)">
-                                <clr-icon shape="trash"></clr-icon>
-                            </button>
-                        </td>
-                        <td class="align-middle sku">{{ surcharge.sku }}</td>
-                        <td class="align-middle"></td>
-                        <td></td>
-                        <td *ngIf="orderLineCustomFields.length"></td>
-                        <td class="align-middle total">
-                            <ng-container *ngIf="getSurchargePrices(surcharge) as surchargePrice">
-                                {{ surchargePrice.priceWithTax | localeCurrency: order.currencyCode }}
                                 <div class="net-price" [title]="'order.net-price' | translate">
                                 <div class="net-price" [title]="'order.net-price' | translate">
-                                    {{ surchargePrice.price | localeCurrency: order.currencyCode }}
-                                </div>
-                            </ng-container>
-                        </td>
-                    </tr>
-                    <tr class="shipping">
-                        <td class="left clr-align-middle">{{ 'order.shipping' | translate }}</td>
-                        <td class="clr-align-middle">{{ order.shippingLines[0]?.shippingMethod?.name }}</td>
-                        <td colspan="3"></td>
-                        <td *ngIf="orderLineCustomFields.length"></td>
-                        <td class="clr-align-middle">
-                            {{ order.shippingWithTax | localeCurrency: order.currencyCode }}
-                            <div class="net-price" [title]="'order.net-price' | translate">
-                                {{ order.shipping | localeCurrency: order.currencyCode }}
-                            </div>
-                        </td>
-                    </tr>
-                </tbody>
-            </table>
-
-            <h4 class="mb-2">{{ 'order.modifications' | translate }}</h4>
-            <clr-accordion>
-                <clr-accordion-panel>
-                    <clr-accordion-title>{{ 'order.add-item-to-order' | translate }}</clr-accordion-title>
-                    <clr-accordion-content *clrIfExpanded>
-                        <vdr-product-variant-selector class="mb-4" (productSelected)="addItemSelectedVariant = $event">
-                        </vdr-product-variant-selector>
-                        <div *ngIf="addItemSelectedVariant" class="flex mb-4">
-                            <img
-                                *ngIf="addItemSelectedVariant.productAsset as asset"
-                                [src]="asset | assetPreview: 'tiny'"
-                                class="mr4"
-                            />
-                            <div>
-                                <strong class="mr4">{{ addItemSelectedVariant.productVariantName }}</strong>
-                                <small>{{ addItemSelectedVariant.sku }}</small>
-                                <div>
                                     {{
                                     {{
-                                        getSelectedItemPrice(addItemSelectedVariant)
-                                            | localeCurrency: order.currencyCode
+                                        (addedLine.price * addedLine.quantity) / 100
+                                            | currency : order.currencyCode
                                     }}
                                     }}
                                 </div>
                                 </div>
-                            </div>
-                        </div>
-                        <ng-container *ngFor="let customField of orderLineCustomFields">
-                            <vdr-custom-field-control
-                                [readonly]="!addItemSelectedVariant"
-                                [customField]="customField"
-                                [customFieldsFormGroup]="addItemCustomFieldsForm"
-                                entityName="OrderLine"
-                                [compact]="true"
-                            ></vdr-custom-field-control>
-                        </ng-container>
-                        <button
-                            class="btn btn-secondary"
-                            [disabled]="!addItemSelectedVariant || addItemCustomFieldsForm.invalid"
-                            (click)="addItemToOrder(addItemSelectedVariant)"
+                            </td>
+                        </tr>
+                        <tr class="surcharge" *ngFor="let surcharge of order.surcharges">
+                            <td class="align-middle name left" colspan="2">{{ surcharge.description }}</td>
+                            <td class="align-middle sku">{{ surcharge.sku }}</td>
+                            <td class="align-middle"></td>
+                            <td></td>
+                            <td *ngIf="orderLineCustomFields.length"></td>
+                            <td class="align-middle total">
+                                {{ surcharge.priceWithTax | localeCurrency : order.currencyCode }}
+                                <div class="net-price" [title]="'order.net-price' | translate">
+                                    {{ surcharge.price | localeCurrency : order.currencyCode }}
+                                </div>
+                            </td>
+                        </tr>
+                        <tr
+                            class="surcharge modified"
+                            *ngFor="let surcharge of modifyOrderInput.surcharges; let i = index"
                         >
                         >
-                            {{ 'order.add-item-to-order' | translate }}
-                        </button>
-                    </clr-accordion-content>
-                </clr-accordion-panel>
-                <clr-accordion-panel>
-                    <clr-accordion-title>{{ 'order.set-coupon-codes' | translate }}</clr-accordion-title>
-                    <clr-accordion-content *clrIfExpanded>
-                        <vdr-coupon-code-selector
-                            [control]="couponCodesControl"
-                        ></vdr-coupon-code-selector>
-                    </clr-accordion-content>
-                </clr-accordion-panel>
+                            <td class="align-middle name left" colspan="2">
+                                {{ surcharge.description }}
+                                <button class="icon-button" (click)="removeSurcharge(i)">
+                                    <clr-icon shape="trash"></clr-icon>
+                                </button>
+                            </td>
+                            <td class="align-middle sku">{{ surcharge.sku }}</td>
+                            <td class="align-middle"></td>
+                            <td></td>
+                            <td *ngIf="orderLineCustomFields.length"></td>
+                            <td class="align-middle total">
+                                <ng-container *ngIf="getSurchargePrices(surcharge) as surchargePrice">
+                                    {{ surchargePrice.priceWithTax | localeCurrency : order.currencyCode }}
+                                    <div class="net-price" [title]="'order.net-price' | translate">
+                                        {{ surchargePrice.price | localeCurrency : order.currencyCode }}
+                                    </div>
+                                </ng-container>
+                            </td>
+                        </tr>
+                        <tr class="shipping">
+                            <td class="left clr-align-middle">{{ 'order.shipping' | translate }}</td>
+                            <td class="clr-align-middle">
+                                {{ order.shippingLines[0]?.shippingMethod?.name }}
+                            </td>
+                            <td colspan="3"></td>
+                            <td *ngIf="orderLineCustomFields.length"></td>
+                            <td class="clr-align-middle">
+                                {{ order.shippingWithTax | localeCurrency : order.currencyCode }}
+                                <div class="net-price" [title]="'order.net-price' | translate">
+                                    {{ order.shipping | localeCurrency : order.currencyCode }}
+                                </div>
+                            </td>
+                        </tr>
+                    </tbody>
+                </table>
 
 
-                <clr-accordion-panel>
-                    <clr-accordion-title>{{ 'order.add-surcharge' | translate }}</clr-accordion-title>
-                    <clr-accordion-content *clrIfExpanded>
-                        <form [formGroup]="surchargeForm" (submit)="addSurcharge(surchargeForm.value)">
-                            <vdr-form-field [label]="'common.description' | translate" for="description"
-                                ><input id="description" type="text" formControlName="description"
-                            /></vdr-form-field>
-                            <vdr-form-field [label]="'order.product-sku' | translate" for="sku"
-                                ><input id="sku" type="text" formControlName="sku"
-                            /></vdr-form-field>
-                            <vdr-form-field [label]="'common.price' | translate" for="price">
-                                <vdr-currency-input
-                                    [currencyCode]="order.currencyCode"
-                                    id="price"
-                                    formControlName="price"
-                                ></vdr-currency-input>
-                            </vdr-form-field>
-                            <vdr-form-field
-                                [label]="
-                                    'catalog.price-includes-tax-at'
-                                        | translate: { rate: surchargeForm.get('taxRate')?.value }
-                                "
-                                for="priceIncludesTax"
-                                ><input
-                                    id="priceIncludesTax"
-                                    type="checkbox"
-                                    clrCheckbox
-                                    formControlName="priceIncludesTax"
-                            /></vdr-form-field>
-                            <vdr-form-field [label]="'order.tax-rate' | translate" for="taxRate">
-                                <vdr-affixed-input suffix="%"
-                                    ><input
-                                        id="taxRate"
-                                        type="number"
-                                        min="0"
-                                        max="100"
-                                        formControlName="taxRate"
-                                /></vdr-affixed-input>
-                            </vdr-form-field>
-                            <vdr-form-field [label]="'order.tax-description' | translate" for="taxDescription"
-                                ><input id="taxDescription" type="text" formControlName="taxDescription"
-                            /></vdr-form-field>
+                <h4 class="mb-2">{{ 'order.modifications' | translate }}</h4>
+                <clr-accordion>
+                    <clr-accordion-panel>
+                        <clr-accordion-title>{{ 'order.add-item-to-order' | translate }}</clr-accordion-title>
+                        <clr-accordion-content *clrIfExpanded>
+                            <vdr-product-variant-selector
+                                class="mb-4"
+                                (productSelected)="addItemSelectedVariant = $event"
+                            >
+                            </vdr-product-variant-selector>
+                            <div *ngIf="addItemSelectedVariant" class="flex mb-4">
+                                <img
+                                    *ngIf="addItemSelectedVariant.productAsset as asset"
+                                    [src]="asset | assetPreview : 'tiny'"
+                                    class="mr4"
+                                />
+                                <div>
+                                    <strong class="mr4">{{
+                                        addItemSelectedVariant.productVariantName
+                                    }}</strong>
+                                    <small>{{ addItemSelectedVariant.sku }}</small>
+                                    <div>
+                                        {{
+                                            getSelectedItemPrice(addItemSelectedVariant)
+                                                | localeCurrency : order.currencyCode
+                                        }}
+                                    </div>
+                                </div>
+                            </div>
+                            <ng-container *ngFor="let customField of orderLineCustomFields">
+                                <vdr-custom-field-control
+                                    [readonly]="!addItemSelectedVariant"
+                                    [customField]="customField"
+                                    [customFieldsFormGroup]="addItemCustomFieldsForm"
+                                    entityName="OrderLine"
+                                    [compact]="true"
+                                ></vdr-custom-field-control>
+                            </ng-container>
                             <button
                             <button
                                 class="btn btn-secondary"
                                 class="btn btn-secondary"
-                                [disabled]="
-                                    surchargeForm.invalid ||
-                                    surchargeForm.pristine ||
-                                    surchargeForm.get('price')?.value === 0
-                                "
+                                [disabled]="!addItemSelectedVariant || addItemCustomFieldsForm.invalid"
+                                (click)="addItemToOrder(addItemSelectedVariant)"
                             >
                             >
-                                {{ 'order.add-surcharge' | translate }}
+                                {{ 'order.add-item-to-order' | translate }}
                             </button>
                             </button>
-                        </form>
-                    </clr-accordion-content>
-                </clr-accordion-panel>
-                <clr-accordion-panel>
-                    <clr-accordion-title>{{ 'order.edit-shipping-address' | translate }}</clr-accordion-title>
-                    <clr-accordion-content *clrIfExpanded>
-                        <vdr-address-form
-                            [formGroup]="shippingAddressForm"
-                            [availableCountries]="availableCountries$ | async"
-                            [customFields]="addressCustomFields"
-                        ></vdr-address-form>
-                    </clr-accordion-content>
-                </clr-accordion-panel>
-                <clr-accordion-panel>
-                    <clr-accordion-title>{{ 'order.edit-billing-address' | translate }}</clr-accordion-title>
-                    <clr-accordion-content *clrIfExpanded>
-                        <vdr-address-form
-                            [formGroup]="billingAddressForm"
-                            [availableCountries]="availableCountries$ | async"
-                            [customFields]="addressCustomFields"
-                        ></vdr-address-form>
-                    </clr-accordion-content>
-                </clr-accordion-panel>
-            </clr-accordion>
-        </div>
-        <div class="clr-col-lg-4 order-cards">
-            <div class="card">
-                <div class="card-header">
-                    {{ 'order.modification-summary' | translate }}
-                </div>
-                <div class="card-block">
-                    <ul>
-                        <li *ngIf="modifyOrderInput.addItems?.length">
-                            {{
-                                'order.modification-adding-items'
-                                    | translate: { count: modifyOrderInput.addItems?.length }
-                            }}
-                        </li>
-                        <li *ngIf="modifyOrderInput.adjustOrderLines?.length">
-                            {{
-                                'order.modification-adjusting-lines'
-                                    | translate: { count: modifyOrderInput.adjustOrderLines?.length }
-                            }}
-                        </li>
-                        <li *ngIf="modifyOrderInput.surcharges?.length">
-                            {{
-                                'order.modification-adding-surcharges'
-                                    | translate: { count: modifyOrderInput.surcharges?.length }
-                            }}
-                        </li>
-                        <li *ngIf="shippingAddressForm.dirty">
-                            {{ 'order.modification-updating-shipping-address' | translate }}
-                        </li>
-                        <li *ngIf="billingAddressForm.dirty">
-                            {{ 'order.modification-updating-billing-address' | translate }}
-                        </li>
-                    </ul>
-                </div>
-                <div class="card-block">
-                    <label class="clr-control-label">{{ 'order.note' | translate }}</label>
-                    <textarea [(ngModel)]="note" name="note" clrTextarea required></textarea>
-                    <clr-checkbox-wrapper class="">
-                        <input type="checkbox" clrCheckbox [(ngModel)]="recalculateShipping" />
-                        <label>{{ 'order.modification-recalculate-shipping' | translate }}</label>
-                    </clr-checkbox-wrapper>
-                </div>
-                <div class="card-footer">
-                    <button
-                        class="btn btn-primary"
-                        [disabled]="!canPreviewChanges()"
-                        (click)="previewAndModify(order)"
-                    >
-                        {{ 'order.preview-changes' | translate }}
-                    </button>
+                        </clr-accordion-content>
+                    </clr-accordion-panel>
+                    <clr-accordion-panel>
+                        <clr-accordion-title>{{ 'order.set-coupon-codes' | translate }}</clr-accordion-title>
+                        <clr-accordion-content *clrIfExpanded>
+                            <vdr-coupon-code-selector
+                                [control]="couponCodesControl"
+                            ></vdr-coupon-code-selector>
+                        </clr-accordion-content>
+                    </clr-accordion-panel>
+
+                    <clr-accordion-panel>
+                        <clr-accordion-title>{{ 'order.add-surcharge' | translate }}</clr-accordion-title>
+                        <clr-accordion-content *clrIfExpanded>
+                            <form [formGroup]="surchargeForm" (submit)="addSurcharge(surchargeForm.value)">
+                                <vdr-form-field [label]="'common.description' | translate" for="description"
+                                    ><input id="description" type="text" formControlName="description"
+                                /></vdr-form-field>
+                                <vdr-form-field [label]="'order.product-sku' | translate" for="sku"
+                                    ><input id="sku" type="text" formControlName="sku"
+                                /></vdr-form-field>
+                                <vdr-form-field [label]="'common.price' | translate" for="price">
+                                    <vdr-currency-input
+                                        [currencyCode]="order.currencyCode"
+                                        id="price"
+                                        formControlName="price"
+                                    ></vdr-currency-input>
+                                </vdr-form-field>
+                                <vdr-form-field
+                                    [label]="
+                                        'catalog.price-includes-tax-at'
+                                            | translate : { rate: surchargeForm.get('taxRate')?.value }
+                                    "
+                                    for="priceIncludesTax"
+                                    ><input
+                                        id="priceIncludesTax"
+                                        type="checkbox"
+                                        clrCheckbox
+                                        formControlName="priceIncludesTax"
+                                /></vdr-form-field>
+                                <vdr-form-field [label]="'order.tax-rate' | translate" for="taxRate">
+                                    <vdr-affixed-input suffix="%"
+                                        ><input
+                                            id="taxRate"
+                                            type="number"
+                                            min="0"
+                                            max="100"
+                                            formControlName="taxRate"
+                                    /></vdr-affixed-input>
+                                </vdr-form-field>
+                                <vdr-form-field
+                                    [label]="'order.tax-description' | translate"
+                                    for="taxDescription"
+                                    ><input id="taxDescription" type="text" formControlName="taxDescription"
+                                /></vdr-form-field>
+                                <button
+                                    class="btn btn-secondary"
+                                    [disabled]="
+                                        surchargeForm.invalid ||
+                                        surchargeForm.pristine ||
+                                        surchargeForm.get('price')?.value === 0
+                                    "
+                                >
+                                    {{ 'order.add-surcharge' | translate }}
+                                </button>
+                            </form>
+                        </clr-accordion-content>
+                    </clr-accordion-panel>
+                    <clr-accordion-panel>
+                        <clr-accordion-title>{{
+                            'order.edit-shipping-address' | translate
+                        }}</clr-accordion-title>
+                        <clr-accordion-content *clrIfExpanded>
+                            <vdr-address-form
+                                [formGroup]="shippingAddressForm"
+                                [availableCountries]="availableCountries$ | async"
+                                [customFields]="addressCustomFields"
+                            ></vdr-address-form>
+                        </clr-accordion-content>
+                    </clr-accordion-panel>
+                    <clr-accordion-panel>
+                        <clr-accordion-title>{{
+                            'order.edit-billing-address' | translate
+                        }}</clr-accordion-title>
+                        <clr-accordion-content *clrIfExpanded>
+                            <vdr-address-form
+                                [formGroup]="billingAddressForm"
+                                [availableCountries]="availableCountries$ | async"
+                                [customFields]="addressCustomFields"
+                            ></vdr-address-form>
+                        </clr-accordion-content>
+                    </clr-accordion-panel>
+                </clr-accordion>
+            </div>
+            <div class="clr-col-lg-4 order-cards">
+                <div class="card">
+                    <div class="card-header">
+                        {{ 'order.modification-summary' | translate }}
+                    </div>
+                    <div class="card-block">
+                        <ul>
+                            <li *ngIf="modifyOrderInput.addItems?.length">
+                                {{
+                                    'order.modification-adding-items'
+                                        | translate : { count: modifyOrderInput.addItems?.length }
+                                }}
+                            </li>
+                            <li *ngIf="modifyOrderInput.adjustOrderLines?.length">
+                                {{
+                                    'order.modification-adjusting-lines'
+                                        | translate : { count: modifyOrderInput.adjustOrderLines?.length }
+                                }}
+                            </li>
+                            <li *ngIf="modifyOrderInput.surcharges?.length">
+                                {{
+                                    'order.modification-adding-surcharges'
+                                        | translate : { count: modifyOrderInput.surcharges?.length }
+                                }}
+                            </li>
+                            <li *ngIf="shippingAddressForm.dirty">
+                                {{ 'order.modification-updating-shipping-address' | translate }}
+                            </li>
+                            <li *ngIf="billingAddressForm.dirty">
+                                {{ 'order.modification-updating-billing-address' | translate }}
+                            </li>
+                        </ul>
+                    </div>
+                    <div class="card-block">
+                        <label class="clr-control-label">{{ 'order.note' | translate }}</label>
+                        <textarea [(ngModel)]="note" name="note" clrTextarea required></textarea>
+                        <clr-checkbox-wrapper class="">
+                            <input type="checkbox" clrCheckbox [(ngModel)]="recalculateShipping" />
+                            <label>{{ 'order.modification-recalculate-shipping' | translate }}</label>
+                        </clr-checkbox-wrapper>
+                    </div>
+                    <div class="card-footer">
+                        <button
+                            class="btn btn-primary"
+                            [disabled]="!canPreviewChanges()"
+                            (click)="previewAndModify(order)"
+                        >
+                            {{ 'order.preview-changes' | translate }}
+                        </button>
+                    </div>
                 </div>
                 </div>
             </div>
             </div>
         </div>
         </div>
     </div>
     </div>
-</div>
+    <vdr-page-block> </vdr-page-block
+></vdr-page-block>

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

@@ -24,10 +24,10 @@
     justify-content: space-between;
     justify-content: space-between;
     align-items: baseline;
     align-items: baseline;
     .public {
     .public {
-        color: var(--color-warning-500);
+        color: var(--color-warning-700);
     }
     }
     .private {
     .private {
-        color: var(--color-success-500);
+        color: var(--color-success-700);
     }
     }
 }
 }
 textarea.note {
 textarea.note {
@@ -47,9 +47,9 @@ textarea.note {
 .note-visibility {
 .note-visibility {
     text-transform: lowercase;
     text-transform: lowercase;
     &.public {
     &.public {
-        color: var(--color-warning-500);
+        color: var(--color-warning-700);
     }
     }
     &.private {
     &.private {
-        color: var(--color-success-500);
+        color: var(--color-success-700);
     }
     }
 }
 }

+ 0 - 1
packages/admin-ui/src/lib/order/src/components/order-payment-card/order-payment-card.component.html

@@ -57,7 +57,6 @@
                 <ng-container *ngFor="let nextState of nextOtherStates()">
                 <ng-container *ngFor="let nextState of nextOtherStates()">
                     <button
                     <button
                         type="button"
                         type="button"
-                        class="btn"
                         vdrDropdownItem
                         vdrDropdownItem
                         (click)="transitionPaymentState.emit({ payment: payment, state: nextState })"
                         (click)="transitionPaymentState.emit({ payment: payment, state: nextState })"
                     >
                     >

+ 1 - 0
packages/admin-ui/src/lib/order/src/components/order-table/order-table-mixin.scss

@@ -51,6 +51,7 @@
     .net-price {
     .net-price {
         font-size: 11px;
         font-size: 11px;
         color: var(--color-text-300);
         color: var(--color-text-300);
+        line-height: 14px;
     }
     }
     .promotions-label {
     .promotions-label {
         text-decoration: underline dotted var(--color-text-200);
         text-decoration: underline dotted var(--color-text-200);

+ 91 - 150
packages/admin-ui/src/lib/order/src/components/order-table/order-table.component.html

@@ -1,156 +1,97 @@
-<table class="order-table 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>
-    <tbody>
-        <ng-container *ngFor="let line of order.lines">
-            <tr class="order-line" [class.is-cancelled]="line.quantity === 0">
-                <td class="align-middle thumb">
-                    <img *ngIf="line.featuredAsset" [src]="line.featuredAsset | assetPreview: 'tiny'" />
-                </td>
-                <td class="align-middle name">{{ line.productVariant.name }}</td>
-                <td class="align-middle sku">{{ line.productVariant.sku }}</td>
-                <td class="align-middle unit-price">
-                    {{ line.unitPriceWithTax | localeCurrency: order.currencyCode }}
-                    <div class="net-price" [title]="'order.net-price' | translate">
-                        {{ line.unitPrice | localeCurrency: order.currencyCode }}
+<vdr-order-data-table id="order-detail-" [items]="order.lines" [order]="order">
+    <vdr-dt2-column [heading]="'common.image' | translate">
+        <ng-template let-line="item">
+            <div class="image-placeholder">
+                <img
+                    *ngIf="line.featuredAsset as asset; else imagePlaceholder"
+                    [src]="asset | assetPreview : 'tiny'"
+                />
+                <ng-template #imagePlaceholder>
+                    <div class="placeholder">
+                        <clr-icon shape="image" size="48"></clr-icon>
                     </div>
                     </div>
-                </td>
-                <td class="align-middle quantity">
-                    <ng-container *ngIf="!isDraft; else draft">
-                        {{ line.quantity }}
-                    </ng-container>
-                    <ng-template #draft>
-                        <div class="flex">
-                            <input
-                                class="draft-qty"
-                                type="number"
-                                min="0"
-                                #qtyInput
-                                [value]="line.quantity"
-                                (blur)="draftInputBlur(line, qtyInput.valueAsNumber)"
-                            />
-                            <button class="icon-button" (click)="remove.emit({ lineId: line.id })">
-                                <clr-icon shape="trash"></clr-icon>
-                            </button>
-                        </div>
-                    </ng-template>
-                    <vdr-line-refunds [line]="line" [payments]="order.payments"></vdr-line-refunds>
-                    <vdr-line-fulfillment
-                        [line]="line"
-                        [orderState]="order.state"
-                        [allOrderFulfillments]="order.fulfillments"
-                    ></vdr-line-fulfillment>
-                </td>
-                <td class="align-middle total">
-                    {{ line.linePriceWithTax | localeCurrency: order.currencyCode }}
-                    <div class="net-price" [title]="'order.net-price' | translate">
-                        {{ line.linePrice | localeCurrency: order.currencyCode }}
-                    </div>
-
-                    <ng-container *ngIf="getLineDiscounts(line) as discounts">
-                        <vdr-dropdown *ngIf="discounts.length">
-                            <div class="promotions-label" vdrDropdownTrigger>
-                                {{ 'order.promotions-applied' | translate }}
-                            </div>
-                            <vdr-dropdown-menu>
-                                <div class="line-promotion" *ngFor="let discount of discounts">
-                                    <a class="promotion-name" [routerLink]="getPromotionLink(discount)">{{
-                                        discount.description
-                                    }}</a>
-                                    <div class="promotion-amount">
-                                        {{ discount.amountWithTax | localeCurrency: order.currencyCode }}
-                                        <div class="net-price" [title]="'order.net-price' | translate">
-                                            {{ discount.amount | localeCurrency: order.currencyCode }}
-                                        </div>
-                                    </div>
-                                </div>
-                            </vdr-dropdown-menu>
-                        </vdr-dropdown>
-                    </ng-container>
-                </td>
-            </tr>
-            <ng-container *ngIf="customFieldsForLine[line.id] as customFields">
-                <tr *ngIf="customFields.length">
-                    <td colspan="6" class="custom-fields-row">
-                        <div class="order-line-custom-fields">
-                            <div class="custom-field" *ngFor="let field of customFields">
-                                <vdr-custom-field-control
-                                    [compact]="true"
-                                    [readonly]="true"
-                                    [customField]="field.config"
-                                    [customFieldsFormGroup]="field.formGroup"
-                                ></vdr-custom-field-control>
-                            </div>
-                        </div>
-                    </td>
-                </tr>
-            </ng-container>
-        </ng-container>
-        <tr class="surcharge" *ngFor="let surcharge of order.surcharges">
-            <td class="align-middle name left" colspan="2">{{ surcharge.description }}</td>
-            <td class="align-middle sku">{{ surcharge.sku }}</td>
-            <td class="align-middle" colspan="2"></td>
-            <td class="align-middle total">
-                {{ surcharge.priceWithTax | localeCurrency: order.currencyCode }}
+                </ng-template>
+            </div>
+        </ng-template>
+    </vdr-dt2-column>
+    <vdr-dt2-column [heading]="'order.product-name' | translate">
+        <ng-template let-line="item">
+            {{ line.productVariant.name }}
+        </ng-template>
+    </vdr-dt2-column>
+    <vdr-dt2-column [heading]="'order.product-sku' | translate" [optional]="false">
+        <ng-template let-line="item">
+            {{ line.productVariant.sku }}
+        </ng-template>
+    </vdr-dt2-column>
+    <vdr-dt2-column [heading]="'order.unit-price' | translate">
+        <ng-template let-line="item">
+            <div class="unit-price">
+                {{ line.unitPriceWithTax | localeCurrency : order.currencyCode }}
                 <div class="net-price" [title]="'order.net-price' | translate">
                 <div class="net-price" [title]="'order.net-price' | translate">
-                    {{ surcharge.price | localeCurrency: order.currencyCode }}
+                    {{ line.unitPrice | localeCurrency : order.currencyCode }}
                 </div>
                 </div>
-            </td>
-        </tr>
-        <ng-container *ngFor="let discount of order.discounts">
-            <tr class="order-adjustment" *ngIf="discount.type !== 'OTHER'">
-                <td colspan="5" class="left clr-align-middle">
-                    <a [routerLink]="getPromotionLink(discount)">{{ discount.description }}</a>
-                    <vdr-chip *ngIf="getCouponCodeForAdjustment(order, discount) as couponCode">{{
-                        couponCode
-                    }}</vdr-chip>
-                </td>
-                <td class="clr-align-middle">
-                    {{ discount.amountWithTax | localeCurrency: order.currencyCode }}
-                    <div class="net-price" [title]="'order.net-price' | translate">
-                        {{ discount.amount | localeCurrency: order.currencyCode }}
-                    </div>
-                </td>
-            </tr>
-        </ng-container>
-        <tr class="sub-total">
-            <td class="left clr-align-middle">{{ 'order.sub-total' | translate }}</td>
-            <td colspan="4"></td>
-            <td class="clr-align-middle">
-                {{ order.subTotalWithTax | localeCurrency: order.currencyCode }}
-                <div class="net-price" [title]="'order.net-price' | translate">
-                    {{ order.subTotal | localeCurrency: order.currencyCode }}
-                </div>
-            </td>
-        </tr>
-        <tr class="shipping">
-            <td class="left clr-align-middle">{{ 'order.shipping' | translate }}</td>
-            <td class="clr-align-middle">{{ getShippingNames(order) }}</td>
-            <td colspan="3"></td>
-            <td class="clr-align-middle">
-                {{ order.shippingWithTax | localeCurrency: order.currencyCode }}
-                <div class="net-price" [title]="'order.net-price' | translate">
-                    {{ order.shipping | localeCurrency: order.currencyCode }}
+            </div>
+        </ng-template>
+    </vdr-dt2-column>
+    <vdr-dt2-column [heading]="'order.unit-price' | translate" [optional]="false">
+        <ng-template let-line="item">
+            <ng-container *ngIf="!isDraft; else draft">
+                {{ line.quantity }}
+            </ng-container>
+            <ng-template #draft>
+                <div class="flex">
+                    <input
+                        class="draft-qty"
+                        type="number"
+                        min="0"
+                        #qtyInput
+                        [value]="line.quantity"
+                        (blur)="draftInputBlur(line, qtyInput.valueAsNumber)"
+                    />
+                    <button class="icon-button" (click)="remove.emit({ lineId: line.id })">
+                        <clr-icon shape="trash"></clr-icon>
+                    </button>
                 </div>
                 </div>
-            </td>
-        </tr>
-        <tr class="total">
-            <td class="left clr-align-middle">{{ 'order.total' | translate }}</td>
-            <td colspan="4"></td>
-            <td class="clr-align-middle">
-                {{ order.totalWithTax | localeCurrency: order.currencyCode }}
+            </ng-template>
+            <vdr-line-refunds [line]="line" [payments]="order.payments"></vdr-line-refunds>
+            <vdr-line-fulfillment
+                [line]="line"
+                [orderState]="order.state"
+                [allOrderFulfillments]="order.fulfillments"
+            ></vdr-line-fulfillment>
+        </ng-template>
+    </vdr-dt2-column>
+    <vdr-dt2-custom-field-column *ngFor="let customField of orderLineCustomFields" [customField]="customField"/>
+    <vdr-order-total-column [heading]="'order.total' | translate" [optional]="false">
+        <ng-template let-line="item">
+            <div class="unit-price">
+                {{ line.linePriceWithTax | localeCurrency : order.currencyCode }}
                 <div class="net-price" [title]="'order.net-price' | translate">
                 <div class="net-price" [title]="'order.net-price' | translate">
-                    {{ order.total | localeCurrency: order.currencyCode }}
+                    {{ line.linePrice | localeCurrency : order.currencyCode }}
                 </div>
                 </div>
-            </td>
-        </tr>
-    </tbody>
-</table>
+            </div>
+
+            <ng-container *ngIf="getLineDiscounts(line) as discounts">
+                <vdr-dropdown *ngIf="discounts.length">
+                    <div class="promotions-label" vdrDropdownTrigger>
+                        {{ 'order.promotions-applied' | translate }}
+                    </div>
+                    <vdr-dropdown-menu>
+                        <div class="line-promotion" *ngFor="let discount of discounts">
+                            <a class="promotion-name" [routerLink]="getPromotionLink(discount)">{{
+                                discount.description
+                                }}</a>
+                            <div class="promotion-amount">
+                                {{ discount.amountWithTax | localeCurrency : order.currencyCode }}
+                                <div class="net-price" [title]="'order.net-price' | translate">
+                                    {{ discount.amount | localeCurrency : order.currencyCode }}
+                                </div>
+                            </div>
+                        </div>
+                    </vdr-dropdown-menu>
+                </vdr-dropdown>
+            </ng-container>
+        </ng-template>
+    </vdr-order-total-column>
+</vdr-order-data-table>

+ 1 - 1
packages/admin-ui/src/lib/order/src/components/order-table/order-table.component.scss

@@ -1,6 +1,6 @@
 @import 'order-table-mixin';
 @import 'order-table-mixin';
 
 
-.order-table {
+:host {
     @include order-table;
     @include order-table;
 }
 }
 
 

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

@@ -21,6 +21,7 @@ import { LineFulfillmentComponent } from './components/line-fulfillment/line-ful
 import { LineRefundsComponent } from './components/line-refunds/line-refunds.component';
 import { LineRefundsComponent } from './components/line-refunds/line-refunds.component';
 import { ModificationDetailComponent } from './components/modification-detail/modification-detail.component';
 import { ModificationDetailComponent } from './components/modification-detail/modification-detail.component';
 import { OrderCustomFieldsCardComponent } from './components/order-custom-fields-card/order-custom-fields-card.component';
 import { OrderCustomFieldsCardComponent } from './components/order-custom-fields-card/order-custom-fields-card.component';
+import { OrderTotalColumnComponent } from './components/order-data-table/order-total-column.component';
 import { OrderDetailComponent } from './components/order-detail/order-detail.component';
 import { OrderDetailComponent } from './components/order-detail/order-detail.component';
 import { OrderEditorComponent } from './components/order-editor/order-editor.component';
 import { OrderEditorComponent } from './components/order-editor/order-editor.component';
 import { OrderEditsPreviewDialogComponent } from './components/order-edits-preview-dialog/order-edits-preview-dialog.component';
 import { OrderEditsPreviewDialogComponent } from './components/order-edits-preview-dialog/order-edits-preview-dialog.component';
@@ -45,6 +46,7 @@ import { SellerOrdersCardComponent } from './components/seller-orders-card/selle
 import { SettleRefundDialogComponent } from './components/settle-refund-dialog/settle-refund-dialog.component';
 import { SettleRefundDialogComponent } from './components/settle-refund-dialog/settle-refund-dialog.component';
 import { SimpleItemListComponent } from './components/simple-item-list/simple-item-list.component';
 import { SimpleItemListComponent } from './components/simple-item-list/simple-item-list.component';
 import { createRoutes } from './order.routes';
 import { createRoutes } from './order.routes';
+import { OrderDataTableComponent } from './components/order-data-table/order-data-table.component';
 
 
 @NgModule({
 @NgModule({
     imports: [SharedModule, RouterModule.forChild([])],
     imports: [SharedModule, RouterModule.forChild([])],
@@ -93,6 +95,8 @@ import { createRoutes } from './order.routes';
         SelectShippingMethodDialogComponent,
         SelectShippingMethodDialogComponent,
         OrderHistoryEntryHostComponent,
         OrderHistoryEntryHostComponent,
         SellerOrdersCardComponent,
         SellerOrdersCardComponent,
+        OrderDataTableComponent,
+        OrderTotalColumnComponent,
     ],
     ],
     exports: [OrderCustomFieldsCardComponent],
     exports: [OrderCustomFieldsCardComponent],
 })
 })

+ 47 - 23
packages/admin-ui/src/lib/static/styles/_mixins.scss

@@ -1,3 +1,5 @@
+@import "variables";
+
 @mixin no-select {
 @mixin no-select {
     -webkit-touch-callout: none;
     -webkit-touch-callout: none;
     -webkit-user-select: none;
     -webkit-user-select: none;
@@ -6,28 +8,50 @@
     -ms-user-select: none;
     -ms-user-select: none;
     user-select: none;
     user-select: none;
 }
 }
-
 @mixin ng-select-facet-values {
 @mixin ng-select-facet-values {
-    //.ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-value {
-    //    background: none;
-    //    margin: 0;
-    //}
-    //
-    //.ng-dropdown-panel-items div.ng-option:last-child {
-    //    display: none;
-    //}
-    //
-    //.ng-dropdown-panel .ng-dropdown-header {
-    //    border: none;
-    //    padding: 0;
-    //}
-    //
-    //.ng-select.ng-select-multiple .ng-select-container .ng-value-container {
-    //    padding: 0 3px;
-    //    gap: 3px;
-    //}
-    //
-    //.ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-placeholder {
-    //    padding-left: 8px;
-    //}
+
+}
+@mixin table-base-styles {
+    th {
+        border-bottom: 1px solid var(--color-weight-200);
+        font-size: var(--font-size-xs);
+        font-weight: 600;
+        text-transform: uppercase;
+        position: relative;
+        white-space: nowrap;
+    }
+
+    th,
+    td {
+        padding: calc(var(--space-unit) * 1.5) calc(var(--space-unit) * 1);
+    }
+
+    tr td:first-of-type,
+    tr th:first-of-type {
+        text-align: center;
+        @media screen and (min-width: $breakpoint-medium) {
+            padding-left: var(--surface-margin-left);
+            text-align: left;
+        }
+    }
+
+    th:last-of-type,
+    td:last-of-type {
+        border-right: 1px solid var(--color-weight-200);
+    }
+
+    tr:first-of-type th:last-of-type {
+        border-image: linear-gradient(0deg, var(--color-weight-200), transparent) 1;
+    }
+    tr:last-of-type td:last-of-type {
+        border-image: linear-gradient(180deg, var(--color-weight-200), transparent) 1;
+    }
+
+    tbody td {
+        border-bottom: 1px solid var(--color-table-row-separator);
+    }
+
+    tbody tr:hover {
+        background-color: var(--color-table-row-hover-bg);
+    }
 }
 }

+ 14 - 10
packages/admin-ui/src/lib/static/styles/global/_overrides.scss

@@ -1,5 +1,6 @@
 @import 'variables';
 @import 'variables';
 @import 'forms';
 @import 'forms';
+@import 'mixins';
 
 
 .main-container {
 .main-container {
     background-color: var(--clr-global-app-background);
     background-color: var(--clr-global-app-background);
@@ -57,35 +58,35 @@ button:focus {
 }
 }
 
 
 .table {
 .table {
-    border-color: var(--color-component-border-100);
-    margin-top: 0;
-
-    td.align-middle,
-    th.align-middle {
-        vertical-align: middle !important;
-    }
-
-    td.right {
-        text-align: right;
+    border: none;
+    margin: 0;
+    background-color: transparent;
+    td {
+        border: none;
     }
     }
+    @include table-base-styles;
 }
 }
 
 
 .full-label,
 .full-label,
 .compact-label {
 .compact-label {
     margin-left: 6px;
     margin-left: 6px;
 }
 }
+
 .full-label {
 .full-label {
     display: none;
     display: none;
 }
 }
+
 .compact-label {
 .compact-label {
     display: initial;
     display: initial;
 }
 }
+
 button.icon-button {
 button.icon-button {
     border: none;
     border: none;
     background: none;
     background: none;
     cursor: pointer;
     cursor: pointer;
     color: var(--color-icon-button);
     color: var(--color-icon-button);
 }
 }
+
 @media screen and (min-width: $breakpoint-small) {
 @media screen and (min-width: $breakpoint-small) {
     .full-label {
     .full-label {
         display: initial;
         display: initial;
@@ -114,16 +115,19 @@ button.icon-button {
     color: var(--color-primary-900);
     color: var(--color-primary-900);
     border-radius: var(--border-radius);
     border-radius: var(--border-radius);
     padding: var(--space-unit);
     padding: var(--space-unit);
+
     .alert-item {
     .alert-item {
         display: flex;
         display: flex;
         align-items: flex-start;
         align-items: flex-start;
         gap: 4px;
         gap: 4px;
     }
     }
+
     &.alert-danger {
     &.alert-danger {
         border-color: var(--color-chip-error-border);
         border-color: var(--color-chip-error-border);
         background-color: var(--color-chip-error-bg);
         background-color: var(--color-chip-error-bg);
         color: var(--color-chip-error-text);
         color: var(--color-chip-error-text);
     }
     }
+
     &.alert-warning {
     &.alert-warning {
         border-color: var(--color-chip-warning-border);
         border-color: var(--color-chip-warning-border);
         background-color: var(--color-chip-warning-bg);
         background-color: var(--color-chip-warning-bg);