@zeniai/web-components 4.3.76-beta12MM → 4.3.76-beta13MM

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.
Files changed (56) hide show
  1. package/dist/{SessionTimeoutPopup-DO46U3vX.js → SessionTimeoutPopup-Bb_GLNw2.js} +989 -804
  2. package/dist/SessionTimeoutPopup-Bb_GLNw2.js.map +4 -0
  3. package/dist/{SessionTimeoutPopup-BP3VhaHV.cjs → SessionTimeoutPopup-ZOLbgX1q.cjs} +433 -289
  4. package/dist/SessionTimeoutPopup-ZOLbgX1q.cjs.map +4 -0
  5. package/dist/appLocale.d.ts +46 -21
  6. package/dist/cockpit.cjs.js +152 -152
  7. package/dist/cockpit.cjs.js.map +1 -1
  8. package/dist/cockpit.esm.js +2096 -2096
  9. package/dist/cockpit.esm.js.map +1 -1
  10. package/dist/components/aiCfo/components/AiCfoRoutinesBrowser.d.ts +1 -1
  11. package/dist/components/aiCfo/components/helpers/aiCfoRoutines.d.ts +2 -0
  12. package/dist/components/commentsAndNotes/utils/CollaborationHelpers.d.ts +15 -0
  13. package/dist/components/common/popUps/RecommendationFeedbackPopup.d.ts +3 -1
  14. package/dist/components/dashboard/Dashboard.d.ts +7 -1
  15. package/dist/components/expenseAutomation/ExpenseAutomationPage.d.ts +6 -0
  16. package/dist/components/expenseAutomation/components/navbarHeaderData.d.ts +4 -3
  17. package/dist/components/expenseAutomation/sections/actualMatching/ActualMatchingMatchNewButton.d.ts +5 -0
  18. package/dist/components/expenseAutomation/sections/jeSchedules/common/JEGroupSectionHeader.d.ts +1 -1
  19. package/dist/components/expenseAutomation/sections/jeSchedules/list/jeScheduleConfig.d.ts +1 -1
  20. package/dist/components/expenseAutomation/sections/jeSchedules/list/jeScheduleListItems.d.ts +9 -3
  21. package/dist/components/expenseAutomation/sections/jeSchedules/list/jeScheduleListRow.helpers.d.ts +30 -1
  22. package/dist/components/expenseAutomation/stories/actualMatchingPageStoryActions.d.ts +2 -0
  23. package/dist/components/invoicing/catalogDetail/InvoicingCatalogItemDetailPage.d.ts +11 -4
  24. package/dist/components/invoicing/catalogForm/InvoicingCatalogItemFormPage.d.ts +18 -10
  25. package/dist/components/invoicing/catalogForm/catalogItemForm.shared.d.ts +11 -7
  26. package/dist/components/invoicing/common/InvoicingFormControls.d.ts +8 -1
  27. package/dist/components/invoicing/common/InvoicingFormRows.d.ts +7 -0
  28. package/dist/components/invoicing/common/InvoicingSkeletons.d.ts +20 -4
  29. package/dist/components/invoicing/common/invoicingAccountPickerOptions.d.ts +15 -0
  30. package/dist/components/invoicing/couponForm/InvoicingCouponFormPage.d.ts +27 -2
  31. package/dist/components/invoicing/couponForm/couponForm.shared.d.ts +17 -1
  32. package/dist/components/invoicing/customerDetail/InvoicingCustomerDetailPage.d.ts +12 -1
  33. package/dist/components/invoicing/customerForm/InvoicingCustomerFormPage.d.ts +9 -1
  34. package/dist/components/invoicing/discountDetail/InvoicingDiscountDetailPage.d.ts +7 -0
  35. package/dist/components/invoicing/subscriptionDetail/InvoicingSubscriptionDetailPage.d.ts +13 -0
  36. package/dist/components/invoicing/subscriptionForm/InvoicingSubscriptionFormPage.d.ts +9 -1
  37. package/dist/components/invoicing/subscriptionForm/SubscriptionItemQuantityRows.d.ts +10 -7
  38. package/dist/components/invoicing/subscriptionForm/subscriptionForm.shared.d.ts +69 -28
  39. package/dist/components/notifications/components/CommentsNotificationTileMedium.d.ts +15 -4
  40. package/dist/components/reportTree/treeNode/accountReconReport/row/v2/statusHelper.d.ts +1 -1
  41. package/dist/components/reports/PdfColumnLimitPopup.d.ts +12 -0
  42. package/dist/context/featureProvider/DynamicConfigNameConstants.d.ts +3 -1
  43. package/dist/context/featureProvider/isActualMatchingEnabledInAgentJeConfig.d.ts +18 -0
  44. package/dist/context/featureProvider/isActualMatchingEnabledInAgentJeConfig.test.d.ts +1 -0
  45. package/dist/context/featureProvider/useActualMatchingEnabled.d.ts +6 -0
  46. package/dist/{index-1_7aslI4.js → index-3FvLYm2f.js} +2 -2
  47. package/dist/{index-1_7aslI4.js.map → index-3FvLYm2f.js.map} +1 -1
  48. package/dist/{index-CWHnH5Fh.cjs → index-B7A5wq5o.cjs} +2 -2
  49. package/dist/{index-CWHnH5Fh.cjs.map → index-B7A5wq5o.cjs.map} +1 -1
  50. package/dist/index.cjs.js +1 -1
  51. package/dist/index.d.ts +4 -1
  52. package/dist/index.esm.js +1 -1
  53. package/dist/strings/strings.d.ts +46 -21
  54. package/package.json +13 -13
  55. package/dist/SessionTimeoutPopup-BP3VhaHV.cjs.map +0 -4
  56. package/dist/SessionTimeoutPopup-DO46U3vX.js.map +0 -4
@@ -1,7 +1,7 @@
1
1
  import { default as React } from 'react';
2
2
  import { Routine } from './helpers/aiCfoRoutines';
3
3
  export interface AiCfoRoutinesBrowserProps {
4
- /** False withholds "New routine" — the server states the cap, not the client. */
4
+ /** False withholds "New scheduled task" — the server states the cap, not the client. */
5
5
  canCreate: boolean;
6
6
  listError: boolean;
7
7
  /**
@@ -79,6 +79,8 @@ export interface RoutineRun {
79
79
  }
80
80
  /** Whether a run is still going, and so has no outcome to report yet. */
81
81
  export declare const isRunInFlight: (run: RoutineRun) => boolean;
82
+ /** Whether this run holds an answer to open: a session, not in flight, and either the answer's message id or (runs from before it was recorded) a succeeded status. */
83
+ export declare const isRunOpenable: (run: RoutineRun) => boolean;
82
84
  /**
83
85
  * When a run happened, in the routine's own timezone.
84
86
  *
@@ -71,6 +71,21 @@ export declare const canonicalizeColumnKey: (columnKey: string) => string;
71
71
  * read so legacy comments match freshly-rendered canonical cells.
72
72
  */
73
73
  export declare const canonicalizeCellEntityId: (entityId: string) => string;
74
+ /**
75
+ * True when a stored thread entityId refers to the cell rendered as
76
+ * `renderedEntityId`.
77
+ *
78
+ * Exact (canonical) equality is the normal case. The fallback covers comments
79
+ * written before the column key used the period end: those carry whatever
80
+ * `data_available_end_date` was on the day they were made, so a comment left on
81
+ * September 9th stored `2026-09-01_2026-09-09` and no longer equals the
82
+ * September cell. Same row and same start date, with the stored end inside the
83
+ * rendered period, identifies it. A legacy month comment can surface on the
84
+ * containing quarter when the report is regrouped, which is intended: the
85
+ * alternative is that it shows nowhere. Keys written now carry the period end,
86
+ * so they only ever match at the same granularity.
87
+ */
88
+ export declare const matchesCellEntityId: (renderedEntityId: string, storedEntityId: string) => boolean;
74
89
  export interface ReportThreadTarget {
75
90
  accountId: string;
76
91
  cellEntityId: string;
@@ -1,7 +1,9 @@
1
+ import { FetchState } from '@zeniai/client-epic-state';
1
2
  import { RecommendationSelectionFeedback } from '../../../common/recommendationSelectionFeedback';
2
3
  export interface RecommendationFeedbackPopupProps {
3
4
  feedback: RecommendationSelectionFeedback;
4
5
  maxReasonLength?: number;
6
+ submitState?: FetchState;
5
7
  onSkip: () => void;
6
8
  onSubmit: (reason: string) => void;
7
9
  onClose?: () => void;
@@ -13,5 +15,5 @@ export interface RecommendationFeedbackPopupProps {
13
15
  * of the transaction read-only, and a required reason. Submit records the
14
16
  * feedback; Skip records analytics only.
15
17
  */
16
- export declare function RecommendationFeedbackPopup({ feedback, maxReasonLength, onSubmit, onSkip, onClose, }: Readonly<RecommendationFeedbackPopupProps>): import("react/jsx-runtime").JSX.Element;
18
+ export declare function RecommendationFeedbackPopup({ feedback, maxReasonLength, submitState, onSubmit, onSkip, onClose, }: Readonly<RecommendationFeedbackPopupProps>): import("react/jsx-runtime").JSX.Element;
17
19
  export default RecommendationFeedbackPopup;
@@ -1,8 +1,9 @@
1
+ import { default as React } from 'react';
1
2
  import { ClientAnalytics } from '@zeniai/client-analytics';
2
3
  import { CardActions, CardType } from '../cards/cardTypes';
3
4
  import { AutoCompleteActions, AutoCompleteData } from '../common/autoComplete/autoCompleteProps';
4
5
  import { DashboardCardData } from './dashboardTypes';
5
- declare function Dashboard({ cardDataArray, cardActions, christmasAnimationEnabled, newYearAnimationEnabled, autoCompleteData, autoCompleteActions, analytics, isReimbursementEnabled, isBillPayEnabled, isVisitingFirstTime, onDragAndDropCard, isAiCfoAccessEnabled, onAskAiCfoClick, onAiCfoDashboardPromptSubmit, onAiCfoDashboardOpenPanel, onAiCfoDashboardOpenScopePage, onAiCfoDashboardExpandToFullPage, isAiCfoFileUploadEnabled, initialSearchFocus, maxColumns, aiAuditScorePercentageEnabled, aiCfoDashboardShouldClearPromptOnSubmit, aiCfoDashboardSuggestedPromptQuestions, aiCfoDashboardSuggestedPromptQuestionsLoading, }: Readonly<{
6
+ declare function Dashboard({ cardDataArray, cardActions, christmasAnimationEnabled, newYearAnimationEnabled, autoCompleteData, autoCompleteActions, analytics, cardsShutdownBanner, isReimbursementEnabled, isBillPayEnabled, isVisitingFirstTime, onDragAndDropCard, isAiCfoAccessEnabled, onAskAiCfoClick, onAiCfoDashboardPromptSubmit, onAiCfoDashboardOpenPanel, onAiCfoDashboardOpenScopePage, onAiCfoDashboardExpandToFullPage, isAiCfoFileUploadEnabled, initialSearchFocus, maxColumns, aiAuditScorePercentageEnabled, aiCfoDashboardShouldClearPromptOnSubmit, aiCfoDashboardSuggestedPromptQuestions, aiCfoDashboardSuggestedPromptQuestionsLoading, }: Readonly<{
6
7
  aiAuditScorePercentageEnabled: boolean;
7
8
  analytics: ClientAnalytics;
8
9
  autoCompleteActions: AutoCompleteActions;
@@ -20,6 +21,11 @@ declare function Dashboard({ cardDataArray, cardActions, christmasAnimationEnabl
20
21
  /** Same source as AI CFO side panel suggested questions for the current route (host / Redux). */
21
22
  aiCfoDashboardSuggestedPromptQuestions?: string[];
22
23
  aiCfoDashboardSuggestedPromptQuestionsLoading?: boolean;
24
+ /**
25
+ * Optional shutdown banner rendered at the top of the dashboard (below the NavBar)
26
+ * for tenants whose cards product is being discontinued. Owned by the host screen.
27
+ */
28
+ cardsShutdownBanner?: React.ReactNode;
23
29
  christmasAnimationEnabled?: boolean;
24
30
  initialSearchFocus?: boolean;
25
31
  isAiCfoAccessEnabled?: boolean;
@@ -61,6 +61,12 @@ export interface ExpenseAutomationPageProps extends MonthEndReportPageProps, Act
61
61
  initialSelectedBaseTransaction?: ScheduleTransaction;
62
62
  /** Cron-2's live gate for this company, per Statsig. Off hides one-time accrual creation. */
63
63
  isAccrualEnabled?: boolean;
64
+ /**
65
+ * Actual Matching JE subtab + match-detail bounce. Independent of
66
+ * `isAccrualEnabled` so one-time accrual create can stay live while AM
67
+ * stays hidden. Host reads `agent_je_config` (`ACTUAL_MATCHING_ENABLED`).
68
+ */
69
+ isActualMatchingEnabled?: boolean;
64
70
  isAiCfoAccessEnabled?: boolean;
65
71
  /**
66
72
  * Statsig `is_create_transfer_entry_from_expense_enabled`. When false the TC list
@@ -49,8 +49,9 @@ missingReceiptsOptions?: {
49
49
  */
50
50
  isExpenseAutomationCommentingEnabled?: boolean, receiptsEmail?: string,
51
51
  /**
52
- * Cron-2 live gate (`is_accrual_enabled`). Off hides the Actual Matching JE
53
- * subtab — there are no one-time accruals to match.
52
+ * Actual Matching JE subtab. Host ANDs `is_accrual_enabled` with
53
+ * `agent_je_config.global.accrual_config.ACTUAL_MATCHING_ENABLED`. Off
54
+ * hides the tab — code stays; this is the master-release UI gate.
54
55
  */
55
- isAccrualEnabled?: boolean) => ExpenseAutomationNavbarHeaderData;
56
+ isActualMatchingEnabled?: boolean) => ExpenseAutomationNavbarHeaderData;
56
57
  export {};
@@ -0,0 +1,5 @@
1
+ export interface ActualMatchingMatchNewButtonProps {
2
+ onClick: () => void;
3
+ }
4
+ declare function ActualMatchingMatchNewButton({ onClick, }: Readonly<ActualMatchingMatchNewButtonProps>): import("react/jsx-runtime").JSX.Element;
5
+ export default ActualMatchingMatchNewButton;
@@ -2,13 +2,13 @@ export interface JEGroupSectionHeaderProps {
2
2
  count: number;
3
3
  isCollapsed: boolean;
4
4
  title: string;
5
+ subtitle?: string;
5
6
  onToggle: () => void;
6
7
  /**
7
8
  * `badge` is the Review Schedules circle. Matching Design puts the count
8
9
  * inline after the title with no ring.
9
10
  */
10
11
  countAppearance?: "badge" | "plain";
11
- subtitle?: string;
12
12
  }
13
13
  declare function JEGroupSectionHeader({ title, count, countAppearance, isCollapsed, onToggle, subtitle, }: Readonly<JEGroupSectionHeaderProps>): import("react/jsx-runtime").JSX.Element;
14
14
  export default JEGroupSectionHeader;
@@ -3,7 +3,7 @@ import { TabSwitcherOption } from '../../../../common/allNavBars/statusSwitcher/
3
3
  import { TextAlign } from '../../../../dataListTable';
4
4
  export type JEScheduleMainTab = JEPageTab;
5
5
  export type JEScheduleColumnKey = "vendor" | "category" | "class" | "depCategory" | "scheduleCategory" | "startMonth" | "type" | "amortizationPeriod" | "jePostingDate" | "remainingMonths" | "runningBalance" | "monthlyAmount" | "totalAmount" | "action" | "transactionDate" | "memo" | "createdBy" | "lifecycle" | "period";
6
- export type JEScheduleGroupKey = "pending_review" | "created" | "existing";
6
+ export type JEScheduleGroupKey = "pending_review" | "created" | "existing" | "failed_postings";
7
7
  export type JESchedulePendingSubTab = "all" | "create" | "resolve";
8
8
  export type JEScheduleOngoingCompletedSubTab = "all" | "ai_accountant" | "manual";
9
9
  export type JEScheduleSubTab = JESchedulePendingSubTab | JEScheduleOngoingCompletedSubTab;
@@ -49,8 +49,14 @@ export type JEScheduleListItem = {
49
49
  */
50
50
  export declare function buildJEScheduleListItems(groups: readonly JEScheduleListGroup[]): JEScheduleListItem[];
51
51
  /**
52
- * Prefixed with the group key deliberately. The same schedule can appear as a resolve row in one
53
- * group and an ongoing row in another while sharing a `jeScheduleTransactionKey`; separate tables
54
- * made that harmless, one list would give react-window two items with the same key.
52
+ * Two collisions to avoid, and both are real.
53
+ *
54
+ * The group prefix: the same schedule can appear as a resolve row in one group and an ongoing row
55
+ * in another while sharing a `jeScheduleTransactionKey`. Separate tables made that harmless; one
56
+ * list would give react-window two items with the same key.
57
+ *
58
+ * The row-instance key: a schedule that failed to post in two months is two resolve rows *inside
59
+ * one group*, which the group prefix cannot separate. `getJEScheduleRowInstanceKey` appends the
60
+ * journal entry id for exactly those rows.
55
61
  */
56
62
  export declare function getJEScheduleListItemKey(item: JEScheduleListItem): string;
@@ -1,4 +1,4 @@
1
- import { ID, JEOneTimeAccrualKey, JEScheduleKey, JEScheduleRow, JEScheduleTransactionKey } from '@zeniai/client-epic-state';
1
+ import { ID, JEOneTimeAccrualKey, JEScheduleKey, JEScheduleRow, JEScheduleTransactionKey, JEScheduledTransactionWithFailedEntries, JournalEntryErrorCodeType } from '@zeniai/client-epic-state';
2
2
  import { ChainOfThoughtData } from '../../../../common/tooltipVariants/types';
3
3
  /**
4
4
  * The chain-of-thought popover shown on the "Created by" cell of an AI Accountant row.
@@ -20,6 +20,35 @@ export type JEScheduleRowKey = JEOneTimeAccrualKey | JEScheduleTransactionKey |
20
20
  * Stable per-row identity for React keys, data-testids and row commenting.
21
21
  */
22
22
  export declare function getJEScheduleRowKey(row: JEScheduleRow): JEScheduleRowKey;
23
+ /**
24
+ * Why this entry could not post, as displayable text.
25
+ *
26
+ * `deletedFieldNameByCode` supplies the name for the codes whose copy quotes one. Those names are
27
+ * frequently unknown -- the entity is deleted, which is the whole reason the posting failed, and
28
+ * the server sends null for it -- so an absent name drops the quoted fragment rather than
29
+ * rendering an empty pair of quotes.
30
+ */
31
+ export declare function buildResolveErrorText(errorCodes: JournalEntryErrorCodeType[], deletedFieldNameByCode?: Partial<Record<JournalEntryErrorCodeType, string | undefined>>): string;
32
+ /**
33
+ * A row standing for one FAILED journal entry rather than a whole schedule.
34
+ *
35
+ * Discriminates structurally on `scheduledJournalEntry` being a single entry instead of the array
36
+ * every other row shape carries — the selector fans a failed schedule out to one row per entry.
37
+ */
38
+ export declare function isResolveRow(row: JEScheduleRow): row is JEScheduledTransactionWithFailedEntries;
39
+ /**
40
+ * Render identity for one ROW INSTANCE — for React keys only, never for routing or lookups.
41
+ *
42
+ * A failed posting is one row per failed journal ENTRY, so a schedule that failed in two months
43
+ * produces two rows sharing a `jeScheduleTransactionKey`. `getJEScheduleRowKey` is the schedule's
44
+ * domain key: it routes the side panel, names comment threads and chain-of-thought entries, and
45
+ * keys the per-row status maps, so it cannot carry the entry and stay correct. This appends the
46
+ * entry id for exactly the rows that need it, and returns the domain key unchanged for the rest.
47
+ *
48
+ * The group prefix `getJEScheduleListItemKey` adds solves a different collision — the same
49
+ * schedule appearing in two groups — and both are real.
50
+ */
51
+ export declare function getJEScheduleRowInstanceKey(row: JEScheduleRow): string;
23
52
  /**
24
53
  * The plain id the side-panel URL carries for this row — deliberately NOT its row key.
25
54
  *
@@ -0,0 +1,2 @@
1
+ import { ActualMatchingActionProps } from '../sections/actualMatching/actualMatchingTypes';
2
+ export declare const actualMatchingPageStoryActions: ActualMatchingActionProps;
@@ -1,14 +1,11 @@
1
- import { InvoicingCatalogItemLifecycleAction, InvoicingCatalogLifecycleActionOption, InvoicingCatalogProductLifecycleAction, InvoicingNamedOptionsByKey, InvoicingPlan, InvoicingCatalogItemFormLocalData, InvoicingNamedOption } from '@zeniai/client-epic-state';
1
+ import { InvoicingCatalogItemLifecycleAction, InvoicingCatalogLifecycleActionOption, InvoicingCatalogProductLifecycleAction, InvoicingNamedOptionsByKey, InvoicingPlan, NestedAccountHierarchyForReport, NestedClassHierarchyForReport, InvoicingCatalogItemFormLocalData, InvoicingNamedOption } from '@zeniai/client-epic-state';
2
2
  import { FieldHelpByCode } from '../../formElements/fieldHelp/FieldHelpContext';
3
- import { GroupedOption } from '../../formElements/labeledGroupSingleSelectFieldWithRecommendationCheckbox/GroupedSingleSelectField';
4
3
  import { InvoicingCatalogApplicableItemOption } from '../catalogForm/InvoicingCatalogItemFormPage';
5
4
  type InvoicingPlanWithIdentifiers = InvoicingPlan & {
6
5
  externalId?: string;
7
6
  productCode?: string;
8
7
  };
9
8
  export interface InvoicingCatalogItemDetailPageProps {
10
- groupedAccountOptions: GroupedOption[];
11
- groupedClassOptions: GroupedOption[];
12
9
  isLoading: boolean;
13
10
  itemId: string;
14
11
  localData: InvoicingCatalogItemFormLocalData;
@@ -23,6 +20,16 @@ export interface InvoicingCatalogItemDetailPageProps {
23
20
  isRunningLifecycleAction?: boolean;
24
21
  isSaving?: boolean;
25
22
  plan?: InvoicingPlanWithIdentifiers;
23
+ /** Account and class trees the price-point pickers render as grouped options. */
24
+ qboAccountHierarchy?: NestedAccountHierarchyForReport[];
25
+ qboClassHierarchy?: NestedClassHierarchyForReport[];
26
+ /**
27
+ * The ids within `qboAccountHierarchy` the income-account picker may offer.
28
+ * QuickBooks takes only an income account on a product, so a price point
29
+ * mapped to anything else stops its invoices syncing. Absent means the tree
30
+ * is offered whole.
31
+ */
32
+ qboIncomeAccountIds?: readonly string[];
26
33
  tierTypeOptionsByPricingModel?: InvoicingNamedOptionsByKey;
27
34
  onBack?: () => void;
28
35
  onEditChange?: (localData: InvoicingCatalogItemFormLocalData) => void;
@@ -36,19 +36,27 @@ export interface InvoicingCatalogItemFormPageProps {
36
36
  */
37
37
  fieldHelp?: FieldHelpByCode;
38
38
  /**
39
- * True while the accounting lists are still loading. The form waits rather
40
- * than render: the accounting fields choose between a picker and a text input
41
- * from these lists, and swapping one for the other on a mounted form replaces
42
- * a plain string value with a grouped one under the same field name.
39
+ * True while anything the fields mount against is still loading — in edit
40
+ * mode the item itself, and either way the accounting lists. The form waits
41
+ * rather than render, for two reasons that both outlive the response:
42
+ * `useForm` seeds its default values once, at first render, so fields mounted
43
+ * against the blank draft stay blank and the first change writes that
44
+ * blankness back over the item; and the accounting fields choose between a
45
+ * picker and a text input from the lists, so swapping one for the other on a
46
+ * mounted form replaces a plain string value with a grouped one under the
47
+ * same field name.
43
48
  */
44
- isQboPickerDataLoading?: boolean;
49
+ isLoading?: boolean;
45
50
  qboAccountHierarchy?: NestedAccountHierarchyForReport[];
51
+ /** Class tree the price-point pickers render as grouped options. */
52
+ qboClassHierarchy?: NestedClassHierarchyForReport[];
46
53
  /**
47
- * Account and class trees the price-point pickers render as grouped options.
48
- * The account tree is already narrowed to the income accounts a price may
49
- * post to, so this page does no filtering of its own.
54
+ * The ids within `qboAccountHierarchy` the income-account picker may offer.
55
+ * QuickBooks takes only an income account on a product, so a price point
56
+ * mapped to anything else stops its invoices syncing. Absent means the tree
57
+ * is offered whole.
50
58
  */
51
- qboClassHierarchy?: NestedClassHierarchyForReport[];
59
+ qboIncomeAccountIds?: readonly string[];
52
60
  /**
53
61
  * `catalog_tier_type_options_by_pricing_model` from the invoicing config; the
54
62
  * tier `Type` column is driven by it. `undefined` until the config loads.
@@ -66,5 +74,5 @@ export interface InvoicingCatalogItemFormPageProps {
66
74
  * invoicing forms. The whole form syncs back to the CES draft (`localData`) on
67
75
  * every change; the save payload is derived from the draft in CES.
68
76
  */
69
- export declare function InvoicingCatalogItemFormPage({ applicabilityValues, fieldHelp, applicableItemOptions, defaultType, errorMessage, isSubmitting, localData, mode, onBack, onCancel, onChange, onSubmit, periodUnitOptions, pricingModelValues, prorationValues, isQboPickerDataLoading, qboClassHierarchy, qboAccountHierarchy, tierTypeOptionsByPricingModel, tierTypeValues, trialEndActionOptions, trialUnitOptions, typeOptions, }: Readonly<InvoicingCatalogItemFormPageProps>): import("react/jsx-runtime").JSX.Element;
77
+ export declare function InvoicingCatalogItemFormPage({ applicabilityValues, fieldHelp, applicableItemOptions, defaultType, errorMessage, isLoading, isSubmitting, localData, mode, onBack, onCancel, onChange, onSubmit, periodUnitOptions, pricingModelValues, prorationValues, qboClassHierarchy, qboAccountHierarchy, qboIncomeAccountIds, tierTypeOptionsByPricingModel, tierTypeValues, trialEndActionOptions, trialUnitOptions, typeOptions, }: Readonly<InvoicingCatalogItemFormPageProps>): import("react/jsx-runtime").JSX.Element;
70
78
  export default InvoicingCatalogItemFormPage;
@@ -28,6 +28,17 @@ export interface CatalogFormConfigOptions {
28
28
  typeOptions: InvoicingNamedOption[];
29
29
  qboAccountHierarchy?: NestedAccountHierarchyForReport[];
30
30
  qboClassHierarchy?: NestedClassHierarchyForReport[];
31
+ /**
32
+ * The QuickBooks ids within {@link qboAccountHierarchy} an income account may
33
+ * be picked from. QuickBooks takes only an income account on a product, so a
34
+ * price point mapped to anything else stops its invoices syncing.
35
+ *
36
+ * Narrowed by id rather than by pruning the tree: the tree is flattened into
37
+ * options by walking each node's children, so dropping a parent that is not
38
+ * an income account would drop its income descendants with it. Absent means
39
+ * no narrowing.
40
+ */
41
+ qboIncomeAccountIds?: readonly string[];
31
42
  }
32
43
  export interface CatalogFormOptions {
33
44
  account: GroupedOption[];
@@ -56,13 +67,6 @@ export declare const CATALOG_LOCAL_TIER_TYPE_OPTIONS: CatalogSelectOption[];
56
67
  export declare const catalogOptionLabel: (allOptions: readonly CatalogSelectOption[], value: string | undefined) => string | undefined;
57
68
  /** Block any keystroke that would put a non-digit into a period field. */
58
69
  export declare const allowOnlyNumericKeys: (event: KeyboardEvent<HTMLInputElement>) => void;
59
- /**
60
- * Accounts as the grouped picker renders them. `getGroupedAccountOptions` falls
61
- * back to the internal id when a node carries no `qboId`; this form submits the
62
- * QuickBooks id, so an account without one is dropped rather than offered as a
63
- * value the invoicing API would reject.
64
- */
65
- export declare const toGroupedQboAccountOptions: (hierarchy?: NestedAccountHierarchyForReport[]) => GroupedOption[];
66
70
  /**
67
71
  * Classes as the grouped picker renders them. `getGroupedClassOptions` labels
68
72
  * each option with the internal class id while accounts use `qboId ?? id`, so the
@@ -68,6 +68,13 @@ export interface InvoicingFormShellProps<TFieldValues extends FieldValues> {
68
68
  title: string;
69
69
  cancelLabel?: string;
70
70
  errorMessage?: string;
71
+ /**
72
+ * Holds submit closed without claiming a save is under way, for a form whose
73
+ * fields are still standing down behind a skeleton. Distinct from
74
+ * `isSubmitting`, which also disables cancel and spins the submit button —
75
+ * neither of which is true of a page that is merely loading.
76
+ */
77
+ isSubmitDisabled?: boolean;
71
78
  onCancel: () => void;
72
79
  onSubmit: (values: TFieldValues) => void;
73
80
  /** When provided, renders a back-arrow icon button to the left of the title. */
@@ -81,4 +88,4 @@ export interface InvoicingFormShellProps<TFieldValues extends FieldValues> {
81
88
  * should use the shared `formElements/Labeled*` primitives so field color,
82
89
  * hover/focus and search behave exactly like bill edit.
83
90
  */
84
- export declare function InvoicingFormShell<TFieldValues extends FieldValues>({ cancelLabel, children, errorMessage, formId, isSubmitting, methods, onBack, onCancel, onSubmit, submitLabel, title, }: Readonly<InvoicingFormShellProps<TFieldValues>>): import("react/jsx-runtime").JSX.Element;
91
+ export declare function InvoicingFormShell<TFieldValues extends FieldValues>({ cancelLabel, children, errorMessage, formId, isSubmitDisabled, isSubmitting, methods, onBack, onCancel, onSubmit, submitLabel, title, }: Readonly<InvoicingFormShellProps<TFieldValues>>): import("react/jsx-runtime").JSX.Element;
@@ -10,6 +10,13 @@ export declare function FormColumn({ children, style, }: Readonly<{
10
10
  children: ReactNode;
11
11
  style?: React.CSSProperties;
12
12
  }>): import("react/jsx-runtime").JSX.Element;
13
+ /**
14
+ * Label-column width shared by these rows and the `formElements/Labeled*` rows
15
+ * they sit beside. Both families must agree or the labels step in and out
16
+ * within a section, so the pages pass this to `label.width` rather than
17
+ * restating the number.
18
+ */
19
+ export declare const INVOICING_FORM_LABEL_WIDTH = 190;
13
20
  export declare const RowInput: import('@emotion/styled').StyledComponent<{
14
21
  theme?: import('@emotion/react').Theme;
15
22
  as?: React.ElementType;
@@ -34,9 +34,25 @@ export declare function InvoicingSettingsSkeleton({ tab, }: Readonly<{
34
34
  /** Shimmer placeholder while create-invoice settings are fetched from the API. */
35
35
  export declare function InvoicingCreateInvoiceSkeleton(): import("react/jsx-runtime").JSX.Element;
36
36
  /**
37
- * Stands in for the catalog item form's sections while the accounting pickers
38
- * load — their options and their registered value shape both depend on the
39
- * shared account and class lists. Renders inside the form shell's own centered
40
- * column, so the page keeps its title, back arrow and footer.
37
+ * Stands in for the catalog item form's sections while the record it edits and
38
+ * the accounting pickers load — the form seeds react-hook-form once, at first
39
+ * render, and the pickers' registered value shape depends on the shared account
40
+ * and class lists, so neither can be picked up after the fields have mounted.
41
+ * Renders inside the form shell's own centered column, so the page keeps its
42
+ * title, back arrow and footer.
41
43
  */
42
44
  export declare function InvoicingCatalogItemFormSkeleton(): import("react/jsx-runtime").JSX.Element;
45
+ /**
46
+ * Stands in for the customer form's sections while the customer it edits loads.
47
+ * Same reason as {@link InvoicingCatalogItemFormSkeleton}: the form seeds
48
+ * react-hook-form once, at first render, so fields mounted against the blank
49
+ * draft stay blank however late the record lands.
50
+ */
51
+ export declare function InvoicingCustomerFormSkeleton(): import("react/jsx-runtime").JSX.Element;
52
+ /** Stands in for the discount form's sections while the coupon it edits loads. */
53
+ export declare function InvoicingCouponFormSkeleton(): import("react/jsx-runtime").JSX.Element;
54
+ /**
55
+ * Stands in for the subscription form's sections while the subscription it
56
+ * edits loads.
57
+ */
58
+ export declare function InvoicingSubscriptionFormSkeleton(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,15 @@
1
+ import { NestedAccountHierarchyForReport } from '@zeniai/client-epic-state';
2
+ import { GroupedOption } from '../../formElements/labeledGroupSingleSelectFieldWithRecommendationCheckbox/GroupedSingleSelectField';
3
+ /**
4
+ * Accounts as the grouped picker renders them. `getGroupedAccountOptions` falls
5
+ * back to the internal id when a node carries no `qboId`; the invoicing forms
6
+ * submit the QuickBooks id, so an account without one is dropped rather than
7
+ * offered as a value the invoicing API would reject.
8
+ *
9
+ * `allowedAccountIds`, when given, narrows the result to the QuickBooks ids a
10
+ * slot accepts — an income-only picker, say. Narrowed here, once the tree has
11
+ * been flattened into options, rather than on the tree itself: the flattener
12
+ * recurses into each node's `children`, so dropping a parent that is not
13
+ * eligible would drop its eligible descendants with it.
14
+ */
15
+ export declare const toGroupedQboAccountOptions: (hierarchy?: NestedAccountHierarchyForReport[], allowedAccountIds?: readonly string[]) => GroupedOption[];
@@ -1,15 +1,40 @@
1
- import { InvoicingCouponFormLocalData, InvoicingFormOption } from '@zeniai/client-epic-state';
1
+ import { InvoicingCouponFormLocalData, InvoicingFormOption, NestedAccountHierarchyForReport } from '@zeniai/client-epic-state';
2
2
  import { FieldHelpByCode } from '../../formElements/fieldHelp/FieldHelpContext';
3
3
  export interface InvoicingCouponFormPageProps {
4
4
  applyOnOptions: InvoicingFormOption[];
5
5
  discountTypeOptions: InvoicingFormOption[];
6
6
  durationTypeOptions: InvoicingFormOption[];
7
+ /**
8
+ * Whether to offer a discounts account at all. With no QuickBooks connection
9
+ * there are no accounts to pick from, and a coupon saves fine without one.
10
+ */
11
+ isQboConnected: boolean;
7
12
  isSubmitting: boolean;
8
13
  localData: InvoicingCouponFormLocalData;
9
14
  mode: "create" | "edit";
10
15
  periodUnitOptions: InvoicingFormOption[];
11
16
  errorMessage?: string;
12
17
  fieldHelp?: FieldHelpByCode;
18
+ /**
19
+ * True while the coupon being edited is still loading. The form holds its
20
+ * skeleton until the record lands: `useForm` seeds its default values once,
21
+ * at first render, so mounting the fields against the blank draft leaves them
22
+ * empty, and the first change writes that blankness back over the coupon.
23
+ * Always false when creating — there is nothing to load.
24
+ *
25
+ * Separate from `isQboPickerDataLoading`, which the discounts-account picker
26
+ * shows on itself: the rest of the form is usable before the lists land.
27
+ */
28
+ isLoading?: boolean;
29
+ isQboPickerDataLoading?: boolean;
30
+ /**
31
+ * The account tree the discounts-account picker renders, and the QuickBooks
32
+ * ids in it the discounts slot accepts. The tree is filtered by the ids
33
+ * rather than pruned, so a parent that is not itself an income account still
34
+ * groups the income accounts under it.
35
+ */
36
+ qboAccountHierarchy?: NestedAccountHierarchyForReport[];
37
+ qboIncomeAccountIds?: string[];
13
38
  onCancel: () => void;
14
39
  onChange: (localData: InvoicingCouponFormLocalData) => void;
15
40
  onSubmit: () => void;
@@ -20,5 +45,5 @@ export interface InvoicingCouponFormPageProps {
20
45
  * CES (`localData`) and is synced back on every change; submit triggers the
21
46
  * CES-stored draft.
22
47
  */
23
- export declare function InvoicingCouponFormPage({ applyOnOptions, fieldHelp, discountTypeOptions, durationTypeOptions, errorMessage, isSubmitting, localData, mode, onBack, onCancel, onChange, onSubmit, periodUnitOptions, }: Readonly<InvoicingCouponFormPageProps>): import("react/jsx-runtime").JSX.Element;
48
+ export declare function InvoicingCouponFormPage({ applyOnOptions, fieldHelp, discountTypeOptions, durationTypeOptions, errorMessage, isLoading, isQboConnected, isQboPickerDataLoading, isSubmitting, localData, mode, onBack, onCancel, onChange, onSubmit, periodUnitOptions, qboAccountHierarchy, qboIncomeAccountIds, }: Readonly<InvoicingCouponFormPageProps>): import("react/jsx-runtime").JSX.Element;
24
49
  export default InvoicingCouponFormPage;
@@ -1,4 +1,5 @@
1
1
  import { InvoicingCouponFormLocalData, InvoicingFormOption } from '@zeniai/client-epic-state';
2
+ import { GroupedOption, GroupedSingleSelectFieldValue } from '../../formElements/labeledGroupSingleSelectFieldWithRecommendationCheckbox/GroupedSingleSelectField';
2
3
  /**
3
4
  * Web-components glue for the coupon form. CES owns the draft `LocalData` and the
4
5
  * select-option value sets; this module maps those to the form's UI value shapes
@@ -17,12 +18,25 @@ export interface PickerValue {
17
18
  * Every plain field (name, couponCode, couponId, …) flows through by identity,
18
19
  * so adding a field to the CES type needs no mapper change.
19
20
  */
20
- export type CouponFormValues = Omit<InvoicingCouponFormLocalData, "applyOn" | "discountAmount" | "discountType" | "durationType" | "periodUnit" | "validFrom" | "validTill"> & {
21
+ export type CouponFormValues = Omit<InvoicingCouponFormLocalData, "applyOn" | "discountAmount" | "discountPercentage" | "discountQuantity" | "discountsAccountId" | "discountType" | "durationType" | "maxRedemptions" | "period" | "periodUnit" | "validFrom" | "validTill"> & {
21
22
  applyOn: PickerValue;
22
23
  /** Held as a float by `returnValueType: "floatValue"`; `null` when cleared. */
23
24
  discountAmount: number | null;
25
+ /**
26
+ * The `type: "number"` fields. `TextField` and `FormattedTextField` register
27
+ * those with react-hook-form's `valueAsNumber`, so the form hands them back
28
+ * as numbers — `NaN` once the box is cleared — while the values seeded from
29
+ * CES are the strings it holds. Declared as both, because both occur:
30
+ * reading one as a string is what `max_redemptions.trim is not a function`
31
+ * was.
32
+ */
33
+ discountPercentage: number | string;
34
+ discountQuantity: number | string;
35
+ discountsAccountId: GroupedSingleSelectFieldValue;
24
36
  discountType: PickerValue;
25
37
  durationType: PickerValue;
38
+ maxRedemptions: number | string;
39
+ period: number | string;
26
40
  periodUnit: PickerValue;
27
41
  validFrom: Date | undefined;
28
42
  validTill: Date | undefined;
@@ -31,6 +45,8 @@ export interface CouponFormOptions {
31
45
  applyOnOptions: InvoicingFormOption[];
32
46
  discountTypeOptions: InvoicingFormOption[];
33
47
  durationTypeOptions: InvoicingFormOption[];
48
+ /** Income accounts for the discounts-account picker, grouped by parent. */
49
+ groupedAccountOptions: GroupedOption[];
34
50
  periodUnitOptions: InvoicingFormOption[];
35
51
  }
36
52
  export declare const localDataToCouponFormValues: (localData: InvoicingCouponFormLocalData, options: CouponFormOptions) => CouponFormValues;
@@ -14,6 +14,12 @@ export interface InvoicingCustomerDetailPageProps {
14
14
  /** Error from the last inline-edit save attempt, shown in edit mode. */
15
15
  fieldHelp?: FieldHelpByCode;
16
16
  hasPaymentLinkEmailPreviewFailed?: boolean;
17
+ /**
18
+ * A billing address entered on the Address screen and not yet saved. It is
19
+ * not a form field, so the form's own dirty state cannot see it; the footer
20
+ * offers Save on this as well, or the address could never be linked.
21
+ */
22
+ hasUnsavedBillingAddress?: boolean;
17
23
  /** Open the payment-link email preview modal on mount (Storybook). */
18
24
  initialPreviewEmailOpen?: boolean;
19
25
  /** Recent invoices for this customer (Figma: "Recent Invoices" table). */
@@ -47,11 +53,16 @@ export interface InvoicingCustomerDetailPageProps {
47
53
  onChange?: (localData: InvoicingCustomerFormLocalData) => void;
48
54
  onCreateInvoice?: () => void;
49
55
  onCreatePromotionalCredits?: () => void;
56
+ /** Open the shared Address screen to add/edit the billing address. */
57
+ /**
58
+ * Cancel discards the pending billing address too — it is not a form field,
59
+ * so `reset` alone would leave the footer up with nothing left to save.
60
+ */
61
+ onDiscardEdit?: () => void;
50
62
  /** Asked for when the composer opens, and again behind its Retry. */
51
63
  onFetchPaymentLinkEmailPreview?: (cacheOverride?: boolean) => void;
52
64
  onInvoiceClick?: (invoiceId: string) => void;
53
65
  onNewSubscription?: () => void;
54
- /** Open the shared Address screen to add/edit the billing address. */
55
66
  onOpenAddress?: () => void;
56
67
  /** Navigate to Settings › Payment Destination (Stripe connect). */
57
68
  onOpenPaymentSettings?: () => void;
@@ -11,6 +11,14 @@ export interface InvoicingCustomerFormPageProps {
11
11
  billingAddressLabel?: string;
12
12
  errorMessage?: string;
13
13
  fieldHelp?: FieldHelpByCode;
14
+ /**
15
+ * True while the customer being edited is still loading. The form holds its
16
+ * skeleton until the record lands: `useForm` seeds its default values once,
17
+ * at first render, so mounting the fields against the blank draft leaves them
18
+ * empty, and the first change writes that blankness back over the customer.
19
+ * Always false when creating — there is nothing to load.
20
+ */
21
+ isLoading?: boolean;
14
22
  onCancel: () => void;
15
23
  onChange: (localData: InvoicingCustomerFormLocalData) => void;
16
24
  onOpenAddress: () => void;
@@ -24,5 +32,5 @@ export interface InvoicingCustomerFormPageProps {
24
32
  * focus, and searchable selects behave exactly like the bill edit page. Emits a
25
33
  * plain data object for the connector to dispatch.
26
34
  */
27
- export declare function InvoicingCustomerFormPage({ billingAddressLabel, fieldHelp, customerTypeOptions, errorMessage, isSubmitting, localData, mode, netTermDayOptions, onBack, onCancel, onChange, onOpenAddress, onSubmit, taxabilityOptions, }: Readonly<InvoicingCustomerFormPageProps>): import("react/jsx-runtime").JSX.Element;
35
+ export declare function InvoicingCustomerFormPage({ billingAddressLabel, fieldHelp, customerTypeOptions, errorMessage, isLoading, isSubmitting, localData, mode, netTermDayOptions, onBack, onCancel, onChange, onOpenAddress, onSubmit, taxabilityOptions, }: Readonly<InvoicingCustomerFormPageProps>): import("react/jsx-runtime").JSX.Element;
28
36
  export default InvoicingCustomerFormPage;
@@ -5,6 +5,13 @@ export interface InvoicingDiscountDetailPageProps {
5
5
  isLoading: boolean;
6
6
  coupon?: InvoicingCoupon;
7
7
  discountLifecycleActionOptions?: InvoicingDiscountLifecycleActionOption[];
8
+ /**
9
+ * The account the coupon's discount line posts to, resolved from the id the
10
+ * coupon stores. Absent while the account list is still loading, or when the
11
+ * coupon names no account of its own — in which case the QBO settings
12
+ * discounts account applies instead.
13
+ */
14
+ discountsAccountName?: string;
8
15
  /** Error from the last inline-edit save attempt, shown in edit mode. */
9
16
  fieldHelp?: FieldHelpByCode;
10
17
  isRunningLifecycleAction?: boolean;
@@ -15,10 +15,18 @@ export interface InvoicingSubscriptionDetailPageProps {
15
15
  * editable whenever save is wired.
16
16
  */
17
17
  canEditCommercial?: boolean;
18
+ /** One-time charge price options for commercial inline edit. */
19
+ charges?: InvoicingSubscriptionFormOption[];
18
20
  /** Coupon options for commercial inline edit. */
19
21
  coupons?: InvoicingSubscriptionFormOption[];
20
22
  /** Error from the last inline-edit save attempt, shown in edit mode. */
21
23
  fieldHelp?: FieldHelpByCode;
24
+ /**
25
+ * A shipping address entered on the Address screen and not yet saved. It is
26
+ * not a form field, so the form's own dirty state cannot see it; the footer
27
+ * offers Save on this as well, or the address could never be linked.
28
+ */
29
+ hasUnsavedShippingAddress?: boolean;
22
30
  isRunningAction?: boolean;
23
31
  /** True while an inline-edit save is in flight. */
24
32
  isSaving?: boolean;
@@ -38,6 +46,11 @@ export interface InvoicingSubscriptionDetailPageProps {
38
46
  /** Called with the entered date once the change-term-end dialog is confirmed. */
39
47
  onChangeTermEnd?: (termEndsAt: string) => void;
40
48
  onCustomerClick?: (customerId: string) => void;
49
+ /**
50
+ * Cancel discards the pending shipping address too — it is not a form field,
51
+ * so `reset` alone would leave the footer up with nothing left to save.
52
+ */
53
+ onDiscardEdit?: () => void;
41
54
  /** `resumeDate` is only supplied for `pause`, collected by the pause dialog. */
42
55
  onLifecycleAction?: (action: InvoicingSubscriptionLifecycleAction, resumeDate?: string) => void;
43
56
  /** Open the shared Address screen to add/edit the shipping address. */