Răsfoiți Sursa

feat(admin-ui): Display nested payment metadata

Michael Bromley 6 ani în urmă
părinte
comite
f90e773d81

+ 1 - 6
admin-ui/src/app/order/components/payment-detail/payment-detail.component.html

@@ -8,10 +8,5 @@
     {{ payment.transactionId }}
 </vdr-labeled-data>
 <vdr-labeled-data [label]="'order.payment-metadata' | translate">
-    <ul class="payment-metadata">
-        <li *ngFor="let entry of paymentMetadata">
-            <span class="metadata-prop">{{ entry[0] }}:</span>
-            {{ entry[1] }}
-        </li>
-    </ul>
+    <vdr-object-tree [value]="payment.metadata"></vdr-object-tree>
 </vdr-labeled-data>

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

@@ -1,10 +1 @@
 @import "variables";
-
-ul.payment-metadata {
-    list-style-type: none;
-    line-height: 16px;
-    margin-top: 8px;
-}
-.metadata-prop {
-    color: $color-grey-400;
-}

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

@@ -11,8 +11,4 @@ import { CurrencyCode, OrderDetail } from '../../../common/generated-types';
 export class PaymentDetailComponent {
     @Input() payment: OrderDetail.Payments;
     @Input() currencyCode: CurrencyCode;
-
-    get paymentMetadata() {
-        return Object.entries(this.payment.metadata);
-    }
 }

+ 20 - 0
admin-ui/src/app/shared/components/object-tree/object-tree.component.html

@@ -0,0 +1,20 @@
+<button class="icon-button" (click)="expanded = !expanded" *ngIf="depth !== 0 && !isArrayItem">
+    <clr-icon shape="caret" size="12" [dir]="expanded ? 'down' : 'right'"></clr-icon>
+</button>
+<ul
+    class="object-tree-node"
+    [ngClass]="'depth-' + depth"
+    [class.array-value]="valueIsArray"
+    [class.array-item]="isArrayItem"
+    [class.expanded]="expanded"
+>
+    <li *ngFor="let entry of entries; trackBy: index">
+        <span class="key">{{ entry.key }}:</span>
+        <ng-container *ngIf="isObject(entry.value)">
+            <vdr-object-tree [value]="entry.value" [isArrayItem]="valueIsArray"></vdr-object-tree>
+        </ng-container>
+        <ng-container *ngIf="!isObject(entry.value)">
+            {{ entry.value }}
+        </ng-container>
+    </li>
+</ul>

+ 41 - 0
admin-ui/src/app/shared/components/object-tree/object-tree.component.scss

@@ -0,0 +1,41 @@
+@import "variables";
+
+:host {}
+
+.object-tree-node {
+    list-style-type: none;
+    line-height: 16px;
+    font-size: 12px;
+    overflow: hidden;
+    &.depth-0 {
+        margin-left: 0;
+        margin-top: 8px;
+    }
+    &.depth-1 { margin-left: 6px; }
+    &.depth-2 { margin-left: 6px; }
+    &.depth-3 { margin-left: 6px; }
+    &.depth-4 { margin-left: 6px; }
+    &.depth-5 { margin-left: 6px; }
+    &.depth-6 { margin-left: 6px; }
+
+    max-height: 0;
+    &.expanded {
+        max-height: 5000px;
+    }
+
+    li {
+        // display: flex;
+    }
+    &.array-item {
+        margin-top: -16px;
+        margin-left: 16px;
+    }
+    &.array-value.expanded > li + li {
+        margin-top: 6px;
+    }
+}
+
+.key {
+    color: $color-grey-400;
+}
+

+ 36 - 0
admin-ui/src/app/shared/components/object-tree/object-tree.component.ts

@@ -0,0 +1,36 @@
+import { ChangeDetectionStrategy, Component, Input, OnInit, Optional, SkipSelf } from '@angular/core';
+
+/**
+ * This component displays a plain JavaScript object as an expandable tree.
+ */
+@Component({
+    selector: 'vdr-object-tree',
+    templateUrl: './object-tree.component.html',
+    styleUrls: ['./object-tree.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class ObjectTreeComponent implements OnInit {
+    @Input() value: { [key: string]: any };
+    @Input() isArrayItem = false;
+    depth: number;
+    expanded: boolean;
+    valueIsArray: boolean;
+    entries: Array<{ key: string; value: any }>;
+    constructor(@Optional() @SkipSelf() parent: ObjectTreeComponent) {
+        if (parent) {
+            this.depth = parent.depth + 1;
+        } else {
+            this.depth = 0;
+        }
+    }
+
+    ngOnInit() {
+        this.entries = Object.entries(this.value).map(([key, value]) => ({ key, value }));
+        this.expanded = this.depth === 0 || this.isArrayItem;
+        this.valueIsArray = Object.keys(this.value).every(v => Number.isInteger(+v));
+    }
+
+    isObject(value: any): boolean {
+        return typeof value === 'object' && value !== null;
+    }
+}

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

@@ -39,6 +39,7 @@ import { DialogButtonsDirective } from './components/modal-dialog/dialog-buttons
 import { DialogComponentOutletComponent } from './components/modal-dialog/dialog-component-outlet.component';
 import { DialogTitleDirective } from './components/modal-dialog/dialog-title.directive';
 import { ModalDialogComponent } from './components/modal-dialog/modal-dialog.component';
+import { ObjectTreeComponent } from './components/object-tree/object-tree.component';
 import { OrderStateLabelComponent } from './components/order-state-label/order-state-label.component';
 import { PaginationControlsComponent } from './components/pagination-controls/pagination-controls.component';
 import { RichTextEditorComponent } from './components/rich-text-editor/rich-text-editor.component';
@@ -108,6 +109,7 @@ const DECLARATIONS = [
     FormattedAddressComponent,
     LabeledDataComponent,
     StringToColorPipe,
+    ObjectTreeComponent,
 ];
 
 @NgModule({