mesauth-angular 1.7.0 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2027,6 +2027,7 @@ class MaArvContainerComponent {
2027
2027
  description;
2028
2028
  referenceId = '';
2029
2029
  templateId;
2030
+ templateIds;
2030
2031
  callbackUrl;
2031
2032
  deadlineHours;
2032
2033
  approvalSubmitted = new EventEmitter();
@@ -2078,6 +2079,8 @@ class MaArvContainerComponent {
2078
2079
  loadTemplates() {
2079
2080
  this.approvalSvc.getTemplates().pipe(takeUntil(this.destroy$)).subscribe({
2080
2081
  next: (t) => {
2082
+ if (this.templateIds && this.templateIds.length > 0)
2083
+ t = t.filter(temp => this.templateIds.includes(temp.id));
2081
2084
  this.templates = t;
2082
2085
  if (this.templateId != null && this.selectedTemplateId == null) {
2083
2086
  this.selectedTemplateId = this.templateId;
@@ -2485,329 +2488,429 @@ ${clone.outerHTML}
2485
2488
  ];
2486
2489
  }
2487
2490
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: MaArvContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2488
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.7", type: MaArvContainerComponent, isStandalone: true, selector: "ma-arv-container", inputs: { title: "title", description: "description", referenceId: "referenceId", templateId: "templateId", callbackUrl: "callbackUrl", deadlineHours: "deadlineHours" }, outputs: { approvalSubmitted: "approvalSubmitted", approvalSubmitting: "approvalSubmitting", cancelled: "cancelled" }, host: { properties: { "class": "this.themeClass" } }, viewQueries: [{ propertyName: "contentBody", first: true, predicate: ["contentBody"], descendants: true, static: true }], ngImport: i0, template: `
2489
- <div class="arv-container">
2491
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: MaArvContainerComponent, isStandalone: true, selector: "ma-arv-container", inputs: { title: "title", description: "description", referenceId: "referenceId", templateId: "templateId", templateIds: "templateIds", callbackUrl: "callbackUrl", deadlineHours: "deadlineHours" }, outputs: { approvalSubmitted: "approvalSubmitted", approvalSubmitting: "approvalSubmitting", cancelled: "cancelled" }, host: { properties: { "class": "this.themeClass" } }, viewQueries: [{ propertyName: "contentBody", first: true, predicate: ["contentBody"], descendants: true, static: true }], ngImport: i0, template: `
2492
+ <div class="arv-container">
2490
2493
  <!-- Content Area -->
2491
2494
  <div class="arv-content-body" #contentBody>
2492
2495
  <ng-content></ng-content>
2493
2496
  </div>
2494
2497
 
2495
2498
  <!-- Approval Footer -->
2496
- <div class="arv-footer" *ngIf="!isSubmitted">
2497
- <div class="arv-footer-inner">
2498
-
2499
- <!-- Routing mode -->
2500
- <div class="arv-routing">
2501
- <div class="arv-routing-header">
2502
- <h4 class="arv-section-title">Approval Routing</h4>
2503
- <!-- Show routing toggle only when templateId is NOT locked -->
2504
- <div *ngIf="!templateId" class="arv-routing-mode">
2505
- <label class="arv-radio">
2506
- <input type="radio" name="routingMode" value="template" [checked]="routingMode === 'template'" (change)="routingMode = 'template'"> Use Template
2507
- </label>
2508
- <label class="arv-radio">
2509
- <input type="radio" name="routingMode" value="adhoc" [checked]="routingMode === 'adhoc'" (change)="routingMode = 'adhoc'"> Custom Steps
2510
- </label>
2511
- </div>
2512
- </div>
2513
-
2514
- <!-- Locked template: show name only, no selector -->
2515
- <div *ngIf="templateId && routingMode === 'template'" class="arv-template-select">
2516
- <div *ngIf="loadingTemplate" class="arv-template-loading">Loading template...</div>
2517
- <div *ngIf="selectedTemplate && !loadingTemplate" class="arv-locked-template">
2518
- <span class="arv-locked-label">Template</span>
2519
- <span class="arv-locked-name">{{ selectedTemplate.name }}</span>
2499
+ <div class="arv-footer">
2500
+ @if (!isSubmitted) {
2501
+ <div class="arv-footer-inner">
2502
+
2503
+ <!-- Routing mode -->
2504
+ <div class="arv-routing">
2505
+ <div class="arv-routing-header">
2506
+ <h4 class="arv-section-title">Approval Routing</h4>
2507
+ <!-- Show routing toggle only when templateId is NOT locked to a single template -->
2508
+ @if (!templateId || (templateIds && templateIds.length > 0)) {
2509
+ <div class="arv-routing-mode">
2510
+ <label class="arv-radio">
2511
+ <input type="radio" name="routingMode" value="template" [checked]="routingMode === 'template'" (change)="routingMode = 'template'"> Use Template
2512
+ </label>
2513
+ <label class="arv-radio">
2514
+ <input type="radio" name="routingMode" value="adhoc" [checked]="routingMode === 'adhoc'" (change)="routingMode = 'adhoc'"> Custom Steps
2515
+ </label>
2516
+ </div>
2517
+ }
2520
2518
  </div>
2521
- </div>
2522
2519
 
2523
- <!-- Free template selector (no locked templateId) -->
2524
- <div *ngIf="!templateId && routingMode === 'template'" class="arv-template-select">
2525
- <label class="arv-label">Select Template</label>
2526
- <select class="arv-select" (change)="onTemplateChange($event)">
2527
- <option value="">-- Select a template --</option>
2528
- <option *ngFor="let t of templates" [value]="t.id" [selected]="t.id === selectedTemplateId">{{ t.name }}</option>
2529
- </select>
2530
- </div>
2531
-
2532
- <!-- Step pickers (shared for both locked and free template) -->
2533
- <div *ngIf="routingMode === 'template'" class="arv-template-select">
2534
- <div *ngIf="!loadingTemplate && selectedTemplate" class="arv-template-steps">
2535
- <div class="arv-step-card" *ngFor="let s of selectedTemplate.steps; let i = index">
2536
- <div class="arv-step-card-header">
2537
- <span class="arv-step-preview-num">Step {{ s.stepOrder }}</span>
2538
- <span class="arv-step-preview-name">{{ s.stepName }}</span>
2539
- <span *ngIf="s.roles && s.roles.length > 0" class="arv-step-role-badge">
2540
- {{ s.roles[0].positionLevel }}{{ s.roles[0].orgName ? ' · ' + s.roles[0].orgName : '' }}
2541
- </span>
2542
- </div>
2543
- <!-- Selectable step (role-based or multi-assignee): show approver picker -->
2544
- <div *ngIf="stepCandidates[i]?.length > 1 || stepLoadingCandidates[i]" class="arv-step-picker">
2545
- <div *ngIf="stepLoadingCandidates[i]" class="arv-template-loading">Loading candidates...</div>
2546
- <select *ngIf="!stepLoadingCandidates[i]" class="arv-select arv-select-sm"
2547
- (change)="onStepUserChange(i, $event)">
2548
- <option value="">-- Select approver --</option>
2549
- <option *ngFor="let u of stepCandidates[i]" [value]="u.userId"
2550
- [selected]="u.userId === stepSelectedUsers[i]">
2551
- {{ u.fullName || u.userId }}{{ u.department ? ' · ' + u.department : '' }}{{ u.position ? ' (' + u.position + ')' : '' }}
2552
- </option>
2553
- </select>
2554
- </div>
2555
- <!-- Single fixed-user step: show who is assigned -->
2556
- <div *ngIf="!stepLoadingCandidates[i] && stepCandidates[i]?.length === 1" class="arv-step-fixed">
2557
- {{ stepCandidates[i][0].fullName || stepCandidates[i][0].userId }}{{ stepCandidates[i][0].department ? ' · ' + stepCandidates[i][0].department : '' }}{{ stepCandidates[i][0].position ? ' (' + stepCandidates[i][0].position + ')' : '' }}
2558
- </div>
2520
+ <!-- Locked template: only when templateId set and no multi-choice -->
2521
+ @if (templateId && (!templateIds || templateIds.length <= 1) && routingMode === 'template') {
2522
+ <div class="arv-template-select">
2523
+ @if (loadingTemplate) {
2524
+ <div class="arv-template-loading">Loading template...</div>
2525
+ }
2526
+ @if (selectedTemplate && !loadingTemplate) {
2527
+ <div class="arv-locked-template">
2528
+ <span class="arv-locked-label">Template</span>
2529
+ <span class="arv-locked-name">{{ selectedTemplate.name }}</span>
2530
+ </div>
2531
+ }
2559
2532
  </div>
2560
- <div *ngIf="selectedTemplate.referenceUserIds.length > 0" class="arv-step-preview arv-step-preview-cc">
2561
- <span class="arv-step-preview-num">CC</span>
2562
- <span class="arv-step-preview-name">{{ selectedTemplate.referenceUserIds.length }} reference user(s) from template</span>
2533
+ }
2534
+
2535
+ <!-- Template selector: shown when no templateId, OR when templateIds has multiple choices -->
2536
+ @if ((!templateId || (templateIds && templateIds.length > 1)) && routingMode === 'template') {
2537
+ <div class="arv-template-select">
2538
+ <label class="arv-label">Select Template</label>
2539
+ <select class="arv-select" (change)="onTemplateChange($event)">
2540
+ <option value="">-- Select a template --</option>
2541
+ @for (t of templates; track t.id) {
2542
+ <option [value]="t.id" [selected]="t.id === selectedTemplateId">{{ t.name }}</option>
2543
+ }
2544
+ </select>
2563
2545
  </div>
2564
- </div>
2546
+ }
2547
+
2548
+ <!-- Step pickers (shared for both locked and free template) -->
2549
+ @if (routingMode === 'template') {
2550
+ <div class="arv-template-select">
2551
+ @if (!loadingTemplate && selectedTemplate) {
2552
+ <div class="arv-template-steps">
2553
+ @for (s of selectedTemplate.steps; track s.stepOrder; let i = $index) {
2554
+ <div class="arv-step-card">
2555
+ <div class="arv-step-card-header">
2556
+ <span class="arv-step-preview-num">Step {{ s.stepOrder }}</span>
2557
+ <span class="arv-step-preview-name">{{ s.stepName }}</span>
2558
+ @if (s.roles && s.roles.length > 0) {
2559
+ <span class="arv-step-role-badge">
2560
+ {{ s.roles[0].positionLevel }}{{ s.roles[0].orgName ? ' · ' + s.roles[0].orgName : '' }}
2561
+ </span>
2562
+ }
2563
+ </div>
2564
+ <!-- Selectable step: show approver picker -->
2565
+ @if (stepCandidates[i]?.length > 1 || stepLoadingCandidates[i]) {
2566
+ <div class="arv-step-picker">
2567
+ @if (stepLoadingCandidates[i]) {
2568
+ <div class="arv-template-loading">Loading candidates...</div>
2569
+ }
2570
+ @if (!stepLoadingCandidates[i]) {
2571
+ <select class="arv-select arv-select-sm" (change)="onStepUserChange(i, $event)">
2572
+ <option value="">-- Select approver --</option>
2573
+ @for (u of stepCandidates[i]; track u.userId) {
2574
+ <option [value]="u.userId" [selected]="u.userId === stepSelectedUsers[i]">
2575
+ {{ u.fullName || u.userId }}{{ u.department ? ' · ' + u.department : '' }}{{ u.position ? ' (' + u.position + ')' : '' }}
2576
+ </option>
2577
+ }
2578
+ </select>
2579
+ }
2580
+ </div>
2581
+ }
2582
+ <!-- Single fixed-user step: show who is assigned -->
2583
+ @if (!stepLoadingCandidates[i] && stepCandidates[i]?.length === 1) {
2584
+ <div class="arv-step-fixed">
2585
+ {{ stepCandidates[i][0].fullName || stepCandidates[i][0].userId }}{{ stepCandidates[i][0].department ? ' · ' + stepCandidates[i][0].department : '' }}{{ stepCandidates[i][0].position ? ' (' + stepCandidates[i][0].position + ')' : '' }}
2586
+ </div>
2587
+ }
2588
+ </div>
2589
+ }
2590
+ @if (selectedTemplate.referenceUserIds.length > 0) {
2591
+ <div class="arv-step-preview arv-step-preview-cc">
2592
+ <span class="arv-step-preview-num">CC</span>
2593
+ <span class="arv-step-preview-name">{{ selectedTemplate.referenceUserIds.length }} reference user(s) from template</span>
2594
+ </div>
2595
+ }
2596
+ </div>
2597
+ }
2598
+ </div>
2599
+ }
2600
+
2601
+ <!-- Ad-hoc steps -->
2602
+ @if (routingMode === 'adhoc') {
2603
+ <div class="arv-steps">
2604
+ @for (step of adHocSteps; track step.stepOrder; let i = $index) {
2605
+ <div class="arv-step">
2606
+ <div class="arv-step-header">
2607
+ <span class="arv-step-num">Step {{ i + 1 }}</span>
2608
+ <input class="arv-input" [value]="step.stepName" (input)="step.stepName = $any($event.target).value" placeholder="Step name" />
2609
+ <button class="arv-btn-icon arv-btn-danger" (click)="removeStep(i)" title="Remove step">
2610
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
2611
+ </button>
2612
+ </div>
2613
+ <div class="arv-approvers">
2614
+ <div class="arv-approver-tags">
2615
+ @for (uid of step.approverUserIds; track uid; let j = $index) {
2616
+ <span class="arv-tag">
2617
+ {{ userLabelMap[uid] || uid }}
2618
+ <button class="arv-tag-remove" (click)="removeApprover(i, j)">x</button>
2619
+ </span>
2620
+ }
2621
+ </div>
2622
+ <div class="arv-user-search">
2623
+ <input class="arv-input arv-input-sm" [value]="userSearchQuery[i] || ''"
2624
+ (input)="onUserSearchInput(i, $any($event.target).value)"
2625
+ placeholder="Search approver..." />
2626
+ @if (userSearchResults[i]?.length) {
2627
+ <div class="arv-search-results">
2628
+ @for (u of userSearchResults[i]; track u.id) {
2629
+ <div class="arv-search-item" (click)="addApprover(i, u.id)">
2630
+ {{ u.fullName || u.userName }}
2631
+ </div>
2632
+ }
2633
+ </div>
2634
+ }
2635
+ </div>
2636
+ </div>
2637
+ </div>
2638
+ }
2639
+ <button class="arv-btn arv-btn-outline" (click)="addStep()">+ Add Step</button>
2640
+ </div>
2641
+ }
2565
2642
  </div>
2566
2643
 
2567
- <!-- Ad-hoc steps -->
2568
- <div *ngIf="routingMode === 'adhoc'" class="arv-steps">
2569
- <div class="arv-step" *ngFor="let step of adHocSteps; let i = index">
2570
- <div class="arv-step-header">
2571
- <span class="arv-step-num">Step {{ i + 1 }}</span>
2572
- <input class="arv-input" [value]="step.stepName" (input)="step.stepName = $any($event.target).value" placeholder="Step name" />
2573
- <button class="arv-btn-icon arv-btn-danger" (click)="removeStep(i)" title="Remove step">
2574
- <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
2575
- </button>
2576
- </div>
2577
- <div class="arv-approvers">
2578
- <div class="arv-approver-tags">
2579
- <span class="arv-tag" *ngFor="let uid of step.approverUserIds; let j = index">
2580
- {{ userLabelMap[uid] || uid }}
2581
- <button class="arv-tag-remove" (click)="removeApprover(i, j)">x</button>
2582
- </span>
2583
- </div>
2584
- <div class="arv-user-search">
2585
- <input class="arv-input arv-input-sm" [value]="userSearchQuery[i] || ''"
2586
- (input)="onUserSearchInput(i, $any($event.target).value)"
2587
- placeholder="Search approver..." />
2588
- <div class="arv-search-results" *ngIf="userSearchResults[i]?.length">
2589
- <div class="arv-search-item"
2590
- *ngFor="let u of userSearchResults[i]"
2591
- (click)="addApprover(i, u.id)">
2644
+ <!-- Reference / CC users -->
2645
+ <div class="arv-references">
2646
+ <h4 class="arv-section-title">CC / Reference</h4>
2647
+ <p class="arv-hint">These users will be notified when the approval completes, but won't be asked to approve.</p>
2648
+ <div class="arv-approver-tags">
2649
+ @for (uid of referenceUserIds; track uid; let j = $index) {
2650
+ <span class="arv-tag">
2651
+ {{ userLabelMap[uid] || uid }}
2652
+ <button class="arv-tag-remove" (click)="removeReference(j)">x</button>
2653
+ </span>
2654
+ }
2655
+ </div>
2656
+ <div class="arv-user-search">
2657
+ <input class="arv-input arv-input-sm" [value]="refSearchQuery"
2658
+ (input)="onRefSearchInput($any($event.target).value)"
2659
+ placeholder="Search CC user..." />
2660
+ @if (refSearchResults?.length) {
2661
+ <div class="arv-search-results">
2662
+ @for (u of refSearchResults; track u.id) {
2663
+ <div class="arv-search-item" (click)="addReference(u.id)">
2592
2664
  {{ u.fullName || u.userName }}
2593
2665
  </div>
2594
- </div>
2666
+ }
2595
2667
  </div>
2596
- </div>
2668
+ }
2597
2669
  </div>
2598
- <button class="arv-btn arv-btn-outline" (click)="addStep()">+ Add Step</button>
2599
2670
  </div>
2600
- </div>
2601
2671
 
2602
- <!-- Reference / CC users -->
2603
- <div class="arv-references">
2604
- <h4 class="arv-section-title">CC / Reference</h4>
2605
- <p class="arv-hint">These users will be notified when the approval completes, but won't be asked to approve.</p>
2606
- <div class="arv-approver-tags">
2607
- <span class="arv-tag" *ngFor="let uid of referenceUserIds; let j = index">
2608
- {{ userLabelMap[uid] || uid }}
2609
- <button class="arv-tag-remove" (click)="removeReference(j)">x</button>
2610
- </span>
2611
- </div>
2612
- <div class="arv-user-search">
2613
- <input class="arv-input arv-input-sm" [value]="refSearchQuery"
2614
- (input)="onRefSearchInput($any($event.target).value)"
2615
- placeholder="Search CC user..." />
2616
- <div class="arv-search-results" *ngIf="refSearchResults?.length">
2617
- <div class="arv-search-item"
2618
- *ngFor="let u of refSearchResults"
2619
- (click)="addReference(u.id)">
2620
- {{ u.fullName || u.userName }}
2621
- </div>
2622
- </div>
2672
+ <!-- Actions -->
2673
+ <div class="arv-actions">
2674
+ <button class="arv-btn arv-btn-secondary" (click)="onCancel()">Cancel</button>
2675
+ <button class="arv-btn arv-btn-primary" [disabled]="submitting" (click)="submit()">
2676
+ @if (submitting) {
2677
+ <span class="arv-spinner"></span>
2678
+ }
2679
+ {{ submitting ? 'Submitting...' : 'Submit for Approval' }}
2680
+ </button>
2623
2681
  </div>
2624
- </div>
2625
2682
 
2626
- <!-- Actions -->
2627
- <div class="arv-actions">
2628
- <button class="arv-btn arv-btn-secondary" (click)="onCancel()">Cancel</button>
2629
- <button class="arv-btn arv-btn-primary" [disabled]="submitting" (click)="submit()">
2630
- <span *ngIf="submitting" class="arv-spinner"></span>
2631
- {{ submitting ? 'Submitting...' : 'Submit for Approval' }}
2632
- </button>
2683
+ @if (errorMessage) {
2684
+ <div class="arv-error">{{ errorMessage }}</div>
2685
+ }
2633
2686
  </div>
2634
-
2635
- <div class="arv-error" *ngIf="errorMessage">{{ errorMessage }}</div>
2636
- </div>
2687
+ }
2637
2688
  </div>
2638
2689
 
2639
2690
  <!-- Success state -->
2640
- <div class="arv-success" *ngIf="isSubmitted">
2641
- <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="#66bb6a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2642
- <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
2643
- <polyline points="22 4 12 14.01 9 11.01"/>
2644
- </svg>
2645
- <p>Submitted for approval successfully.</p>
2646
- </div>
2691
+ @if (isSubmitted) {
2692
+ <div class="arv-success">
2693
+ <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="#66bb6a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2694
+ <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
2695
+ <polyline points="22 4 12 14.01 9 11.01"/>
2696
+ </svg>
2697
+ <p>Submitted for approval successfully.</p>
2698
+ </div>
2699
+ }
2647
2700
  </div>
2648
- `, isInline: true, styles: [":host{display:block;--arv-primary: #90caf9;--arv-primary-hover: #64b5f6;--arv-success: #66bb6a;--arv-danger: #ef5350;--arv-text: #e0e0e0;--arv-text-muted: #9e9e9e;--arv-bg: #1e1e2e;--arv-bg2: #27273a;--arv-border: #383850;--arv-radius: 8px}:host(.theme-light){--arv-primary: #1565c0;--arv-primary-hover: #0d47a1;--arv-success: #2e7d32;--arv-danger: #c62828;--arv-text: #212121;--arv-text-muted: #616161;--arv-bg: #ffffff;--arv-bg2: #f5f5f5;--arv-border: #e0e0e0}.arv-container{display:flex;flex-direction:column;height:100%}.arv-content-body{flex:1;overflow:auto}.arv-footer{border-top:1px solid var(--arv-border);background:var(--arv-bg2)}.arv-footer-inner{padding:16px;display:flex;flex-direction:column;gap:14px}.arv-section-title{margin:0 0 8px;font-size:13px;font-weight:700;color:var(--arv-primary);text-transform:uppercase;letter-spacing:.5px}.arv-routing-header{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px}.arv-routing-mode{display:flex;gap:12px}.arv-radio{display:flex;align-items:center;gap:6px;font-size:13px;color:var(--arv-text);cursor:pointer}.arv-label{font-size:12px;color:var(--arv-text-muted);display:block;margin-bottom:4px}.arv-select{width:100%;padding:8px 10px;border:1px solid var(--arv-border);border-radius:var(--arv-radius);background:var(--arv-bg);color:var(--arv-text);font-size:13px}.arv-input{padding:7px 10px;border:1px solid var(--arv-border);border-radius:var(--arv-radius);background:var(--arv-bg);color:var(--arv-text);font-size:13px}.arv-input-sm{width:100%;margin-top:4px}.arv-steps{display:flex;flex-direction:column;gap:10px}.arv-step{background:var(--arv-bg);border:1px solid var(--arv-border);border-radius:var(--arv-radius);padding:10px}.arv-step-header{display:flex;align-items:center;gap:8px;margin-bottom:8px}.arv-step-num{font-size:12px;font-weight:700;color:var(--arv-primary);min-width:44px}.arv-approver-tags{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:6px}.arv-tag{display:inline-flex;align-items:center;gap:4px;padding:3px 8px;background:#90caf91f;border:1px solid rgba(144,202,249,.3);border-radius:12px;font-size:12px;color:var(--arv-primary)}.arv-tag-remove{background:none;border:none;cursor:pointer;color:var(--arv-text-muted);font-size:11px;padding:0 2px;line-height:1}.arv-tag-remove:hover{color:var(--arv-danger)}.arv-user-search{position:relative}.arv-search-results{position:absolute;left:0;right:0;z-index:100;background:var(--arv-bg2);border:1px solid var(--arv-border);border-radius:var(--arv-radius);max-height:150px;overflow-y:auto}.arv-search-item{padding:8px 12px;cursor:pointer;font-size:13px;color:var(--arv-text)}.arv-search-item:hover{background:var(--arv-bg)}.arv-hint{font-size:12px;color:var(--arv-text-muted);margin:0 0 8px}.arv-btn-icon{background:none;border:none;cursor:pointer;padding:2px;border-radius:4px;display:inline-flex;align-items:center;justify-content:center}.arv-btn-danger{color:var(--arv-danger)}.arv-btn-danger:hover{background:#ef53501a}.arv-actions{display:flex;justify-content:flex-end;gap:10px}.arv-btn{padding:9px 18px;border-radius:var(--arv-radius);font-size:13px;font-weight:600;cursor:pointer;border:none;transition:background .15s}.arv-btn-primary{background:var(--arv-primary);color:#fff}.arv-btn-primary:hover:not(:disabled){background:var(--arv-primary-hover)}.arv-btn-primary:disabled{opacity:.6;cursor:not-allowed}.arv-btn-secondary{background:transparent;border:1px solid var(--arv-border);color:var(--arv-text-muted)}.arv-btn-secondary:hover{color:var(--arv-text);border-color:var(--arv-text-muted)}.arv-btn-outline{background:transparent;border:1px dashed var(--arv-border);color:var(--arv-text-muted);font-size:12px;padding:6px 12px}.arv-btn-outline:hover{border-color:var(--arv-primary);color:var(--arv-primary)}.arv-spinner{display:inline-block;width:14px;height:14px;border:2px solid rgba(255,255,255,.3);border-top-color:#fff;border-radius:50%;animation:arv-spin .6s linear infinite;margin-right:6px}@keyframes arv-spin{to{transform:rotate(360deg)}}.arv-error{padding:8px 12px;background:#ef53501a;border:1px solid rgba(239,83,80,.3);border-radius:var(--arv-radius);font-size:13px;color:var(--arv-danger)}.arv-success{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:32px;gap:12px;text-align:center}.arv-success p{color:var(--arv-success);font-size:15px;font-weight:600;margin:0}.arv-references{border-top:1px solid var(--arv-border);padding-top:12px}.arv-template-loading{font-size:12px;color:var(--arv-text-muted);margin-top:8px;padding:4px 0}.arv-template-steps{margin-top:8px;display:flex;flex-direction:column;gap:6px}.arv-step-card{background:var(--arv-bg);border:1px solid var(--arv-border);border-radius:var(--arv-radius);overflow:hidden}.arv-step-card-header{display:flex;align-items:center;gap:8px;padding:7px 10px;background:#00000008;border-bottom:1px solid var(--arv-border);flex-wrap:wrap}.arv-step-preview{display:flex;align-items:center;gap:8px;padding:5px 8px;background:var(--arv-bg);border:1px solid var(--arv-border);border-radius:4px;font-size:12px}.arv-step-preview-num{font-weight:700;color:var(--arv-primary);min-width:44px;flex-shrink:0;font-size:12px}.arv-step-preview-name{flex:1;color:var(--arv-text);font-size:12px;font-weight:600}.arv-step-role-badge{font-size:11px;color:var(--arv-text-muted);background:#90caf91a;border:1px solid rgba(144,202,249,.25);border-radius:10px;padding:1px 7px}.arv-step-preview-cc .arv-step-preview-num{color:var(--arv-text-muted)}.arv-locked-template{display:flex;align-items:center;gap:8px;padding:7px 10px;background:var(--arv-bg);border:1px solid var(--arv-border);border-radius:var(--arv-radius);margin-top:6px}.arv-locked-label{font-size:11px;color:var(--arv-text-muted);text-transform:uppercase;letter-spacing:.5px;flex-shrink:0}.arv-locked-name{font-size:13px;font-weight:600;color:var(--arv-text)}.arv-step-picker{padding:8px 10px}.arv-select-sm{font-size:12px;padding:6px 8px}.arv-step-fixed{padding:6px 10px;font-size:12px;color:var(--arv-text-muted)}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
2701
+ `, isInline: true, styles: [":host{display:block;--arv-primary: #90caf9;--arv-primary-hover: #64b5f6;--arv-success: #66bb6a;--arv-danger: #ef5350;--arv-text: #e0e0e0;--arv-text-muted: #9e9e9e;--arv-bg: #1e1e2e;--arv-bg2: #27273a;--arv-border: #383850;--arv-radius: 8px}:host(.theme-light){--arv-primary: #1565c0;--arv-primary-hover: #0d47a1;--arv-success: #2e7d32;--arv-danger: #c62828;--arv-text: #212121;--arv-text-muted: #616161;--arv-bg: #ffffff;--arv-bg2: #f5f5f5;--arv-border: #e0e0e0}.arv-container{display:flex;flex-direction:column;height:100%}.arv-content-body{flex:1;overflow:auto}.arv-footer{border-top:1px solid var(--arv-border);background:var(--arv-bg2)}.arv-footer-inner{padding:16px;display:flex;flex-direction:column;gap:14px}.arv-section-title{margin:0 0 8px;font-size:13px;font-weight:700;color:var(--arv-primary);text-transform:uppercase;letter-spacing:.5px}.arv-routing-header{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px}.arv-routing-mode{display:flex;gap:12px}.arv-radio{display:flex;align-items:center;gap:6px;font-size:13px;color:var(--arv-text);cursor:pointer}.arv-label{font-size:12px;color:var(--arv-text-muted);display:block;margin-bottom:4px}.arv-select{width:100%;padding:8px 10px;border:1px solid var(--arv-border);border-radius:var(--arv-radius);background:var(--arv-bg);color:var(--arv-text);font-size:13px}.arv-input{padding:7px 10px;border:1px solid var(--arv-border);border-radius:var(--arv-radius);background:var(--arv-bg);color:var(--arv-text);font-size:13px}.arv-input-sm{width:100%;margin-top:4px}.arv-steps{display:flex;flex-direction:column;gap:10px}.arv-step{background:var(--arv-bg);border:1px solid var(--arv-border);border-radius:var(--arv-radius);padding:10px}.arv-step-header{display:flex;align-items:center;gap:8px;margin-bottom:8px}.arv-step-num{font-size:12px;font-weight:700;color:var(--arv-primary);min-width:44px}.arv-approver-tags{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:6px}.arv-tag{display:inline-flex;align-items:center;gap:4px;padding:3px 8px;background:#90caf91f;border:1px solid rgba(144,202,249,.3);border-radius:12px;font-size:12px;color:var(--arv-primary)}.arv-tag-remove{background:none;border:none;cursor:pointer;color:var(--arv-text-muted);font-size:11px;padding:0 2px;line-height:1}.arv-tag-remove:hover{color:var(--arv-danger)}.arv-user-search{position:relative}.arv-search-results{position:absolute;left:0;right:0;z-index:100;background:var(--arv-bg2);border:1px solid var(--arv-border);border-radius:var(--arv-radius);max-height:150px;overflow-y:auto}.arv-search-item{padding:8px 12px;cursor:pointer;font-size:13px;color:var(--arv-text)}.arv-search-item:hover{background:var(--arv-bg)}.arv-hint{font-size:12px;color:var(--arv-text-muted);margin:0 0 8px}.arv-btn-icon{background:none;border:none;cursor:pointer;padding:2px;border-radius:4px;display:inline-flex;align-items:center;justify-content:center}.arv-btn-danger{color:var(--arv-danger)}.arv-btn-danger:hover{background:#ef53501a}.arv-actions{display:flex;justify-content:flex-end;gap:10px}.arv-btn{padding:9px 18px;border-radius:var(--arv-radius);font-size:13px;font-weight:600;cursor:pointer;border:none;transition:background .15s}.arv-btn-primary{background:var(--arv-primary);color:#fff}.arv-btn-primary:hover:not(:disabled){background:var(--arv-primary-hover)}.arv-btn-primary:disabled{opacity:.6;cursor:not-allowed}.arv-btn-secondary{background:transparent;border:1px solid var(--arv-border);color:var(--arv-text-muted)}.arv-btn-secondary:hover{color:var(--arv-text);border-color:var(--arv-text-muted)}.arv-btn-outline{background:transparent;border:1px dashed var(--arv-border);color:var(--arv-text-muted);font-size:12px;padding:6px 12px}.arv-btn-outline:hover{border-color:var(--arv-primary);color:var(--arv-primary)}.arv-spinner{display:inline-block;width:14px;height:14px;border:2px solid rgba(255,255,255,.3);border-top-color:#fff;border-radius:50%;animation:arv-spin .6s linear infinite;margin-right:6px}@keyframes arv-spin{to{transform:rotate(360deg)}}.arv-error{padding:8px 12px;background:#ef53501a;border:1px solid rgba(239,83,80,.3);border-radius:var(--arv-radius);font-size:13px;color:var(--arv-danger)}.arv-success{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:32px;gap:12px;text-align:center}.arv-success p{color:var(--arv-success);font-size:15px;font-weight:600;margin:0}.arv-references{border-top:1px solid var(--arv-border);padding-top:12px}.arv-template-loading{font-size:12px;color:var(--arv-text-muted);margin-top:8px;padding:4px 0}.arv-template-steps{margin-top:8px;display:flex;flex-direction:column;gap:6px}.arv-step-card{background:var(--arv-bg);border:1px solid var(--arv-border);border-radius:var(--arv-radius);overflow:hidden}.arv-step-card-header{display:flex;align-items:center;gap:8px;padding:7px 10px;background:#00000008;border-bottom:1px solid var(--arv-border);flex-wrap:wrap}.arv-step-preview{display:flex;align-items:center;gap:8px;padding:5px 8px;background:var(--arv-bg);border:1px solid var(--arv-border);border-radius:4px;font-size:12px}.arv-step-preview-num{font-weight:700;color:var(--arv-primary);min-width:44px;flex-shrink:0;font-size:12px}.arv-step-preview-name{flex:1;color:var(--arv-text);font-size:12px;font-weight:600}.arv-step-role-badge{font-size:11px;color:var(--arv-text-muted);background:#90caf91a;border:1px solid rgba(144,202,249,.25);border-radius:10px;padding:1px 7px}.arv-step-preview-cc .arv-step-preview-num{color:var(--arv-text-muted)}.arv-locked-template{display:flex;align-items:center;gap:8px;padding:7px 10px;background:var(--arv-bg);border:1px solid var(--arv-border);border-radius:var(--arv-radius);margin-top:6px}.arv-locked-label{font-size:11px;color:var(--arv-text-muted);text-transform:uppercase;letter-spacing:.5px;flex-shrink:0}.arv-locked-name{font-size:13px;font-weight:600;color:var(--arv-text)}.arv-step-picker{padding:8px 10px}.arv-select-sm{font-size:12px;padding:6px 8px}.arv-step-fixed{padding:6px 10px;font-size:12px;color:var(--arv-text-muted)}\n"] });
2649
2702
  }
2650
2703
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: MaArvContainerComponent, decorators: [{
2651
2704
  type: Component,
2652
- args: [{ selector: 'ma-arv-container', standalone: true, imports: [NgIf, NgFor], template: `
2653
- <div class="arv-container">
2705
+ args: [{ selector: 'ma-arv-container', standalone: true, template: `
2706
+ <div class="arv-container">
2654
2707
  <!-- Content Area -->
2655
2708
  <div class="arv-content-body" #contentBody>
2656
2709
  <ng-content></ng-content>
2657
2710
  </div>
2658
2711
 
2659
2712
  <!-- Approval Footer -->
2660
- <div class="arv-footer" *ngIf="!isSubmitted">
2661
- <div class="arv-footer-inner">
2662
-
2663
- <!-- Routing mode -->
2664
- <div class="arv-routing">
2665
- <div class="arv-routing-header">
2666
- <h4 class="arv-section-title">Approval Routing</h4>
2667
- <!-- Show routing toggle only when templateId is NOT locked -->
2668
- <div *ngIf="!templateId" class="arv-routing-mode">
2669
- <label class="arv-radio">
2670
- <input type="radio" name="routingMode" value="template" [checked]="routingMode === 'template'" (change)="routingMode = 'template'"> Use Template
2671
- </label>
2672
- <label class="arv-radio">
2673
- <input type="radio" name="routingMode" value="adhoc" [checked]="routingMode === 'adhoc'" (change)="routingMode = 'adhoc'"> Custom Steps
2674
- </label>
2675
- </div>
2676
- </div>
2677
-
2678
- <!-- Locked template: show name only, no selector -->
2679
- <div *ngIf="templateId && routingMode === 'template'" class="arv-template-select">
2680
- <div *ngIf="loadingTemplate" class="arv-template-loading">Loading template...</div>
2681
- <div *ngIf="selectedTemplate && !loadingTemplate" class="arv-locked-template">
2682
- <span class="arv-locked-label">Template</span>
2683
- <span class="arv-locked-name">{{ selectedTemplate.name }}</span>
2713
+ <div class="arv-footer">
2714
+ @if (!isSubmitted) {
2715
+ <div class="arv-footer-inner">
2716
+
2717
+ <!-- Routing mode -->
2718
+ <div class="arv-routing">
2719
+ <div class="arv-routing-header">
2720
+ <h4 class="arv-section-title">Approval Routing</h4>
2721
+ <!-- Show routing toggle only when templateId is NOT locked to a single template -->
2722
+ @if (!templateId || (templateIds && templateIds.length > 0)) {
2723
+ <div class="arv-routing-mode">
2724
+ <label class="arv-radio">
2725
+ <input type="radio" name="routingMode" value="template" [checked]="routingMode === 'template'" (change)="routingMode = 'template'"> Use Template
2726
+ </label>
2727
+ <label class="arv-radio">
2728
+ <input type="radio" name="routingMode" value="adhoc" [checked]="routingMode === 'adhoc'" (change)="routingMode = 'adhoc'"> Custom Steps
2729
+ </label>
2730
+ </div>
2731
+ }
2684
2732
  </div>
2685
- </div>
2686
2733
 
2687
- <!-- Free template selector (no locked templateId) -->
2688
- <div *ngIf="!templateId && routingMode === 'template'" class="arv-template-select">
2689
- <label class="arv-label">Select Template</label>
2690
- <select class="arv-select" (change)="onTemplateChange($event)">
2691
- <option value="">-- Select a template --</option>
2692
- <option *ngFor="let t of templates" [value]="t.id" [selected]="t.id === selectedTemplateId">{{ t.name }}</option>
2693
- </select>
2694
- </div>
2695
-
2696
- <!-- Step pickers (shared for both locked and free template) -->
2697
- <div *ngIf="routingMode === 'template'" class="arv-template-select">
2698
- <div *ngIf="!loadingTemplate && selectedTemplate" class="arv-template-steps">
2699
- <div class="arv-step-card" *ngFor="let s of selectedTemplate.steps; let i = index">
2700
- <div class="arv-step-card-header">
2701
- <span class="arv-step-preview-num">Step {{ s.stepOrder }}</span>
2702
- <span class="arv-step-preview-name">{{ s.stepName }}</span>
2703
- <span *ngIf="s.roles && s.roles.length > 0" class="arv-step-role-badge">
2704
- {{ s.roles[0].positionLevel }}{{ s.roles[0].orgName ? ' · ' + s.roles[0].orgName : '' }}
2705
- </span>
2706
- </div>
2707
- <!-- Selectable step (role-based or multi-assignee): show approver picker -->
2708
- <div *ngIf="stepCandidates[i]?.length > 1 || stepLoadingCandidates[i]" class="arv-step-picker">
2709
- <div *ngIf="stepLoadingCandidates[i]" class="arv-template-loading">Loading candidates...</div>
2710
- <select *ngIf="!stepLoadingCandidates[i]" class="arv-select arv-select-sm"
2711
- (change)="onStepUserChange(i, $event)">
2712
- <option value="">-- Select approver --</option>
2713
- <option *ngFor="let u of stepCandidates[i]" [value]="u.userId"
2714
- [selected]="u.userId === stepSelectedUsers[i]">
2715
- {{ u.fullName || u.userId }}{{ u.department ? ' · ' + u.department : '' }}{{ u.position ? ' (' + u.position + ')' : '' }}
2716
- </option>
2717
- </select>
2718
- </div>
2719
- <!-- Single fixed-user step: show who is assigned -->
2720
- <div *ngIf="!stepLoadingCandidates[i] && stepCandidates[i]?.length === 1" class="arv-step-fixed">
2721
- {{ stepCandidates[i][0].fullName || stepCandidates[i][0].userId }}{{ stepCandidates[i][0].department ? ' · ' + stepCandidates[i][0].department : '' }}{{ stepCandidates[i][0].position ? ' (' + stepCandidates[i][0].position + ')' : '' }}
2722
- </div>
2734
+ <!-- Locked template: only when templateId set and no multi-choice -->
2735
+ @if (templateId && (!templateIds || templateIds.length <= 1) && routingMode === 'template') {
2736
+ <div class="arv-template-select">
2737
+ @if (loadingTemplate) {
2738
+ <div class="arv-template-loading">Loading template...</div>
2739
+ }
2740
+ @if (selectedTemplate && !loadingTemplate) {
2741
+ <div class="arv-locked-template">
2742
+ <span class="arv-locked-label">Template</span>
2743
+ <span class="arv-locked-name">{{ selectedTemplate.name }}</span>
2744
+ </div>
2745
+ }
2723
2746
  </div>
2724
- <div *ngIf="selectedTemplate.referenceUserIds.length > 0" class="arv-step-preview arv-step-preview-cc">
2725
- <span class="arv-step-preview-num">CC</span>
2726
- <span class="arv-step-preview-name">{{ selectedTemplate.referenceUserIds.length }} reference user(s) from template</span>
2747
+ }
2748
+
2749
+ <!-- Template selector: shown when no templateId, OR when templateIds has multiple choices -->
2750
+ @if ((!templateId || (templateIds && templateIds.length > 1)) && routingMode === 'template') {
2751
+ <div class="arv-template-select">
2752
+ <label class="arv-label">Select Template</label>
2753
+ <select class="arv-select" (change)="onTemplateChange($event)">
2754
+ <option value="">-- Select a template --</option>
2755
+ @for (t of templates; track t.id) {
2756
+ <option [value]="t.id" [selected]="t.id === selectedTemplateId">{{ t.name }}</option>
2757
+ }
2758
+ </select>
2727
2759
  </div>
2728
- </div>
2760
+ }
2761
+
2762
+ <!-- Step pickers (shared for both locked and free template) -->
2763
+ @if (routingMode === 'template') {
2764
+ <div class="arv-template-select">
2765
+ @if (!loadingTemplate && selectedTemplate) {
2766
+ <div class="arv-template-steps">
2767
+ @for (s of selectedTemplate.steps; track s.stepOrder; let i = $index) {
2768
+ <div class="arv-step-card">
2769
+ <div class="arv-step-card-header">
2770
+ <span class="arv-step-preview-num">Step {{ s.stepOrder }}</span>
2771
+ <span class="arv-step-preview-name">{{ s.stepName }}</span>
2772
+ @if (s.roles && s.roles.length > 0) {
2773
+ <span class="arv-step-role-badge">
2774
+ {{ s.roles[0].positionLevel }}{{ s.roles[0].orgName ? ' · ' + s.roles[0].orgName : '' }}
2775
+ </span>
2776
+ }
2777
+ </div>
2778
+ <!-- Selectable step: show approver picker -->
2779
+ @if (stepCandidates[i]?.length > 1 || stepLoadingCandidates[i]) {
2780
+ <div class="arv-step-picker">
2781
+ @if (stepLoadingCandidates[i]) {
2782
+ <div class="arv-template-loading">Loading candidates...</div>
2783
+ }
2784
+ @if (!stepLoadingCandidates[i]) {
2785
+ <select class="arv-select arv-select-sm" (change)="onStepUserChange(i, $event)">
2786
+ <option value="">-- Select approver --</option>
2787
+ @for (u of stepCandidates[i]; track u.userId) {
2788
+ <option [value]="u.userId" [selected]="u.userId === stepSelectedUsers[i]">
2789
+ {{ u.fullName || u.userId }}{{ u.department ? ' · ' + u.department : '' }}{{ u.position ? ' (' + u.position + ')' : '' }}
2790
+ </option>
2791
+ }
2792
+ </select>
2793
+ }
2794
+ </div>
2795
+ }
2796
+ <!-- Single fixed-user step: show who is assigned -->
2797
+ @if (!stepLoadingCandidates[i] && stepCandidates[i]?.length === 1) {
2798
+ <div class="arv-step-fixed">
2799
+ {{ stepCandidates[i][0].fullName || stepCandidates[i][0].userId }}{{ stepCandidates[i][0].department ? ' · ' + stepCandidates[i][0].department : '' }}{{ stepCandidates[i][0].position ? ' (' + stepCandidates[i][0].position + ')' : '' }}
2800
+ </div>
2801
+ }
2802
+ </div>
2803
+ }
2804
+ @if (selectedTemplate.referenceUserIds.length > 0) {
2805
+ <div class="arv-step-preview arv-step-preview-cc">
2806
+ <span class="arv-step-preview-num">CC</span>
2807
+ <span class="arv-step-preview-name">{{ selectedTemplate.referenceUserIds.length }} reference user(s) from template</span>
2808
+ </div>
2809
+ }
2810
+ </div>
2811
+ }
2812
+ </div>
2813
+ }
2814
+
2815
+ <!-- Ad-hoc steps -->
2816
+ @if (routingMode === 'adhoc') {
2817
+ <div class="arv-steps">
2818
+ @for (step of adHocSteps; track step.stepOrder; let i = $index) {
2819
+ <div class="arv-step">
2820
+ <div class="arv-step-header">
2821
+ <span class="arv-step-num">Step {{ i + 1 }}</span>
2822
+ <input class="arv-input" [value]="step.stepName" (input)="step.stepName = $any($event.target).value" placeholder="Step name" />
2823
+ <button class="arv-btn-icon arv-btn-danger" (click)="removeStep(i)" title="Remove step">
2824
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
2825
+ </button>
2826
+ </div>
2827
+ <div class="arv-approvers">
2828
+ <div class="arv-approver-tags">
2829
+ @for (uid of step.approverUserIds; track uid; let j = $index) {
2830
+ <span class="arv-tag">
2831
+ {{ userLabelMap[uid] || uid }}
2832
+ <button class="arv-tag-remove" (click)="removeApprover(i, j)">x</button>
2833
+ </span>
2834
+ }
2835
+ </div>
2836
+ <div class="arv-user-search">
2837
+ <input class="arv-input arv-input-sm" [value]="userSearchQuery[i] || ''"
2838
+ (input)="onUserSearchInput(i, $any($event.target).value)"
2839
+ placeholder="Search approver..." />
2840
+ @if (userSearchResults[i]?.length) {
2841
+ <div class="arv-search-results">
2842
+ @for (u of userSearchResults[i]; track u.id) {
2843
+ <div class="arv-search-item" (click)="addApprover(i, u.id)">
2844
+ {{ u.fullName || u.userName }}
2845
+ </div>
2846
+ }
2847
+ </div>
2848
+ }
2849
+ </div>
2850
+ </div>
2851
+ </div>
2852
+ }
2853
+ <button class="arv-btn arv-btn-outline" (click)="addStep()">+ Add Step</button>
2854
+ </div>
2855
+ }
2729
2856
  </div>
2730
2857
 
2731
- <!-- Ad-hoc steps -->
2732
- <div *ngIf="routingMode === 'adhoc'" class="arv-steps">
2733
- <div class="arv-step" *ngFor="let step of adHocSteps; let i = index">
2734
- <div class="arv-step-header">
2735
- <span class="arv-step-num">Step {{ i + 1 }}</span>
2736
- <input class="arv-input" [value]="step.stepName" (input)="step.stepName = $any($event.target).value" placeholder="Step name" />
2737
- <button class="arv-btn-icon arv-btn-danger" (click)="removeStep(i)" title="Remove step">
2738
- <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
2739
- </button>
2740
- </div>
2741
- <div class="arv-approvers">
2742
- <div class="arv-approver-tags">
2743
- <span class="arv-tag" *ngFor="let uid of step.approverUserIds; let j = index">
2744
- {{ userLabelMap[uid] || uid }}
2745
- <button class="arv-tag-remove" (click)="removeApprover(i, j)">x</button>
2746
- </span>
2747
- </div>
2748
- <div class="arv-user-search">
2749
- <input class="arv-input arv-input-sm" [value]="userSearchQuery[i] || ''"
2750
- (input)="onUserSearchInput(i, $any($event.target).value)"
2751
- placeholder="Search approver..." />
2752
- <div class="arv-search-results" *ngIf="userSearchResults[i]?.length">
2753
- <div class="arv-search-item"
2754
- *ngFor="let u of userSearchResults[i]"
2755
- (click)="addApprover(i, u.id)">
2858
+ <!-- Reference / CC users -->
2859
+ <div class="arv-references">
2860
+ <h4 class="arv-section-title">CC / Reference</h4>
2861
+ <p class="arv-hint">These users will be notified when the approval completes, but won't be asked to approve.</p>
2862
+ <div class="arv-approver-tags">
2863
+ @for (uid of referenceUserIds; track uid; let j = $index) {
2864
+ <span class="arv-tag">
2865
+ {{ userLabelMap[uid] || uid }}
2866
+ <button class="arv-tag-remove" (click)="removeReference(j)">x</button>
2867
+ </span>
2868
+ }
2869
+ </div>
2870
+ <div class="arv-user-search">
2871
+ <input class="arv-input arv-input-sm" [value]="refSearchQuery"
2872
+ (input)="onRefSearchInput($any($event.target).value)"
2873
+ placeholder="Search CC user..." />
2874
+ @if (refSearchResults?.length) {
2875
+ <div class="arv-search-results">
2876
+ @for (u of refSearchResults; track u.id) {
2877
+ <div class="arv-search-item" (click)="addReference(u.id)">
2756
2878
  {{ u.fullName || u.userName }}
2757
2879
  </div>
2758
- </div>
2880
+ }
2759
2881
  </div>
2760
- </div>
2882
+ }
2761
2883
  </div>
2762
- <button class="arv-btn arv-btn-outline" (click)="addStep()">+ Add Step</button>
2763
2884
  </div>
2764
- </div>
2765
2885
 
2766
- <!-- Reference / CC users -->
2767
- <div class="arv-references">
2768
- <h4 class="arv-section-title">CC / Reference</h4>
2769
- <p class="arv-hint">These users will be notified when the approval completes, but won't be asked to approve.</p>
2770
- <div class="arv-approver-tags">
2771
- <span class="arv-tag" *ngFor="let uid of referenceUserIds; let j = index">
2772
- {{ userLabelMap[uid] || uid }}
2773
- <button class="arv-tag-remove" (click)="removeReference(j)">x</button>
2774
- </span>
2775
- </div>
2776
- <div class="arv-user-search">
2777
- <input class="arv-input arv-input-sm" [value]="refSearchQuery"
2778
- (input)="onRefSearchInput($any($event.target).value)"
2779
- placeholder="Search CC user..." />
2780
- <div class="arv-search-results" *ngIf="refSearchResults?.length">
2781
- <div class="arv-search-item"
2782
- *ngFor="let u of refSearchResults"
2783
- (click)="addReference(u.id)">
2784
- {{ u.fullName || u.userName }}
2785
- </div>
2786
- </div>
2886
+ <!-- Actions -->
2887
+ <div class="arv-actions">
2888
+ <button class="arv-btn arv-btn-secondary" (click)="onCancel()">Cancel</button>
2889
+ <button class="arv-btn arv-btn-primary" [disabled]="submitting" (click)="submit()">
2890
+ @if (submitting) {
2891
+ <span class="arv-spinner"></span>
2892
+ }
2893
+ {{ submitting ? 'Submitting...' : 'Submit for Approval' }}
2894
+ </button>
2787
2895
  </div>
2788
- </div>
2789
2896
 
2790
- <!-- Actions -->
2791
- <div class="arv-actions">
2792
- <button class="arv-btn arv-btn-secondary" (click)="onCancel()">Cancel</button>
2793
- <button class="arv-btn arv-btn-primary" [disabled]="submitting" (click)="submit()">
2794
- <span *ngIf="submitting" class="arv-spinner"></span>
2795
- {{ submitting ? 'Submitting...' : 'Submit for Approval' }}
2796
- </button>
2897
+ @if (errorMessage) {
2898
+ <div class="arv-error">{{ errorMessage }}</div>
2899
+ }
2797
2900
  </div>
2798
-
2799
- <div class="arv-error" *ngIf="errorMessage">{{ errorMessage }}</div>
2800
- </div>
2901
+ }
2801
2902
  </div>
2802
2903
 
2803
2904
  <!-- Success state -->
2804
- <div class="arv-success" *ngIf="isSubmitted">
2805
- <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="#66bb6a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2806
- <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
2807
- <polyline points="22 4 12 14.01 9 11.01"/>
2808
- </svg>
2809
- <p>Submitted for approval successfully.</p>
2810
- </div>
2905
+ @if (isSubmitted) {
2906
+ <div class="arv-success">
2907
+ <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="#66bb6a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2908
+ <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
2909
+ <polyline points="22 4 12 14.01 9 11.01"/>
2910
+ </svg>
2911
+ <p>Submitted for approval successfully.</p>
2912
+ </div>
2913
+ }
2811
2914
  </div>
2812
2915
  `, styles: [":host{display:block;--arv-primary: #90caf9;--arv-primary-hover: #64b5f6;--arv-success: #66bb6a;--arv-danger: #ef5350;--arv-text: #e0e0e0;--arv-text-muted: #9e9e9e;--arv-bg: #1e1e2e;--arv-bg2: #27273a;--arv-border: #383850;--arv-radius: 8px}:host(.theme-light){--arv-primary: #1565c0;--arv-primary-hover: #0d47a1;--arv-success: #2e7d32;--arv-danger: #c62828;--arv-text: #212121;--arv-text-muted: #616161;--arv-bg: #ffffff;--arv-bg2: #f5f5f5;--arv-border: #e0e0e0}.arv-container{display:flex;flex-direction:column;height:100%}.arv-content-body{flex:1;overflow:auto}.arv-footer{border-top:1px solid var(--arv-border);background:var(--arv-bg2)}.arv-footer-inner{padding:16px;display:flex;flex-direction:column;gap:14px}.arv-section-title{margin:0 0 8px;font-size:13px;font-weight:700;color:var(--arv-primary);text-transform:uppercase;letter-spacing:.5px}.arv-routing-header{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px}.arv-routing-mode{display:flex;gap:12px}.arv-radio{display:flex;align-items:center;gap:6px;font-size:13px;color:var(--arv-text);cursor:pointer}.arv-label{font-size:12px;color:var(--arv-text-muted);display:block;margin-bottom:4px}.arv-select{width:100%;padding:8px 10px;border:1px solid var(--arv-border);border-radius:var(--arv-radius);background:var(--arv-bg);color:var(--arv-text);font-size:13px}.arv-input{padding:7px 10px;border:1px solid var(--arv-border);border-radius:var(--arv-radius);background:var(--arv-bg);color:var(--arv-text);font-size:13px}.arv-input-sm{width:100%;margin-top:4px}.arv-steps{display:flex;flex-direction:column;gap:10px}.arv-step{background:var(--arv-bg);border:1px solid var(--arv-border);border-radius:var(--arv-radius);padding:10px}.arv-step-header{display:flex;align-items:center;gap:8px;margin-bottom:8px}.arv-step-num{font-size:12px;font-weight:700;color:var(--arv-primary);min-width:44px}.arv-approver-tags{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:6px}.arv-tag{display:inline-flex;align-items:center;gap:4px;padding:3px 8px;background:#90caf91f;border:1px solid rgba(144,202,249,.3);border-radius:12px;font-size:12px;color:var(--arv-primary)}.arv-tag-remove{background:none;border:none;cursor:pointer;color:var(--arv-text-muted);font-size:11px;padding:0 2px;line-height:1}.arv-tag-remove:hover{color:var(--arv-danger)}.arv-user-search{position:relative}.arv-search-results{position:absolute;left:0;right:0;z-index:100;background:var(--arv-bg2);border:1px solid var(--arv-border);border-radius:var(--arv-radius);max-height:150px;overflow-y:auto}.arv-search-item{padding:8px 12px;cursor:pointer;font-size:13px;color:var(--arv-text)}.arv-search-item:hover{background:var(--arv-bg)}.arv-hint{font-size:12px;color:var(--arv-text-muted);margin:0 0 8px}.arv-btn-icon{background:none;border:none;cursor:pointer;padding:2px;border-radius:4px;display:inline-flex;align-items:center;justify-content:center}.arv-btn-danger{color:var(--arv-danger)}.arv-btn-danger:hover{background:#ef53501a}.arv-actions{display:flex;justify-content:flex-end;gap:10px}.arv-btn{padding:9px 18px;border-radius:var(--arv-radius);font-size:13px;font-weight:600;cursor:pointer;border:none;transition:background .15s}.arv-btn-primary{background:var(--arv-primary);color:#fff}.arv-btn-primary:hover:not(:disabled){background:var(--arv-primary-hover)}.arv-btn-primary:disabled{opacity:.6;cursor:not-allowed}.arv-btn-secondary{background:transparent;border:1px solid var(--arv-border);color:var(--arv-text-muted)}.arv-btn-secondary:hover{color:var(--arv-text);border-color:var(--arv-text-muted)}.arv-btn-outline{background:transparent;border:1px dashed var(--arv-border);color:var(--arv-text-muted);font-size:12px;padding:6px 12px}.arv-btn-outline:hover{border-color:var(--arv-primary);color:var(--arv-primary)}.arv-spinner{display:inline-block;width:14px;height:14px;border:2px solid rgba(255,255,255,.3);border-top-color:#fff;border-radius:50%;animation:arv-spin .6s linear infinite;margin-right:6px}@keyframes arv-spin{to{transform:rotate(360deg)}}.arv-error{padding:8px 12px;background:#ef53501a;border:1px solid rgba(239,83,80,.3);border-radius:var(--arv-radius);font-size:13px;color:var(--arv-danger)}.arv-success{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:32px;gap:12px;text-align:center}.arv-success p{color:var(--arv-success);font-size:15px;font-weight:600;margin:0}.arv-references{border-top:1px solid var(--arv-border);padding-top:12px}.arv-template-loading{font-size:12px;color:var(--arv-text-muted);margin-top:8px;padding:4px 0}.arv-template-steps{margin-top:8px;display:flex;flex-direction:column;gap:6px}.arv-step-card{background:var(--arv-bg);border:1px solid var(--arv-border);border-radius:var(--arv-radius);overflow:hidden}.arv-step-card-header{display:flex;align-items:center;gap:8px;padding:7px 10px;background:#00000008;border-bottom:1px solid var(--arv-border);flex-wrap:wrap}.arv-step-preview{display:flex;align-items:center;gap:8px;padding:5px 8px;background:var(--arv-bg);border:1px solid var(--arv-border);border-radius:4px;font-size:12px}.arv-step-preview-num{font-weight:700;color:var(--arv-primary);min-width:44px;flex-shrink:0;font-size:12px}.arv-step-preview-name{flex:1;color:var(--arv-text);font-size:12px;font-weight:600}.arv-step-role-badge{font-size:11px;color:var(--arv-text-muted);background:#90caf91a;border:1px solid rgba(144,202,249,.25);border-radius:10px;padding:1px 7px}.arv-step-preview-cc .arv-step-preview-num{color:var(--arv-text-muted)}.arv-locked-template{display:flex;align-items:center;gap:8px;padding:7px 10px;background:var(--arv-bg);border:1px solid var(--arv-border);border-radius:var(--arv-radius);margin-top:6px}.arv-locked-label{font-size:11px;color:var(--arv-text-muted);text-transform:uppercase;letter-spacing:.5px;flex-shrink:0}.arv-locked-name{font-size:13px;font-weight:600;color:var(--arv-text)}.arv-step-picker{padding:8px 10px}.arv-select-sm{font-size:12px;padding:6px 8px}.arv-step-fixed{padding:6px 10px;font-size:12px;color:var(--arv-text-muted)}\n"] }]
2813
2916
  }], propDecorators: { title: [{
@@ -2818,6 +2921,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
2818
2921
  type: Input
2819
2922
  }], templateId: [{
2820
2923
  type: Input
2924
+ }], templateIds: [{
2925
+ type: Input
2821
2926
  }], callbackUrl: [{
2822
2927
  type: Input
2823
2928
  }], deadlineHours: [{