@forgeroute/ui 0.4.39 → 0.4.40

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.
@@ -8160,6 +8160,7 @@ var DetailCardComponent = class DetailCardComponent {
8160
8160
  this.editIcon = input(true, ...ngDevMode ? [{ debugName: "editIcon" }] : /* istanbul ignore next */ []);
8161
8161
  this.editAriaLabel = input("", ...ngDevMode ? [{ debugName: "editAriaLabel" }] : /* istanbul ignore next */ []);
8162
8162
  this.fill = input(false, ...ngDevMode ? [{ debugName: "fill" }] : /* istanbul ignore next */ []);
8163
+ this.maxBodyHeight = input("", ...ngDevMode ? [{ debugName: "maxBodyHeight" }] : /* istanbul ignore next */ []);
8163
8164
  this.selectable = input(false, ...ngDevMode ? [{ debugName: "selectable" }] : /* istanbul ignore next */ []);
8164
8165
  this.selected = model(false, ...ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []);
8165
8166
  this.selectedColor = input("#3b82f6", ...ngDevMode ? [{ debugName: "selectedColor" }] : /* istanbul ignore next */ []);
@@ -8342,6 +8343,13 @@ var DetailCardComponent = class DetailCardComponent {
8342
8343
  isRequired: false,
8343
8344
  transformFunction: null
8344
8345
  },
8346
+ maxBodyHeight: {
8347
+ classPropertyName: "maxBodyHeight",
8348
+ publicName: "maxBodyHeight",
8349
+ isSignal: true,
8350
+ isRequired: false,
8351
+ transformFunction: null
8352
+ },
8345
8353
  selectable: {
8346
8354
  classPropertyName: "selectable",
8347
8355
  publicName: "selectable",
@@ -8390,7 +8398,7 @@ var DetailCardComponent = class DetailCardComponent {
8390
8398
  <div class="dc" [class.dc-fill]="fill()" [class.dc-has-text-color]="!!textColor()" [class.dc-has-bg]="!!((selectable() && selected() && selectedBackground()) || background())" [class.dc-selectable]="selectable()" [class.dc-selected]="selectable() && selected()"
8391
8399
  [style.outline-color]="selectable() && selected() ? selectedColor() : null"
8392
8400
  [style.background]="(selectable() && selected() && selectedBackground()) || background() || null" (click)="onCardClick($event)"
8393
- [style.color]="textColor() || null" [style.--dc-text]="textColor() || null">
8401
+ [style.color]="textColor() || null" [style.--dc-text]="textColor() || null" [style.--dc-body-max-height]="maxBodyHeight() || null">
8394
8402
  @if (showHeader()) {
8395
8403
  <div class="dc-header" [class.collapsible]="collapsible()" [class.open]="isOpen()" [style.background]="headerBackground() || null" (click)="onHeaderClick($event)">
8396
8404
  @if (collapsible()) {
@@ -8433,7 +8441,7 @@ var DetailCardComponent = class DetailCardComponent {
8433
8441
  </div>
8434
8442
  `,
8435
8443
  isInline: true,
8436
- styles: [":host{display:block;margin-bottom:16px}:host(:last-child){margin-bottom:0}:host(.dc-fill-host){height:100%;margin-bottom:0}.dc.dc-fill{height:100%;box-sizing:border-box;display:flex;flex-direction:column}.dc.dc-fill .dc-body-wrap{flex:1 1 auto;min-height:0}.dc.dc-fill .dc-body{display:flex;flex-direction:column}.dc-selectable{cursor:pointer}.dc-selected{outline:2px solid #3b82f6;outline-offset:-1px}.dc{background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden}.dc-header{display:flex;align-items:center;flex-wrap:wrap;gap:8px 10px;padding:14px 18px}.dc-header.open{border-bottom:1px solid #f1f5f9}.dc-has-bg .dc-header.open{border-bottom-color:#94a3b859}.dc-header.collapsible{cursor:pointer;-webkit-user-select:none;user-select:none}@media(hover:hover){.dc-header.collapsible:hover{background:#f8fafc}}.dc-chevron{flex-shrink:0;color:var(--dc-text, #64748b)}.dc-chevron svg{transition:transform .2s}.dc-chevron svg.rotated{transform:rotate(-90deg)}.dc-header-text{display:flex;flex-direction:column;gap:2px;flex:1 1 160px;min-width:0;word-break:break-word;overflow-wrap:anywhere}.dc-title{font-size:.8125rem;font-weight:800;letter-spacing:.07em;color:var(--dc-text, #1e293b);line-height:1.3;text-transform:uppercase}.dc-title--as-written{text-transform:none}.dc-subtitle{font-size:.8125rem;color:var(--dc-text, #475569)}.dc-has-text-color .dc-subtitle{opacity:.8}.dc-has-text-color .dc-header.open{border-bottom-color:#94a3b859}.dc-edit{flex-shrink:0;margin-left:auto}.dc-edit-action{display:inline-flex;align-items:center;justify-content:center;gap:5px;font-family:inherit;font-size:.8rem;font-weight:500;line-height:1.4;color:#64748b;background:none;border:1px solid #e2e8f0;border-radius:6px;padding:4px 10px;cursor:pointer;white-space:nowrap;transition:background-color .15s ease,color .15s ease}.dc-edit-action:hover{background:#f1f5f9;color:#1e293b}.dc-edit-action:focus-visible{outline:2px solid var(--ui-focus, #3b82f6);outline-offset:2px}@media(pointer:coarse),(max-width:480px){.dc-edit-action{min-height:44px}}.dc-body-wrap{display:grid;grid-template-rows:1fr;transition:grid-template-rows .25s ease}.dc-body-wrap.collapsed{grid-template-rows:0fr}.dc-body{padding:16px 18px;min-height:0;overflow:hidden}.dc-footer{padding:10px 18px;border-top:1px solid #f1f5f9}.dc-footer:empty,.dc-footer.dc-footer--hidden{display:none}.dc-has-bg .dc-footer{border-top-color:#94a3b859}@media(max-width:480px){.dc-footer{padding:10px 14px}.dc-header{padding:12px 14px}.dc-body{padding:14px}.dc-edit{margin-left:0}}@media(prefers-reduced-motion:reduce){.dc-body-wrap,.dc-chevron svg{transition:none}}\n"],
8444
+ styles: [":host{display:block;margin-bottom:16px}:host(:last-child){margin-bottom:0}:host(.dc-fill-host){height:100%;margin-bottom:0}.dc.dc-fill{height:100%;box-sizing:border-box;display:flex;flex-direction:column}.dc.dc-fill .dc-body-wrap{flex:1 1 auto;min-height:0}.dc.dc-fill .dc-body{display:flex;flex-direction:column}.dc-selectable{cursor:pointer}.dc-selected{outline:2px solid #3b82f6;outline-offset:-1px}.dc{background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden}.dc-header{display:flex;align-items:center;flex-wrap:wrap;gap:8px 10px;padding:14px 18px}.dc-header.open{border-bottom:1px solid #f1f5f9}.dc-has-bg .dc-header.open{border-bottom-color:#94a3b859}.dc-header.collapsible{cursor:pointer;-webkit-user-select:none;user-select:none}@media(hover:hover){.dc-header.collapsible:hover{background:#f8fafc}}.dc-chevron{flex-shrink:0;color:var(--dc-text, #64748b)}.dc-chevron svg{transition:transform .2s}.dc-chevron svg.rotated{transform:rotate(-90deg)}.dc-header-text{display:flex;flex-direction:column;gap:2px;flex:1 1 160px;min-width:0;word-break:break-word;overflow-wrap:anywhere}.dc-title{font-size:.8125rem;font-weight:800;letter-spacing:.07em;color:var(--dc-text, #1e293b);line-height:1.3;text-transform:uppercase}.dc-title--as-written{text-transform:none}.dc-subtitle{font-size:.8125rem;color:var(--dc-text, #475569)}.dc-has-text-color .dc-subtitle{opacity:.8}.dc-has-text-color .dc-header.open{border-bottom-color:#94a3b859}.dc-edit{flex-shrink:0;margin-left:auto}.dc-edit-action{display:inline-flex;align-items:center;justify-content:center;gap:5px;font-family:inherit;font-size:.8rem;font-weight:500;line-height:1.4;color:#64748b;background:none;border:1px solid #e2e8f0;border-radius:6px;padding:4px 10px;cursor:pointer;white-space:nowrap;transition:background-color .15s ease,color .15s ease}.dc-edit-action:hover{background:#f1f5f9;color:#1e293b}.dc-edit-action:focus-visible{outline:2px solid var(--ui-focus, #3b82f6);outline-offset:2px}@media(pointer:coarse),(max-width:480px){.dc-edit-action{min-height:44px}}.dc-body-wrap{display:grid;grid-template-rows:1fr;transition:grid-template-rows .25s ease}.dc-body-wrap.collapsed{grid-template-rows:0fr}.dc-body{padding:16px 18px;min-height:0;overflow-x:hidden;overflow-y:auto;max-height:var(--dc-body-max-height, none)}.dc-footer{padding:10px 18px;border-top:1px solid #f1f5f9}.dc-footer:empty,.dc-footer.dc-footer--hidden{display:none}.dc-has-bg .dc-footer{border-top-color:#94a3b859}@media(max-width:480px){.dc-footer{padding:10px 14px}.dc-header{padding:12px 14px}.dc-body{padding:14px}.dc-edit{margin-left:0}}@media(prefers-reduced-motion:reduce){.dc-body-wrap,.dc-chevron svg{transition:none}}\n"],
8437
8445
  dependencies: [{
8438
8446
  kind: "component",
8439
8447
  type: ButtonComponent,
@@ -8491,7 +8499,7 @@ i0.ɵɵngDeclareClassMetadata({
8491
8499
  <div class="dc" [class.dc-fill]="fill()" [class.dc-has-text-color]="!!textColor()" [class.dc-has-bg]="!!((selectable() && selected() && selectedBackground()) || background())" [class.dc-selectable]="selectable()" [class.dc-selected]="selectable() && selected()"
8492
8500
  [style.outline-color]="selectable() && selected() ? selectedColor() : null"
8493
8501
  [style.background]="(selectable() && selected() && selectedBackground()) || background() || null" (click)="onCardClick($event)"
8494
- [style.color]="textColor() || null" [style.--dc-text]="textColor() || null">
8502
+ [style.color]="textColor() || null" [style.--dc-text]="textColor() || null" [style.--dc-body-max-height]="maxBodyHeight() || null">
8495
8503
  @if (showHeader()) {
8496
8504
  <div class="dc-header" [class.collapsible]="collapsible()" [class.open]="isOpen()" [style.background]="headerBackground() || null" (click)="onHeaderClick($event)">
8497
8505
  @if (collapsible()) {
@@ -8533,7 +8541,7 @@ i0.ɵɵngDeclareClassMetadata({
8533
8541
  <div class="dc-footer" [class.dc-footer--hidden]="!isOpen()" [style.background]="footerBackground() || null"><ng-content select="[card-footer]" /></div>
8534
8542
  </div>
8535
8543
  `,
8536
- styles: [":host{display:block;margin-bottom:16px}:host(:last-child){margin-bottom:0}:host(.dc-fill-host){height:100%;margin-bottom:0}.dc.dc-fill{height:100%;box-sizing:border-box;display:flex;flex-direction:column}.dc.dc-fill .dc-body-wrap{flex:1 1 auto;min-height:0}.dc.dc-fill .dc-body{display:flex;flex-direction:column}.dc-selectable{cursor:pointer}.dc-selected{outline:2px solid #3b82f6;outline-offset:-1px}.dc{background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden}.dc-header{display:flex;align-items:center;flex-wrap:wrap;gap:8px 10px;padding:14px 18px}.dc-header.open{border-bottom:1px solid #f1f5f9}.dc-has-bg .dc-header.open{border-bottom-color:#94a3b859}.dc-header.collapsible{cursor:pointer;-webkit-user-select:none;user-select:none}@media(hover:hover){.dc-header.collapsible:hover{background:#f8fafc}}.dc-chevron{flex-shrink:0;color:var(--dc-text, #64748b)}.dc-chevron svg{transition:transform .2s}.dc-chevron svg.rotated{transform:rotate(-90deg)}.dc-header-text{display:flex;flex-direction:column;gap:2px;flex:1 1 160px;min-width:0;word-break:break-word;overflow-wrap:anywhere}.dc-title{font-size:.8125rem;font-weight:800;letter-spacing:.07em;color:var(--dc-text, #1e293b);line-height:1.3;text-transform:uppercase}.dc-title--as-written{text-transform:none}.dc-subtitle{font-size:.8125rem;color:var(--dc-text, #475569)}.dc-has-text-color .dc-subtitle{opacity:.8}.dc-has-text-color .dc-header.open{border-bottom-color:#94a3b859}.dc-edit{flex-shrink:0;margin-left:auto}.dc-edit-action{display:inline-flex;align-items:center;justify-content:center;gap:5px;font-family:inherit;font-size:.8rem;font-weight:500;line-height:1.4;color:#64748b;background:none;border:1px solid #e2e8f0;border-radius:6px;padding:4px 10px;cursor:pointer;white-space:nowrap;transition:background-color .15s ease,color .15s ease}.dc-edit-action:hover{background:#f1f5f9;color:#1e293b}.dc-edit-action:focus-visible{outline:2px solid var(--ui-focus, #3b82f6);outline-offset:2px}@media(pointer:coarse),(max-width:480px){.dc-edit-action{min-height:44px}}.dc-body-wrap{display:grid;grid-template-rows:1fr;transition:grid-template-rows .25s ease}.dc-body-wrap.collapsed{grid-template-rows:0fr}.dc-body{padding:16px 18px;min-height:0;overflow:hidden}.dc-footer{padding:10px 18px;border-top:1px solid #f1f5f9}.dc-footer:empty,.dc-footer.dc-footer--hidden{display:none}.dc-has-bg .dc-footer{border-top-color:#94a3b859}@media(max-width:480px){.dc-footer{padding:10px 14px}.dc-header{padding:12px 14px}.dc-body{padding:14px}.dc-edit{margin-left:0}}@media(prefers-reduced-motion:reduce){.dc-body-wrap,.dc-chevron svg{transition:none}}\n"]
8544
+ styles: [":host{display:block;margin-bottom:16px}:host(:last-child){margin-bottom:0}:host(.dc-fill-host){height:100%;margin-bottom:0}.dc.dc-fill{height:100%;box-sizing:border-box;display:flex;flex-direction:column}.dc.dc-fill .dc-body-wrap{flex:1 1 auto;min-height:0}.dc.dc-fill .dc-body{display:flex;flex-direction:column}.dc-selectable{cursor:pointer}.dc-selected{outline:2px solid #3b82f6;outline-offset:-1px}.dc{background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden}.dc-header{display:flex;align-items:center;flex-wrap:wrap;gap:8px 10px;padding:14px 18px}.dc-header.open{border-bottom:1px solid #f1f5f9}.dc-has-bg .dc-header.open{border-bottom-color:#94a3b859}.dc-header.collapsible{cursor:pointer;-webkit-user-select:none;user-select:none}@media(hover:hover){.dc-header.collapsible:hover{background:#f8fafc}}.dc-chevron{flex-shrink:0;color:var(--dc-text, #64748b)}.dc-chevron svg{transition:transform .2s}.dc-chevron svg.rotated{transform:rotate(-90deg)}.dc-header-text{display:flex;flex-direction:column;gap:2px;flex:1 1 160px;min-width:0;word-break:break-word;overflow-wrap:anywhere}.dc-title{font-size:.8125rem;font-weight:800;letter-spacing:.07em;color:var(--dc-text, #1e293b);line-height:1.3;text-transform:uppercase}.dc-title--as-written{text-transform:none}.dc-subtitle{font-size:.8125rem;color:var(--dc-text, #475569)}.dc-has-text-color .dc-subtitle{opacity:.8}.dc-has-text-color .dc-header.open{border-bottom-color:#94a3b859}.dc-edit{flex-shrink:0;margin-left:auto}.dc-edit-action{display:inline-flex;align-items:center;justify-content:center;gap:5px;font-family:inherit;font-size:.8rem;font-weight:500;line-height:1.4;color:#64748b;background:none;border:1px solid #e2e8f0;border-radius:6px;padding:4px 10px;cursor:pointer;white-space:nowrap;transition:background-color .15s ease,color .15s ease}.dc-edit-action:hover{background:#f1f5f9;color:#1e293b}.dc-edit-action:focus-visible{outline:2px solid var(--ui-focus, #3b82f6);outline-offset:2px}@media(pointer:coarse),(max-width:480px){.dc-edit-action{min-height:44px}}.dc-body-wrap{display:grid;grid-template-rows:1fr;transition:grid-template-rows .25s ease}.dc-body-wrap.collapsed{grid-template-rows:0fr}.dc-body{padding:16px 18px;min-height:0;overflow-x:hidden;overflow-y:auto;max-height:var(--dc-body-max-height, none)}.dc-footer{padding:10px 18px;border-top:1px solid #f1f5f9}.dc-footer:empty,.dc-footer.dc-footer--hidden{display:none}.dc-has-bg .dc-footer{border-top-color:#94a3b859}@media(max-width:480px){.dc-footer{padding:10px 14px}.dc-header{padding:12px 14px}.dc-body{padding:14px}.dc-edit{margin-left:0}}@media(prefers-reduced-motion:reduce){.dc-body-wrap,.dc-chevron svg{transition:none}}\n"]
8537
8545
  }]
8538
8546
  }],
8539
8547
  propDecorators: {
@@ -8696,6 +8704,14 @@ i0.ɵɵngDeclareClassMetadata({
8696
8704
  required: false
8697
8705
  }]
8698
8706
  }],
8707
+ maxBodyHeight: [{
8708
+ type: i0.Input,
8709
+ args: [{
8710
+ isSignal: true,
8711
+ alias: "maxBodyHeight",
8712
+ required: false
8713
+ }]
8714
+ }],
8699
8715
  selectable: [{
8700
8716
  type: i0.Input,
8701
8717
  args: [{
@@ -8922,6 +8938,7 @@ var OptionCardComponent = class OptionCardComponent {
8922
8938
  "editIcon",
8923
8939
  "editAriaLabel",
8924
8940
  "fill",
8941
+ "maxBodyHeight",
8925
8942
  "selectable",
8926
8943
  "selected",
8927
8944
  "selectedColor",
@@ -11082,6 +11099,17 @@ var DataTableComponent = class DataTableComponent {
11082
11099
  });
11083
11100
  return true;
11084
11101
  }
11102
+ _actionKeyLabel(key) {
11103
+ const words = key.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[\s_-]+/).filter(Boolean).join(" ").toLowerCase();
11104
+ return words.charAt(0).toUpperCase() + words.slice(1);
11105
+ }
11106
+ _isActionEligible(def, row) {
11107
+ try {
11108
+ return !!def.eligible(row);
11109
+ } catch {
11110
+ return false;
11111
+ }
11112
+ }
11085
11113
  _bulkOptionsFor(def) {
11086
11114
  const opts = this._getOptionsForField(def.field);
11087
11115
  return def.exclude?.length ? opts.filter((o) => !this._isBulkExcluded(def, o.value)) : opts;
@@ -11142,7 +11170,7 @@ var DataTableComponent = class DataTableComponent {
11142
11170
  this._visibleColumns.set([]);
11143
11171
  }
11144
11172
  _hostConfirm(def, ctx, skippedNote, fallback) {
11145
- const fn = typeof def.confirm === "function" ? def.confirm : def.confirm?.[ctx.targetValue];
11173
+ const fn = typeof def.confirm === "function" ? def.confirm : def.action ? void 0 : def.confirm?.[ctx.targetValue];
11146
11174
  if (!fn) return null;
11147
11175
  const out = fn(ctx);
11148
11176
  const copy = typeof out === "string" ? { message: out } : out;
@@ -11156,6 +11184,7 @@ var DataTableComponent = class DataTableComponent {
11156
11184
  };
11157
11185
  }
11158
11186
  _bulkBlockedReason(def) {
11187
+ if (def.action) return def.eligible && this._actionSplit(def).eligible.length === 0 ? `None of the selected rows qualify for ${def.label.toLowerCase()}.` : "";
11159
11188
  if (def.type !== "delete" || !def.deletable) return "";
11160
11189
  return this._deleteSplit(def).deletable.length === 0 ? "None of the selected rows can be deleted." : "";
11161
11190
  }
@@ -11173,7 +11202,96 @@ var DataTableComponent = class DataTableComponent {
11173
11202
  blocked: selected.filter((r) => !def.deletable(r))
11174
11203
  };
11175
11204
  }
11205
+ _actionSplit(def) {
11206
+ const selected = this._items().filter((i) => this._selectedIds().has(i["id"]));
11207
+ if (!def.eligible) return {
11208
+ eligible: selected,
11209
+ refused: []
11210
+ };
11211
+ const ok = selected.map((r) => this._isActionEligible(def, r));
11212
+ return {
11213
+ eligible: selected.filter((_, i) => ok[i]),
11214
+ refused: selected.filter((_, i) => !ok[i])
11215
+ };
11216
+ }
11217
+ async _runDirectAction(def) {
11218
+ if (this._confirmingAction || this._runningAction()) return;
11219
+ const split = this._actionSplit(def);
11220
+ if (split.eligible.length === 0) return;
11221
+ const ids = split.eligible.map((r) => r["id"]);
11222
+ const skippedCount = split.refused.length;
11223
+ const lower = def.label.toLowerCase();
11224
+ const skippedNote = skippedCount === 0 ? "" : skippedCount === 1 ? ` 1 selected row doesn't qualify for ${lower} and will be skipped.` : ` ${skippedCount} selected rows don't qualify for ${lower} and will be skipped.`;
11225
+ const variant = def.variant === "danger" ? "danger" : "primary";
11226
+ this._confirmingAction = true;
11227
+ let confirmed = false;
11228
+ try {
11229
+ const defaultCopy = {
11230
+ title: def.label,
11231
+ message: `${def.label} for ${ids.length} ${this._countLabel(ids.length)}?${skippedNote}`,
11232
+ confirmLabel: def.label,
11233
+ variant
11234
+ };
11235
+ const hostCopy = def.confirm ? this._hostConfirm(def, {
11236
+ count: ids.length,
11237
+ skippedAlready: 0,
11238
+ skippedBlocked: skippedCount,
11239
+ selectedCount: this._selectedIds().size,
11240
+ itemLabel: this.itemLabel(),
11241
+ itemLabelPlural: this.itemLabelPlural() || pluralize(this.itemLabel()),
11242
+ columnLabel: def.label,
11243
+ targetValue: "",
11244
+ targetLabel: "",
11245
+ rows: split.eligible,
11246
+ skippedRows: split.refused
11247
+ }, skippedNote, {
11248
+ title: defaultCopy.title,
11249
+ confirmLabel: defaultCopy.confirmLabel,
11250
+ variant
11251
+ }) : null;
11252
+ confirmed = await this._confirmSvc.confirm(hostCopy ?? defaultCopy);
11253
+ } catch {
11254
+ confirmed = false;
11255
+ } finally {
11256
+ this._confirmingAction = false;
11257
+ }
11258
+ if (!confirmed) return;
11259
+ this._runningAction.set(def.type);
11260
+ const done = def.doneLabel?.trim() || "updated";
11261
+ const url = `${this.config.baseUrl}${this.endpoint()}/bulk/${def.type.slice(7)}`;
11262
+ this.http.post(url, { ids }).pipe(takeUntilDestroyed(this.destroyRef)).subscribe({
11263
+ next: (r) => {
11264
+ this._runningAction.set(null);
11265
+ const count = r == null ? ids.length : r.updated ?? r.deleted ?? 0;
11266
+ const totalSkipped = skippedCount + (r?.skipped ?? 0);
11267
+ const notAllowed = r?.notAllowed ?? 0;
11268
+ const msg = totalSkipped > 0 || notAllowed > 0 ? [
11269
+ `${count} ${done}`,
11270
+ totalSkipped > 0 ? `${totalSkipped} skipped` : "",
11271
+ notAllowed > 0 ? `${notAllowed} not allowed` : ""
11272
+ ].filter(Boolean).join(", ") + "." : `${count} ${this.itemLabel()}(s) ${done}.`;
11273
+ const serverMsg = humanReadableMessage(r?.message);
11274
+ if (serverMsg && count === 0) this._toast.error(serverMsg);
11275
+ else this._toast.show(serverMsg ? `${msg} ${serverMsg}` : msg);
11276
+ this.clearSelection();
11277
+ this.refresh();
11278
+ this.bulkAction.emit({
11279
+ type: def.type,
11280
+ ids,
11281
+ skippedCount
11282
+ });
11283
+ },
11284
+ error: (err) => {
11285
+ this._runningAction.set(null);
11286
+ this._toast.error(err?.error?.message ?? "Something went wrong. Please try again.");
11287
+ }
11288
+ });
11289
+ }
11176
11290
  onBulkActionClick(def) {
11291
+ if (def.action) {
11292
+ this._runDirectAction(def);
11293
+ return;
11294
+ }
11177
11295
  if (def.type === "delete") {
11178
11296
  const split = this._deleteSplit(def);
11179
11297
  if (def.deletable && split.deletable.length === 0) return;
@@ -11291,6 +11409,8 @@ var DataTableComponent = class DataTableComponent {
11291
11409
  variant: "primary"
11292
11410
  }) : null;
11293
11411
  confirmed = await this._confirmSvc.confirm(hostCopy ?? defaultCopy);
11412
+ } catch {
11413
+ confirmed = false;
11294
11414
  } finally {
11295
11415
  this._confirmingBulk = false;
11296
11416
  }
@@ -11614,6 +11734,18 @@ var DataTableComponent = class DataTableComponent {
11614
11734
  ...confirm ? { confirm } : {},
11615
11735
  ...deletable ? { deletable } : {}
11616
11736
  };
11737
+ if (typeof entry !== "string" && entry.action) {
11738
+ const label = entry.label?.trim() || this._actionKeyLabel(key);
11739
+ return {
11740
+ type: `action-${key}`,
11741
+ label,
11742
+ variant: entry.variant === "danger" ? "danger" : "secondary",
11743
+ action: true,
11744
+ ...entry.eligible ? { eligible: entry.eligible } : {},
11745
+ ...entry.doneLabel ? { doneLabel: entry.doneLabel } : {},
11746
+ ...confirm ? { confirm } : {}
11747
+ };
11748
+ }
11617
11749
  const exclude = typeof entry === "string" ? [] : entry.exclude ?? [];
11618
11750
  const col = this.columns().find((c) => c.key === key);
11619
11751
  const allowedFrom = typeof entry === "string" ? void 0 : entry.allowedFrom;
@@ -11630,7 +11762,7 @@ var DataTableComponent = class DataTableComponent {
11630
11762
  ...canChange ? { canChange } : {},
11631
11763
  ...confirm ? { confirm } : {}
11632
11764
  };
11633
- }).filter((def) => !def.field || !def.exclude?.length || this._bulkOptionsFor(def).length > 0), ...ngDevMode ? [{ debugName: "_bulkDefs" }] : /* istanbul ignore next */ []);
11765
+ }).filter((def) => !def.field || !def.exclude?.length || this._bulkOptionsFor(def).length > 0).filter((def, i, all) => !def.action || all.findIndex((d) => d.type === def.type) === i), ...ngDevMode ? [{ debugName: "_bulkDefs" }] : /* istanbul ignore next */ []);
11634
11766
  this._activeBulkType = signal(null, ...ngDevMode ? [{ debugName: "_activeBulkType" }] : /* istanbul ignore next */ []);
11635
11767
  this._applyingBulk = signal(false, ...ngDevMode ? [{ debugName: "_applyingBulk" }] : /* istanbul ignore next */ []);
11636
11768
  this._deletingBulk = signal(false, ...ngDevMode ? [{ debugName: "_deletingBulk" }] : /* istanbul ignore next */ []);
@@ -11670,6 +11802,8 @@ var DataTableComponent = class DataTableComponent {
11670
11802
  });
11671
11803
  return out;
11672
11804
  }, ...ngDevMode ? [{ debugName: "_rowNameSuffix" }] : /* istanbul ignore next */ []);
11805
+ this._runningAction = signal(null, ...ngDevMode ? [{ debugName: "_runningAction" }] : /* istanbul ignore next */ []);
11806
+ this._confirmingAction = false;
11673
11807
  this._confirmingBulk = false;
11674
11808
  this._rowDeleteTarget = signal(null, ...ngDevMode ? [{ debugName: "_rowDeleteTarget" }] : /* istanbul ignore next */ []);
11675
11809
  this._rowDeleting = signal(false, ...ngDevMode ? [{ debugName: "_rowDeleting" }] : /* istanbul ignore next */ []);
@@ -11806,6 +11940,14 @@ var DataTableComponent = class DataTableComponent {
11806
11940
  if (this.page()) this._loadSavedViews();
11807
11941
  this._fetchCounts();
11808
11942
  }
11943
+ _retryAfterError() {
11944
+ if (this._search()) {
11945
+ this._search.set("");
11946
+ this._page.set(1);
11947
+ this._fetchCounts();
11948
+ }
11949
+ this.load();
11950
+ }
11809
11951
  onSearchChange(val) {
11810
11952
  if (val === this._search()) return;
11811
11953
  this._search.set(val);
@@ -12542,8 +12684,8 @@ var DataTableComponent = class DataTableComponent {
12542
12684
  <div class="dt-host">
12543
12685
 
12544
12686
  <!-- ── HEADER ──────────────────────────────────────────────── -->
12545
- @if (!_hasLoaded()) {
12546
- <!-- Skeleton header: shaped to match real controls -->
12687
+ @if (!_hasLoaded() && !_error()) {
12688
+ <!-- Skeleton header: shaped to match real controls (only while the first load is running: a failed first load keeps no placeholder bars) -->
12547
12689
  @if (_showHeader()) {
12548
12690
  <div class="dt-sk-toolbar">
12549
12691
  <div class="dt-sk-toolbar-left">
@@ -12578,14 +12720,14 @@ var DataTableComponent = class DataTableComponent {
12578
12720
  @if (tableTitle()) {
12579
12721
  <h3 class="dt-title">{{ tableTitle() }}</h3>
12580
12722
  }
12581
- @if (showSearch() && !_emptyUnfiltered()) {
12723
+ @if (showSearch() && !_emptyUnfiltered() && !_error()) {
12582
12724
  <ui-search-input
12583
12725
  [value]="_search()"
12584
12726
  [placeholder]="_effectiveSearchPlaceholder()"
12585
- [loading]="_searchLoading()"
12727
+ [loading]="_searchLoading() && !_error()"
12586
12728
  (searchChange)="onSearchChange($event)" />
12587
12729
  }
12588
- @if (_effectiveTabs().length && !_emptyUnfiltered()) {
12730
+ @if (_effectiveTabs().length && !_emptyUnfiltered() && !_error()) {
12589
12731
  <ui-quick-filter-tabs
12590
12732
  [tabs]="_effectiveTabs()"
12591
12733
  [counts]="_tabCounts()"
@@ -12628,8 +12770,8 @@ var DataTableComponent = class DataTableComponent {
12628
12770
  } @else {
12629
12771
  @for (def of _bulkDefs(); track def.type) {
12630
12772
  <ui-button [variant]="def.variant ?? 'secondary'" size="sm"
12631
- [loading]="def.type === 'delete' && _deletingBulk()"
12632
- [disabled]="_deletingBulk() || !!_bulkBlockedReason(def)"
12773
+ [loading]="(def.type === 'delete' && _deletingBulk()) || (!!def.action && _runningAction() === def.type)"
12774
+ [disabled]="_deletingBulk() || !!_runningAction() || !!_bulkBlockedReason(def)"
12633
12775
  [ariaLabel]="_bulkBlockedReason(def) ? def.label + ' — ' + _bulkBlockedReason(def) : undefined"
12634
12776
  [attr.title]="_bulkBlockedReason(def) || null"
12635
12777
  (click)="onBulkActionClick(def)">{{ def.label }}</ui-button>
@@ -12759,7 +12901,7 @@ var DataTableComponent = class DataTableComponent {
12759
12901
  @else if (_error()) {
12760
12902
  <tr>
12761
12903
  <td [attr.colspan]="visibleCols().length + (_isSelectable() ? 1 : 0) + (_reorderOn() ? 1 : 0)" class="dt-state-cell">
12762
- <ui-error-state [message]="_error()!" [retryLabel]="_retryLabel()" (retry)="load()" />
12904
+ <ui-error-state [message]="_error()!" [retryLabel]="_retryLabel()" (retry)="_retryAfterError()" />
12763
12905
  </td>
12764
12906
  </tr>
12765
12907
  }
@@ -13021,7 +13163,7 @@ var DataTableComponent = class DataTableComponent {
13021
13163
  }
13022
13164
 
13023
13165
  } @else if (_error()) {
13024
- <ui-error-state [message]="_error()!" [retryLabel]="_retryLabel()" (retry)="load()" />
13166
+ <ui-error-state [message]="_error()!" [retryLabel]="_retryLabel()" (retry)="_retryAfterError()" />
13025
13167
 
13026
13168
  } @else if (_items().length === 0 && !_hasActiveFilters()) {
13027
13169
  <ui-empty-state iconType="table" [message]="emptyMessage()" />
@@ -13609,8 +13751,8 @@ i0.ɵɵngDeclareClassMetadata({
13609
13751
  <div class="dt-host">
13610
13752
 
13611
13753
  <!-- ── HEADER ──────────────────────────────────────────────── -->
13612
- @if (!_hasLoaded()) {
13613
- <!-- Skeleton header: shaped to match real controls -->
13754
+ @if (!_hasLoaded() && !_error()) {
13755
+ <!-- Skeleton header: shaped to match real controls (only while the first load is running: a failed first load keeps no placeholder bars) -->
13614
13756
  @if (_showHeader()) {
13615
13757
  <div class="dt-sk-toolbar">
13616
13758
  <div class="dt-sk-toolbar-left">
@@ -13645,14 +13787,14 @@ i0.ɵɵngDeclareClassMetadata({
13645
13787
  @if (tableTitle()) {
13646
13788
  <h3 class="dt-title">{{ tableTitle() }}</h3>
13647
13789
  }
13648
- @if (showSearch() && !_emptyUnfiltered()) {
13790
+ @if (showSearch() && !_emptyUnfiltered() && !_error()) {
13649
13791
  <ui-search-input
13650
13792
  [value]="_search()"
13651
13793
  [placeholder]="_effectiveSearchPlaceholder()"
13652
- [loading]="_searchLoading()"
13794
+ [loading]="_searchLoading() && !_error()"
13653
13795
  (searchChange)="onSearchChange($event)" />
13654
13796
  }
13655
- @if (_effectiveTabs().length && !_emptyUnfiltered()) {
13797
+ @if (_effectiveTabs().length && !_emptyUnfiltered() && !_error()) {
13656
13798
  <ui-quick-filter-tabs
13657
13799
  [tabs]="_effectiveTabs()"
13658
13800
  [counts]="_tabCounts()"
@@ -13695,8 +13837,8 @@ i0.ɵɵngDeclareClassMetadata({
13695
13837
  } @else {
13696
13838
  @for (def of _bulkDefs(); track def.type) {
13697
13839
  <ui-button [variant]="def.variant ?? 'secondary'" size="sm"
13698
- [loading]="def.type === 'delete' && _deletingBulk()"
13699
- [disabled]="_deletingBulk() || !!_bulkBlockedReason(def)"
13840
+ [loading]="(def.type === 'delete' && _deletingBulk()) || (!!def.action && _runningAction() === def.type)"
13841
+ [disabled]="_deletingBulk() || !!_runningAction() || !!_bulkBlockedReason(def)"
13700
13842
  [ariaLabel]="_bulkBlockedReason(def) ? def.label + ' — ' + _bulkBlockedReason(def) : undefined"
13701
13843
  [attr.title]="_bulkBlockedReason(def) || null"
13702
13844
  (click)="onBulkActionClick(def)">{{ def.label }}</ui-button>
@@ -13826,7 +13968,7 @@ i0.ɵɵngDeclareClassMetadata({
13826
13968
  @else if (_error()) {
13827
13969
  <tr>
13828
13970
  <td [attr.colspan]="visibleCols().length + (_isSelectable() ? 1 : 0) + (_reorderOn() ? 1 : 0)" class="dt-state-cell">
13829
- <ui-error-state [message]="_error()!" [retryLabel]="_retryLabel()" (retry)="load()" />
13971
+ <ui-error-state [message]="_error()!" [retryLabel]="_retryLabel()" (retry)="_retryAfterError()" />
13830
13972
  </td>
13831
13973
  </tr>
13832
13974
  }
@@ -14088,7 +14230,7 @@ i0.ɵɵngDeclareClassMetadata({
14088
14230
  }
14089
14231
 
14090
14232
  } @else if (_error()) {
14091
- <ui-error-state [message]="_error()!" [retryLabel]="_retryLabel()" (retry)="load()" />
14233
+ <ui-error-state [message]="_error()!" [retryLabel]="_retryLabel()" (retry)="_retryAfterError()" />
14092
14234
 
14093
14235
  } @else if (_items().length === 0 && !_hasActiveFilters()) {
14094
14236
  <ui-empty-state iconType="table" [message]="emptyMessage()" />
@@ -25209,12 +25351,17 @@ var DropdownComponent = class DropdownComponent {
25209
25351
  this.menuWidth = input("200px", ...ngDevMode ? [{ debugName: "menuWidth" }] : /* istanbul ignore next */ []);
25210
25352
  this.ariaLabel = input("", ...ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []);
25211
25353
  this.itemClick = output();
25354
+ this.align = input("auto", ...ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []);
25212
25355
  this.open = signal(false, ...ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []);
25213
25356
  this.loading = signal(false, ...ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []);
25357
+ this.alignEnd = signal(false, ...ngDevMode ? [{ debugName: "alignEnd" }] : /* istanbul ignore next */ []);
25358
+ this.openUp = signal(false, ...ngDevMode ? [{ debugName: "openUp" }] : /* istanbul ignore next */ []);
25359
+ this.menuMaxHeight = signal(null, ...ngDevMode ? [{ debugName: "menuMaxHeight" }] : /* istanbul ignore next */ []);
25214
25360
  this._loaded = signal(null, ...ngDevMode ? [{ debugName: "_loaded" }] : /* istanbul ignore next */ []);
25215
25361
  this._id = Math.random().toString(36).slice(2);
25216
25362
  this._host = inject(ElementRef);
25217
25363
  this._cdr = inject(ChangeDetectorRef);
25364
+ this._injector = inject(Injector);
25218
25365
  this._pendingFocus = null;
25219
25366
  this._typeahead = "";
25220
25367
  this._typeaheadTimer = null;
@@ -25328,13 +25475,45 @@ var DropdownComponent = class DropdownComponent {
25328
25475
  _setOpen(next) {
25329
25476
  if (this.open() === next) return;
25330
25477
  this.open.set(next);
25331
- if (next) _activeDropdownId.set(this._id);
25332
- else {
25478
+ if (next) {
25479
+ _activeDropdownId.set(this._id);
25480
+ afterNextRender(() => this._place(), { injector: this._injector });
25481
+ } else {
25333
25482
  this._pendingFocus = null;
25334
25483
  this._typeahead = "";
25335
25484
  if (_activeDropdownId() === this._id) _activeDropdownId.set("");
25485
+ this.alignEnd.set(false);
25486
+ this.openUp.set(false);
25487
+ this.menuMaxHeight.set(null);
25336
25488
  }
25337
25489
  }
25490
+ _place() {
25491
+ const host = this._host.nativeElement;
25492
+ const trigger = host.querySelector(".dd-trigger");
25493
+ const menu = host.querySelector(".dd-menu");
25494
+ if (!trigger || !menu || typeof window === "undefined") return;
25495
+ const MARGIN = 16, GAP = 6;
25496
+ this.alignEnd.set(false);
25497
+ this.openUp.set(false);
25498
+ this.menuMaxHeight.set(null);
25499
+ this._cdr.detectChanges();
25500
+ const t = trigger.getBoundingClientRect();
25501
+ const vw = document.documentElement.clientWidth || window.innerWidth;
25502
+ const vh = window.innerHeight;
25503
+ const width = menu.offsetWidth;
25504
+ const naturalHeight = menu.scrollHeight + 2;
25505
+ const mode = this.align();
25506
+ const fitsStart = t.left + width <= vw - MARGIN;
25507
+ const fitsEnd = t.right - width >= MARGIN;
25508
+ const end = mode === "end" || mode === "auto" && !fitsStart && (fitsEnd || t.right > vw / 2);
25509
+ const below = vh - t.bottom - GAP - MARGIN;
25510
+ const above = t.top - GAP - MARGIN;
25511
+ const up = naturalHeight > below && above > below;
25512
+ this.alignEnd.set(end);
25513
+ this.openUp.set(up);
25514
+ this.menuMaxHeight.set(Math.max(160, Math.floor(up ? above : below)));
25515
+ this._cdr.markForCheck();
25516
+ }
25338
25517
  _openAndFocus(which) {
25339
25518
  this._pendingFocus = which;
25340
25519
  this._setOpen(true);
@@ -25445,6 +25624,13 @@ var DropdownComponent = class DropdownComponent {
25445
25624
  isSignal: true,
25446
25625
  isRequired: false,
25447
25626
  transformFunction: null
25627
+ },
25628
+ align: {
25629
+ classPropertyName: "align",
25630
+ publicName: "align",
25631
+ isSignal: true,
25632
+ isRequired: false,
25633
+ transformFunction: null
25448
25634
  }
25449
25635
  },
25450
25636
  outputs: { itemClick: "itemClick" },
@@ -25456,18 +25642,18 @@ var DropdownComponent = class DropdownComponent {
25456
25642
  template: `
25457
25643
  <div class="dd-wrapper">
25458
25644
  @if (variant() === 'badge') {
25459
- <button class="dd-trigger dd-trigger-badge" [class.dd-active]="active()" [attr.id]="triggerId" [attr.aria-label]="_ariaLabel()" [attr.aria-expanded]="open()" aria-haspopup="menu" [attr.aria-controls]="open() ? menuId : null" (click)="toggle($event)" (keydown)="onTriggerKeydown($event)" (keyup)="onTriggerKeyup($event)">
25645
+ <button class="dd-trigger dd-trigger-badge" [class.dd-active]="active()" [class.dd-open]="open()" [attr.id]="triggerId" [attr.aria-label]="_ariaLabel()" [attr.aria-expanded]="open()" aria-haspopup="menu" [attr.aria-controls]="open() ? menuId : null" (click)="toggle($event)" (keydown)="onTriggerKeydown($event)" (keyup)="onTriggerKeyup($event)">
25460
25646
  {{ label() }}
25461
- <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
25647
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
25462
25648
  </button>
25463
25649
  } @else {
25464
- <button class="dd-trigger dd-trigger-btn" [class.dd-active]="active()" [attr.id]="triggerId" [attr.aria-label]="_ariaLabel()" [attr.aria-expanded]="open()" aria-haspopup="menu" [attr.aria-controls]="open() ? menuId : null" (click)="toggle($event)" (keydown)="onTriggerKeydown($event)" (keyup)="onTriggerKeyup($event)">
25650
+ <button class="dd-trigger dd-trigger-btn" [class.dd-active]="active()" [class.dd-open]="open()" [attr.id]="triggerId" [attr.aria-label]="_ariaLabel()" [attr.aria-expanded]="open()" aria-haspopup="menu" [attr.aria-controls]="open() ? menuId : null" (click)="toggle($event)" (keydown)="onTriggerKeydown($event)" (keyup)="onTriggerKeyup($event)">
25465
25651
  {{ label() }}
25466
- <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
25652
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
25467
25653
  </button>
25468
25654
  }
25469
25655
  @if (open()) {
25470
- <div class="dd-menu" role="menu" [attr.id]="menuId" [attr.aria-label]="_ariaLabel()" [attr.aria-labelledby]="_ariaLabel() ? null : triggerId" [style.min-width]="menuWidth()" (click)="$event.stopPropagation()" (keydown)="onMenuKeydown($event)">
25656
+ <div class="dd-menu" role="menu" [attr.id]="menuId" [attr.aria-label]="_ariaLabel()" [attr.aria-labelledby]="_ariaLabel() ? null : triggerId" [style.min-width]="menuWidth()" [class.dd-menu-end]="alignEnd()" [class.dd-menu-up]="openUp()" [style.max-height.px]="menuMaxHeight()" (click)="$event.stopPropagation()" (keydown)="onMenuKeydown($event)">
25471
25657
  @if (loading()) {
25472
25658
  <div class="dd-loading">
25473
25659
  <span class="dd-spinner"></span>
@@ -25476,7 +25662,10 @@ var DropdownComponent = class DropdownComponent {
25476
25662
  } @else {
25477
25663
  @for (item of displayItems(); track item.value) {
25478
25664
  <button type="button" class="dd-item" role="menuitem" tabindex="-1" [disabled]="item.disabled === true" [class.dd-item-active]="activeValue() === item.value" (click)="select(item)">
25479
- <span>{{ item.label }}</span>
25665
+ @if (item.icon) {
25666
+ <svg class="dd-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path [attr.d]="item.icon" /></svg>
25667
+ }
25668
+ <span class="dd-label">{{ item.label }}</span>
25480
25669
  @if (item.count !== undefined) {
25481
25670
  <span class="dd-count">{{ item.count }}</span>
25482
25671
  }
@@ -25488,7 +25677,7 @@ var DropdownComponent = class DropdownComponent {
25488
25677
  </div>
25489
25678
  `,
25490
25679
  isInline: true,
25491
- styles: [":host{display:inline-flex}.dd-wrapper{position:relative;display:inline-flex}.dd-trigger{display:inline-flex;align-items:center;gap:6px;cursor:pointer;font-family:inherit;transition:all .15s ease}.dd-trigger:focus-visible{outline:2px solid #60a5fa;outline-offset:2px}.dd-trigger-badge{padding:6px 14px;border-radius:999px;border:1px solid #e2e8f0;background:#fff;font-size:.8125rem;font-weight:500;color:#64748b}.dd-trigger-badge:hover{background:#f8fafc;border-color:#94a3b8}.dd-trigger-badge.dd-active{background:#1e293b;color:#fff;border-color:#1e293b}.dd-trigger-btn{padding:6px 12px;border-radius:8px;border:1px solid #e2e8f0;background:#fff;font-size:.8125rem;font-weight:500;color:#334155}.dd-trigger-btn:hover{background:#f8fafc;border-color:#94a3b8}.dd-trigger-btn.dd-active{background:#1e293b;color:#fff;border-color:#1e293b}.dd-menu{position:absolute;top:calc(100% + 6px);left:0;z-index:9999;background:#fff;border:1px solid #e2e8f0;border-radius:8px;box-shadow:0 4px 16px #0000001f;max-height:300px;overflow-y:auto;padding:4px;max-width:calc(100vw - 32px)}.dd-item{display:flex;align-items:center;justify-content:space-between;width:100%;padding:10px 12px;border:none;background:none;font-size:.8125rem;font-weight:500;color:#374151;cursor:pointer;font-family:inherit;border-radius:6px;text-align:left;transition:background .1s;border-bottom:1px solid #f1f5f9;min-height:44px}.dd-item:last-child{border-bottom:none}.dd-item:hover{background:#f8fafc}.dd-item:focus-visible{outline:2px solid #60a5fa;outline-offset:-2px}.dd-item.dd-item-active{background:#eff6ff;color:#2563eb}.dd-item:disabled{opacity:.5;cursor:not-allowed;background:none}.dd-count{font-size:.6875rem;font-weight:700;background:#f1f5f9;color:#64748b;padding:1px 7px;border-radius:999px;flex-shrink:0}.dd-loading{display:flex;align-items:center;gap:8px;padding:12px;color:#64748b;font-size:.8125rem}.dd-spinner{width:14px;height:14px;border-radius:50%;flex-shrink:0;border:2px solid #e2e8f0;border-top-color:#64748b;animation:dd-spin .6s linear infinite}@keyframes dd-spin{to{transform:rotate(360deg)}}@media(max-width:768px){.dd-trigger-badge,.dd-trigger-btn{min-height:44px}.dd-menu{max-height:50vh;left:auto;right:0}}@media(prefers-reduced-motion:reduce){.dd-spinner{animation-duration:2s}}\n"],
25680
+ styles: [":host{display:inline-flex}.dd-wrapper{position:relative;display:inline-flex}.dd-trigger{display:inline-flex;align-items:center;gap:6px;cursor:pointer;font-family:inherit;transition:all .15s ease}.dd-trigger:focus-visible{outline:2px solid #60a5fa;outline-offset:2px}.dd-trigger-badge{padding:6px 14px;border-radius:999px;border:1px solid #e2e8f0;background:#fff;font-size:.8125rem;font-weight:500;color:#64748b}.dd-trigger-badge:hover{background:#f8fafc;border-color:#94a3b8}.dd-trigger-badge.dd-active{background:#1e293b;color:#fff;border-color:#1e293b}.dd-trigger-btn{padding:6px 10px;border-radius:6px;border:1px solid #e2e8f0;background:#fff;font-size:.8125rem;font-weight:500;color:#334155;transition:background .1s,border-color .1s}.dd-trigger-btn:hover{background:#f8fafc}.dd-trigger-btn.dd-open{border-color:#1e293b;background:#f8fafc}.dd-trigger-btn.dd-active{background:#1e293b;color:#fff;border-color:#1e293b}.dd-menu{position:absolute;top:calc(100% + 6px);left:0;z-index:9999;background:#fff;border:1px solid #e2e8f0;border-radius:10px;box-shadow:0 4px 6px #0000000a,0 12px 28px #0000001a;max-height:70vh;overflow-y:auto;overflow-x:hidden;padding:0;max-width:calc(100vw - 32px);box-sizing:border-box}.dd-menu.dd-menu-end{left:auto;right:0}.dd-menu.dd-menu-up{top:auto;bottom:calc(100% + 6px)}.dd-item{display:flex;align-items:center;gap:10px;width:100%;padding:10px 14px;border:none;background:none;font-size:.875rem;font-weight:400;color:#1e293b;cursor:pointer;font-family:inherit;text-align:left;transition:background .1s;border-bottom:1px solid #f1f5f9;box-sizing:border-box}.dd-item:last-child{border-bottom:none}.dd-item:hover{background:#f8fafc}.dd-item:focus-visible{outline:2px solid #3b82f6;outline-offset:-2px}.dd-item.dd-item-active{background:#eff6ff;color:#2563eb}.dd-item:disabled{opacity:.5;cursor:not-allowed;background:none}.dd-icon{flex-shrink:0;color:#64748b}.dd-label{min-width:0}.dd-count{font-size:.6875rem;font-weight:700;margin-left:auto;background:#f1f5f9;color:#64748b;padding:1px 7px;border-radius:999px;flex-shrink:0}.dd-loading{display:flex;align-items:center;gap:8px;padding:12px;color:#64748b;font-size:.8125rem}.dd-spinner{width:14px;height:14px;border-radius:50%;flex-shrink:0;border:2px solid #e2e8f0;border-top-color:#64748b;animation:dd-spin .6s linear infinite}@keyframes dd-spin{to{transform:rotate(360deg)}}@media(pointer:coarse),(max-width:768px){.dd-trigger-badge,.dd-trigger-btn,.dd-item{min-height:44px}}@media(prefers-reduced-motion:reduce){.dd-spinner{animation-duration:2s}}\n"],
25492
25681
  changeDetection: i0.ChangeDetectionStrategy.OnPush
25493
25682
  });
25494
25683
  }
@@ -25511,18 +25700,18 @@ i0.ɵɵngDeclareClassMetadata({
25511
25700
  template: `
25512
25701
  <div class="dd-wrapper">
25513
25702
  @if (variant() === 'badge') {
25514
- <button class="dd-trigger dd-trigger-badge" [class.dd-active]="active()" [attr.id]="triggerId" [attr.aria-label]="_ariaLabel()" [attr.aria-expanded]="open()" aria-haspopup="menu" [attr.aria-controls]="open() ? menuId : null" (click)="toggle($event)" (keydown)="onTriggerKeydown($event)" (keyup)="onTriggerKeyup($event)">
25703
+ <button class="dd-trigger dd-trigger-badge" [class.dd-active]="active()" [class.dd-open]="open()" [attr.id]="triggerId" [attr.aria-label]="_ariaLabel()" [attr.aria-expanded]="open()" aria-haspopup="menu" [attr.aria-controls]="open() ? menuId : null" (click)="toggle($event)" (keydown)="onTriggerKeydown($event)" (keyup)="onTriggerKeyup($event)">
25515
25704
  {{ label() }}
25516
- <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
25705
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
25517
25706
  </button>
25518
25707
  } @else {
25519
- <button class="dd-trigger dd-trigger-btn" [class.dd-active]="active()" [attr.id]="triggerId" [attr.aria-label]="_ariaLabel()" [attr.aria-expanded]="open()" aria-haspopup="menu" [attr.aria-controls]="open() ? menuId : null" (click)="toggle($event)" (keydown)="onTriggerKeydown($event)" (keyup)="onTriggerKeyup($event)">
25708
+ <button class="dd-trigger dd-trigger-btn" [class.dd-active]="active()" [class.dd-open]="open()" [attr.id]="triggerId" [attr.aria-label]="_ariaLabel()" [attr.aria-expanded]="open()" aria-haspopup="menu" [attr.aria-controls]="open() ? menuId : null" (click)="toggle($event)" (keydown)="onTriggerKeydown($event)" (keyup)="onTriggerKeyup($event)">
25520
25709
  {{ label() }}
25521
- <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
25710
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
25522
25711
  </button>
25523
25712
  }
25524
25713
  @if (open()) {
25525
- <div class="dd-menu" role="menu" [attr.id]="menuId" [attr.aria-label]="_ariaLabel()" [attr.aria-labelledby]="_ariaLabel() ? null : triggerId" [style.min-width]="menuWidth()" (click)="$event.stopPropagation()" (keydown)="onMenuKeydown($event)">
25714
+ <div class="dd-menu" role="menu" [attr.id]="menuId" [attr.aria-label]="_ariaLabel()" [attr.aria-labelledby]="_ariaLabel() ? null : triggerId" [style.min-width]="menuWidth()" [class.dd-menu-end]="alignEnd()" [class.dd-menu-up]="openUp()" [style.max-height.px]="menuMaxHeight()" (click)="$event.stopPropagation()" (keydown)="onMenuKeydown($event)">
25526
25715
  @if (loading()) {
25527
25716
  <div class="dd-loading">
25528
25717
  <span class="dd-spinner"></span>
@@ -25531,7 +25720,10 @@ i0.ɵɵngDeclareClassMetadata({
25531
25720
  } @else {
25532
25721
  @for (item of displayItems(); track item.value) {
25533
25722
  <button type="button" class="dd-item" role="menuitem" tabindex="-1" [disabled]="item.disabled === true" [class.dd-item-active]="activeValue() === item.value" (click)="select(item)">
25534
- <span>{{ item.label }}</span>
25723
+ @if (item.icon) {
25724
+ <svg class="dd-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path [attr.d]="item.icon" /></svg>
25725
+ }
25726
+ <span class="dd-label">{{ item.label }}</span>
25535
25727
  @if (item.count !== undefined) {
25536
25728
  <span class="dd-count">{{ item.count }}</span>
25537
25729
  }
@@ -25542,7 +25734,7 @@ i0.ɵɵngDeclareClassMetadata({
25542
25734
  }
25543
25735
  </div>
25544
25736
  `,
25545
- styles: [":host{display:inline-flex}.dd-wrapper{position:relative;display:inline-flex}.dd-trigger{display:inline-flex;align-items:center;gap:6px;cursor:pointer;font-family:inherit;transition:all .15s ease}.dd-trigger:focus-visible{outline:2px solid #60a5fa;outline-offset:2px}.dd-trigger-badge{padding:6px 14px;border-radius:999px;border:1px solid #e2e8f0;background:#fff;font-size:.8125rem;font-weight:500;color:#64748b}.dd-trigger-badge:hover{background:#f8fafc;border-color:#94a3b8}.dd-trigger-badge.dd-active{background:#1e293b;color:#fff;border-color:#1e293b}.dd-trigger-btn{padding:6px 12px;border-radius:8px;border:1px solid #e2e8f0;background:#fff;font-size:.8125rem;font-weight:500;color:#334155}.dd-trigger-btn:hover{background:#f8fafc;border-color:#94a3b8}.dd-trigger-btn.dd-active{background:#1e293b;color:#fff;border-color:#1e293b}.dd-menu{position:absolute;top:calc(100% + 6px);left:0;z-index:9999;background:#fff;border:1px solid #e2e8f0;border-radius:8px;box-shadow:0 4px 16px #0000001f;max-height:300px;overflow-y:auto;padding:4px;max-width:calc(100vw - 32px)}.dd-item{display:flex;align-items:center;justify-content:space-between;width:100%;padding:10px 12px;border:none;background:none;font-size:.8125rem;font-weight:500;color:#374151;cursor:pointer;font-family:inherit;border-radius:6px;text-align:left;transition:background .1s;border-bottom:1px solid #f1f5f9;min-height:44px}.dd-item:last-child{border-bottom:none}.dd-item:hover{background:#f8fafc}.dd-item:focus-visible{outline:2px solid #60a5fa;outline-offset:-2px}.dd-item.dd-item-active{background:#eff6ff;color:#2563eb}.dd-item:disabled{opacity:.5;cursor:not-allowed;background:none}.dd-count{font-size:.6875rem;font-weight:700;background:#f1f5f9;color:#64748b;padding:1px 7px;border-radius:999px;flex-shrink:0}.dd-loading{display:flex;align-items:center;gap:8px;padding:12px;color:#64748b;font-size:.8125rem}.dd-spinner{width:14px;height:14px;border-radius:50%;flex-shrink:0;border:2px solid #e2e8f0;border-top-color:#64748b;animation:dd-spin .6s linear infinite}@keyframes dd-spin{to{transform:rotate(360deg)}}@media(max-width:768px){.dd-trigger-badge,.dd-trigger-btn{min-height:44px}.dd-menu{max-height:50vh;left:auto;right:0}}@media(prefers-reduced-motion:reduce){.dd-spinner{animation-duration:2s}}\n"]
25737
+ styles: [":host{display:inline-flex}.dd-wrapper{position:relative;display:inline-flex}.dd-trigger{display:inline-flex;align-items:center;gap:6px;cursor:pointer;font-family:inherit;transition:all .15s ease}.dd-trigger:focus-visible{outline:2px solid #60a5fa;outline-offset:2px}.dd-trigger-badge{padding:6px 14px;border-radius:999px;border:1px solid #e2e8f0;background:#fff;font-size:.8125rem;font-weight:500;color:#64748b}.dd-trigger-badge:hover{background:#f8fafc;border-color:#94a3b8}.dd-trigger-badge.dd-active{background:#1e293b;color:#fff;border-color:#1e293b}.dd-trigger-btn{padding:6px 10px;border-radius:6px;border:1px solid #e2e8f0;background:#fff;font-size:.8125rem;font-weight:500;color:#334155;transition:background .1s,border-color .1s}.dd-trigger-btn:hover{background:#f8fafc}.dd-trigger-btn.dd-open{border-color:#1e293b;background:#f8fafc}.dd-trigger-btn.dd-active{background:#1e293b;color:#fff;border-color:#1e293b}.dd-menu{position:absolute;top:calc(100% + 6px);left:0;z-index:9999;background:#fff;border:1px solid #e2e8f0;border-radius:10px;box-shadow:0 4px 6px #0000000a,0 12px 28px #0000001a;max-height:70vh;overflow-y:auto;overflow-x:hidden;padding:0;max-width:calc(100vw - 32px);box-sizing:border-box}.dd-menu.dd-menu-end{left:auto;right:0}.dd-menu.dd-menu-up{top:auto;bottom:calc(100% + 6px)}.dd-item{display:flex;align-items:center;gap:10px;width:100%;padding:10px 14px;border:none;background:none;font-size:.875rem;font-weight:400;color:#1e293b;cursor:pointer;font-family:inherit;text-align:left;transition:background .1s;border-bottom:1px solid #f1f5f9;box-sizing:border-box}.dd-item:last-child{border-bottom:none}.dd-item:hover{background:#f8fafc}.dd-item:focus-visible{outline:2px solid #3b82f6;outline-offset:-2px}.dd-item.dd-item-active{background:#eff6ff;color:#2563eb}.dd-item:disabled{opacity:.5;cursor:not-allowed;background:none}.dd-icon{flex-shrink:0;color:#64748b}.dd-label{min-width:0}.dd-count{font-size:.6875rem;font-weight:700;margin-left:auto;background:#f1f5f9;color:#64748b;padding:1px 7px;border-radius:999px;flex-shrink:0}.dd-loading{display:flex;align-items:center;gap:8px;padding:12px;color:#64748b;font-size:.8125rem}.dd-spinner{width:14px;height:14px;border-radius:50%;flex-shrink:0;border:2px solid #e2e8f0;border-top-color:#64748b;animation:dd-spin .6s linear infinite}@keyframes dd-spin{to{transform:rotate(360deg)}}@media(pointer:coarse),(max-width:768px){.dd-trigger-badge,.dd-trigger-btn,.dd-item{min-height:44px}}@media(prefers-reduced-motion:reduce){.dd-spinner{animation-duration:2s}}\n"]
25546
25738
  }]
25547
25739
  }],
25548
25740
  ctorParameters: () => [],
@@ -25614,6 +25806,14 @@ i0.ɵɵngDeclareClassMetadata({
25614
25806
  itemClick: [{
25615
25807
  type: i0.Output,
25616
25808
  args: ["itemClick"]
25809
+ }],
25810
+ align: [{
25811
+ type: i0.Input,
25812
+ args: [{
25813
+ isSignal: true,
25814
+ alias: "align",
25815
+ required: false
25816
+ }]
25617
25817
  }]
25618
25818
  }
25619
25819
  });
@@ -42748,6 +42948,34 @@ const SETUP_INTEGRATIONS = {
42748
42948
  connectEndpoint: "/integrations/twilio/connect"
42749
42949
  }
42750
42950
  },
42951
+ stripe: {
42952
+ key: "stripe",
42953
+ letter: "S",
42954
+ color: "#635bff",
42955
+ title: "Stripe",
42956
+ icon: "bank",
42957
+ description: "Accept card and bank payments from your customers. Payouts go straight to your Stripe account.",
42958
+ statusEndpoint: "/integrations/stripe/status",
42959
+ connectedDetail: (s) => str(s["accountName"]) ? `Connected — ${str(s["accountName"])}` : "Connected",
42960
+ connect: {
42961
+ type: "oauth",
42962
+ authorizeEndpoint: "/integrations/stripe/connect"
42963
+ }
42964
+ },
42965
+ square: {
42966
+ key: "square",
42967
+ letter: "S",
42968
+ color: "#111827",
42969
+ title: "Square",
42970
+ icon: "bank",
42971
+ description: "Accept card and bank payments from your customers. Payouts go straight to your Square account.",
42972
+ statusEndpoint: "/integrations/square/status",
42973
+ connectedDetail: (s) => str(s["merchantId"]) ? `Connected — merchant ${str(s["merchantId"])}` : "Connected",
42974
+ connect: {
42975
+ type: "oauth",
42976
+ authorizeEndpoint: "/integrations/square/connect"
42977
+ }
42978
+ },
42751
42979
  gmail: {
42752
42980
  key: "gmail",
42753
42981
  letter: "G",
@@ -43246,13 +43474,32 @@ var SetupWizardComponent = class SetupWizardComponent {
43246
43474
  this.areaCodeOpen.set(i.key);
43247
43475
  return;
43248
43476
  }
43477
+ this.busy.set(true);
43249
43478
  this.http.get(this.apiBase + i.connect.authorizeEndpoint).pipe(takeUntilDestroyed(this.destroyRef)).subscribe({
43250
43479
  next: (res) => {
43251
- if (res?.url) this.redirect(res.url);
43480
+ this.busy.set(false);
43481
+ const url = res?.url ?? res?.authUrl;
43482
+ if (!url) return;
43483
+ if (!this.isWebAddress(url)) {
43484
+ this.error.set(this.t().connectIntegrationFailed(i.title));
43485
+ return;
43486
+ }
43487
+ this.redirect(url);
43252
43488
  },
43253
- error: () => this.error.set(this.t().connectIntegrationFailed(i.title))
43489
+ error: () => {
43490
+ this.busy.set(false);
43491
+ this.error.set(this.t().connectIntegrationFailed(i.title));
43492
+ }
43254
43493
  });
43255
43494
  }
43495
+ isWebAddress(url) {
43496
+ try {
43497
+ const p = new URL(url, window.location.origin).protocol;
43498
+ return p === "https:" || p === "http:";
43499
+ } catch {
43500
+ return false;
43501
+ }
43502
+ }
43256
43503
  iconD(name) {
43257
43504
  return iconPath(name) ?? "";
43258
43505
  }
@@ -43456,7 +43703,7 @@ var SetupWizardComponent = class SetupWizardComponent {
43456
43703
  }
43457
43704
  }
43458
43705
  @case ('integrations') {
43459
- <div class="sw-provider-grid">
43706
+ <div class="sw-provider-grid" [class.sw-provider-grid--single]="(current().integrations?.length ?? 0) <= 1" data-testid="wizard-provider-grid">
43460
43707
  @for (it of current().integrations ?? []; track it.key) {
43461
43708
  <ui-option-card class="sw-integration" [attr.data-testid]="'integration-' + it.key" [title]="it.title" [description]="detailFor(it)" [letter]="it.letter || ''"
43462
43709
  [icon]="it.letter ? '' : (it.icon ?? 'link')" [color]="it.color || '#64748b'" [actionLabel]="isConnected(it) ? t().reconnect : t().connect"
@@ -43486,7 +43733,7 @@ var SetupWizardComponent = class SetupWizardComponent {
43486
43733
  </div>
43487
43734
  `,
43488
43735
  isInline: true,
43489
- styles: [".sw-page{display:flex;min-height:100vh;min-height:100dvh;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.sw-sidebar{width:260px;min-width:260px;background:#faf9f7;border-right:1px solid #e5e2dc;display:flex;flex-direction:column;justify-content:space-between;padding:28px 20px}.sw-brand{display:flex;align-items:center;gap:10px;margin-bottom:28px}.sw-brand-text{font-size:1.125rem;font-weight:700;color:#1e293b}.sw-step-nav{display:block}.sw-sidebar-bottom{margin-top:24px}.sw-progress-text{display:block;margin-top:8px;font-size:.75rem;color:#64748b}.sw-content{flex:1;min-width:0;background:#fff;display:flex;flex-direction:column;overflow-y:auto}.sw-content-header{display:flex;justify-content:space-between;align-items:center;gap:16px;padding:20px 32px;border-bottom:1px solid #f1f5f9}.sw-step-title{font-size:1.25rem;font-weight:700;color:#111827;margin:0}.sw-step-content{max-width:780px;padding:16px 48px 60px}.sw-intro{margin-bottom:24px}.sw-step-number{font-size:1.25rem;font-weight:800;line-height:1}.sw-split-tabs{display:block;margin-bottom:16px}.sw-hint.sw-continue-hint{margin:14px 0 0}.sw-extra-status{font-size:.8125rem;color:#64748b;margin:12px 0 0}.sw-extra-status:empty{display:none}.sw-hint{font-size:.8125rem;line-height:1.5;color:#64748b;margin:0 0 16px}.sw-import-tags{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:28px}.sw-tag{font-size:.6875rem;font-weight:700;letter-spacing:1px;color:#6b7280}.sw-provider-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:10px;margin-bottom:8px}@media(max-width:768px){.sw-provider-grid{grid-template-columns:1fr}}.sw-provider-grid .sw-integration{margin-bottom:0}.sw-area-code{display:flex;align-items:flex-end;gap:12px;margin-top:12px;max-width:320px}.sw-area-code ui-text-input{flex:1}.sw-content ui-alert{display:block;margin-bottom:16px}.sw-card{display:block}.sw-header-action{margin-left:auto;min-width:0;max-width:100%}.sw-step-actions{display:flex;justify-content:space-between;align-items:center}.sw-ico{flex-shrink:0;vertical-align:-2px}.sw-actions-right{display:flex;align-items:center;gap:16px;margin-left:auto}.sw-summary{display:block}.sw-done-hint{margin:16px 0 0}@media(max-width:768px){.sw-step-actions{flex-direction:column-reverse;align-items:stretch;gap:10px}.sw-actions-right{flex-direction:column-reverse;align-items:stretch;gap:10px;margin-left:0}.sw-page{flex-direction:column}.sw-sidebar{width:100%;min-width:unset;padding:16px}.sw-brand{margin-bottom:12px}.sw-sidebar-bottom{display:none}.sw-step-content{padding:12px 20px 40px}}\n"],
43736
+ styles: [".sw-page{display:flex;min-height:100vh;min-height:100dvh;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.sw-sidebar{width:260px;min-width:260px;background:#faf9f7;border-right:1px solid #e5e2dc;display:flex;flex-direction:column;justify-content:space-between;padding:28px 20px}.sw-brand{display:flex;align-items:center;gap:10px;margin-bottom:28px}.sw-brand-text{font-size:1.125rem;font-weight:700;color:#1e293b}.sw-step-nav{display:block}.sw-sidebar-bottom{margin-top:24px}.sw-progress-text{display:block;margin-top:8px;font-size:.75rem;color:#64748b}.sw-content{flex:1;min-width:0;background:#fff;display:flex;flex-direction:column;overflow-y:auto}.sw-content-header{display:flex;justify-content:space-between;align-items:center;gap:16px;padding:20px 32px;border-bottom:1px solid #f1f5f9}.sw-step-title{font-size:1.25rem;font-weight:700;color:#111827;margin:0}.sw-step-content{max-width:780px;padding:16px 48px 60px}.sw-intro{margin-bottom:24px}.sw-step-number{font-size:1.25rem;font-weight:800;line-height:1}.sw-split-tabs{display:block;margin-bottom:16px}.sw-hint.sw-continue-hint{margin:14px 0 0}.sw-extra-status{font-size:.8125rem;color:#64748b;margin:12px 0 0}.sw-extra-status:empty{display:none}.sw-hint{font-size:.8125rem;line-height:1.5;color:#64748b;margin:0 0 16px}.sw-import-tags{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:28px}.sw-tag{font-size:.6875rem;font-weight:700;letter-spacing:1px;color:#6b7280}.sw-provider-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:10px;margin-bottom:8px}.sw-provider-grid--single{grid-template-columns:1fr}@media(max-width:768px){.sw-provider-grid{grid-template-columns:1fr}}.sw-provider-grid .sw-integration{margin-bottom:0}.sw-area-code{display:flex;align-items:flex-end;gap:12px;margin-top:12px;max-width:320px}.sw-area-code ui-text-input{flex:1}.sw-content ui-alert{display:block;margin-bottom:16px}.sw-card{display:block}.sw-header-action{margin-left:auto;min-width:0;max-width:100%}.sw-step-actions{display:flex;justify-content:space-between;align-items:center}.sw-ico{flex-shrink:0;vertical-align:-2px}.sw-actions-right{display:flex;align-items:center;gap:16px;margin-left:auto}.sw-summary{display:block}.sw-done-hint{margin:16px 0 0}@media(max-width:768px){.sw-step-actions{flex-direction:column-reverse;align-items:stretch;gap:10px}.sw-actions-right{flex-direction:column-reverse;align-items:stretch;gap:10px;margin-left:0}.sw-page{flex-direction:column}.sw-sidebar{width:100%;min-width:unset;padding:16px}.sw-brand{margin-bottom:12px}.sw-sidebar-bottom{display:none}.sw-step-content{padding:12px 20px 40px}}\n"],
43490
43737
  dependencies: [
43491
43738
  {
43492
43739
  kind: "directive",
@@ -43613,6 +43860,7 @@ var SetupWizardComponent = class SetupWizardComponent {
43613
43860
  "editIcon",
43614
43861
  "editAriaLabel",
43615
43862
  "fill",
43863
+ "maxBodyHeight",
43616
43864
  "selectable",
43617
43865
  "selected",
43618
43866
  "selectedColor",
@@ -43931,7 +44179,7 @@ i0.ɵɵngDeclareClassMetadata({
43931
44179
  }
43932
44180
  }
43933
44181
  @case ('integrations') {
43934
- <div class="sw-provider-grid">
44182
+ <div class="sw-provider-grid" [class.sw-provider-grid--single]="(current().integrations?.length ?? 0) <= 1" data-testid="wizard-provider-grid">
43935
44183
  @for (it of current().integrations ?? []; track it.key) {
43936
44184
  <ui-option-card class="sw-integration" [attr.data-testid]="'integration-' + it.key" [title]="it.title" [description]="detailFor(it)" [letter]="it.letter || ''"
43937
44185
  [icon]="it.letter ? '' : (it.icon ?? 'link')" [color]="it.color || '#64748b'" [actionLabel]="isConnected(it) ? t().reconnect : t().connect"
@@ -43960,7 +44208,7 @@ i0.ɵɵngDeclareClassMetadata({
43960
44208
  </main>
43961
44209
  </div>
43962
44210
  `,
43963
- styles: [".sw-page{display:flex;min-height:100vh;min-height:100dvh;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.sw-sidebar{width:260px;min-width:260px;background:#faf9f7;border-right:1px solid #e5e2dc;display:flex;flex-direction:column;justify-content:space-between;padding:28px 20px}.sw-brand{display:flex;align-items:center;gap:10px;margin-bottom:28px}.sw-brand-text{font-size:1.125rem;font-weight:700;color:#1e293b}.sw-step-nav{display:block}.sw-sidebar-bottom{margin-top:24px}.sw-progress-text{display:block;margin-top:8px;font-size:.75rem;color:#64748b}.sw-content{flex:1;min-width:0;background:#fff;display:flex;flex-direction:column;overflow-y:auto}.sw-content-header{display:flex;justify-content:space-between;align-items:center;gap:16px;padding:20px 32px;border-bottom:1px solid #f1f5f9}.sw-step-title{font-size:1.25rem;font-weight:700;color:#111827;margin:0}.sw-step-content{max-width:780px;padding:16px 48px 60px}.sw-intro{margin-bottom:24px}.sw-step-number{font-size:1.25rem;font-weight:800;line-height:1}.sw-split-tabs{display:block;margin-bottom:16px}.sw-hint.sw-continue-hint{margin:14px 0 0}.sw-extra-status{font-size:.8125rem;color:#64748b;margin:12px 0 0}.sw-extra-status:empty{display:none}.sw-hint{font-size:.8125rem;line-height:1.5;color:#64748b;margin:0 0 16px}.sw-import-tags{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:28px}.sw-tag{font-size:.6875rem;font-weight:700;letter-spacing:1px;color:#6b7280}.sw-provider-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:10px;margin-bottom:8px}@media(max-width:768px){.sw-provider-grid{grid-template-columns:1fr}}.sw-provider-grid .sw-integration{margin-bottom:0}.sw-area-code{display:flex;align-items:flex-end;gap:12px;margin-top:12px;max-width:320px}.sw-area-code ui-text-input{flex:1}.sw-content ui-alert{display:block;margin-bottom:16px}.sw-card{display:block}.sw-header-action{margin-left:auto;min-width:0;max-width:100%}.sw-step-actions{display:flex;justify-content:space-between;align-items:center}.sw-ico{flex-shrink:0;vertical-align:-2px}.sw-actions-right{display:flex;align-items:center;gap:16px;margin-left:auto}.sw-summary{display:block}.sw-done-hint{margin:16px 0 0}@media(max-width:768px){.sw-step-actions{flex-direction:column-reverse;align-items:stretch;gap:10px}.sw-actions-right{flex-direction:column-reverse;align-items:stretch;gap:10px;margin-left:0}.sw-page{flex-direction:column}.sw-sidebar{width:100%;min-width:unset;padding:16px}.sw-brand{margin-bottom:12px}.sw-sidebar-bottom{display:none}.sw-step-content{padding:12px 20px 40px}}\n"]
44211
+ styles: [".sw-page{display:flex;min-height:100vh;min-height:100dvh;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.sw-sidebar{width:260px;min-width:260px;background:#faf9f7;border-right:1px solid #e5e2dc;display:flex;flex-direction:column;justify-content:space-between;padding:28px 20px}.sw-brand{display:flex;align-items:center;gap:10px;margin-bottom:28px}.sw-brand-text{font-size:1.125rem;font-weight:700;color:#1e293b}.sw-step-nav{display:block}.sw-sidebar-bottom{margin-top:24px}.sw-progress-text{display:block;margin-top:8px;font-size:.75rem;color:#64748b}.sw-content{flex:1;min-width:0;background:#fff;display:flex;flex-direction:column;overflow-y:auto}.sw-content-header{display:flex;justify-content:space-between;align-items:center;gap:16px;padding:20px 32px;border-bottom:1px solid #f1f5f9}.sw-step-title{font-size:1.25rem;font-weight:700;color:#111827;margin:0}.sw-step-content{max-width:780px;padding:16px 48px 60px}.sw-intro{margin-bottom:24px}.sw-step-number{font-size:1.25rem;font-weight:800;line-height:1}.sw-split-tabs{display:block;margin-bottom:16px}.sw-hint.sw-continue-hint{margin:14px 0 0}.sw-extra-status{font-size:.8125rem;color:#64748b;margin:12px 0 0}.sw-extra-status:empty{display:none}.sw-hint{font-size:.8125rem;line-height:1.5;color:#64748b;margin:0 0 16px}.sw-import-tags{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:28px}.sw-tag{font-size:.6875rem;font-weight:700;letter-spacing:1px;color:#6b7280}.sw-provider-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:10px;margin-bottom:8px}.sw-provider-grid--single{grid-template-columns:1fr}@media(max-width:768px){.sw-provider-grid{grid-template-columns:1fr}}.sw-provider-grid .sw-integration{margin-bottom:0}.sw-area-code{display:flex;align-items:flex-end;gap:12px;margin-top:12px;max-width:320px}.sw-area-code ui-text-input{flex:1}.sw-content ui-alert{display:block;margin-bottom:16px}.sw-card{display:block}.sw-header-action{margin-left:auto;min-width:0;max-width:100%}.sw-step-actions{display:flex;justify-content:space-between;align-items:center}.sw-ico{flex-shrink:0;vertical-align:-2px}.sw-actions-right{display:flex;align-items:center;gap:16px;margin-left:auto}.sw-summary{display:block}.sw-done-hint{margin:16px 0 0}@media(max-width:768px){.sw-step-actions{flex-direction:column-reverse;align-items:stretch;gap:10px}.sw-actions-right{flex-direction:column-reverse;align-items:stretch;gap:10px;margin-left:0}.sw-page{flex-direction:column}.sw-sidebar{width:100%;min-width:unset;padding:16px}.sw-brand{margin-bottom:12px}.sw-sidebar-bottom{display:none}.sw-step-content{padding:12px 20px 40px}}\n"]
43964
44212
  }]
43965
44213
  }],
43966
44214
  ctorParameters: () => [],