|
@@ -0,0 +1,116 @@
|
|
|
|
|
+import {
|
|
|
|
|
+ AfterViewInit,
|
|
|
|
|
+ ChangeDetectionStrategy,
|
|
|
|
|
+ ChangeDetectorRef,
|
|
|
|
|
+ Component,
|
|
|
|
|
+ HostBinding,
|
|
|
|
|
+ Input,
|
|
|
|
|
+ OnChanges,
|
|
|
|
|
+ OnInit,
|
|
|
|
|
+ QueryList,
|
|
|
|
|
+ SimpleChanges,
|
|
|
|
|
+ ViewChildren,
|
|
|
|
|
+} from '@angular/core';
|
|
|
|
|
+import { OrderProcessState } from '@vendure/admin-ui/core';
|
|
|
|
|
+import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
|
|
|
|
+import { debounceTime } from 'rxjs/operators';
|
|
|
|
|
+
|
|
|
|
|
+import { NODE_HEIGHT } from './constants';
|
|
|
|
|
+import { OrderProcessNodeComponent } from './order-process-node.component';
|
|
|
|
|
+
|
|
|
|
|
+export type StateNode = {
|
|
|
|
|
+ name: string;
|
|
|
|
|
+ to: StateNode[];
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+@Component({
|
|
|
|
|
+ selector: 'vdr-order-process-graph',
|
|
|
|
|
+ templateUrl: './order-process-graph.component.html',
|
|
|
|
|
+ styleUrls: ['./order-process-graph.component.scss'],
|
|
|
|
|
+ changeDetection: ChangeDetectionStrategy.OnPush,
|
|
|
|
|
+})
|
|
|
|
|
+export class OrderProcessGraphComponent implements OnInit, OnChanges, AfterViewInit {
|
|
|
|
|
+ @Input() states: OrderProcessState[];
|
|
|
|
|
+ @Input() initialState?: string;
|
|
|
|
|
+ setActiveState$ = new BehaviorSubject<string | undefined>(undefined);
|
|
|
|
|
+ activeState$: Observable<string | undefined>;
|
|
|
|
|
+ nodes: StateNode[] = [];
|
|
|
|
|
+ edges: Array<{ from: OrderProcessNodeComponent; to: OrderProcessNodeComponent; index: number }> = [];
|
|
|
|
|
+
|
|
|
|
|
+ @ViewChildren(OrderProcessNodeComponent) nodeComponents: QueryList<OrderProcessNodeComponent>;
|
|
|
|
|
+
|
|
|
|
|
+ constructor(private changeDetector: ChangeDetectorRef) {}
|
|
|
|
|
+
|
|
|
|
|
+ @HostBinding('style.height.px')
|
|
|
|
|
+ get outerHeight(): number {
|
|
|
|
|
+ return this.nodes.length * NODE_HEIGHT;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ngOnInit() {
|
|
|
|
|
+ this.setActiveState$.next(this.initialState);
|
|
|
|
|
+ this.activeState$ = this.setActiveState$.pipe(debounceTime(150));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ngOnChanges(changes: SimpleChanges) {
|
|
|
|
|
+ this.populateNodes();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ngAfterViewInit() {
|
|
|
|
|
+ setTimeout(() => this.populateEdges());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ onMouseOver(stateName: string) {
|
|
|
|
|
+ this.setActiveState$.next(stateName);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ onMouseOut() {
|
|
|
|
|
+ this.setActiveState$.next(this.initialState);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ getNodeFor(state: string): OrderProcessNodeComponent | undefined {
|
|
|
|
|
+ if (this.nodeComponents) {
|
|
|
|
|
+ return this.nodeComponents.find((n) => n.node.name === state);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private populateNodes() {
|
|
|
|
|
+ const stateNodeMap = new Map<string, StateNode>();
|
|
|
|
|
+ for (const state of this.states) {
|
|
|
|
|
+ stateNodeMap.set(state.name, {
|
|
|
|
|
+ name: state.name,
|
|
|
|
|
+ to: [],
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for (const [name, stateNode] of stateNodeMap.entries()) {
|
|
|
|
|
+ const targets = this.states.find((s) => s.name === name)?.to ?? [];
|
|
|
|
|
+ for (const target of targets) {
|
|
|
|
|
+ const targetNode = stateNodeMap.get(target);
|
|
|
|
|
+ if (targetNode) {
|
|
|
|
|
+ stateNode.to.push(targetNode);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ this.nodes = [...stateNodeMap.values()].filter((n) => n.name !== 'Cancelled');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private populateEdges() {
|
|
|
|
|
+ for (const node of this.nodes) {
|
|
|
|
|
+ const nodeCmp = this.getNodeFor(node.name);
|
|
|
|
|
+ let index = 0;
|
|
|
|
|
+ for (const to of node.to) {
|
|
|
|
|
+ const toCmp = this.getNodeFor(to.name);
|
|
|
|
|
+ if (nodeCmp && toCmp && nodeCmp !== toCmp) {
|
|
|
|
|
+ this.edges.push({
|
|
|
|
|
+ to: toCmp,
|
|
|
|
|
+ from: nodeCmp,
|
|
|
|
|
+ index,
|
|
|
|
|
+ });
|
|
|
|
|
+ index++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ this.edges = [...this.edges];
|
|
|
|
|
+ this.changeDetector.markForCheck();
|
|
|
|
|
+ }
|
|
|
|
|
+}
|