@zeniai/client-epic-state 5.0.65-betaRR1 → 5.0.65-betaRR3

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 (24) hide show
  1. package/lib/common/aiCfo/aiCfoSuggestedQuestionsPageContext.d.ts +1 -1
  2. package/lib/entity/aiCfo/aiCfoPayload.d.ts +37 -4
  3. package/lib/entity/aiCfo/aiCfoReducer.js +80 -5
  4. package/lib/entity/aiCfo/aiCfoState.d.ts +25 -2
  5. package/lib/entity/cardPolicy/cardPolicyPayload.d.ts +4 -4
  6. package/lib/entity/cardPolicy/cardPolicyPayload.js +1 -1
  7. package/lib/entity/cardPolicy/cardPolicyReducer.js +24 -5
  8. package/lib/entity/jeSchedules/jeSchedulesTypes.d.ts +1 -1
  9. package/lib/entity/monthEndCloseChecks/monthEndCloseChecksState.d.ts +1 -1
  10. package/lib/esm/entity/aiCfo/aiCfoReducer.js +80 -5
  11. package/lib/esm/entity/cardPolicy/cardPolicyPayload.js +1 -1
  12. package/lib/esm/entity/cardPolicy/cardPolicyReducer.js +24 -5
  13. package/lib/esm/view/aiCfoView/epics/fetchSuggestedQuestionsEpic.js +12 -2
  14. package/lib/index.d.ts +1 -1
  15. package/lib/view/aiCfoView/aiCfoViewReducer.d.ts +7 -0
  16. package/lib/view/aiCfoView/epics/fetchSuggestedQuestionsEpic.d.ts +8 -1
  17. package/lib/view/aiCfoView/epics/fetchSuggestedQuestionsEpic.js +12 -2
  18. package/lib/view/expenseAutomationView/types/jeSchedulesViewState.d.ts +1 -1
  19. package/lib/view/expenseAutomationView/types/missingReceiptsViewState.d.ts +2 -2
  20. package/lib/view/expenseAutomationView/types/transactionsViewState.d.ts +1 -1
  21. package/lib/view/people/peopleTypes.d.ts +1 -1
  22. package/lib/view/scheduleView/scheduleListView/scheduleListTypes.d.ts +1 -1
  23. package/lib/view/spendManagement/chargeCards/chargeCardList/chargeCardList.d.ts +1 -1
  24. package/package.json +1 -1
@@ -16,4 +16,4 @@
16
16
  *
17
17
  * @see README.md in this directory for maintenance expectations.
18
18
  */
19
- export type AiCfoSuggestedQuestionsPageContext = 'balance_sheet' | 'profit_loss' | 'vendors' | 'transactions' | 'cash_flow' | 'accounts_receivable' | 'accounts_payable' | 'transaction_details' | 'dashboard' | 'cash_balance' | 'card_balance' | 'cash_in_cash_out' | 'net_burn_or_income' | 'cash_position' | 'top_expenses' | 'revenue' | 'operating_expenses' | 'command_centre' | 'je_schedules' | 'reconciliation' | 'flux_analysis' | 'receipts' | 'mei' | 'task_checklist' | 'performance' | 'bills_list' | 'bills_details' | 'reimbursement_list' | 'reimbursement_details' | 'vendors_summary' | 'opex_by_vendor' | 'prepaid_expenses' | 'fixed_assets' | 'form_1099' | 'accrued_expenses' | 'forecast' | 'checking' | 'checking_details' | 'treasury' | 'cards' | 'cards_detail' | 'people' | 'rewards' | 'notifications';
19
+ export type AiCfoSuggestedQuestionsPageContext = 'balance_sheet' | 'profit_loss' | 'vendors' | 'transactions' | 'cash_flow' | 'accounts_receivable' | 'accounts_payable' | 'transaction_details' | 'dashboard' | 'cash_balance' | 'card_balance' | 'cash_in_cash_out' | 'net_burn_or_income' | 'cash_position' | 'top_expenses' | 'revenue' | 'operating_expenses' | 'command_centre' | 'je_schedules' | 'reconciliation' | 'flux_analysis' | 'receipts' | 'mei' | 'task_checklist' | 'performance' | 'bills_list' | 'bills_details' | 'reimbursement_list' | 'reimbursement_details' | 'vendors_summary' | 'opex_by_vendor' | 'prepaid_expenses' | 'fixed_assets' | 'form_1099' | 'accrued_expenses' | 'forecast' | 'checking' | 'checking_details' | 'treasury' | 'cards' | 'cards_detail' | 'card_policy' | 'card_policy_detail' | 'people' | 'rewards' | 'notifications';
@@ -25,25 +25,58 @@ export interface CardsCardOptionPayload {
25
25
  label: string;
26
26
  description?: string | null;
27
27
  }
28
+ /**
29
+ * Single AI-suggested card on the wire. The controller ships an array of
30
+ * these mixing vendor and department entries; entries with
31
+ * `is_suggested: true` seed step 1 of the form pre-checked, while entries
32
+ * with `is_suggested: false` are available in the step-2 Card dropdown and
33
+ * step-1 search but don't appear as initial rows.
34
+ */
35
+ export interface SuggestedCardPayload {
36
+ avg_spend: number;
37
+ card_name: string;
38
+ card_name_id: string;
39
+ card_type: 'vendor' | 'department';
40
+ credit_limit: number;
41
+ is_suggested: boolean;
42
+ owner_id: string;
43
+ }
28
44
  /**
29
45
  * Discriminated payload for the AI Card Creation interactive form. The wire
30
46
  * format mirrors the FE-side `CardsCreationInitialData` field-for-field
31
47
  * (with snake_case field names converted to camelCase by the reducer).
32
48
  *
33
- * - `team-then-cards` (legacy): the AI suggests teams; user reviews them in
34
- * step 1, then configures cards in step 2.
49
+ * - `team-then-cards` (default): the AI suggests rows (vendors + departments);
50
+ * user reviews them in step 1, then configures cards in step 2. The wire
51
+ * format ships a flat `suggested_cards` array. Both suggested and
52
+ * not-suggested entries are included; the reducer splits them into seed
53
+ * rows vs. dropdown options. The legacy `team_rows` /
54
+ * `suggested_owner_user_id_by_team_id` shape is also accepted for
55
+ * backward compatibility with mocks and tests.
35
56
  * - `cards-only` (1-step): the AI has already chosen cards; user lands
36
57
  * directly on the editable card table for confirmation.
37
58
  */
38
59
  export type CardsCreationInitialDataPayload = {
39
60
  card_options: CardsCardOptionPayload[];
40
61
  mode: 'team-then-cards';
41
- suggested_owner_user_id_by_team_id: Record<string, string | null>;
42
- team_rows: Array<{
62
+ /**
63
+ * Preferred wire format: flat list of vendor/department cards with an
64
+ * `is_suggested` discriminator. The reducer derives `team_rows`,
65
+ * `suggested_owner_user_id_by_team_id`, and the full `cardNameOptions`
66
+ * universe from this single array.
67
+ */
68
+ suggested_cards?: SuggestedCardPayload[];
69
+ /**
70
+ * Legacy/test shape — pre-shaped seed rows. Mutually exclusive with
71
+ * `suggested_cards`; when both are present, `suggested_cards` wins.
72
+ */
73
+ suggested_owner_user_id_by_team_id?: Record<string, string | null>;
74
+ team_rows?: Array<{
43
75
  avg_monthly_spend: string;
44
76
  id: string;
45
77
  label: string;
46
78
  recommended_limit: string;
79
+ card_kind?: 'vendor' | 'class' | null;
47
80
  recommended_limit_cadence?: string | null;
48
81
  }>;
49
82
  } | {
@@ -180,19 +180,94 @@ const toCardsCreationInitialData = (payload) => {
180
180
  })),
181
181
  };
182
182
  }
183
+ // Preferred wire format: a flat `suggested_cards` array with an
184
+ // `is_suggested` discriminator. The adapter splits it into seed rows,
185
+ // owner map, and the full dropdown universe.
186
+ if (payload.suggested_cards != null) {
187
+ return fromSuggestedCards(payload.suggested_cards, cardOptions);
188
+ }
189
+ // Legacy / test shape: backend already pre-shapes seeds. Synthesize an
190
+ // empty `cardNameOptions` (step 2 dropdown still works via row-self ids).
191
+ const teamRows = (payload.team_rows ?? []).map((row) => ({
192
+ avgMonthlySpend: row.avg_monthly_spend,
193
+ cardKind: (row.card_kind ?? 'vendor'),
194
+ id: row.id,
195
+ label: row.label,
196
+ recommendedLimit: row.recommended_limit,
197
+ recommendedLimitCadence: row.recommended_limit_cadence ?? undefined,
198
+ }));
183
199
  return {
184
200
  mode: 'team-then-cards',
185
201
  cardOptions,
186
- suggestedOwnerUserIdByTeamId: Object.fromEntries(Object.entries(payload.suggested_owner_user_id_by_team_id ?? {}).map(([teamId, userId]) => [teamId, userId ?? undefined])),
187
- teamRows: (payload.team_rows ?? []).map((row) => ({
188
- avgMonthlySpend: row.avg_monthly_spend,
202
+ cardNameOptions: teamRows.map((row) => ({
189
203
  id: row.id,
204
+ kind: row.cardKind,
190
205
  label: row.label,
191
- recommendedLimit: row.recommended_limit,
192
- recommendedLimitCadence: row.recommended_limit_cadence ?? undefined,
193
206
  })),
207
+ suggestedOwnerUserIdByTeamId: Object.fromEntries(Object.entries(payload.suggested_owner_user_id_by_team_id ?? {}).map(([teamId, userId]) => [teamId, userId ?? undefined])),
208
+ teamRows,
194
209
  };
195
210
  };
211
+ const CURRENCY_FORMATTER = new Intl.NumberFormat('en-US', {
212
+ style: 'currency',
213
+ currency: 'USD',
214
+ maximumFractionDigits: 0,
215
+ });
216
+ const fromSuggestedCards = (suggestedCards, cardOptions) => {
217
+ const ordered = [...suggestedCards].sort((a, b) => {
218
+ if (a.card_type === b.card_type) {
219
+ return 0;
220
+ }
221
+ return a.card_type === 'vendor' ? -1 : 1;
222
+ });
223
+ const withUniqueIds = withSynthesizedIds(ordered);
224
+ const teamRows = withUniqueIds
225
+ .filter(({ card }) => card.is_suggested)
226
+ .map(({ card, uniqueId }) => ({
227
+ avgMonthlySpend: CURRENCY_FORMATTER.format(card.avg_spend),
228
+ cardKind: card.card_type === 'department' ? 'class' : 'vendor',
229
+ id: uniqueId,
230
+ label: card.card_name,
231
+ recommendedLimit: CURRENCY_FORMATTER.format(card.credit_limit),
232
+ recommendedLimitCadence: 'Monthly',
233
+ }));
234
+ const suggestedOwnerUserIdByTeamId = Object.fromEntries(withUniqueIds
235
+ .filter(({ card }) => card.is_suggested)
236
+ .map(({ card, uniqueId }) => [uniqueId, card.owner_id]));
237
+ const cardNameOptions = withUniqueIds.map(({ card, uniqueId }) => ({
238
+ id: uniqueId,
239
+ kind: card.card_type === 'department' ? 'class' : 'vendor',
240
+ label: card.card_name,
241
+ }));
242
+ return {
243
+ mode: 'team-then-cards',
244
+ cardOptions,
245
+ cardNameOptions,
246
+ suggestedOwnerUserIdByTeamId,
247
+ teamRows,
248
+ };
249
+ };
250
+ /**
251
+ * Stamp each suggested card with a stable unique id derived from its
252
+ * `card_name_id`. If two cards in the same payload share the same
253
+ * `card_name_id` (which happens in the current sample payload where
254
+ * `card_name_id` is the literal `"vendor_uuid"`), fall back to a
255
+ * synthesized id `${kind}__${card_name_id}__${index}` so RHF row keying
256
+ * stays unique.
257
+ */
258
+ const withSynthesizedIds = (cards) => {
259
+ const seen = new Set();
260
+ return cards.map((card, index) => {
261
+ const preferred = card.card_name_id;
262
+ if (preferred != null && preferred !== '' && !seen.has(preferred)) {
263
+ seen.add(preferred);
264
+ return { card, uniqueId: preferred };
265
+ }
266
+ const synthesized = `${card.card_type}__${preferred ?? 'unknown'}__${index}`;
267
+ seen.add(synthesized);
268
+ return { card, uniqueId: synthesized };
269
+ });
270
+ };
196
271
  const toCardPolicyInitialData = (payload) => {
197
272
  const suggestedPolicies = (payload.suggested_policies ?? []).map((policy) => ({
198
273
  id: policy.id,
@@ -62,16 +62,38 @@ export interface CardsCardOption {
62
62
  label: string;
63
63
  description?: string;
64
64
  }
65
+ /**
66
+ * Card kind for AI Card Creation rows. `vendor` = vendor-bound card,
67
+ * `class` = department-scoped card. The wire format ships `department`;
68
+ * the reducer maps it to `class` (the FE-side term for accounting class /
69
+ * department) so it lines up with the rest of the codebase.
70
+ */
71
+ export type CardsCardKind = 'vendor' | 'class';
72
+ /**
73
+ * Single entry in the unified vendor + department dropdown universe shipped
74
+ * by the AI controller for the `team-then-cards` flow. Powers both the
75
+ * step-2 Card cell dropdown (filtered to entries with matching `kind`) and
76
+ * the step-1 "Add something else" search (no kind filter — user can add
77
+ * either).
78
+ */
79
+ export interface CardNameOption {
80
+ id: string;
81
+ kind: CardsCardKind;
82
+ label: string;
83
+ }
65
84
  /**
66
85
  * Initial data for the AI Card Creation interactive form. Discriminated on
67
86
  * `mode`:
68
87
  *
69
- * - `team-then-cards` (default): AI suggests teams; user reviews them in
70
- * step 1, then configures one card per team in step 2.
88
+ * - `team-then-cards` (default): AI suggests rows (mix of vendors +
89
+ * departments); user reviews them in step 1, then configures one card per
90
+ * row in step 2. `cardNameOptions` is the full universe (suggested +
91
+ * not-suggested) used to populate the dropdown / search.
71
92
  * - `cards-only` (1-step): AI has already chosen cards; user lands directly
72
93
  * on the editable card table for confirmation.
73
94
  */
74
95
  export type CardsCreationInitialData = {
96
+ cardNameOptions: CardNameOption[];
75
97
  cardOptions: CardsCardOption[];
76
98
  mode: 'team-then-cards';
77
99
  /**
@@ -81,6 +103,7 @@ export type CardsCreationInitialData = {
81
103
  suggestedOwnerUserIdByTeamId: Record<string, ID | undefined>;
82
104
  teamRows: Array<{
83
105
  avgMonthlySpend: string;
106
+ cardKind: CardsCardKind;
84
107
  id: string;
85
108
  label: string;
86
109
  recommendedLimit: string;
@@ -27,13 +27,13 @@ export type CardPolicyMccCategoriesResponse = ZeniAPIResponse<CardPolicyMccCateg
27
27
  export declare const toMccCategory: (payload: MccCategoryPayload) => MccCategory;
28
28
  /**
29
29
  * Vendor row returned by `/accounting/1.0/vendors`. The card-policy
30
- * vendor search dropdown only needs `vendor_id` + `name`; we deliberately
31
- * ignore the rest of the payload to keep this slice independent of the
32
- * heavy `vendor` entity store.
30
+ * vendor search dropdown only needs `vendor_id` + `vendor_name`; we
31
+ * deliberately ignore the rest of the payload to keep this slice
32
+ * independent of the heavy `vendor` entity store.
33
33
  */
34
34
  export interface CardPolicyVendorSearchVendorPayload {
35
- name: string;
36
35
  vendor_id: string;
36
+ vendor_name: string;
37
37
  }
38
38
  export interface CardPolicyVendorSearchData {
39
39
  vendors: CardPolicyVendorSearchVendorPayload[];
@@ -11,7 +11,7 @@ const toMccCategory = (payload) => ({
11
11
  exports.toMccCategory = toMccCategory;
12
12
  const toCardPolicyVendorSearchOption = (payload) => ({
13
13
  vendorId: payload.vendor_id,
14
- name: payload.name,
14
+ name: payload.vendor_name,
15
15
  });
16
16
  exports.toCardPolicyVendorSearchOption = toCardPolicyVendorSearchOption;
17
17
  const toCardPolicyStats = (payload) => ({
@@ -4,6 +4,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.clearCardPolicy = exports.clearPolicyDocumentExtraction = exports.updatePolicyDocumentExtractionFailure = exports.updatePolicyDocumentExtractionSuccess = exports.extractPolicyDocument = exports.updateCardPolicyStats = exports.removeCardPolicyTemplate = exports.updateCardPolicyTemplates = exports.updateCreatedCardPolicyTemplate = exports.updateCardPolicyVendorOptionsFailure = exports.updateCardPolicyVendorOptions = exports.updateCardPolicyMccCategoriesFailure = exports.updateCardPolicyMccCategories = exports.fetchCardPolicyVendorOptions = exports.fetchCardPolicyMccCategories = exports.initialState = void 0;
5
5
  const toolkit_1 = require("@reduxjs/toolkit");
6
6
  const cardPolicyPayload_1 = require("./cardPolicyPayload");
7
+ /**
8
+ * Defensive predicate for vendor / merchant names that flow into
9
+ * `buildVendorChipId(name)` (in `@zeniai/web-components`). That helper
10
+ * calls `name.trim().toLowerCase()`, so a null / undefined / whitespace-
11
+ * only value would throw at render time, and an empty string would
12
+ * collapse every nameless row into a single colliding `vendor-` chip-id.
13
+ * Used at the redux ingestion boundary for both the vendor-search results
14
+ * and the BE-suggested merchant seed arrays.
15
+ */
16
+ const isUsableMerchantName = (name) => typeof name === 'string' && name.trim() !== '';
7
17
  const initialPolicyDocumentExtractionState = {
8
18
  fetchState: 'Not-Started',
9
19
  error: undefined,
@@ -42,10 +52,12 @@ const cardPolicy = (0, toolkit_1.createSlice)({
42
52
  // Persist BE-suggested allow / block seeds alongside the universe.
43
53
  // Both flows (manual create page + AI CFO interactive form) read
44
54
  // these to pre-populate the chip cloud body before the user edits.
45
- draft.suggestedAllowMerchants =
46
- action.payload.suggested_allow_merchants ?? [];
47
- draft.suggestedBlockMerchants =
48
- action.payload.suggested_block_merchants ?? [];
55
+ // Drop empty / nullish merchant names defensively — they feed
56
+ // `buildVendorChipId(name)` (in @zeniai/web-components) which calls
57
+ // `.trim().toLowerCase()` and would crash the chip-picker render
58
+ // path if any entry slipped through as undefined / "".
59
+ draft.suggestedAllowMerchants = (action.payload.suggested_allow_merchants ?? []).filter(isUsableMerchantName);
60
+ draft.suggestedBlockMerchants = (action.payload.suggested_block_merchants ?? []).filter(isUsableMerchantName);
49
61
  draft.suggestedAllowCategories = (action.payload.suggested_allow_catagories ?? []).map(cardPolicyPayload_1.toMccCategory);
50
62
  draft.suggestedBlockCategories = (action.payload.suggested_block_catagories ?? []).map(cardPolicyPayload_1.toMccCategory);
51
63
  draft.mccCategoriesFetchState = 'Completed';
@@ -167,7 +179,14 @@ const cardPolicy = (0, toolkit_1.createSlice)({
167
179
  },
168
180
  },
169
181
  updateCardPolicyVendorOptions(draft, action) {
170
- draft.vendorSearch.vendors = (action.payload.vendors ?? []).map(cardPolicyPayload_1.toCardPolicyVendorSearchOption);
182
+ // Drop rows with no usable name. The chip-picker keys vendors by
183
+ // `buildVendorChipId(name)` (in @zeniai/web-components), which calls
184
+ // `name.trim().toLowerCase()`. A null / undefined / whitespace-only
185
+ // name would either throw at render time or collapse every nameless
186
+ // row into a single colliding `vendor-` chip-id.
187
+ draft.vendorSearch.vendors = (action.payload.vendors ?? [])
188
+ .map(cardPolicyPayload_1.toCardPolicyVendorSearchOption)
189
+ .filter((vendor) => isUsableMerchantName(vendor.name));
171
190
  draft.vendorSearch.fetchState = 'Completed';
172
191
  draft.vendorSearch.error = undefined;
173
192
  },
@@ -10,7 +10,7 @@ export declare const ALL_SCHEDULES_TYPES: readonly ["prepaid_expenses", "fixed_a
10
10
  export declare const toScheduleTypesType: (v: string) => "prepaid_expenses" | "fixed_assets" | "accrued_expenses" | "deferred_revenue";
11
11
  export type ScheduleTypes = ReturnType<typeof toScheduleTypesType>;
12
12
  export declare const toScheduleTypesTypeStrict: (v?: string) => "prepaid_expenses" | "fixed_assets" | "accrued_expenses" | "deferred_revenue" | undefined;
13
- export declare const toScheduleJournalEntryStatusCodeType: (v: string) => "pending" | "posted" | "unknown" | "canceled" | "recorded";
13
+ export declare const toScheduleJournalEntryStatusCodeType: (v: string) => "pending" | "unknown" | "posted" | "canceled" | "recorded";
14
14
  export type ScheduleJournalEntryStatusCodeType = ReturnType<typeof toScheduleJournalEntryStatusCodeType>;
15
15
  export declare const toScheduleStatusCodeType: (v: string) => "completed" | "draft" | "new" | "ongoing" | "marked_as_completed";
16
16
  export type ScheduleStatusCodeType = ReturnType<typeof toScheduleStatusCodeType>;
@@ -3,7 +3,7 @@ import { MonthYearPeriod, MonthYearPeriodId } from '../../commonStateTypes/timeP
3
3
  import { ExpenseAutomationViewType } from '../../view/expenseAutomationView/expenseAutomationViewState';
4
4
  import { ZeniDate } from '../../zeniDayJS';
5
5
  export declare const ALL_MONTH_END_CLOSE_CHECKS_FREQUENCY: readonly ["Weekly", "Monthly"];
6
- export declare const toMonthEndCloseCheckFrequency: (v?: string) => "Weekly" | "Monthly";
6
+ export declare const toMonthEndCloseCheckFrequency: (v?: string) => "Monthly" | "Weekly";
7
7
  export type MonthEndCloseCheckFrequency = NonNullable<ReturnType<typeof toMonthEndCloseCheckFrequency>>;
8
8
  export interface MonthCloseCheckMetrics {
9
9
  columnIndex: number;
@@ -175,19 +175,94 @@ const toCardsCreationInitialData = (payload) => {
175
175
  })),
176
176
  };
177
177
  }
178
+ // Preferred wire format: a flat `suggested_cards` array with an
179
+ // `is_suggested` discriminator. The adapter splits it into seed rows,
180
+ // owner map, and the full dropdown universe.
181
+ if (payload.suggested_cards != null) {
182
+ return fromSuggestedCards(payload.suggested_cards, cardOptions);
183
+ }
184
+ // Legacy / test shape: backend already pre-shapes seeds. Synthesize an
185
+ // empty `cardNameOptions` (step 2 dropdown still works via row-self ids).
186
+ const teamRows = (payload.team_rows ?? []).map((row) => ({
187
+ avgMonthlySpend: row.avg_monthly_spend,
188
+ cardKind: (row.card_kind ?? 'vendor'),
189
+ id: row.id,
190
+ label: row.label,
191
+ recommendedLimit: row.recommended_limit,
192
+ recommendedLimitCadence: row.recommended_limit_cadence ?? undefined,
193
+ }));
178
194
  return {
179
195
  mode: 'team-then-cards',
180
196
  cardOptions,
181
- suggestedOwnerUserIdByTeamId: Object.fromEntries(Object.entries(payload.suggested_owner_user_id_by_team_id ?? {}).map(([teamId, userId]) => [teamId, userId ?? undefined])),
182
- teamRows: (payload.team_rows ?? []).map((row) => ({
183
- avgMonthlySpend: row.avg_monthly_spend,
197
+ cardNameOptions: teamRows.map((row) => ({
184
198
  id: row.id,
199
+ kind: row.cardKind,
185
200
  label: row.label,
186
- recommendedLimit: row.recommended_limit,
187
- recommendedLimitCadence: row.recommended_limit_cadence ?? undefined,
188
201
  })),
202
+ suggestedOwnerUserIdByTeamId: Object.fromEntries(Object.entries(payload.suggested_owner_user_id_by_team_id ?? {}).map(([teamId, userId]) => [teamId, userId ?? undefined])),
203
+ teamRows,
189
204
  };
190
205
  };
206
+ const CURRENCY_FORMATTER = new Intl.NumberFormat('en-US', {
207
+ style: 'currency',
208
+ currency: 'USD',
209
+ maximumFractionDigits: 0,
210
+ });
211
+ const fromSuggestedCards = (suggestedCards, cardOptions) => {
212
+ const ordered = [...suggestedCards].sort((a, b) => {
213
+ if (a.card_type === b.card_type) {
214
+ return 0;
215
+ }
216
+ return a.card_type === 'vendor' ? -1 : 1;
217
+ });
218
+ const withUniqueIds = withSynthesizedIds(ordered);
219
+ const teamRows = withUniqueIds
220
+ .filter(({ card }) => card.is_suggested)
221
+ .map(({ card, uniqueId }) => ({
222
+ avgMonthlySpend: CURRENCY_FORMATTER.format(card.avg_spend),
223
+ cardKind: card.card_type === 'department' ? 'class' : 'vendor',
224
+ id: uniqueId,
225
+ label: card.card_name,
226
+ recommendedLimit: CURRENCY_FORMATTER.format(card.credit_limit),
227
+ recommendedLimitCadence: 'Monthly',
228
+ }));
229
+ const suggestedOwnerUserIdByTeamId = Object.fromEntries(withUniqueIds
230
+ .filter(({ card }) => card.is_suggested)
231
+ .map(({ card, uniqueId }) => [uniqueId, card.owner_id]));
232
+ const cardNameOptions = withUniqueIds.map(({ card, uniqueId }) => ({
233
+ id: uniqueId,
234
+ kind: card.card_type === 'department' ? 'class' : 'vendor',
235
+ label: card.card_name,
236
+ }));
237
+ return {
238
+ mode: 'team-then-cards',
239
+ cardOptions,
240
+ cardNameOptions,
241
+ suggestedOwnerUserIdByTeamId,
242
+ teamRows,
243
+ };
244
+ };
245
+ /**
246
+ * Stamp each suggested card with a stable unique id derived from its
247
+ * `card_name_id`. If two cards in the same payload share the same
248
+ * `card_name_id` (which happens in the current sample payload where
249
+ * `card_name_id` is the literal `"vendor_uuid"`), fall back to a
250
+ * synthesized id `${kind}__${card_name_id}__${index}` so RHF row keying
251
+ * stays unique.
252
+ */
253
+ const withSynthesizedIds = (cards) => {
254
+ const seen = new Set();
255
+ return cards.map((card, index) => {
256
+ const preferred = card.card_name_id;
257
+ if (preferred != null && preferred !== '' && !seen.has(preferred)) {
258
+ seen.add(preferred);
259
+ return { card, uniqueId: preferred };
260
+ }
261
+ const synthesized = `${card.card_type}__${preferred ?? 'unknown'}__${index}`;
262
+ seen.add(synthesized);
263
+ return { card, uniqueId: synthesized };
264
+ });
265
+ };
191
266
  const toCardPolicyInitialData = (payload) => {
192
267
  const suggestedPolicies = (payload.suggested_policies ?? []).map((policy) => ({
193
268
  id: policy.id,
@@ -7,7 +7,7 @@ export const toMccCategory = (payload) => ({
7
7
  });
8
8
  export const toCardPolicyVendorSearchOption = (payload) => ({
9
9
  vendorId: payload.vendor_id,
10
- name: payload.name,
10
+ name: payload.vendor_name,
11
11
  });
12
12
  export const toCardPolicyStats = (payload) => ({
13
13
  activePolicies: payload.active_policies,
@@ -1,5 +1,15 @@
1
1
  import { createSlice } from '@reduxjs/toolkit';
2
2
  import { toCardPolicyTemplate, toCardPolicyVendorSearchOption, toExtractedCardPolicyRules, toMccCategory, } from './cardPolicyPayload';
3
+ /**
4
+ * Defensive predicate for vendor / merchant names that flow into
5
+ * `buildVendorChipId(name)` (in `@zeniai/web-components`). That helper
6
+ * calls `name.trim().toLowerCase()`, so a null / undefined / whitespace-
7
+ * only value would throw at render time, and an empty string would
8
+ * collapse every nameless row into a single colliding `vendor-` chip-id.
9
+ * Used at the redux ingestion boundary for both the vendor-search results
10
+ * and the BE-suggested merchant seed arrays.
11
+ */
12
+ const isUsableMerchantName = (name) => typeof name === 'string' && name.trim() !== '';
3
13
  const initialPolicyDocumentExtractionState = {
4
14
  fetchState: 'Not-Started',
5
15
  error: undefined,
@@ -38,10 +48,12 @@ const cardPolicy = createSlice({
38
48
  // Persist BE-suggested allow / block seeds alongside the universe.
39
49
  // Both flows (manual create page + AI CFO interactive form) read
40
50
  // these to pre-populate the chip cloud body before the user edits.
41
- draft.suggestedAllowMerchants =
42
- action.payload.suggested_allow_merchants ?? [];
43
- draft.suggestedBlockMerchants =
44
- action.payload.suggested_block_merchants ?? [];
51
+ // Drop empty / nullish merchant names defensively — they feed
52
+ // `buildVendorChipId(name)` (in @zeniai/web-components) which calls
53
+ // `.trim().toLowerCase()` and would crash the chip-picker render
54
+ // path if any entry slipped through as undefined / "".
55
+ draft.suggestedAllowMerchants = (action.payload.suggested_allow_merchants ?? []).filter(isUsableMerchantName);
56
+ draft.suggestedBlockMerchants = (action.payload.suggested_block_merchants ?? []).filter(isUsableMerchantName);
45
57
  draft.suggestedAllowCategories = (action.payload.suggested_allow_catagories ?? []).map(toMccCategory);
46
58
  draft.suggestedBlockCategories = (action.payload.suggested_block_catagories ?? []).map(toMccCategory);
47
59
  draft.mccCategoriesFetchState = 'Completed';
@@ -163,7 +175,14 @@ const cardPolicy = createSlice({
163
175
  },
164
176
  },
165
177
  updateCardPolicyVendorOptions(draft, action) {
166
- draft.vendorSearch.vendors = (action.payload.vendors ?? []).map(toCardPolicyVendorSearchOption);
178
+ // Drop rows with no usable name. The chip-picker keys vendors by
179
+ // `buildVendorChipId(name)` (in @zeniai/web-components), which calls
180
+ // `name.trim().toLowerCase()`. A null / undefined / whitespace-only
181
+ // name would either throw at render time or collapse every nameless
182
+ // row into a single colliding `vendor-` chip-id.
183
+ draft.vendorSearch.vendors = (action.payload.vendors ?? [])
184
+ .map(toCardPolicyVendorSearchOption)
185
+ .filter((vendor) => isUsableMerchantName(vendor.name));
167
186
  draft.vendorSearch.fetchState = 'Completed';
168
187
  draft.vendorSearch.error = undefined;
169
188
  },
@@ -2,13 +2,23 @@ import { of } from 'rxjs';
2
2
  import { catchError, filter, mergeMap } from 'rxjs/operators';
3
3
  import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
4
4
  import { fetchSuggestedQuestions, fetchSuggestedQuestionsFailure, fetchSuggestedQuestionsSuccess, } from '../aiCfoViewReducer';
5
- /** GET {aiCfoMicroServiceBaseUrl}/1.0/suggested-questions?page_context=... */
5
+ /**
6
+ * GET {aiCfoMicroServiceBaseUrl}/1.0/suggested-questions?page_context=...
7
+ *
8
+ * Appends `&no_card_created=true` when the action payload sets
9
+ * `noCardCreated: true` (AI Card Creation flow on the cards list — the tenant
10
+ * has not yet created any card). Any other value omits the flag entirely so
11
+ * the existing call sites continue to issue the unchanged request.
12
+ */
6
13
  export const fetchSuggestedQuestionsEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchSuggestedQuestions.match),
7
14
  // mergeMap (not switchMap): each page_context is keyed separately in Redux; switchMap
8
15
  // would cancel in-flight HTTP for context A when B is requested, leaving A stuck In-Progress.
9
16
  mergeMap((action) => {
10
- const { pageContext } = action.payload;
17
+ const { pageContext, noCardCreated } = action.payload;
11
18
  const query = new URLSearchParams({ page_context: pageContext });
19
+ if (noCardCreated === true) {
20
+ query.set('no_card_created', 'true');
21
+ }
12
22
  const url = `${zeniAPI.apiEndPoints.aiCfoMicroServiceBaseUrl}/1.0/suggested-questions?${query.toString()}`;
13
23
  return zeniAPI.getJSON(url).pipe(mergeMap((response) => {
14
24
  if (isSuccessResponse(response) && response.data != null) {
package/lib/index.d.ts CHANGED
@@ -906,7 +906,7 @@ export { AiCfoViewSelector, getAiCfoView, getSuggestedQuestionsForPageContext, }
906
906
  export { MessagePayload, ChatSessionPayload, AiCfoAnswerPayload, } from './entity/aiCfo/aiCfoPayload';
907
907
  export { setNewSession, setSessions, upsertAnswerPayload, upsertOrAddQuestionAnswerPayload, addQuestionPayload, clearSession, clearAiCfo, toAiCfoVisualization, } from './entity/aiCfo/aiCfoReducer';
908
908
  export { AiCfoSelectorView, getAllQuestionAnswersForChatSession, getQuestionAnswerByIdForChatSession, getAllQuestionsForChatSession, getAiCfoSelectorView, ChatSessionWithOrderedQuestionAnswers, } from './entity/aiCfo/aiCfoSelector';
909
- export { AiCfoQuestionWithAnswer, AiCfoAnswerParagraph, AiCfoAnswerStateType, AiCfoChartType, AiCfoAnswerResponseType, TableVisualization, ChartVisualization, CardsCardOption, CardsCreationInitialData, CardPolicyCardRowSeed, CardPolicyInitialData, CardPolicyLimitRowSeed, CardPolicyOwnerSeed, CardPolicySuggestedPolicy, CardPolicySuggestedPolicyChip, CardPolicySuggestedPolicyChipGroup, CardPolicyTagSeed, InteractiveFormType, InteractiveFormVisualization, AiCfoVisualization, AiCfoVisualizationType, AiCfoState, ChatSession, ALL_AI_CFO_CHARTS_TYPES, ALL_AI_CFO_VISUALIZATION_TYPES, ALL_AI_CFO_ANSWER_STATE_TYPES, ALL_AI_CFO_ANSWER_RESPONSE_TYPES, ALL_INTERACTIVE_FORM_TYPES, toAiCfoVisualizationType, toAiCfoVisualizationTypeStrict, toAiCfoChartType, toAiCfoChartTypeStrict, toAiCfoAnswerStateType, toAiCfoAnswerStateTypeStrict, toAiCfoAnswerResponseType, toAiCfoAnswerResponseTypeStrict, toInteractiveFormType, toInteractiveFormTypeStrict, ChatSessionWithMessages, ResponseBlockBase, AnswerContentBlockBase, FileAttachmentMetadata, QuestionContentBlockBase, ResponseBlockType, toMessageSender, toMessageType, MessageSender, MessageType, } from './entity/aiCfo/aiCfoState';
909
+ export { AiCfoQuestionWithAnswer, AiCfoAnswerParagraph, AiCfoAnswerStateType, AiCfoChartType, AiCfoAnswerResponseType, TableVisualization, ChartVisualization, CardNameOption, CardsCardKind, CardsCardOption, CardsCreationInitialData, CardPolicyCardRowSeed, CardPolicyInitialData, CardPolicyLimitRowSeed, CardPolicyOwnerSeed, CardPolicySuggestedPolicy, CardPolicySuggestedPolicyChip, CardPolicySuggestedPolicyChipGroup, CardPolicyTagSeed, InteractiveFormType, InteractiveFormVisualization, AiCfoVisualization, AiCfoVisualizationType, AiCfoState, ChatSession, ALL_AI_CFO_CHARTS_TYPES, ALL_AI_CFO_VISUALIZATION_TYPES, ALL_AI_CFO_ANSWER_STATE_TYPES, ALL_AI_CFO_ANSWER_RESPONSE_TYPES, ALL_INTERACTIVE_FORM_TYPES, toAiCfoVisualizationType, toAiCfoVisualizationTypeStrict, toAiCfoChartType, toAiCfoChartTypeStrict, toAiCfoAnswerStateType, toAiCfoAnswerStateTypeStrict, toAiCfoAnswerResponseType, toAiCfoAnswerResponseTypeStrict, toInteractiveFormType, toInteractiveFormTypeStrict, ChatSessionWithMessages, ResponseBlockBase, AnswerContentBlockBase, FileAttachmentMetadata, QuestionContentBlockBase, ResponseBlockType, toMessageSender, toMessageType, MessageSender, MessageType, } from './entity/aiCfo/aiCfoState';
910
910
  export { clearPolicyDocumentExtraction, extractPolicyDocument, fetchCardPolicyMccCategories, fetchCardPolicyVendorOptions, removeCardPolicyTemplate, updateCardPolicyMccCategories, updateCardPolicyMccCategoriesFailure, updateCardPolicyStats, updateCardPolicyTemplates, updateCardPolicyVendorOptions, updateCardPolicyVendorOptionsFailure, updateCreatedCardPolicyTemplate, updatePolicyDocumentExtractionFailure, updatePolicyDocumentExtractionSuccess, clearCardPolicy, } from './entity/cardPolicy/cardPolicyReducer';
911
911
  export { getAllCardPolicyTemplates, getCardPolicyMccCategories, getCardPolicyMccCategoriesError, getCardPolicyMccCategoriesFetchState, getCardPolicyStats, getCardPolicySuggestedAllowCategories, getCardPolicySuggestedAllowMerchants, getCardPolicySuggestedBlockCategories, getCardPolicySuggestedBlockMerchants, getCardPolicyTemplateById, getCardPolicyTemplatesByIds, getCardPolicyVendorSearchFetchState, getCardPolicyVendorSearchOptions, getCardPolicyVendorSearchString, getExtractedCardPolicyRules, getPolicyDocumentExtractionError, getPolicyDocumentExtractionFetchState, getUploadedPolicyDocumentFileName, } from './entity/cardPolicy/cardPolicySelector';
912
912
  export { ALL_CARD_POLICY_TEMPLATE_MODES, ALL_CARD_POLICY_TEMPLATE_STATUSES, CardPolicyState, CardPolicyStats, CardPolicyTemplate, CardPolicyTemplateEntityList, CardPolicyTemplateMode, CardPolicyTemplateSpendLimits, CardPolicyTemplateStatus, CardPolicyVendorSearchEntry, CardPolicyVendorSearchState, ExtractedCardPolicyRules, MccCategory, PolicyDocumentExtractionState, toCardPolicyTemplateMode, toCardPolicyTemplateStatus, } from './entity/cardPolicy/cardPolicyState';
@@ -92,6 +92,13 @@ export declare const submitQuestion: import("@reduxjs/toolkit").ActionCreatorWit
92
92
  questionAnswerId: ID;
93
93
  }, "aiCfoView/updateCotCollapsedState">, fetchSuggestedQuestions: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
94
94
  pageContext: AiCfoSuggestedQuestionsPageContext;
95
+ /**
96
+ * AI Card Creation flow: when true, the epic appends `&no_card_created=true`
97
+ * so the backend tailors prompts for a tenant that has not yet created a
98
+ * card. The bucket key is unchanged (`pageContext`); see
99
+ * `useFetchSuggestedQuestionsWhenReady` for the refetch-on-flag-flip rule.
100
+ */
101
+ noCardCreated?: boolean;
95
102
  }, "aiCfoView/fetchSuggestedQuestions">, fetchSuggestedQuestionsSuccess: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
96
103
  generatedAt: string;
97
104
  pageContext: AiCfoSuggestedQuestionsPageContext;
@@ -4,5 +4,12 @@ import { RootState } from '../../../reducer';
4
4
  import { ZeniAPI } from '../../../zeniAPI';
5
5
  import { fetchSuggestedQuestions, fetchSuggestedQuestionsFailure, fetchSuggestedQuestionsSuccess } from '../aiCfoViewReducer';
6
6
  export type ActionType = ReturnType<typeof fetchSuggestedQuestions> | ReturnType<typeof fetchSuggestedQuestionsSuccess> | ReturnType<typeof fetchSuggestedQuestionsFailure>;
7
- /** GET {aiCfoMicroServiceBaseUrl}/1.0/suggested-questions?page_context=... */
7
+ /**
8
+ * GET {aiCfoMicroServiceBaseUrl}/1.0/suggested-questions?page_context=...
9
+ *
10
+ * Appends `&no_card_created=true` when the action payload sets
11
+ * `noCardCreated: true` (AI Card Creation flow on the cards list — the tenant
12
+ * has not yet created any card). Any other value omits the flag entirely so
13
+ * the existing call sites continue to issue the unchanged request.
14
+ */
8
15
  export declare const fetchSuggestedQuestionsEpic: (actions$: ActionsObservable<ActionType>, _state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => Observable<ActionType>;
@@ -5,13 +5,23 @@ const rxjs_1 = require("rxjs");
5
5
  const operators_1 = require("rxjs/operators");
6
6
  const responsePayload_1 = require("../../../responsePayload");
7
7
  const aiCfoViewReducer_1 = require("../aiCfoViewReducer");
8
- /** GET {aiCfoMicroServiceBaseUrl}/1.0/suggested-questions?page_context=... */
8
+ /**
9
+ * GET {aiCfoMicroServiceBaseUrl}/1.0/suggested-questions?page_context=...
10
+ *
11
+ * Appends `&no_card_created=true` when the action payload sets
12
+ * `noCardCreated: true` (AI Card Creation flow on the cards list — the tenant
13
+ * has not yet created any card). Any other value omits the flag entirely so
14
+ * the existing call sites continue to issue the unchanged request.
15
+ */
9
16
  const fetchSuggestedQuestionsEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(aiCfoViewReducer_1.fetchSuggestedQuestions.match),
10
17
  // mergeMap (not switchMap): each page_context is keyed separately in Redux; switchMap
11
18
  // would cancel in-flight HTTP for context A when B is requested, leaving A stuck In-Progress.
12
19
  (0, operators_1.mergeMap)((action) => {
13
- const { pageContext } = action.payload;
20
+ const { pageContext, noCardCreated } = action.payload;
14
21
  const query = new URLSearchParams({ page_context: pageContext });
22
+ if (noCardCreated === true) {
23
+ query.set('no_card_created', 'true');
24
+ }
15
25
  const url = `${zeniAPI.apiEndPoints.aiCfoMicroServiceBaseUrl}/1.0/suggested-questions?${query.toString()}`;
16
26
  return zeniAPI.getJSON(url).pipe((0, operators_1.mergeMap)((response) => {
17
27
  if ((0, responsePayload_1.isSuccessResponse)(response) && response.data != null) {
@@ -6,7 +6,7 @@ import { MonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
6
6
  import { AccountBase } from '../../../entity/account/accountState';
7
7
  import { JEScheduleTransactionKey, ScheduleTypes } from '../../../entity/jeSchedules/jeSchedulesTypes';
8
8
  import { VendorBase } from '../../../entity/vendor/vendorState';
9
- export declare const toJEScheduleSortKey: (v: string) => "vendor" | "type" | "category" | "class" | "memo" | "transactionDate" | "totalAmount" | "runningBalance" | "depCategory" | "scheduleCategory" | "startMonth" | "amortizationPeriod" | "jePostingDate" | "remainingMonths";
9
+ export declare const toJEScheduleSortKey: (v: string) => "vendor" | "type" | "class" | "category" | "memo" | "transactionDate" | "totalAmount" | "runningBalance" | "depCategory" | "scheduleCategory" | "startMonth" | "amortizationPeriod" | "jePostingDate" | "remainingMonths";
10
10
  export type JEScheduleSortKey = ReturnType<typeof toJEScheduleSortKey>;
11
11
  export declare const toJEScheduleMainTab: (v: string) => "completed" | "pending_review" | "ongoing";
12
12
  export type JEScheduleMainTab = ReturnType<typeof toJEScheduleMainTab>;
@@ -2,7 +2,7 @@ import { FetchStateAndError, FetchedState, ID } from '../../../commonStateTypes/
2
2
  import { SortOrder } from '../../../commonStateTypes/selectorTypes/sortOrderTypes';
3
3
  import { MonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
4
4
  import { CompletedSubTab } from './completedSubTab';
5
- export declare const toMissingReceiptsSortKey: (v: string) => "date" | "amount" | "vendor" | "category" | "class";
5
+ export declare const toMissingReceiptsSortKey: (v: string) => "date" | "amount" | "vendor" | "class" | "category";
6
6
  export type MissingReceiptsSortKey = ReturnType<typeof toMissingReceiptsSortKey>;
7
7
  export interface MissingReceiptsViewUIState {
8
8
  scrollPosition: {
@@ -34,7 +34,7 @@ export declare const toMissingReceiptsTab: (v: string) => "completed" | "unmatch
34
34
  export type MissingReceiptsTab = ReturnType<typeof toMissingReceiptsTab>;
35
35
  export declare const toMatchSource: (v: string) => "manual" | "ai";
36
36
  export type MatchSource = ReturnType<typeof toMatchSource>;
37
- export declare const toBulkUploadSortKey: (v: string) => "date" | "amount" | "vendor" | "category" | "class";
37
+ export declare const toBulkUploadSortKey: (v: string) => "date" | "amount" | "vendor" | "class" | "category";
38
38
  export type BulkUploadSortKey = ReturnType<typeof toBulkUploadSortKey>;
39
39
  export interface CandidateRef {
40
40
  matchScore: number;
@@ -18,7 +18,7 @@ import { ZeniDate } from '../../../zeniDayJS';
18
18
  import { EntityRecommendationKey } from '../../recommendation/recommendationState';
19
19
  import { TransactionDetailKey } from '../../transactionDetail/transactionDetailState';
20
20
  import { CompletedSubTab } from './completedSubTab';
21
- export declare const toTransactionsSortKey: (v: string) => "date" | "amount" | "vendor" | "project" | "category" | "class" | "payment_account" | "transaction_type" | "memo_and_receipt";
21
+ export declare const toTransactionsSortKey: (v: string) => "date" | "amount" | "vendor" | "project" | "class" | "category" | "payment_account" | "transaction_type" | "memo_and_receipt";
22
22
  export type TransactionsSortKey = ReturnType<typeof toTransactionsSortKey>;
23
23
  export declare const TRANSACTIONS_TABS: readonly ["review", "autoCategorized"];
24
24
  export declare const toTransactionsTabKey: (v: string) => "review" | "autoCategorized";
@@ -1,3 +1,3 @@
1
- declare const toPeopleSortKeyType: (v: string) => "name" | "role" | "manager" | "department" | "lastSeen";
1
+ declare const toPeopleSortKeyType: (v: string) => "department" | "name" | "role" | "manager" | "lastSeen";
2
2
  export type PeopleViewSortKey = ReturnType<typeof toPeopleSortKeyType>;
3
3
  export {};
@@ -7,7 +7,7 @@ export declare const getScheduleListKey: (selectedCategory: ID, selectedTimefram
7
7
  export type ScheduleListKey = ReturnType<typeof getScheduleListKey>;
8
8
  export declare const toScheduleSubTabType: (v: string) => "completed" | "new" | "ongoing" | "all";
9
9
  export type ScheduleSubTabType = ReturnType<typeof toScheduleSubTabType>;
10
- declare const toScheduleSortKeyType: (v: string) => "months" | "amount" | "vendor" | "status" | "user" | "createTime" | "class" | "memo" | "balanceAsOfToday" | "transactionDate" | "transactionType" | "assetId" | "scheduleCategory" | "startMonth" | "accruedExpenseCategory" | "expenseCategory" | "accumulatedDepreciationCategory" | "endMonth";
10
+ declare const toScheduleSortKeyType: (v: string) => "months" | "amount" | "vendor" | "status" | "class" | "user" | "createTime" | "memo" | "balanceAsOfToday" | "transactionDate" | "transactionType" | "assetId" | "scheduleCategory" | "startMonth" | "accruedExpenseCategory" | "expenseCategory" | "accumulatedDepreciationCategory" | "endMonth";
11
11
  export declare type ScheduleListSortKey = ReturnType<typeof toScheduleSortKeyType>;
12
12
  export interface DownloadJEScheduleTabOptions {
13
13
  categoryId: ID;
@@ -61,7 +61,7 @@ export declare const initialCreditAcc: CreditAccount;
61
61
  export declare const initialDebitCardSummaries: DebitCardSummaries;
62
62
  export declare const initialResendRevokeCardInvite: ResendRevokeCardInvite;
63
63
  export declare const initialCreditAccountRepayment: CreditAccountRepayment;
64
- export declare const toChargeCardSortKeyType: (v: string) => "status" | "cardName" | "owner" | "accountType" | "limit" | "department" | "cardType" | "utilisation";
64
+ export declare const toChargeCardSortKeyType: (v: string) => "status" | "department" | "cardName" | "owner" | "accountType" | "limit" | "cardType" | "utilisation";
65
65
  export declare type ChargeCardViewSortKey = ReturnType<typeof toChargeCardSortKeyType>;
66
66
  export declare const ALL_CASHBACK_SORT_KEYS: readonly ["month", "cashback"];
67
67
  export declare const toCashbackSortKeyType: (v: string) => "month" | "cashback";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeniai/client-epic-state",
3
- "version": "5.0.65-betaRR1",
3
+ "version": "5.0.65-betaRR3",
4
4
  "description": "Shared module between Web & Mobile containing required abstractions for state management, async network communication. ",
5
5
  "main": "lib/index.js",
6
6
  "module": "lib/esm/index.js",