mesauth-angular 1.6.9 → 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.
@@ -192,10 +192,10 @@ class MesAuthService {
192
192
  refreshUser() {
193
193
  return this.fetchCurrentUser();
194
194
  }
195
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: MesAuthService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
196
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: MesAuthService });
195
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: MesAuthService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
196
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: MesAuthService });
197
197
  }
198
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: MesAuthService, decorators: [{
198
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: MesAuthService, decorators: [{
199
199
  type: Injectable
200
200
  }], ctorParameters: () => [] });
201
201
 
@@ -267,13 +267,13 @@ function appendPermissions(body, allowedActions) {
267
267
  }
268
268
 
269
269
  class MesAuthModule {
270
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: MesAuthModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
271
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.18", ngImport: i0, type: MesAuthModule });
272
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: MesAuthModule, providers: [
270
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: MesAuthModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
271
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.7", ngImport: i0, type: MesAuthModule });
272
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: MesAuthModule, providers: [
273
273
  MesAuthService
274
274
  ] });
275
275
  }
276
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: MesAuthModule, decorators: [{
276
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: MesAuthModule, decorators: [{
277
277
  type: NgModule,
278
278
  args: [{
279
279
  providers: [
@@ -332,10 +332,10 @@ class ThemeService {
332
332
  refreshTheme() {
333
333
  this.detectTheme();
334
334
  }
335
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ThemeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
336
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ThemeService, providedIn: 'root' });
335
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ThemeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
336
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ThemeService, providedIn: 'root' });
337
337
  }
338
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ThemeService, decorators: [{
338
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ThemeService, decorators: [{
339
339
  type: Injectable,
340
340
  args: [{
341
341
  providedIn: 'root'
@@ -353,7 +353,7 @@ class UserProfileComponent {
353
353
  get themeClass() {
354
354
  return `theme-${this.currentTheme}`;
355
355
  }
356
- currentUser = signal(null, ...(ngDevMode ? [{ debugName: "currentUser" }] : []));
356
+ currentUser = signal(null, ...(ngDevMode ? [{ debugName: "currentUser" }] : /* istanbul ignore next */ []));
357
357
  currentTheme = 'light';
358
358
  unreadCount = 0;
359
359
  pendingApprovalCount = 0;
@@ -361,7 +361,7 @@ class UserProfileComponent {
361
361
  hasUser = false;
362
362
  destroy$ = new Subject();
363
363
  // Signal to force avatar refresh
364
- avatarRefresh = signal(Date.now(), ...(ngDevMode ? [{ debugName: "avatarRefresh" }] : []));
364
+ avatarRefresh = signal(Date.now(), ...(ngDevMode ? [{ debugName: "avatarRefresh" }] : /* istanbul ignore next */ []));
365
365
  constructor(authService, router, themeService, cdr, http) {
366
366
  this.authService = authService;
367
367
  this.router = router;
@@ -512,8 +512,8 @@ class UserProfileComponent {
512
512
  onNotificationClick() {
513
513
  this.notificationClick.emit();
514
514
  }
515
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: UserProfileComponent, deps: [{ token: MesAuthService }, { token: i2.Router }, { token: ThemeService }, { token: i0.ChangeDetectorRef }, { token: i4.HttpClient }], target: i0.ɵɵFactoryTarget.Component });
516
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: UserProfileComponent, isStandalone: true, selector: "ma-user-profile", outputs: { notificationClick: "notificationClick", approvalClick: "approvalClick" }, host: { listeners: { "document:click": "onDocumentClick($event)" }, properties: { "class": "this.themeClass" } }, ngImport: i0, template: `
515
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: UserProfileComponent, deps: [{ token: MesAuthService }, { token: i2.Router }, { token: ThemeService }, { token: i0.ChangeDetectorRef }, { token: i4.HttpClient }], target: i0.ɵɵFactoryTarget.Component });
516
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.7", type: UserProfileComponent, isStandalone: true, selector: "ma-user-profile", outputs: { notificationClick: "notificationClick", approvalClick: "approvalClick" }, host: { listeners: { "document:click": "onDocumentClick($event)" }, properties: { "class": "this.themeClass" } }, ngImport: i0, template: `
517
517
  <div class="user-profile-container">
518
518
  <!-- Not logged in -->
519
519
  <ng-container *ngIf="!currentUser()">
@@ -603,7 +603,7 @@ class UserProfileComponent {
603
603
  </div>
604
604
  `, isInline: true, styles: [":host{--primary-color: #1976d2;--primary-hover: #1565c0;--primary-light: rgba(25, 118, 210, .12);--primary-glow: rgba(25, 118, 210, .3);--error-color: #f44336;--error-light: rgba(244, 67, 54, .1);--text-primary: #212121;--text-secondary: #616161;--text-muted: #9e9e9e;--bg-primary: #ffffff;--bg-secondary: #f8f9fa;--bg-hover: #f0f4ff;--border-color: #e0e0e0;--shadow: rgba(0, 0, 0, .12);--shadow-lg: rgba(0, 0, 0, .18)}:host(.theme-dark){--primary-color: #90caf9;--primary-hover: #64b5f6;--primary-light: rgba(144, 202, 249, .12);--primary-glow: rgba(144, 202, 249, .25);--error-color: #ef5350;--error-light: rgba(239, 83, 80, .12);--text-primary: #e0e0e0;--text-secondary: #b0b0b0;--text-muted: #757575;--bg-primary: #1e1e2e;--bg-secondary: #27273a;--bg-hover: #2a2d4a;--border-color: #383850;--shadow: rgba(0, 0, 0, .35);--shadow-lg: rgba(0, 0, 0, .5)}.user-profile-container{display:flex;align-items:center;gap:4px}.login-btn{padding:7px 18px;background-color:var(--primary-color);color:#fff;border:none;border-radius:8px;cursor:pointer;font-weight:500;font-size:13px;letter-spacing:.2px;transition:background-color .2s,transform .15s}.login-btn:hover{background-color:var(--primary-hover);transform:translateY(-1px)}.user-header{display:flex;align-items:center;gap:4px}.notification-btn{position:relative;background:none;border:none;cursor:pointer;padding:8px;border-radius:10px;color:var(--text-secondary);display:flex;align-items:center;justify-content:center;transition:color .2s,background-color .2s}.notification-btn:hover{background-color:var(--primary-light);color:var(--primary-color)}.notification-btn.has-unread{color:var(--primary-color)}.bell-icon{display:block;transition:transform .35s cubic-bezier(.34,1.56,.64,1)}.notification-btn:hover .bell-icon{transform:rotate(-20deg) scale(1.15)}.badge{position:absolute;top:2px;right:2px;background-color:var(--error-color);color:#fff;border-radius:10px;min-width:17px;height:17px;padding:0 4px;display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;line-height:1;box-shadow:0 0 0 2px var(--bg-primary);animation:badge-pop .25s cubic-bezier(.34,1.56,.64,1)}@keyframes badge-pop{0%{transform:scale(0)}to{transform:scale(1)}}.user-menu-wrapper{position:relative}.user-menu-btn{background:none;border:none;cursor:pointer;padding:2px;border-radius:50%;display:flex;align-items:center;justify-content:center;transition:transform .2s}.user-menu-btn:hover{transform:scale(1.06)}.avatar-ring{border-radius:50%;padding:2px;border:2px solid transparent;transition:border-color .25s,box-shadow .25s}.avatar-ring.active,.user-menu-btn:hover .avatar-ring{border-color:var(--primary-color);box-shadow:0 0 0 3px var(--primary-glow)}.avatar{width:36px;height:36px;border-radius:50%;object-fit:cover;display:block}.avatar-initial{width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,var(--primary-color),var(--primary-hover));color:#fff;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:15px}.mes-dropdown-menu{position:absolute;top:calc(100% + 10px);right:0;background:var(--bg-primary);border:1px solid var(--border-color);border-radius:14px;box-shadow:0 8px 32px var(--shadow-lg),0 2px 8px var(--shadow);min-width:220px;z-index:1000;overflow:hidden;animation:dropdown-in .16s cubic-bezier(.16,1,.3,1)}@keyframes dropdown-in{0%{opacity:0;transform:translateY(-8px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}.mes-dropdown-header{display:flex;align-items:center;gap:12px;padding:16px;background:var(--bg-secondary)}.dropdown-avatar-wrap{flex-shrink:0}.dropdown-avatar{width:46px;height:46px;border-radius:50%;object-fit:cover;border:2px solid var(--primary-color);display:block}.dropdown-avatar-initial{width:46px;height:46px;border-radius:50%;background:linear-gradient(135deg,var(--primary-color),var(--primary-hover));color:#fff;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:18px;border:2px solid var(--primary-color)}.dropdown-user-info{display:flex;flex-direction:column;gap:3px;min-width:0}.dropdown-user-name{font-weight:600;font-size:14px;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dropdown-user-sub{font-size:11px;color:var(--primary-color);font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mes-dropdown-divider{height:1px;background:var(--border-color)}.mes-dropdown-item{display:flex;align-items:center;gap:10px;width:100%;padding:11px 16px;border:none;background:none;text-align:left;cursor:pointer;font-size:13.5px;font-weight:500;transition:background-color .15s}.item-icon{flex-shrink:0;opacity:.8}.profile-link{color:var(--primary-color)}.profile-link:hover{background-color:var(--primary-light)}.logout-item{color:var(--error-color)}.logout-item:hover{background-color:var(--error-light)}@media(max-width:768px){.avatar,.avatar-initial{width:32px;height:32px;font-size:13px}}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
605
605
  }
606
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: UserProfileComponent, decorators: [{
606
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: UserProfileComponent, decorators: [{
607
607
  type: Component,
608
608
  args: [{ selector: 'ma-user-profile', standalone: true, imports: [NgIf], template: `
609
609
  <div class="user-profile-container">
@@ -734,10 +734,10 @@ class ToastService {
734
734
  clear() {
735
735
  this.toasts$.next([]);
736
736
  }
737
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ToastService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
738
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ToastService, providedIn: 'root' });
737
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ToastService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
738
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ToastService, providedIn: 'root' });
739
739
  }
740
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ToastService, decorators: [{
740
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ToastService, decorators: [{
741
741
  type: Injectable,
742
742
  args: [{ providedIn: 'root' }]
743
743
  }] });
@@ -774,8 +774,8 @@ class ToastContainerComponent {
774
774
  close(id) {
775
775
  this.toastService.remove(id);
776
776
  }
777
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ToastContainerComponent, deps: [{ token: ToastService }, { token: ThemeService }], target: i0.ɵɵFactoryTarget.Component });
778
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: ToastContainerComponent, isStandalone: true, selector: "ma-toast-container", host: { properties: { "class": "this.themeClass" } }, ngImport: i0, template: `
777
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ToastContainerComponent, deps: [{ token: ToastService }, { token: ThemeService }], target: i0.ɵɵFactoryTarget.Component });
778
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.7", type: ToastContainerComponent, isStandalone: true, selector: "ma-toast-container", host: { properties: { "class": "this.themeClass" } }, ngImport: i0, template: `
779
779
  <div class="toast-container">
780
780
  <div *ngFor="let toast of toasts" class="toast toast-{{ toast.type }}">
781
781
 
@@ -814,7 +814,7 @@ class ToastContainerComponent {
814
814
  </div>
815
815
  `, isInline: true, styles: [":host{--info-color: #2196f3;--info-bg: rgba(33, 150, 243, .1);--success-color: #43a047;--success-bg: rgba(67, 160, 71, .1);--warning-color: #f57c00;--warning-bg: rgba(245, 124, 0, .1);--error-color: #e53935;--error-bg: rgba(229, 57, 53, .1);--text-primary: #212121;--text-secondary: #757575;--bg-primary: #ffffff;--border-color: rgba(0, 0, 0, .08);--shadow: rgba(0, 0, 0, .1);--shadow-lg: rgba(0, 0, 0, .18)}:host(.theme-dark){--info-color: #64b5f6;--info-bg: rgba(100, 181, 246, .12);--success-color: #66bb6a;--success-bg: rgba(102, 187, 106, .12);--warning-color: #ffb74d;--warning-bg: rgba(255, 183, 77, .12);--error-color: #ef5350;--error-bg: rgba(239, 83, 80, .12);--text-primary: #e0e0e0;--text-secondary: #9e9e9e;--bg-primary: #1e1e2e;--border-color: rgba(255, 255, 255, .08);--shadow: rgba(0, 0, 0, .35);--shadow-lg: rgba(0, 0, 0, .5)}.toast-container{position:fixed;top:20px;right:20px;z-index:9999;pointer-events:none;display:flex;flex-direction:column;gap:10px}.toast{position:relative;display:flex;align-items:flex-start;gap:11px;padding:13px 13px 16px 16px;border-radius:12px;background:var(--bg-primary);border:1px solid var(--border-color);box-shadow:0 8px 28px var(--shadow-lg),0 2px 8px var(--shadow);pointer-events:auto;min-width:300px;max-width:420px;overflow:hidden;animation:toast-in .35s cubic-bezier(.16,1,.3,1)}@keyframes toast-in{0%{opacity:0;transform:translate(36px) scale(.96)}to{opacity:1;transform:translate(0) scale(1)}}.toast:before{content:\"\";position:absolute;left:0;top:0;bottom:0;width:4px;border-radius:12px 0 0 12px}.toast-info:before{background:var(--info-color)}.toast-success:before{background:var(--success-color)}.toast-warning:before{background:var(--warning-color)}.toast-error:before{background:var(--error-color)}.toast-icon{flex-shrink:0;width:34px;height:34px;border-radius:9px;display:flex;align-items:center;justify-content:center;margin-left:2px}.toast-info .toast-icon{color:var(--info-color);background:var(--info-bg)}.toast-success .toast-icon{color:var(--success-color);background:var(--success-bg)}.toast-warning .toast-icon{color:var(--warning-color);background:var(--warning-bg)}.toast-error .toast-icon{color:var(--error-color);background:var(--error-bg)}.toast-content{flex:1;min-width:0;padding-top:1px}.toast-title{font-weight:700;font-size:13.5px;margin-bottom:3px;line-height:1.3}.toast-info .toast-title{color:var(--info-color)}.toast-success .toast-title{color:var(--success-color)}.toast-warning .toast-title{color:var(--warning-color)}.toast-error .toast-title{color:var(--error-color)}.toast-message{font-size:12.5px;line-height:1.45;color:var(--text-secondary)}.toast-close{background:none;border:none;cursor:pointer;color:var(--text-secondary);width:26px;height:26px;border-radius:6px;display:flex;align-items:center;justify-content:center;flex-shrink:0;padding:0;transition:color .15s,background-color .15s}.toast-close:hover{color:var(--text-primary);background:var(--border-color)}.toast-progress{position:absolute;bottom:0;left:4px;right:0;height:3px;border-radius:0 0 12px;animation:toast-progress linear forwards;opacity:.7}.toast-info .toast-progress{background:var(--info-color)}.toast-success .toast-progress{background:var(--success-color)}.toast-warning .toast-progress{background:var(--warning-color)}.toast-error .toast-progress{background:var(--error-color)}@keyframes toast-progress{0%{width:calc(100% - 4px)}to{width:0}}@media(max-width:600px){.toast-container{top:10px;right:10px;left:10px}.toast{min-width:auto;max-width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
816
816
  }
817
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ToastContainerComponent, decorators: [{
817
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ToastContainerComponent, decorators: [{
818
818
  type: Component,
819
819
  args: [{ selector: 'ma-toast-container', standalone: true, imports: [CommonModule], template: `
820
820
  <div class="toast-container">
@@ -1116,8 +1116,8 @@ class NotificationPanelComponent {
1116
1116
  }
1117
1117
  return normalized;
1118
1118
  }
1119
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: NotificationPanelComponent, deps: [{ token: MesAuthService }, { token: ToastService }, { token: ThemeService }], target: i0.ɵɵFactoryTarget.Component });
1120
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: NotificationPanelComponent, isStandalone: true, selector: "ma-notification-panel", outputs: { notificationRead: "notificationRead" }, host: { properties: { "class": "this.themeClass" } }, ngImport: i0, template: `
1119
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: NotificationPanelComponent, deps: [{ token: MesAuthService }, { token: ToastService }, { token: ThemeService }], target: i0.ɵɵFactoryTarget.Component });
1120
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.7", type: NotificationPanelComponent, isStandalone: true, selector: "ma-notification-panel", outputs: { notificationRead: "notificationRead" }, host: { properties: { "class": "this.themeClass" } }, ngImport: i0, template: `
1121
1121
  <div class="notification-panel" [class.open]="isOpen">
1122
1122
  <!-- Header -->
1123
1123
  <div class="panel-header">
@@ -1275,7 +1275,7 @@ class NotificationPanelComponent {
1275
1275
  </div>
1276
1276
  `, isInline: true, styles: [".panel-header{display:flex;justify-content:space-between;align-items:center;padding:16px 18px;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);flex-shrink:0}.panel-header-left{display:flex;align-items:center;gap:9px;color:var(--primary)}.panel-header h3{margin:0;font-size:16px;font-weight:700;color:var(--text-primary)}.close-btn{background:none;border:none;cursor:pointer;color:var(--text-muted);width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:background .15s,color .15s}.close-btn:hover{background:var(--bg-hover);color:var(--text-primary)}.tabs{display:flex;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);flex-shrink:0}.tab-btn{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;padding:11px 8px;background:none;border:none;border-bottom:2px solid transparent;color:var(--text-muted);cursor:pointer;font-size:13px;font-weight:500;transition:color .15s,border-color .15s}.tab-btn.active{color:var(--primary);border-bottom-color:var(--primary)}.tab-btn:hover:not(.active){color:var(--text-secondary)}.tab-badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;background:var(--primary);color:#fff;font-size:11px;font-weight:700;border-radius:9px}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 20px;gap:12px;color:var(--text-muted)}.empty-state p{margin:0;font-size:13px}.loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px;gap:12px;color:var(--text-muted);font-size:13px}.spinner{width:24px;height:24px;border:2px solid var(--border-color);border-top-color:var(--primary);border-radius:50%;animation:spin .7s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}\n", ":host{display:block;position:relative;--primary: #1976d2;--primary-hover: #1565c0;--success: #43a047;--error: #f44336;--error-hover: #d32f2f;--info-color: #2196f3;--info-bg: rgba(33, 150, 243, .1);--success-bg: rgba(67, 160, 71, .1);--warning-color: #f57c00;--warning-bg: rgba(245, 124, 0, .1);--error-bg: rgba(244, 67, 54, .1);--text-primary: #212121;--text-secondary: #616161;--text-muted: #9e9e9e;--bg-primary: #ffffff;--bg-secondary: #f8f9fa;--bg-hover: #f0f4ff;--bg-unread: rgba(25, 118, 210, .06);--border-color: #e0e0e0;--border-light: #eeeeee;--shadow: rgba(0, 0, 0, .15)}:host(.theme-dark){--primary: #90caf9;--primary-hover: #64b5f6;--success: #66bb6a;--error: #ef5350;--error-hover: #c62828;--info-color: #64b5f6;--info-bg: rgba(100, 181, 246, .12);--success-bg: rgba(102, 187, 106, .12);--warning-color: #ffb74d;--warning-bg: rgba(255, 183, 77, .12);--error-bg: rgba(239, 83, 80, .12);--text-primary: #e0e0e0;--text-secondary: #b0b0b0;--text-muted: #757575;--bg-primary: #1e1e2e;--bg-secondary: #27273a;--bg-hover: #2a2d4a;--bg-unread: rgba(144, 202, 249, .08);--border-color: #383850;--border-light: #2e2e42;--shadow: rgba(0, 0, 0, .4)}.tab-btn:not(.active) .tab-badge{background:var(--error)}.read-badge{background:var(--text-muted)}.notification-panel{position:fixed;top:0;right:-360px;width:360px;height:100vh;background:var(--bg-primary);box-shadow:-4px 0 24px var(--shadow);display:flex;flex-direction:column;z-index:1030;transition:right .3s cubic-bezier(.16,1,.3,1)}.notification-panel.open{right:0}.notifications-list{flex:1;overflow-y:auto}.notification-item{display:flex;align-items:flex-start;gap:0;border-bottom:1px solid var(--border-light);cursor:pointer;background:var(--bg-primary);transition:background-color .15s;position:relative}.notification-item:hover{background:var(--bg-hover)}.notification-item.unread{background:var(--bg-unread)}.notif-accent{width:3px;align-self:stretch;flex-shrink:0;background:transparent;border-radius:0 2px 2px 0;opacity:.3}.notification-item.unread .notif-accent{opacity:1}.notif-accent.type-info{background:var(--info-color)}.notif-accent.type-success{background:var(--success)}.notif-accent.type-warning{background:var(--warning-color)}.notif-accent.type-error{background:var(--error)}.notif-type-icon{flex-shrink:0;width:26px;height:26px;border-radius:7px;display:flex;align-items:center;justify-content:center;align-self:center;margin-left:10px}.notif-type-icon.type-info{color:var(--info-color);background:var(--info-bg)}.notif-type-icon.type-success{color:var(--success);background:var(--success-bg)}.notif-type-icon.type-warning{color:var(--warning-color);background:var(--warning-bg)}.notif-type-icon.type-error{color:var(--error);background:var(--error-bg)}.notification-content{flex:1;min-width:0;padding:12px 8px 12px 12px}.notification-title{font-weight:600;color:var(--text-primary);font-size:13.5px;margin-bottom:3px;line-height:1.35}.notification-message{color:var(--text-secondary);font-size:12px;line-height:1.45;margin-bottom:7px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.notification-meta{display:flex;justify-content:space-between;font-size:11px;color:var(--text-muted)}.app-name{font-weight:600;color:var(--primary)}.icon-btn{background:none;border:none;cursor:pointer;width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center;flex-shrink:0;align-self:center;margin-right:8px;transition:color .15s,background-color .15s;color:var(--text-muted)}.read-btn:hover{color:var(--success);background:#43a0471a}.delete-btn:hover{color:var(--error);background:#f443361a}.panel-footer{padding:10px 14px;border-top:1px solid var(--border-color);background:var(--bg-secondary);flex-shrink:0}.footer-actions{display:flex;gap:8px}.footer-actions .action-btn{flex:1}.action-btn{display:flex;align-items:center;justify-content:center;gap:6px;width:100%;padding:8px 12px;background:var(--primary);color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:12.5px;font-weight:600;transition:background-color .18s,transform .12s}.action-btn:hover{background:var(--primary-hover);transform:translateY(-1px)}.delete-all-btn{background:var(--error)}.delete-all-btn:hover{background:var(--error-hover)}.modal-overlay{position:fixed;inset:0;background:#00000080;display:flex;align-items:center;justify-content:center;z-index:1060;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.modal-container{background:var(--bg-primary);border-radius:14px;width:90%;max-width:580px;max-height:80vh;display:flex;flex-direction:column;box-shadow:0 16px 48px #00000040;animation:modal-in .2s cubic-bezier(.16,1,.3,1)}@keyframes modal-in{0%{opacity:0;transform:scale(.94) translateY(8px)}to{opacity:1;transform:scale(1) translateY(0)}}.modal-header{display:flex;justify-content:space-between;align-items:center;padding:16px 20px;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);border-radius:14px 14px 0 0;border-top:3px solid transparent}.modal-header.modal-type-info{border-top-color:var(--info-color)}.modal-header.modal-type-success{border-top-color:var(--success)}.modal-header.modal-type-warning{border-top-color:var(--warning-color)}.modal-header.modal-type-error{border-top-color:var(--error)}.modal-header-left{display:flex;align-items:center;gap:10px;min-width:0}.modal-type-icon{flex-shrink:0;width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center}.modal-type-info .modal-type-icon{color:var(--info-color);background:var(--info-bg)}.modal-type-success .modal-type-icon{color:var(--success);background:var(--success-bg)}.modal-type-warning .modal-type-icon{color:var(--warning-color);background:var(--warning-bg)}.modal-type-error .modal-type-icon{color:var(--error);background:var(--error-bg)}.modal-header h3{margin:0;font-size:15px;font-weight:700;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.modal-meta{display:flex;justify-content:space-between;padding:8px 20px;font-size:11.5px;color:var(--text-muted);border-bottom:1px solid var(--border-light)}.modal-body{padding:20px;overflow-y:auto;flex:1;color:var(--text-primary);font-size:14px;line-height:1.65}.modal-footer{padding:12px 20px;border-top:1px solid var(--border-color);background:var(--bg-secondary);border-radius:0 0 14px 14px;display:flex;justify-content:flex-end;gap:8px}.modal-footer .action-btn{width:auto;padding:8px 24px}.modal-footer .see-details-btn{background:var(--info-bg);color:var(--info-color);border:1px solid var(--info-color)}.modal-footer .see-details-btn:hover{opacity:.85}@media(max-width:600px){.notification-panel{width:100%;right:-100%}.modal-container{width:95%;max-height:90vh}}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
1277
1277
  }
1278
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: NotificationPanelComponent, decorators: [{
1278
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: NotificationPanelComponent, decorators: [{
1279
1279
  type: Component,
1280
1280
  args: [{ selector: 'ma-notification-panel', standalone: true, imports: [NgIf, NgFor], template: `
1281
1281
  <div class="notification-panel" [class.open]="isOpen">
@@ -1526,10 +1526,10 @@ class MaApprovalService {
1526
1526
  createApproval(request) {
1527
1527
  return this.http.post(`${this.apiBase}/approval/documents`, request, this.opts);
1528
1528
  }
1529
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: MaApprovalService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1530
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: MaApprovalService });
1529
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: MaApprovalService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1530
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: MaApprovalService });
1531
1531
  }
1532
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: MaApprovalService, decorators: [{
1532
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: MaApprovalService, decorators: [{
1533
1533
  type: Injectable
1534
1534
  }], ctorParameters: () => [] });
1535
1535
 
@@ -1669,8 +1669,8 @@ class MaApprovalPanelComponent {
1669
1669
  this.close();
1670
1670
  this.router.navigate(['/auth/approval/my-requests'], { queryParams: { status } });
1671
1671
  }
1672
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: MaApprovalPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1673
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: MaApprovalPanelComponent, isStandalone: true, selector: "ma-approval-panel", outputs: { approvalActioned: "approvalActioned" }, ngImport: i0, template: `
1672
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: MaApprovalPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1673
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.7", type: MaApprovalPanelComponent, isStandalone: true, selector: "ma-approval-panel", outputs: { approvalActioned: "approvalActioned" }, ngImport: i0, template: `
1674
1674
  <div class="approval-backdrop" [class.open]="isOpen" (click)="close()"></div>
1675
1675
  <div class="approval-panel" [class.open]="isOpen">
1676
1676
 
@@ -1776,7 +1776,7 @@ class MaApprovalPanelComponent {
1776
1776
  </div>
1777
1777
  `, isInline: true, styles: [".panel-header{display:flex;justify-content:space-between;align-items:center;padding:16px 18px;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);flex-shrink:0}.panel-header-left{display:flex;align-items:center;gap:9px;color:var(--primary)}.panel-header h3{margin:0;font-size:16px;font-weight:700;color:var(--text-primary)}.close-btn{background:none;border:none;cursor:pointer;color:var(--text-muted);width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:background .15s,color .15s}.close-btn:hover{background:var(--bg-hover);color:var(--text-primary)}.tabs{display:flex;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);flex-shrink:0}.tab-btn{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;padding:11px 8px;background:none;border:none;border-bottom:2px solid transparent;color:var(--text-muted);cursor:pointer;font-size:13px;font-weight:500;transition:color .15s,border-color .15s}.tab-btn.active{color:var(--primary);border-bottom-color:var(--primary)}.tab-btn:hover:not(.active){color:var(--text-secondary)}.tab-badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;background:var(--primary);color:#fff;font-size:11px;font-weight:700;border-radius:9px}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 20px;gap:12px;color:var(--text-muted)}.empty-state p{margin:0;font-size:13px}.loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px;gap:12px;color:var(--text-muted);font-size:13px}.spinner{width:24px;height:24px;border:2px solid var(--border-color);border-top-color:var(--primary);border-radius:50%;animation:spin .7s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}\n", ":host{--primary: #90caf9;--success: #66bb6a;--error: #ef5350;--text-primary: #e0e0e0;--text-secondary: #b0b0b0;--text-muted: #757575;--bg-primary: #1e1e2e;--bg-secondary: #27273a;--bg-hover: #2a2d4a;--border-color: #383850;--shadow: rgba(0,0,0,.4)}:host(.theme-light){--primary: #1565c0;--success: #2e7d32;--error: #c62828;--text-primary: #212121;--text-secondary: #616161;--text-muted: #9e9e9e;--bg-primary: #ffffff;--bg-secondary: #f5f5f5;--bg-hover: #e8eaf6;--border-color: #e0e0e0;--shadow: rgba(0,0,0,.15)}.approval-backdrop{display:none;position:fixed;inset:0;background:#0006;z-index:1029}.approval-backdrop.open{display:block}.approval-panel{position:fixed;top:0;right:-380px;width:380px;height:100vh;background:var(--bg-primary);box-shadow:-4px 0 24px var(--shadow);display:flex;flex-direction:column;z-index:1030;transition:right .3s cubic-bezier(.16,1,.3,1)}.approval-panel.open{right:0}.panel-content{flex:1;overflow-y:auto;padding:8px 0}.approval-item{padding:14px 18px;border-bottom:1px solid var(--border-color);cursor:pointer;transition:background .15s}.approval-item:hover{background:var(--bg-hover)}.approval-item:last-child{border-bottom:none}.item-title{font-size:14px;font-weight:600;color:var(--text-primary);margin-bottom:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.item-meta{font-size:12px;color:var(--text-muted);margin-bottom:8px;display:flex;gap:4px;flex-wrap:wrap}.item-footer{display:flex;align-items:center;gap:8px}.item-time{font-size:11px;color:var(--text-muted);margin-right:auto}.item-link{font-size:12px;color:var(--primary);font-weight:500}.status-badge{display:inline-block;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:600;letter-spacing:.3px}.approved-badge{background:#66bb6a26;color:var(--success)}.rejected-badge{background:#ef53501f;color:var(--error)}.show-more{text-align:center;padding:14px;font-size:13px;color:var(--primary);cursor:pointer;font-weight:500}.show-more:hover{text-decoration:underline}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "pipe", type: DatePipe, name: "date" }] });
1778
1778
  }
1779
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: MaApprovalPanelComponent, decorators: [{
1779
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: MaApprovalPanelComponent, decorators: [{
1780
1780
  type: Component,
1781
1781
  args: [{ selector: 'ma-approval-panel', standalone: true, imports: [NgIf, NgFor, DatePipe], template: `
1782
1782
  <div class="approval-backdrop" [class.open]="isOpen" (click)="close()"></div>
@@ -1906,8 +1906,8 @@ class MaUserComponent {
1906
1906
  this.userProfile.loadPendingApprovalCount();
1907
1907
  }
1908
1908
  }
1909
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: MaUserComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1910
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: MaUserComponent, isStandalone: true, selector: "ma-user", viewQueries: [{ propertyName: "userProfile", first: true, predicate: UserProfileComponent, descendants: true }, { propertyName: "approvalPanel", first: true, predicate: MaApprovalPanelComponent, descendants: true }], ngImport: i0, template: `
1909
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: MaUserComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1910
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.7", type: MaUserComponent, isStandalone: true, selector: "ma-user", viewQueries: [{ propertyName: "userProfile", first: true, predicate: UserProfileComponent, descendants: true }, { propertyName: "approvalPanel", first: true, predicate: MaApprovalPanelComponent, descendants: true }], ngImport: i0, template: `
1911
1911
  <ma-toast-container></ma-toast-container>
1912
1912
  <div class="user-header">
1913
1913
  <ma-user-profile
@@ -1919,7 +1919,7 @@ class MaUserComponent {
1919
1919
  <ma-approval-panel #approvalPanel (approvalActioned)="onApprovalActioned()"></ma-approval-panel>
1920
1920
  `, isInline: true, styles: [".user-header{display:flex;justify-content:flex-end}\n"], dependencies: [{ kind: "component", type: ToastContainerComponent, selector: "ma-toast-container" }, { kind: "component", type: UserProfileComponent, selector: "ma-user-profile", outputs: ["notificationClick", "approvalClick"] }, { kind: "component", type: NotificationPanelComponent, selector: "ma-notification-panel", outputs: ["notificationRead"] }, { kind: "component", type: MaApprovalPanelComponent, selector: "ma-approval-panel", outputs: ["approvalActioned"] }] });
1921
1921
  }
1922
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: MaUserComponent, decorators: [{
1922
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: MaUserComponent, decorators: [{
1923
1923
  type: Component,
1924
1924
  args: [{ selector: 'ma-user', standalone: true, imports: [ToastContainerComponent, UserProfileComponent, NotificationPanelComponent, MaApprovalPanelComponent], template: `
1925
1925
  <ma-toast-container></ma-toast-container>
@@ -1999,15 +1999,15 @@ class NotificationBadgeComponent {
1999
1999
  onNotificationClick() {
2000
2000
  this.notificationClick.emit();
2001
2001
  }
2002
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: NotificationBadgeComponent, deps: [{ token: MesAuthService }, { token: ThemeService }], target: i0.ɵɵFactoryTarget.Component });
2003
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: NotificationBadgeComponent, isStandalone: true, selector: "ma-notification-badge", outputs: { notificationClick: "notificationClick" }, host: { properties: { "class": "this.themeClass" } }, ngImport: i0, template: `
2002
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: NotificationBadgeComponent, deps: [{ token: MesAuthService }, { token: ThemeService }], target: i0.ɵɵFactoryTarget.Component });
2003
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.7", type: NotificationBadgeComponent, isStandalone: true, selector: "ma-notification-badge", outputs: { notificationClick: "notificationClick" }, host: { properties: { "class": "this.themeClass" } }, ngImport: i0, template: `
2004
2004
  <button class="notification-btn" (click)="onNotificationClick()" title="Notifications">
2005
2005
  <span class="icon">🔔</span>
2006
2006
  <span class="badge" *ngIf="unreadCount > 0">{{ unreadCount }}</span>
2007
2007
  </button>
2008
2008
  `, isInline: true, styles: [":host{--error-color: #f44336}:host(.theme-dark){--error-color: #ef5350}.notification-btn{position:relative;background:none;border:none;font-size:24px;cursor:pointer;padding:8px;transition:opacity .2s}.notification-btn:hover{opacity:.7}.icon{display:inline-block}.badge{position:absolute;top:0;right:0;background-color:var(--error-color);color:#fff;border-radius:50%;width:20px;height:20px;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
2009
2009
  }
2010
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: NotificationBadgeComponent, decorators: [{
2010
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: NotificationBadgeComponent, decorators: [{
2011
2011
  type: Component,
2012
2012
  args: [{ selector: 'ma-notification-badge', standalone: true, imports: [NgIf], template: `
2013
2013
  <button class="notification-btn" (click)="onNotificationClick()" title="Notifications">
@@ -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;
@@ -2484,330 +2487,430 @@ ${clone.outerHTML}
2484
2487
  'text-transform', 'letter-spacing', 'white-space', 'word-break'
2485
2488
  ];
2486
2489
  }
2487
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: MaArvContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2488
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", 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">
2490
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: MaArvContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
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
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: MaArvContainerComponent, decorators: [{
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: "20.3.18", ngImpo
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: [{