@forgeroute/ui 0.4.40 → 0.4.41

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgeroute/ui",
3
- "version": "0.4.40",
3
+ "version": "0.4.41",
4
4
  "peerDependencies": {
5
5
  "@angular/common": ">=22.0.0",
6
6
  "@angular/core": ">=22.0.0",
@@ -115,7 +115,7 @@ export declare class TemplatePickerComponent {
115
115
  private readonly host;
116
116
  private readonly search$;
117
117
  /** Filter results to a specific channel: 'email', 'text', or omit for all. */
118
- readonly channel: import("@angular/core").InputSignal<"" | "email" | "text">;
118
+ readonly channel: import("@angular/core").InputSignal<"" | "text" | "email">;
119
119
  readonly selectedId: import("@angular/core").InputSignal<string | null>;
120
120
  readonly label: import("@angular/core").InputSignal<string>;
121
121
  /** Shows the red required asterisk (same as the library inputs) — e.g. when a template must be picked to send. */
@@ -543,7 +543,7 @@ export declare class DocumentSendOptionsComponent implements UiFieldValidity {
543
543
  /** API root from FORGEROUTE_CONFIG (e.g. a cross-origin prod API); falls back to same-origin `/api`. */
544
544
  private readonly apiBase;
545
545
  /** `email` → purpose select + attachment allowed; `text` → link only (purpose fixed to e-signature). */
546
- readonly channel: import("@angular/core").InputSignal<"email" | "text">;
546
+ readonly channel: import("@angular/core").InputSignal<"text" | "email">;
547
547
  readonly label: import("@angular/core").InputSignal<string>;
548
548
  /** When true, at least one document must be chosen. The message forms leave it false: documents are optional there. */
549
549
  readonly required: import("@angular/core").InputSignal<boolean>;
@@ -608,7 +608,7 @@ export declare class DocumentSendOptionsComponent implements UiFieldValidity {
608
608
  /** Roles of documents whose row had no `signerRoles` and that the host resolves (`signers().resolveRoles`). */
609
609
  readonly roleStates: import("@angular/core").WritableSignal<Record<string, RoleState>>;
610
610
  /** The envelope's documents: `loading` / `error` while the panel reads them (`loadDocuments`), `ready` once known. */
611
- readonly envelopeStatus: import("@angular/core").WritableSignal<"idle" | "loading" | "ready" | "error">;
611
+ readonly envelopeStatus: import("@angular/core").WritableSignal<"error" | "loading" | "ready" | "idle">;
612
612
  private envelopeSub;
613
613
  private readonly roleSubs;
614
614
  /** The sender opened the collapsed "Signing options" line. */
@@ -1813,7 +1813,7 @@ export declare class SendMessagePanelComponent {
1813
1813
  readonly activeContact: import("@angular/core").WritableSignal<MessageContact | null>;
1814
1814
  /** What the sender should know about the picked contact before sending (why a channel is missing, what to add). */
1815
1815
  readonly contactNotices: import("@angular/core").Signal<string[]>;
1816
- readonly channel: import("@angular/core").WritableSignal<"email" | "text">;
1816
+ readonly channel: import("@angular/core").WritableSignal<"text" | "email">;
1817
1817
  readonly valid: import("@angular/core").WritableSignal<boolean>;
1818
1818
  /** Bumped on every fresh open / new contact — re-creates the email and text forms so no draft carries over. */
1819
1819
  readonly formEpoch: import("@angular/core").WritableSignal<number>;
@@ -1,5 +1,6 @@
1
1
  import * as i0 from "@angular/core";
2
- import { InjectionToken, Provider } from "@angular/core";
2
+ import { DestroyRef, InjectionToken, Provider, WritableSignal } from "@angular/core";
3
+ import { Observable } from "rxjs";
3
4
  /**
4
5
  * The small part of Stripe.js the library calls, written as plain structural types so the library has NO compile-time or run-time
5
6
  * dependency on `@stripe/stripe-js` outside of the lazy loader (`stripe-loader.ts`). The real `Stripe` object satisfies these; so does a hand-written fake.
@@ -1141,104 +1142,213 @@ export declare class MicrodepositVerifyComponent {
1141
1142
  "verifyFailed": "verifyFailed";
1142
1143
  }, never, never, true, never>;
1143
1144
  }
1145
+ /** What the account-status call answers (the shape of cremo's `GET /organizations/connect/status`). `isComplete` = Stripe accepts charges and payouts. */
1146
+ interface StripeConnectStatus {
1147
+ isComplete: boolean;
1148
+ detailsSubmitted?: boolean;
1149
+ /** The address of Stripe's onboarding page for an unfinished account. */
1150
+ onboardingUrl?: string;
1151
+ }
1152
+ /** What the create-account call answers (cremo's `POST /organizations/connect/create`): the new (or resumed) account and the address of Stripe's onboarding page. */
1153
+ interface StripeConnectAccount {
1154
+ accountId: string;
1155
+ onboardingUrl: string;
1156
+ }
1157
+ /**
1158
+ * What happened after the user pressed a button or the page asked for the state. The flow never shows toasts itself (each app has its own): the host words
1159
+ * each outcome, and decides what follows a finished account (for example switching payment collection on).
1160
+ *
1161
+ * - `complete`: Stripe accepts the account. `source: 'check'` = after "Check Status"; `'resume'` = "Complete Setup" found it already complete; `'return'` =
1162
+ * the state was read again when the user came back to this tab (`refreshOnReturn`).
1163
+ * - `verifying`: the details were submitted and Stripe is reviewing them.
1164
+ * - `incomplete`: "Check Status" found an unfinished onboarding (`onboardingUrl` present).
1165
+ * - `link-unavailable`: the answer had no https Stripe address; nothing was opened.
1166
+ * - `failed`: a call failed (`action` says which); `error` is what it threw.
1167
+ */
1168
+ type StripeConnectOutcome = {
1169
+ kind: 'complete';
1170
+ source: 'check' | 'resume' | 'return';
1171
+ } | {
1172
+ kind: 'verifying';
1173
+ source: 'check' | 'resume' | 'return';
1174
+ } | {
1175
+ kind: 'incomplete';
1176
+ } | {
1177
+ kind: 'link-unavailable';
1178
+ } | {
1179
+ kind: 'failed';
1180
+ action: 'start' | 'check' | 'resume';
1181
+ error: unknown;
1182
+ };
1183
+ /** The state the flow reads and writes: signals owned by the host (or by the card's two-way models). */
1184
+ interface StripeConnectState {
1185
+ accountId: WritableSignal<string | null>;
1186
+ complete: WritableSignal<boolean>;
1187
+ detailsSubmitted: WritableSignal<boolean>;
1188
+ loading: WritableSignal<boolean>;
1189
+ }
1190
+ interface StripeConnectFlowOptions {
1191
+ /** Read at call time, so a host can swap the call. Null = no call yet (`start()` does nothing). */
1192
+ createAccount: () => (() => Observable<StripeConnectAccount>) | null;
1193
+ getStatus: () => (() => Observable<StripeConnectStatus>) | null;
1194
+ state: StripeConnectState;
1195
+ /** Where outcomes go. */
1196
+ emit: (outcome: StripeConnectOutcome) => void;
1197
+ /** The buttons and methods do nothing while this says true (a read-only user). */
1198
+ disabled?: () => boolean;
1199
+ /** How THIS tab is sent to Stripe when the browser refused the new tab (default `window.location.href = url`). */
1200
+ navigate?: (url: string) => void;
1201
+ destroyRef: DestroyRef;
1202
+ }
1203
+ /** Only an https address on stripe.com or a real subdomain (never `evilstripe.com` / `connect.stripe.com.evil.com`, `http:`, `javascript:`). */
1204
+ export declare function isStripeUrl(url: string | undefined): url is string;
1205
+ /**
1206
+ * The Stripe Connect flow of cremo Settings > Payment & Billing, as a plain class: create the account, "Check Status", "Complete Setup", and the "back from Stripe" read.
1207
+ * `ui-stripe-connect` runs it behind its card; a host that has to start the flow from code before any view exists (Settings: the first change-detection pass with
1208
+ * `?connect=return`) creates its own instance over the same state signals. Stripe's page always opens in a NEW tab opened inside the click (a tab opened after the
1209
+ * request is blocked by popup blockers); only an https Stripe address is opened; a failed call closes the empty tab.
1210
+ */
1211
+ export declare class StripeConnectFlow {
1212
+ private readonly o;
1213
+ /** A Stripe tab was opened: the card reads the state again when the user comes back (`refreshOnReturn`). */
1214
+ tabOpened: boolean;
1215
+ constructor(o: StripeConnectFlowOptions);
1216
+ private get blocked();
1217
+ /** Creates (or resumes) the account and opens Stripe's onboarding page in a new tab. */
1218
+ start(): void;
1219
+ /** "Check Status": reads the state; a finished or submitted account updates the card, an unfinished one is reported. */
1220
+ check(): void;
1221
+ /** "Complete Setup": reads the state and opens the onboarding address of an unfinished account in a new tab (a finished or submitted one just updates the card). */
1222
+ resume(): void;
1223
+ /** The state read when the user came back to this tab: silent unless something finished or was submitted. */
1224
+ readStatusOnReturn(): void;
1225
+ private openStripe;
1226
+ /** The empty tab, opened inside the click and cut from this page (`opener`); null when the browser refused. */
1227
+ openBlankTab(): Window | null;
1228
+ /** The browser refused the new tab: Stripe opens in this one. */
1229
+ private navigateCurrentTab;
1230
+ private closeTab;
1231
+ }
1232
+ /**
1233
+ * The default wording of an outcome (the texts cremo Settings has always used), for apps that just want a toast: `{ variant, message }`, or null when
1234
+ * nothing should be said. `completeText` replaces the sentence of a finished account when the host has more to say about what follows.
1235
+ */
1236
+ export declare function stripeConnectNotice(outcome: StripeConnectOutcome): {
1237
+ variant: 'success' | 'info' | 'error';
1238
+ message: string;
1239
+ } | null;
1144
1240
  /**
1145
- * "Connect your Stripe account": the one card every app shows where a business connects the Stripe account it collects payments into (Stripe Connect). It reads
1146
- * the account's state from the app's backend, and Connect asks the backend for the address of Stripe's onboarding page and sends the browser there: the user
1147
- * finishes setting up the account on Stripe and Stripe sends them back to the address the backend chose. An unfinished onboarding simply shows Connect again
1148
- * (the backend resumes the same account); a finished one shows Connected with Reconnect.
1241
+ * "Stripe Connect Account": the card where a business connects the Stripe account it collects payments into (cremo Settings > Payment & Billing, built here
1242
+ * once so every app and the setup wizard show and run the SAME flow). The host owns the data (its two calls and the three state values); the component owns
1243
+ * the screen and the flow:
1149
1244
  *
1150
- * <ui-stripe-connect [connectParams]="{ returnTo: 'setup' }" (statusChange)="onConnected($event)" />
1245
+ * - no account yet (`accountId` empty): nothing is shown (the host has its own entry point and calls `start()`), or with `showWhenNoAccount` a card with
1246
+ * "Set Up Payment Account";;
1247
+ * - account, not complete, nothing submitted: warning "Account setup incomplete" with "Complete Setup" (asks the status for the onboarding address and opens it);
1248
+ * - details submitted, not complete: info "Stripe is verifying your account" with "Check Status";
1249
+ * - complete: success "Account setup complete" with the account id and "View Stripe Dashboard".
1151
1250
  *
1152
- * The backend follows the convention of the setup wizard's `stripe` preset: `GET /integrations/stripe/status` answers `{ connected }` and
1153
- * `GET /integrations/stripe/connect` answers `{ url }` (`authUrl` / `onboardingUrl` are read too). Both addresses and the texts can be replaced. The calls go
1154
- * through `FORGEROUTE_CONFIG.baseUrl` and the app's HTTP interceptors (its sign-in token).
1251
+ * Stripe's onboarding page always opens in a NEW tab: the empty tab is opened inside the click (a tab opened after the request is blocked by popup
1252
+ * blockers) and receives the address when it arrives; with the tab refused the address opens in the current tab. Only an https Stripe address is ever opened,
1253
+ * never one an answer made up (`javascript:`, `data:`). A failed call closes the empty tab.
1254
+ *
1255
+ * <ui-stripe-connect [(accountId)]="accountId" [(complete)]="complete" [(detailsSubmitted)]="submitted"
1256
+ * [createAccount]="createAccount" [getStatus]="getStatus" [showWhenNoAccount]="true" (outcome)="onOutcome($event)" />
1257
+ *
1258
+ * where `createAccount = () => http.post<StripeConnectAccount>('/connect/create', {})` and `getStatus = () => http.get<StripeConnectStatus>('/connect/status')`.
1155
1259
  */
1156
1260
  export declare class StripeConnectComponent {
1157
- private readonly http;
1158
1261
  private readonly destroyRef;
1159
- private readonly apiBase;
1160
- /** GET: answers `{ connected }`. Relative to `FORGEROUTE_CONFIG.baseUrl`. */
1161
- readonly statusEndpoint: import("@angular/core").InputSignal<string>;
1162
- /** GET: answers the address of Stripe's onboarding page (`{ url }`). Relative to `FORGEROUTE_CONFIG.baseUrl`. */
1163
- readonly connectEndpoint: import("@angular/core").InputSignal<string>;
1164
- /** Query parameters sent with the connect request (for example `{ returnTo: 'setup' }`: where Stripe sends the user back to). */
1165
- readonly connectParams: import("@angular/core").InputSignal<Record<string, string> | null>;
1262
+ /** The call that creates the organization's Stripe account (or resumes it) and answers its id and the onboarding address. Needed for `start()`. */
1263
+ readonly createAccount: import("@angular/core").InputSignal<(() => Observable<StripeConnectAccount>) | null>;
1264
+ /** The call that reads the account's state. Needed for `check()`, `resume()` and `refreshOnReturn`. */
1265
+ readonly getStatus: import("@angular/core").InputSignal<(() => Observable<StripeConnectStatus>) | null>;
1266
+ /** The Stripe account's id (null / empty = no account yet). Two-way: the component sets it when `start()` created the account. */
1267
+ readonly accountId: import("@angular/core").ModelSignal<string | null>;
1268
+ /** Stripe accepts the account (charges and payouts on). Two-way: set by `check()` / `resume()`. */
1269
+ readonly complete: import("@angular/core").ModelSignal<boolean>;
1270
+ /** The details were submitted and Stripe is reviewing them. Two-way: set by `check()` / `resume()`. */
1271
+ readonly detailsSubmitted: import("@angular/core").ModelSignal<boolean>;
1166
1272
  readonly title: import("@angular/core").InputSignal<string>;
1167
- readonly description: import("@angular/core").InputSignal<string>;
1168
- readonly connectedText: import("@angular/core").InputSignal<string>;
1169
- readonly connectLabel: import("@angular/core").InputSignal<string>;
1170
- readonly reconnectLabel: import("@angular/core").InputSignal<string>;
1171
- /** What the user sees when the status or the address could not be loaded and the server gave no message. */
1172
- readonly loadFailedText: import("@angular/core").InputSignal<string>;
1173
- readonly connectFailedText: import("@angular/core").InputSignal<string>;
1273
+ /** Shows the card (with "Set Up Payment Account") while there is no account yet. Default false: hosts with their own entry point show nothing and call `start()`. */
1274
+ readonly showWhenNoAccount: import("@angular/core").InputSignal<boolean>;
1275
+ readonly startTitle: import("@angular/core").InputSignal<string>;
1276
+ readonly startText: import("@angular/core").InputSignal<string>;
1277
+ readonly startLabel: import("@angular/core").InputSignal<string>;
1278
+ /** The buttons cannot be pressed (a read-only user). */
1174
1279
  readonly disabled: import("@angular/core").InputSignal<boolean>;
1175
- /** The account's state changed (it was read, or read again): true = connected. */
1176
- readonly statusChange: import("@angular/core").OutputEmitterRef<boolean>;
1177
- protected readonly connected: import("@angular/core").WritableSignal<boolean>;
1178
- protected readonly statusKnown: import("@angular/core").WritableSignal<boolean>;
1179
- protected readonly connecting: import("@angular/core").WritableSignal<boolean>;
1180
- protected readonly error: import("@angular/core").WritableSignal<string>;
1181
- protected readonly busy: import("@angular/core").Signal<boolean>;
1182
- private started;
1280
+ /** After a Stripe tab was opened, the state is read again when this tab becomes visible again (the user came back): `(outcome)` then reports `complete` / `verifying` with source `return`. */
1281
+ readonly refreshOnReturn: import("@angular/core").InputSignal<boolean>;
1282
+ /** The page "View Stripe Dashboard" opens. */
1283
+ readonly dashboardUrl: import("@angular/core").InputSignal<string>;
1284
+ /** What happened (see {@link StripeConnectOutcome}); the host words it. */
1285
+ readonly outcome: import("@angular/core").OutputEmitterRef<StripeConnectOutcome>;
1286
+ /** A request is running: the buttons show "Loading..." / "Checking..." and cannot be pressed again. Two-way, so a host that shows the state elsewhere can read it. */
1287
+ readonly loading: import("@angular/core").ModelSignal<boolean>;
1288
+ /** How the current tab is sent to Stripe when the browser refused the new tab (default: `window.location.href = url`); a host can route it through its own method. */
1289
+ readonly navigateTab: import("@angular/core").InputSignal<((url: string) => void) | null>;
1290
+ protected readonly visible: import("@angular/core").Signal<boolean>;
1291
+ private readonly flow;
1183
1292
  constructor();
1184
- /** Reads the account's state again (after the user came back from Stripe, for example). */
1185
- refresh(): void;
1186
- /** Asks the backend for Stripe's onboarding address and sends the browser there. */
1187
- protected connect(): void;
1188
- /** Sends the browser to Stripe. */
1189
- protected redirect(url: string): void;
1190
- /** Only an http(s) address is ever opened (never `javascript:` or `data:` from a response). */
1191
- private isWebAddress;
1192
- private messageOf;
1293
+ /** Creates (or resumes) the account and opens Stripe's onboarding page in a new tab. */
1294
+ start(): void;
1295
+ /** "Check Status": reads the state; a finished or submitted account updates the card, an unfinished one is reported. */
1296
+ check(): void;
1297
+ /** "Complete Setup": reads the state and opens the onboarding address of an unfinished account in a new tab. */
1298
+ resume(): void;
1299
+ /** Opens the Stripe dashboard in a new tab. */
1300
+ openDashboard(): void;
1301
+ /** The browser refused the new tab: Stripe opens in this one (through the host's `navigateTab` when it gave one). */
1302
+ protected navigateCurrentTab(url: string): void;
1193
1303
  static ɵfac: i0.ɵɵFactoryDeclaration<StripeConnectComponent, never>;
1194
1304
  static ɵcmp: i0.ɵɵComponentDeclaration<StripeConnectComponent, "ui-stripe-connect", never, {
1195
- "statusEndpoint": {
1196
- "alias": "statusEndpoint";
1305
+ "createAccount": {
1306
+ "alias": "createAccount";
1197
1307
  "required": false;
1198
1308
  "isSignal": true;
1199
1309
  };
1200
- "connectEndpoint": {
1201
- "alias": "connectEndpoint";
1310
+ "getStatus": {
1311
+ "alias": "getStatus";
1202
1312
  "required": false;
1203
1313
  "isSignal": true;
1204
1314
  };
1205
- "connectParams": {
1206
- "alias": "connectParams";
1315
+ "accountId": {
1316
+ "alias": "accountId";
1207
1317
  "required": false;
1208
1318
  "isSignal": true;
1209
1319
  };
1210
- "title": {
1211
- "alias": "title";
1320
+ "complete": {
1321
+ "alias": "complete";
1212
1322
  "required": false;
1213
1323
  "isSignal": true;
1214
1324
  };
1215
- "description": {
1216
- "alias": "description";
1325
+ "detailsSubmitted": {
1326
+ "alias": "detailsSubmitted";
1217
1327
  "required": false;
1218
1328
  "isSignal": true;
1219
1329
  };
1220
- "connectedText": {
1221
- "alias": "connectedText";
1330
+ "title": {
1331
+ "alias": "title";
1222
1332
  "required": false;
1223
1333
  "isSignal": true;
1224
1334
  };
1225
- "connectLabel": {
1226
- "alias": "connectLabel";
1335
+ "showWhenNoAccount": {
1336
+ "alias": "showWhenNoAccount";
1227
1337
  "required": false;
1228
1338
  "isSignal": true;
1229
1339
  };
1230
- "reconnectLabel": {
1231
- "alias": "reconnectLabel";
1340
+ "startTitle": {
1341
+ "alias": "startTitle";
1232
1342
  "required": false;
1233
1343
  "isSignal": true;
1234
1344
  };
1235
- "loadFailedText": {
1236
- "alias": "loadFailedText";
1345
+ "startText": {
1346
+ "alias": "startText";
1237
1347
  "required": false;
1238
1348
  "isSignal": true;
1239
1349
  };
1240
- "connectFailedText": {
1241
- "alias": "connectFailedText";
1350
+ "startLabel": {
1351
+ "alias": "startLabel";
1242
1352
  "required": false;
1243
1353
  "isSignal": true;
1244
1354
  };
@@ -1247,8 +1357,32 @@ export declare class StripeConnectComponent {
1247
1357
  "required": false;
1248
1358
  "isSignal": true;
1249
1359
  };
1360
+ "refreshOnReturn": {
1361
+ "alias": "refreshOnReturn";
1362
+ "required": false;
1363
+ "isSignal": true;
1364
+ };
1365
+ "dashboardUrl": {
1366
+ "alias": "dashboardUrl";
1367
+ "required": false;
1368
+ "isSignal": true;
1369
+ };
1370
+ "loading": {
1371
+ "alias": "loading";
1372
+ "required": false;
1373
+ "isSignal": true;
1374
+ };
1375
+ "navigateTab": {
1376
+ "alias": "navigateTab";
1377
+ "required": false;
1378
+ "isSignal": true;
1379
+ };
1250
1380
  }, {
1251
- "statusChange": "statusChange";
1381
+ "accountId": "accountIdChange";
1382
+ "complete": "completeChange";
1383
+ "detailsSubmitted": "detailsSubmittedChange";
1384
+ "outcome": "outcome";
1385
+ "loading": "loadingChange";
1252
1386
  }, never, never, true, never>;
1253
1387
  }
1254
- export type { HandleReturnOptions, MicrodepositStartedBy, MicrodepositStepsContext, MicrodepositType, MicrodepositVerifyData, MicrodepositVerifyEvent, ProvideStripePaymentOptions, StripeA11yMessages, StripeAppearance, StripeBillingDetails, StripeConfirmResult, StripeConfirmedEvent, StripeConnection, StripeCreateIntentFn, StripeDeferredIntent, StripeElementChange, StripeElementsLike, StripeErrorDisplay, StripeErrorLike, StripeErrorMessageOptions, StripeFailure, StripeFailureStage, StripeIntentKind, StripeIntentLike, StripeLike, StripeLikeWithBankSetup, StripeLoadOptions, StripeLoaderFn, StripeMicrodepositNextAction, StripeMicrodepositResult, StripeOutcome, StripePaymentDefaults, StripePaymentElementLike, StripeReadyEvent, StripeReturnParams, StripeReturnResult, StripeVerification };
1388
+ export type { HandleReturnOptions, MicrodepositStartedBy, MicrodepositStepsContext, MicrodepositType, MicrodepositVerifyData, MicrodepositVerifyEvent, ProvideStripePaymentOptions, StripeA11yMessages, StripeAppearance, StripeBillingDetails, StripeConfirmResult, StripeConfirmedEvent, StripeConnectAccount, StripeConnectFlowOptions, StripeConnectOutcome, StripeConnectState, StripeConnectStatus, StripeConnection, StripeCreateIntentFn, StripeDeferredIntent, StripeElementChange, StripeElementsLike, StripeErrorDisplay, StripeErrorLike, StripeErrorMessageOptions, StripeFailure, StripeFailureStage, StripeIntentKind, StripeIntentLike, StripeLike, StripeLikeWithBankSetup, StripeLoadOptions, StripeLoaderFn, StripeMicrodepositNextAction, StripeMicrodepositResult, StripeOutcome, StripePaymentDefaults, StripePaymentElementLike, StripeReadyEvent, StripeReturnParams, StripeReturnResult, StripeVerification };
@@ -7599,6 +7599,8 @@ export declare class AlertComponent {
7599
7599
  * Default false: the body is a flex row with a gap, exactly as before (use it for text plus a button).
7600
7600
  */
7601
7601
  readonly inline: import("@angular/core").InputSignal<boolean>;
7602
+ /** The box fills the height of its parent cell (two alerts in one grid row get the same height whatever their text length). Default false. */
7603
+ readonly fill: import("@angular/core").InputSignal<boolean>;
7602
7604
  readonly dismissed: import("@angular/core").OutputEmitterRef<void>;
7603
7605
  static ɵfac: i0.ɵɵFactoryDeclaration<AlertComponent, never>;
7604
7606
  static ɵcmp: i0.ɵɵComponentDeclaration<AlertComponent, "ui-alert", never, {
@@ -7637,6 +7639,11 @@ export declare class AlertComponent {
7637
7639
  "required": false;
7638
7640
  "isSignal": true;
7639
7641
  };
7642
+ "fill": {
7643
+ "alias": "fill";
7644
+ "required": false;
7645
+ "isSignal": true;
7646
+ };
7640
7647
  }, {
7641
7648
  "dismissed": "dismissed";
7642
7649
  }, never, ["*"], true, never>;
@@ -9466,6 +9473,55 @@ export declare class DividerComponent {
9466
9473
  };
9467
9474
  }, {}, never, never, true, never>;
9468
9475
  }
9476
+ export type FixedFooterAlign = 'start' | 'center' | 'end' | 'between';
9477
+ /**
9478
+ * A footer bar fixed to the bottom of the page: it stays visible while the page scrolls, sits flush with the bottom of the viewport
9479
+ * and spans the full width of the content area (from the side nav to the end of the screen). Put it as the LAST child of the page:
9480
+ *
9481
+ * <div class="settings-page">
9482
+ * ...the page content...
9483
+ * <ui-fixed-footer>
9484
+ * <ui-button variant="primary" (click)="save()">Save All Settings</ui-button>
9485
+ * </ui-fixed-footer>
9486
+ * </div>
9487
+ *
9488
+ * How it works: the bar is `position: sticky` at the bottom of the scrolling page. Inside `ui-app-shell` the content area is padded
9489
+ * (24px / 16px / 12px by width) and the shell publishes that padding as `--shell-content-padding`; the footer cancels it with negative
9490
+ * margins and a negative `bottom`, so it reaches the viewport edges instead of stopping inside the padding. Outside the shell the
9491
+ * variable is unset (0px) and the bar simply sticks to the bottom of its scrolling parent. Nothing else needs to be set up.
9492
+ *
9493
+ * Inputs:
9494
+ * - `align` (default `end`): where the projected content sits: start | center | end | between.
9495
+ * - `ariaLabel` (default `Page actions`): the accessible name of the bar (a labelled region).
9496
+ * - `elevated` (default true): the soft shadow above the bar; false = just the top border.
9497
+ * Theming by CSS custom properties: `--ui-footer-bg` (white), `--ui-footer-border` (#e2e8f0), `--ui-footer-pad-y` (20px; 16px up to 1024px, 14px up to 480px).
9498
+ */
9499
+ export declare class FixedFooterComponent {
9500
+ /** Where the projected content sits in the bar. */
9501
+ readonly align: import("@angular/core").InputSignal<FixedFooterAlign>;
9502
+ /** Accessible name of the bar. */
9503
+ readonly ariaLabel: import("@angular/core").InputSignal<string>;
9504
+ /** The soft shadow above the bar (false = only the top border). */
9505
+ readonly elevated: import("@angular/core").InputSignal<boolean>;
9506
+ static ɵfac: i0.ɵɵFactoryDeclaration<FixedFooterComponent, never>;
9507
+ static ɵcmp: i0.ɵɵComponentDeclaration<FixedFooterComponent, "ui-fixed-footer", never, {
9508
+ "align": {
9509
+ "alias": "align";
9510
+ "required": false;
9511
+ "isSignal": true;
9512
+ };
9513
+ "ariaLabel": {
9514
+ "alias": "ariaLabel";
9515
+ "required": false;
9516
+ "isSignal": true;
9517
+ };
9518
+ "elevated": {
9519
+ "alias": "elevated";
9520
+ "required": false;
9521
+ "isSignal": true;
9522
+ };
9523
+ }, {}, never, ["*"], true, never>;
9524
+ }
9469
9525
  /** One column (stage) of a [ui-board]. `items` are the host's own records; the board never reads their fields. */
9470
9526
  export interface BoardColumn<T = unknown> {
9471
9527
  /** Unique id of the column (the stage key); reported in `from` / `to` of a move. */
@@ -11313,12 +11369,13 @@ export interface IntegrationPlainText {
11313
11369
  export declare function integrationPlainText(html: string): IntegrationPlainText;
11314
11370
  /**
11315
11371
  * How a connect button for an integration works.
11316
- * - `oauth`: GET `authorizeEndpoint` answers `{ url }` (or `{ authUrl }`) and the browser goes there (QuickBooks, Slack, Gmail, Outlook, Google / Outlook calendar, Stripe, Square).
11372
+ * - `oauth`: GET `authorizeEndpoint` answers `{ url }` (or `{ authUrl }`) and the browser goes there (in a NEW tab with `newTab: true`, opened inside the click so popup blockers allow it, else in this tab) (QuickBooks, Slack, Gmail, Outlook, Google / Outlook calendar, Stripe, Square).
11317
11373
  * - `area-code`: asks for a US area code and POSTs `{ areaCode }` to `connectEndpoint` (Twilio buys a number that way); the status is reloaded after.
11318
11374
  */
11319
11375
  export type SetupIntegrationConnect = {
11320
11376
  type: 'oauth';
11321
11377
  authorizeEndpoint: string;
11378
+ newTab?: boolean;
11322
11379
  } | {
11323
11380
  type: 'area-code';
11324
11381
  connectEndpoint: string;
@@ -11365,6 +11422,8 @@ export interface SetupWizardTexts {
11365
11422
  selectSource: string;
11366
11423
  connected: string;
11367
11424
  importMore: string;
11425
+ /** The link on an import step (before data came in) that downloads the template CSV: the header row and the config's `templateSampleRows`, so the user can add their data to it. */
11426
+ downloadSample: string;
11368
11427
  /** Shown (and described by the disabled Continue) on an import step with `requireImport` until data came in. */
11369
11428
  importRequired: string;
11370
11429
  /** An import where some rows were refused: the warning's title and text (rows that failed must be fixed and imported again before Continue) and the button that returns to the sources. */
@@ -11394,6 +11453,8 @@ export interface SetupImportStep {
11394
11453
  buttonLabel: string;
11395
11454
  /** Hint above the button. */
11396
11455
  hint?: string;
11456
+ /** Text of the template download link, e.g. "Download Products/Services CSV Template" (default: the `downloadSample` text, "Download CSV Template"). The file is this step's own template: the config's field labels and `templateSampleRows`. */
11457
+ templateLabel?: string;
11397
11458
  /** Result alert title, built from the number imported (default `${n} ${config.entityNamePlural.toLowerCase()} imported successfully.`). */
11398
11459
  resultTitle?: (imported: number) => string;
11399
11460
  /** Result alert body. */
@@ -11560,6 +11621,8 @@ export declare class SetupWizardComponent {
11560
11621
  readonly steps: import("@angular/core").InputSignal<SetupStep[]>;
11561
11622
  /** Replaces any of the built-in texts. */
11562
11623
  readonly texts: import("@angular/core").InputSignal<Partial<SetupWizardTexts>>;
11624
+ /** The key of the step to open first (for example the one a payment provider's return address names); default (empty or unknown) = the first step. Read once, when the wizard starts. */
11625
+ readonly initialStep: import("@angular/core").InputSignal<string>;
11563
11626
  /** The last step's button ("Go to Dashboard"). */
11564
11627
  readonly finished: import("@angular/core").OutputEmitterRef<void>;
11565
11628
  /** "Exit Setup". */
@@ -11579,6 +11642,12 @@ export declare class SetupWizardComponent {
11579
11642
  private readonly importErrors;
11580
11643
  private readonly statuses;
11581
11644
  readonly busy: import("@angular/core").WritableSignal<boolean>;
11645
+ /** Key of the integration whose authorization address is being requested: only that card's button shows a loader (the footer's Save & Continue is not busy), and no second press starts a second connection. */
11646
+ protected readonly connecting: import("@angular/core").WritableSignal<string>;
11647
+ /** The blank tab opened on the Connect click (the click is the user gesture; the address arrives later), or null when none was opened / the browser blocked it. */
11648
+ private pendingTab;
11649
+ /** A provider's page was opened in another tab: when this tab becomes visible again the step's statuses are read again. */
11650
+ private connectTabOpened;
11582
11651
  /** What a custom step's template receives (`let-wizard`). */
11583
11652
  protected readonly api: SetupWizardApi;
11584
11653
  protected readonly error: import("@angular/core").WritableSignal<string>;
@@ -11626,6 +11695,8 @@ export declare class SetupWizardComponent {
11626
11695
  private messageOf;
11627
11696
  /** A source card was picked on the step itself: opens the import window already past its source step (at the upload step for that source). */
11628
11697
  protected startImport(key: string, source: string): void;
11698
+ /** The sample CSV (the import window's template): the field labels as the header row and the config's example rows. */
11699
+ protected downloadSample(): void;
11629
11700
  protected openImport(key: string): void;
11630
11701
  protected onImported(key: string): void;
11631
11702
  protected importResult(key: string): number | null;
@@ -11638,13 +11709,18 @@ export declare class SetupWizardComponent {
11638
11709
  protected isConnected(i: SetupIntegration): boolean;
11639
11710
  protected detailFor(i: SetupIntegration): string;
11640
11711
  protected connect(i: SetupIntegration): void;
11712
+ /** Opens an empty tab for the provider's page (null when the browser refuses; the address then opens in this tab). */
11713
+ protected openTab(): Window | null;
11714
+ private closePendingTab;
11641
11715
  private startConnect;
11642
11716
  private isWebAddress;
11643
11717
  /** SVG path of a library icon (the buttons' leading / trailing glyph). */
11644
11718
  protected iconD(name: string): string;
11645
11719
  protected areaCodeLabelFor(i: SetupIntegration): string;
11646
- /** Sends the browser to the provider's authorization page. */
11720
+ /** Opens the provider's authorization page: in the tab opened on the click, else (a blocked tab) in this one. */
11647
11721
  protected redirect(url: string): void;
11722
+ /** Sends THIS tab to the address (no new tab was opened, the browser refused it or the user closed it). */
11723
+ protected navigateHere(url: string): void;
11648
11724
  protected submitAreaCode(i: SetupIntegration): void;
11649
11725
  static ɵfac: i0.ɵɵFactoryDeclaration<SetupWizardComponent, never>;
11650
11726
  static ɵcmp: i0.ɵɵComponentDeclaration<SetupWizardComponent, "ui-setup-wizard", never, {
@@ -11658,6 +11734,11 @@ export declare class SetupWizardComponent {
11658
11734
  "required": false;
11659
11735
  "isSignal": true;
11660
11736
  };
11737
+ "initialStep": {
11738
+ "alias": "initialStep";
11739
+ "required": false;
11740
+ "isSignal": true;
11741
+ };
11661
11742
  }, {
11662
11743
  "finished": "finished";
11663
11744
  "exited": "exited";