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

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.
@@ -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' | 'card_policy' | 'card_policy_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_creation' | 'cards_detail' | 'card_policy' | 'card_policy_detail' | 'people' | 'rewards' | 'notifications';
@@ -49,7 +49,7 @@ interface CardPolicyTemplateEntityListPayload {
49
49
  merchant_names: string[];
50
50
  }
51
51
  interface CardPolicyTemplateSpendLimitsPayload {
52
- transaction: number;
52
+ transaction: number | null;
53
53
  }
54
54
  /**
55
55
  * Request body for `POST /cards/1.0/policy-templates`. Consumers pass the
@@ -63,7 +63,12 @@ export interface CreateCardPolicyTemplateRequest {
63
63
  description: string;
64
64
  mode: CardPolicyTemplateMode;
65
65
  name: string;
66
- requiredReceiptThreshold: number;
66
+ /**
67
+ * `null` indicates the require-receipt toggle is off (no threshold
68
+ * configured). The BE accepts `null` and treats it as "no requirement";
69
+ * `0` means the user explicitly entered zero with the toggle on.
70
+ */
71
+ requiredReceiptThreshold: number | null;
67
72
  spendLimits: CardPolicyTemplateSpendLimits;
68
73
  }
69
74
  export interface CreateCardPolicyTemplateRequestBody {
@@ -73,7 +78,7 @@ export interface CreateCardPolicyTemplateRequestBody {
73
78
  description: string;
74
79
  mode: string;
75
80
  name: string;
76
- required_receipt_threshold: number;
81
+ required_receipt_threshold: number | null;
77
82
  spend_limits: CardPolicyTemplateSpendLimitsPayload;
78
83
  }
79
84
  export interface CardPolicyTemplatePayload {
@@ -87,7 +92,7 @@ export interface CardPolicyTemplatePayload {
87
92
  description: string;
88
93
  mode: string;
89
94
  name: string;
90
- required_receipt_threshold: number;
95
+ required_receipt_threshold: number | null;
91
96
  spend_limits: CardPolicyTemplateSpendLimitsPayload;
92
97
  status: string;
93
98
  template_id: ID;
@@ -30,9 +30,14 @@ export interface CardPolicyTemplateEntityList {
30
30
  * Spend-limit caps configured per template. Only `transaction` is modeled
31
31
  * today; additional cadences (e.g. daily, monthly) will be added if/when
32
32
  * the backend exposes them.
33
+ *
34
+ * `transaction` is `number | null`: `null` means the row's toggle is off
35
+ * (no limit configured), `0` means the user explicitly entered zero with
36
+ * the toggle on, and any positive value is the configured per-transaction
37
+ * cap.
33
38
  */
34
39
  export interface CardPolicyTemplateSpendLimits {
35
- transaction: number;
40
+ transaction: number | null;
36
41
  }
37
42
  /**
38
43
  * Mapped (camelCase) representation of a card-policy template returned by
@@ -51,7 +56,12 @@ export interface CardPolicyTemplate {
51
56
  description: string;
52
57
  mode: CardPolicyTemplateMode;
53
58
  name: string;
54
- requiredReceiptThreshold: number;
59
+ /**
60
+ * Receipt-required threshold in dollars. `null` means the toggle is off
61
+ * (no receipt requirement); `0` means the user explicitly entered zero
62
+ * with the toggle on; any positive value is the configured threshold.
63
+ */
64
+ requiredReceiptThreshold: number | null;
55
65
  spendLimits: CardPolicyTemplateSpendLimits;
56
66
  status: CardPolicyTemplateStatus;
57
67
  templateId: ID;
@@ -1,4 +1,4 @@
1
- export declare const toSnackbarMessageSection: (v: string) => "common" | "transactionDetails_updatingPastTransactions" | "transactionDetails_transactionUpdated" | "cockpit_month_end_email_sent" | "cockpit_month_end_email_save" | "cockpit_month_end_email_attachment_upload" | "je_bill_link" | "je_posted" | "audit_score_updated" | "people_invite_people" | "people_invite_multiple_people" | "people_delete_person" | "people_update_person" | "reimbursement_updated" | "reimbursement_deleted" | "reimbursement_cancelled_deleted" | "reimbursement_cancelled" | "reimbursement_approved" | "reimbursement_rejected" | "reimbursement_sent_for_approval" | "account_added" | "business_verification_save" | "business_verification_submit" | "business_verification_from_bills_submit" | "plaid_connection" | "unlink_deposit_account" | "reimbursement_setup" | "reimbursement_approval_create" | "reimbursement_duplicate_approval_create" | "reimbursement_approval_update" | "reimbursement_duplicate_approval_update" | "reimbursement_approval_delete" | "reimbursement_create_mileage" | "reimbursement_update_mileage" | "reimbursement_accept_term" | "reimbursement_accept_employee_term" | "reimbursement_bulk_submit" | "reimbursement_bulk_processed" | "bill_pay_setup" | "zeni_accounts_setup" | "bill_pay_approval_create" | "bill_pay_duplicate_approval_create" | "bill_pay_approval_update" | "bill_pay_duplicate_approval_update" | "bill_pay_approval_delete" | "bill_pay_updated" | "bill_pay_deleted" | "bill_pay_cancelled_deleted" | "bill_pay_cancelled" | "bill_pay_approved" | "real_time_approver_added" | "bill_pay_rejected" | "bill_pay_sent_for_approval" | "bill_pay_accept_term" | "bill_pay_bulk_submit" | "bill_pay_bulk_processed" | "bill_pay_refund" | "bill_pay_retry" | "bill_pay_marked_as_paid" | "zeni_account_accept_term" | "update_vendor" | "data_refresh_update" | "delete_bank_account" | "create_bank_account" | "create_bank_account_ach" | "create_bank_account_wire" | "create_bank_account_international" | "transfer_money" | "update_zeni_account_nickname" | "create_checking_account" | "deposit_check" | "create_vendor" | "onboarding_customer_view" | "onboarding_customer_view_complete" | "onboarding_customer_identity_verification_save" | "onboarding_customer_identity_verification_submit" | "onboarding_customer_business_verification_save" | "onboarding_customer_business_verification_submit" | "retry_bank_account_connection" | "dashboard_invite_sent" | "onboarding_info_saved" | "approve_original_merchant" | "approve_global_merchant" | "reject_global_merchant" | "create_global_merchant" | "fetch_global_merchant_no_recommendation" | "save_vendor_renamed" | "save_vendor_sent_for_review" | "save_vendor_marked_as_employee" | "save_vendor_marked_as_local_contractor" | "charge_card_setup" | "charge_card_accept_term" | "charge_card_receipt_upload" | "charge_card_resend_invite" | "charge_card_revoke_invite" | "charge_card_update_limit" | "charge_cards_update_limit" | "close_charge_card" | "lock_charge_card" | "lock_charge_card_card_user" | "lock_charge_cards" | "unlock_charge_card" | "unlock_charge_card_card_user" | "unlock_charge_cards" | "close_charge_cards" | "revoke_invite_charge_cards" | "charge_card_express_interest" | "create_schedule" | "save_schedule" | "delete_schedule" | "ignore_schedule" | "save_task_detail" | "fetch_task_detail" | "delete_task" | "archive_task" | "snooze_task" | "unsnooze_task" | "create_tag" | "delete_tag" | "update_charge_card" | "update_charge_card_name" | "update_charge_card_name_card_user" | "issue_charge_card" | "issue_charge_cards" | "notification_settings_saved" | "referral_invite_sent" | "notification_mark_as_read" | "mark_as_complete_schedule" | "cancel_journal_entry" | "settings_accounting_accounts_updated" | "create_card_setup" | "confirm_card_setup" | "add_card_payment_source" | "fetch_payment_sources" | "task_assigned_toast_notification" | "task_due_tomorrow_toast_notification" | "task_deleted_toast_notification" | "task_archived_toast_notification" | "task_overdue_toast_notification" | "task_overdue_toast_notification_creator" | "task_notification_count" | "task_activities_toast_notification" | "task_created_toast_notification" | "task_group_creation_success" | "task_group_deletion_success" | "task_group_update_success" | "primary_funding_account_updated" | "task_time_spent_validation" | "missing_receipts_attachment" | "flux_analysis_unreviewed" | "flux_analysis_reviewed" | "receipt_match" | "receipts_upload" | "receipts_bulk_match" | "billing_address_view" | "express_pay_submit" | "exclude_transaction" | "reconcile" | "save_reconcile_for_later" | "recon_transaction_categorize" | "recon_transaction_match" | "update_debit_card_pin_attempt" | "set_debit_card_pin" | "ai_cfo_create_session_and_submit" | "ai_cfo_chat_session_deleted" | "charge_card_auto_pay_enable" | "charge_card_auto_pay_disable" | "treasury_setup" | "treasury_accept_term" | "treasury_transfer_money" | "treasury_transfer_money_failed" | "auto_transfer_rule_create" | "auto_transfer_rule_update" | "auto_transfer_rule_delete" | "auto_transfer_rule_pause" | "auto_transfer_rule_resume" | "treasury_update_portfolio_allocation" | "send_email_magic_link_to_user" | "complete_profile_done" | "reports_resync" | "invalid_phone_number" | "transactions_categorized_updated_failed" | "transaction_categorized_updated_failed" | "transactionsCategorized_transactionsUpdated" | "transactionsCategorized_transactionUpdated" | "transactionCategorized_transactionsUpdated" | "transactionCategorized_transactionUpdated" | "transactionsCategorized_transactionsFailed" | "transactionsCategorized_transactionFailed" | "transactionCategorized_transactionsFailed" | "transactionCategorized_transactionFailed" | "transactionsUpdated_transactionsFailed" | "transactionsUpdated_transactionFailed" | "transactionUpdated_transactionsFailed" | "transactionUpdated_transactionFailed" | "transactionsCategorized_transactionsUpdated_transactionsFailed" | "transactionsCategorized_transactionsUpdated_transactionFailed" | "transactionsCategorized_transactionUpdated_transactionsFailed" | "transactionCategorized_transactionsUpdated_transactionsFailed" | "transactionsCategorized_transactionUpdated_transactionFailed" | "transactionCategorized_transactionsUpdated_transactionFailed" | "transactionCategorized_transactionUpdated_transactionsFailed" | "transactionCategorized_transactionUpdated_transactionFailed" | "accounting_classes_enabled_update" | "account_excluded_from_reconciliation" | "account_included_in_reconciliation" | "ai_accountant_trigger_job" | "oauth_consent_approve" | "oauth_consent_invalid_request" | "credit_agent_update_profile" | "credit_agent_save_macro" | "credit_agent_schedule_cron";
1
+ export declare const toSnackbarMessageSection: (v: string) => "common" | "transactionDetails_updatingPastTransactions" | "transactionDetails_transactionUpdated" | "cockpit_month_end_email_sent" | "cockpit_month_end_email_save" | "cockpit_month_end_email_attachment_upload" | "je_bill_link" | "je_posted" | "audit_score_updated" | "people_invite_people" | "people_invite_multiple_people" | "people_delete_person" | "people_update_person" | "reimbursement_updated" | "reimbursement_deleted" | "reimbursement_cancelled_deleted" | "reimbursement_cancelled" | "reimbursement_approved" | "reimbursement_rejected" | "reimbursement_sent_for_approval" | "account_added" | "business_verification_save" | "business_verification_submit" | "business_verification_from_bills_submit" | "plaid_connection" | "unlink_deposit_account" | "reimbursement_setup" | "reimbursement_approval_create" | "reimbursement_duplicate_approval_create" | "reimbursement_approval_update" | "reimbursement_duplicate_approval_update" | "reimbursement_approval_delete" | "reimbursement_create_mileage" | "reimbursement_update_mileage" | "reimbursement_accept_term" | "reimbursement_accept_employee_term" | "reimbursement_bulk_submit" | "reimbursement_bulk_processed" | "bill_pay_setup" | "zeni_accounts_setup" | "bill_pay_approval_create" | "bill_pay_duplicate_approval_create" | "bill_pay_approval_update" | "bill_pay_duplicate_approval_update" | "bill_pay_approval_delete" | "bill_pay_updated" | "bill_pay_deleted" | "bill_pay_cancelled_deleted" | "bill_pay_cancelled" | "bill_pay_approved" | "real_time_approver_added" | "bill_pay_rejected" | "bill_pay_sent_for_approval" | "bill_pay_accept_term" | "bill_pay_bulk_submit" | "bill_pay_bulk_processed" | "bill_pay_refund" | "bill_pay_retry" | "bill_pay_marked_as_paid" | "zeni_account_accept_term" | "update_vendor" | "data_refresh_update" | "delete_bank_account" | "create_bank_account" | "create_bank_account_ach" | "create_bank_account_wire" | "create_bank_account_international" | "transfer_money" | "update_zeni_account_nickname" | "create_checking_account" | "deposit_check" | "create_vendor" | "onboarding_customer_view" | "onboarding_customer_view_complete" | "onboarding_customer_identity_verification_save" | "onboarding_customer_identity_verification_submit" | "onboarding_customer_business_verification_save" | "onboarding_customer_business_verification_submit" | "retry_bank_account_connection" | "dashboard_invite_sent" | "onboarding_info_saved" | "approve_original_merchant" | "approve_global_merchant" | "reject_global_merchant" | "create_global_merchant" | "fetch_global_merchant_no_recommendation" | "save_vendor_renamed" | "save_vendor_sent_for_review" | "save_vendor_marked_as_employee" | "save_vendor_marked_as_local_contractor" | "charge_card_setup" | "charge_card_accept_term" | "charge_card_receipt_upload" | "charge_card_resend_invite" | "charge_card_revoke_invite" | "card_policy_created" | "card_policy_updated" | "charge_card_update_limit" | "charge_cards_update_limit" | "close_charge_card" | "lock_charge_card" | "lock_charge_card_card_user" | "lock_charge_cards" | "unlock_charge_card" | "unlock_charge_card_card_user" | "unlock_charge_cards" | "close_charge_cards" | "revoke_invite_charge_cards" | "charge_card_express_interest" | "create_schedule" | "save_schedule" | "delete_schedule" | "ignore_schedule" | "save_task_detail" | "fetch_task_detail" | "delete_task" | "archive_task" | "snooze_task" | "unsnooze_task" | "create_tag" | "delete_tag" | "update_charge_card" | "update_charge_card_name" | "update_charge_card_name_card_user" | "issue_charge_card" | "issue_charge_cards" | "notification_settings_saved" | "referral_invite_sent" | "notification_mark_as_read" | "mark_as_complete_schedule" | "cancel_journal_entry" | "settings_accounting_accounts_updated" | "create_card_setup" | "confirm_card_setup" | "add_card_payment_source" | "fetch_payment_sources" | "task_assigned_toast_notification" | "task_due_tomorrow_toast_notification" | "task_deleted_toast_notification" | "task_archived_toast_notification" | "task_overdue_toast_notification" | "task_overdue_toast_notification_creator" | "task_notification_count" | "task_activities_toast_notification" | "task_created_toast_notification" | "task_group_creation_success" | "task_group_deletion_success" | "task_group_update_success" | "primary_funding_account_updated" | "task_time_spent_validation" | "missing_receipts_attachment" | "flux_analysis_unreviewed" | "flux_analysis_reviewed" | "receipt_match" | "receipts_upload" | "receipts_bulk_match" | "billing_address_view" | "express_pay_submit" | "exclude_transaction" | "reconcile" | "save_reconcile_for_later" | "recon_transaction_categorize" | "recon_transaction_match" | "update_debit_card_pin_attempt" | "set_debit_card_pin" | "ai_cfo_create_session_and_submit" | "ai_cfo_chat_session_deleted" | "charge_card_auto_pay_enable" | "charge_card_auto_pay_disable" | "treasury_setup" | "treasury_accept_term" | "treasury_transfer_money" | "treasury_transfer_money_failed" | "auto_transfer_rule_create" | "auto_transfer_rule_update" | "auto_transfer_rule_delete" | "auto_transfer_rule_pause" | "auto_transfer_rule_resume" | "treasury_update_portfolio_allocation" | "send_email_magic_link_to_user" | "complete_profile_done" | "reports_resync" | "invalid_phone_number" | "transactions_categorized_updated_failed" | "transaction_categorized_updated_failed" | "transactionsCategorized_transactionsUpdated" | "transactionsCategorized_transactionUpdated" | "transactionCategorized_transactionsUpdated" | "transactionCategorized_transactionUpdated" | "transactionsCategorized_transactionsFailed" | "transactionsCategorized_transactionFailed" | "transactionCategorized_transactionsFailed" | "transactionCategorized_transactionFailed" | "transactionsUpdated_transactionsFailed" | "transactionsUpdated_transactionFailed" | "transactionUpdated_transactionsFailed" | "transactionUpdated_transactionFailed" | "transactionsCategorized_transactionsUpdated_transactionsFailed" | "transactionsCategorized_transactionsUpdated_transactionFailed" | "transactionsCategorized_transactionUpdated_transactionsFailed" | "transactionCategorized_transactionsUpdated_transactionsFailed" | "transactionsCategorized_transactionUpdated_transactionFailed" | "transactionCategorized_transactionsUpdated_transactionFailed" | "transactionCategorized_transactionUpdated_transactionsFailed" | "transactionCategorized_transactionUpdated_transactionFailed" | "accounting_classes_enabled_update" | "account_excluded_from_reconciliation" | "account_included_in_reconciliation" | "ai_accountant_trigger_job" | "oauth_consent_approve" | "oauth_consent_invalid_request" | "credit_agent_update_profile" | "credit_agent_save_macro" | "credit_agent_schedule_cron";
2
2
  export type SnackbarMessageSections = ReturnType<typeof toSnackbarMessageSection>;
3
3
  export declare const toSnackbarMessageSectionTexts: (v: string) => "notification" | "failed" | "success";
4
4
  export type SnackbarMessageSectionTexts = ReturnType<typeof toSnackbarMessageSectionTexts>;
@@ -98,6 +98,8 @@ const ALL_SNACKBAR_MESSAGE_SECTIONS = [
98
98
  'charge_card_receipt_upload',
99
99
  'charge_card_resend_invite',
100
100
  'charge_card_revoke_invite',
101
+ 'card_policy_created',
102
+ 'card_policy_updated',
101
103
  'charge_card_update_limit',
102
104
  'charge_cards_update_limit',
103
105
  'close_charge_card',
@@ -95,6 +95,8 @@ const ALL_SNACKBAR_MESSAGE_SECTIONS = [
95
95
  'charge_card_receipt_upload',
96
96
  'charge_card_resend_invite',
97
97
  'charge_card_revoke_invite',
98
+ 'card_policy_created',
99
+ 'card_policy_updated',
98
100
  'charge_card_update_limit',
99
101
  'charge_cards_update_limit',
100
102
  'close_charge_card',
@@ -1,8 +1,8 @@
1
1
  import { from, of } from 'rxjs';
2
- import { catchError, filter, mergeMap } from 'rxjs/operators';
2
+ import { catchError, exhaustMap, filter, mergeMap } from 'rxjs/operators';
3
3
  import { toCardPolicyEditFormDraft, toCardPolicyTemplate, } from '../../../../../entity/cardPolicy/cardPolicyPayload';
4
4
  import { updateCreatedCardPolicyTemplate } from '../../../../../entity/cardPolicy/cardPolicyReducer';
5
- import { createZeniAPIStatus, isSuccessStatus } from '../../../../../responsePayload';
5
+ import { createZeniAPIStatus, isSuccessStatus, } from '../../../../../responsePayload';
6
6
  import { fetchCardPolicyDetail, updateCardPolicyDetailFetchStatus, updateCardPolicyFormDraft, } from './cardPolicyDetailReducer';
7
7
  /**
8
8
  * `GET /cards/1.0/policy-templates/{id}`. Response is the same
@@ -15,7 +15,19 @@ import { fetchCardPolicyDetail, updateCardPolicyDetailFetchStatus, updateCardPol
15
15
  * the page can render with the existing values pre-populated.
16
16
  * 3. Flip the view lifecycle to `Completed` / `Error`.
17
17
  */
18
- export const fetchCardPolicyDetailEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchCardPolicyDetail.match), mergeMap((action) => {
18
+ export const fetchCardPolicyDetailEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchCardPolicyDetail.match),
19
+ // `exhaustMap` (rather than `mergeMap`) so a duplicate
20
+ // `fetchCardPolicyDetail` dispatched while the previous one is
21
+ // still in flight is dropped instead of producing a second HTTP
22
+ // request. The edit screen dispatches this on mount, and React 18
23
+ // StrictMode invokes its mount effect twice with a stale
24
+ // `detailFetchState` closure — without `exhaustMap` that yields
25
+ // two `/policy-templates/{id}` calls. (The screen also dedupes
26
+ // list→edit navigations via the entity-cached template, so the
27
+ // detail GET is only emitted on direct landings — there's no
28
+ // realistic cross-templateId scenario that needs cancel-and-retry
29
+ // semantics here.)
30
+ exhaustMap((action) => {
19
31
  const templateId = action.payload;
20
32
  return zeniAPI
21
33
  .getJSON(`${zeniAPI.apiEndPoints.cardMicroServiceBaseUrl}/1.0/policy-templates/${templateId}`)
@@ -1,8 +1,8 @@
1
1
  import { from, of } from 'rxjs';
2
- import { catchError, filter, mergeMap } from 'rxjs/operators';
2
+ import { catchError, exhaustMap, filter, mergeMap } from 'rxjs/operators';
3
3
  import { toCardPolicyStats, toCardPolicyTemplateList, } from '../../../../../entity/cardPolicy/cardPolicyPayload';
4
4
  import { updateCardPolicyStats, updateCardPolicyTemplates, } from '../../../../../entity/cardPolicy/cardPolicyReducer';
5
- import { createZeniAPIStatus, isSuccessStatus } from '../../../../../responsePayload';
5
+ import { createZeniAPIStatus, isSuccessStatus, } from '../../../../../responsePayload';
6
6
  import { fetchCardPolicyList, updateCardPolicyListFetchStatus, } from './cardPolicyListReducer';
7
7
  /**
8
8
  * `GET /cards/1.0/policy-templates`. On success the response carries
@@ -15,7 +15,17 @@ import { fetchCardPolicyList, updateCardPolicyListFetchStatus, } from './cardPol
15
15
  * 2. `updateCardPolicyStats` — entity-level cached stats.
16
16
  * 3. `updateCardPolicyListFetchStatus` — view lifecycle + ordered IDs.
17
17
  */
18
- export const fetchCardPolicyListEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchCardPolicyList.match), mergeMap(() => {
18
+ export const fetchCardPolicyListEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchCardPolicyList.match),
19
+ // `exhaustMap` (rather than `mergeMap`) so a duplicate
20
+ // `fetchCardPolicyList` dispatched while the previous one is still
21
+ // in flight is dropped instead of producing a second HTTP request.
22
+ // This guards against React 18 StrictMode, which mount-cleanup-
23
+ // remounts the screen's mount effect with a stale closure of
24
+ // `listFetchState`, causing the same effect to dispatch this
25
+ // action twice synchronously. Retry-after-error still works
26
+ // because the previous inner observable has completed by the time
27
+ // the user retries.
28
+ exhaustMap(() => {
19
29
  return zeniAPI
20
30
  .getJSON(`${zeniAPI.apiEndPoints.cardMicroServiceBaseUrl}/1.0/policy-templates`)
21
31
  .pipe(mergeMap((response) => {
@@ -1,8 +1,16 @@
1
1
  import { from, of } from 'rxjs';
2
- import { catchError, filter, mergeMap } from 'rxjs/operators';
2
+ import { catchError, exhaustMap, filter, mergeMap } from 'rxjs/operators';
3
3
  import { fetchCardPolicyMccCategories, updateCardPolicyMccCategories, updateCardPolicyMccCategoriesFailure, } from '../../../../../entity/cardPolicy/cardPolicyReducer';
4
- import { createZeniAPIStatus, isSuccessStatus } from '../../../../../responsePayload';
5
- export const fetchCardPolicyMccCategoriesEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchCardPolicyMccCategories.match), mergeMap(() => {
4
+ import { createZeniAPIStatus, isSuccessStatus, } from '../../../../../responsePayload';
5
+ export const fetchCardPolicyMccCategoriesEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchCardPolicyMccCategories.match),
6
+ // `exhaustMap` (rather than `mergeMap`) so a duplicate
7
+ // `fetchCardPolicyMccCategories` dispatched while the previous one
8
+ // is still in flight is dropped instead of producing a second HTTP
9
+ // request. Both the Create and Edit policy screens dispatch this
10
+ // on mount, and React 18 StrictMode invokes their mount effect
11
+ // twice with a stale `mccCategoriesFetchState` closure — without
12
+ // `exhaustMap` that yields two `/cards/mcc-codes` calls.
13
+ exhaustMap(() => {
6
14
  return zeniAPI
7
15
  .getJSON(`${zeniAPI.apiEndPoints.cardMicroServiceBaseUrl}/1.0/cards/mcc-codes`)
8
16
  .pipe(mergeMap((response) => {
@@ -38,7 +38,7 @@ export declare const getLineItemsByTransactionsIdsFromResponse: (transactions: S
38
38
  export declare const getLineItemsByTransactionIdsFromLocalData: (transactionLocalData: TransactionReviewLocalDataSelectorView[], selectedTab: TransactionsTab) => Record<ID, string[]>;
39
39
  export declare const mergeTabSpecificLineItems: (currentTabSpecificLineItems: TabSpecificLineItems, selectedTab: TransactionsTab, newLineItems: TabSpecificLineItems) => TabSpecificLineItems;
40
40
  export declare const getSnackbarMessageForTransactionReview: (updatedCount: number, failedCount: number, categorizedCount: number) => {
41
- messageSection: "common" | "transactionDetails_updatingPastTransactions" | "transactionDetails_transactionUpdated" | "cockpit_month_end_email_sent" | "cockpit_month_end_email_save" | "cockpit_month_end_email_attachment_upload" | "je_bill_link" | "je_posted" | "audit_score_updated" | "people_invite_people" | "people_invite_multiple_people" | "people_delete_person" | "people_update_person" | "reimbursement_updated" | "reimbursement_deleted" | "reimbursement_cancelled_deleted" | "reimbursement_cancelled" | "reimbursement_approved" | "reimbursement_rejected" | "reimbursement_sent_for_approval" | "account_added" | "business_verification_save" | "business_verification_submit" | "business_verification_from_bills_submit" | "plaid_connection" | "unlink_deposit_account" | "reimbursement_setup" | "reimbursement_approval_create" | "reimbursement_duplicate_approval_create" | "reimbursement_approval_update" | "reimbursement_duplicate_approval_update" | "reimbursement_approval_delete" | "reimbursement_create_mileage" | "reimbursement_update_mileage" | "reimbursement_accept_term" | "reimbursement_accept_employee_term" | "reimbursement_bulk_submit" | "reimbursement_bulk_processed" | "bill_pay_setup" | "zeni_accounts_setup" | "bill_pay_approval_create" | "bill_pay_duplicate_approval_create" | "bill_pay_approval_update" | "bill_pay_duplicate_approval_update" | "bill_pay_approval_delete" | "bill_pay_updated" | "bill_pay_deleted" | "bill_pay_cancelled_deleted" | "bill_pay_cancelled" | "bill_pay_approved" | "real_time_approver_added" | "bill_pay_rejected" | "bill_pay_sent_for_approval" | "bill_pay_accept_term" | "bill_pay_bulk_submit" | "bill_pay_bulk_processed" | "bill_pay_refund" | "bill_pay_retry" | "bill_pay_marked_as_paid" | "zeni_account_accept_term" | "update_vendor" | "data_refresh_update" | "delete_bank_account" | "create_bank_account" | "create_bank_account_ach" | "create_bank_account_wire" | "create_bank_account_international" | "transfer_money" | "update_zeni_account_nickname" | "create_checking_account" | "deposit_check" | "create_vendor" | "onboarding_customer_view" | "onboarding_customer_view_complete" | "onboarding_customer_identity_verification_save" | "onboarding_customer_identity_verification_submit" | "onboarding_customer_business_verification_save" | "onboarding_customer_business_verification_submit" | "retry_bank_account_connection" | "dashboard_invite_sent" | "onboarding_info_saved" | "approve_original_merchant" | "approve_global_merchant" | "reject_global_merchant" | "create_global_merchant" | "fetch_global_merchant_no_recommendation" | "save_vendor_renamed" | "save_vendor_sent_for_review" | "save_vendor_marked_as_employee" | "save_vendor_marked_as_local_contractor" | "charge_card_setup" | "charge_card_accept_term" | "charge_card_receipt_upload" | "charge_card_resend_invite" | "charge_card_revoke_invite" | "charge_card_update_limit" | "charge_cards_update_limit" | "close_charge_card" | "lock_charge_card" | "lock_charge_card_card_user" | "lock_charge_cards" | "unlock_charge_card" | "unlock_charge_card_card_user" | "unlock_charge_cards" | "close_charge_cards" | "revoke_invite_charge_cards" | "charge_card_express_interest" | "create_schedule" | "save_schedule" | "delete_schedule" | "ignore_schedule" | "save_task_detail" | "fetch_task_detail" | "delete_task" | "archive_task" | "snooze_task" | "unsnooze_task" | "create_tag" | "delete_tag" | "update_charge_card" | "update_charge_card_name" | "update_charge_card_name_card_user" | "issue_charge_card" | "issue_charge_cards" | "notification_settings_saved" | "referral_invite_sent" | "notification_mark_as_read" | "mark_as_complete_schedule" | "cancel_journal_entry" | "settings_accounting_accounts_updated" | "create_card_setup" | "confirm_card_setup" | "add_card_payment_source" | "fetch_payment_sources" | "task_assigned_toast_notification" | "task_due_tomorrow_toast_notification" | "task_deleted_toast_notification" | "task_archived_toast_notification" | "task_overdue_toast_notification" | "task_overdue_toast_notification_creator" | "task_notification_count" | "task_activities_toast_notification" | "task_created_toast_notification" | "task_group_creation_success" | "task_group_deletion_success" | "task_group_update_success" | "primary_funding_account_updated" | "task_time_spent_validation" | "missing_receipts_attachment" | "flux_analysis_unreviewed" | "flux_analysis_reviewed" | "receipt_match" | "receipts_upload" | "receipts_bulk_match" | "billing_address_view" | "express_pay_submit" | "exclude_transaction" | "reconcile" | "save_reconcile_for_later" | "recon_transaction_categorize" | "recon_transaction_match" | "update_debit_card_pin_attempt" | "set_debit_card_pin" | "ai_cfo_create_session_and_submit" | "ai_cfo_chat_session_deleted" | "charge_card_auto_pay_enable" | "charge_card_auto_pay_disable" | "treasury_setup" | "treasury_accept_term" | "treasury_transfer_money" | "treasury_transfer_money_failed" | "auto_transfer_rule_create" | "auto_transfer_rule_update" | "auto_transfer_rule_delete" | "auto_transfer_rule_pause" | "auto_transfer_rule_resume" | "treasury_update_portfolio_allocation" | "send_email_magic_link_to_user" | "complete_profile_done" | "reports_resync" | "invalid_phone_number" | "transactions_categorized_updated_failed" | "transaction_categorized_updated_failed" | "transactionsCategorized_transactionsUpdated" | "transactionsCategorized_transactionUpdated" | "transactionCategorized_transactionsUpdated" | "transactionCategorized_transactionUpdated" | "transactionsCategorized_transactionsFailed" | "transactionsCategorized_transactionFailed" | "transactionCategorized_transactionsFailed" | "transactionCategorized_transactionFailed" | "transactionsUpdated_transactionsFailed" | "transactionsUpdated_transactionFailed" | "transactionUpdated_transactionsFailed" | "transactionUpdated_transactionFailed" | "transactionsCategorized_transactionsUpdated_transactionsFailed" | "transactionsCategorized_transactionsUpdated_transactionFailed" | "transactionsCategorized_transactionUpdated_transactionsFailed" | "transactionCategorized_transactionsUpdated_transactionsFailed" | "transactionsCategorized_transactionUpdated_transactionFailed" | "transactionCategorized_transactionsUpdated_transactionFailed" | "transactionCategorized_transactionUpdated_transactionsFailed" | "transactionCategorized_transactionUpdated_transactionFailed" | "accounting_classes_enabled_update" | "account_excluded_from_reconciliation" | "account_included_in_reconciliation" | "ai_accountant_trigger_job" | "oauth_consent_approve" | "oauth_consent_invalid_request" | "credit_agent_update_profile" | "credit_agent_save_macro" | "credit_agent_schedule_cron";
41
+ messageSection: "common" | "transactionDetails_updatingPastTransactions" | "transactionDetails_transactionUpdated" | "cockpit_month_end_email_sent" | "cockpit_month_end_email_save" | "cockpit_month_end_email_attachment_upload" | "je_bill_link" | "je_posted" | "audit_score_updated" | "people_invite_people" | "people_invite_multiple_people" | "people_delete_person" | "people_update_person" | "reimbursement_updated" | "reimbursement_deleted" | "reimbursement_cancelled_deleted" | "reimbursement_cancelled" | "reimbursement_approved" | "reimbursement_rejected" | "reimbursement_sent_for_approval" | "account_added" | "business_verification_save" | "business_verification_submit" | "business_verification_from_bills_submit" | "plaid_connection" | "unlink_deposit_account" | "reimbursement_setup" | "reimbursement_approval_create" | "reimbursement_duplicate_approval_create" | "reimbursement_approval_update" | "reimbursement_duplicate_approval_update" | "reimbursement_approval_delete" | "reimbursement_create_mileage" | "reimbursement_update_mileage" | "reimbursement_accept_term" | "reimbursement_accept_employee_term" | "reimbursement_bulk_submit" | "reimbursement_bulk_processed" | "bill_pay_setup" | "zeni_accounts_setup" | "bill_pay_approval_create" | "bill_pay_duplicate_approval_create" | "bill_pay_approval_update" | "bill_pay_duplicate_approval_update" | "bill_pay_approval_delete" | "bill_pay_updated" | "bill_pay_deleted" | "bill_pay_cancelled_deleted" | "bill_pay_cancelled" | "bill_pay_approved" | "real_time_approver_added" | "bill_pay_rejected" | "bill_pay_sent_for_approval" | "bill_pay_accept_term" | "bill_pay_bulk_submit" | "bill_pay_bulk_processed" | "bill_pay_refund" | "bill_pay_retry" | "bill_pay_marked_as_paid" | "zeni_account_accept_term" | "update_vendor" | "data_refresh_update" | "delete_bank_account" | "create_bank_account" | "create_bank_account_ach" | "create_bank_account_wire" | "create_bank_account_international" | "transfer_money" | "update_zeni_account_nickname" | "create_checking_account" | "deposit_check" | "create_vendor" | "onboarding_customer_view" | "onboarding_customer_view_complete" | "onboarding_customer_identity_verification_save" | "onboarding_customer_identity_verification_submit" | "onboarding_customer_business_verification_save" | "onboarding_customer_business_verification_submit" | "retry_bank_account_connection" | "dashboard_invite_sent" | "onboarding_info_saved" | "approve_original_merchant" | "approve_global_merchant" | "reject_global_merchant" | "create_global_merchant" | "fetch_global_merchant_no_recommendation" | "save_vendor_renamed" | "save_vendor_sent_for_review" | "save_vendor_marked_as_employee" | "save_vendor_marked_as_local_contractor" | "charge_card_setup" | "charge_card_accept_term" | "charge_card_receipt_upload" | "charge_card_resend_invite" | "charge_card_revoke_invite" | "card_policy_created" | "card_policy_updated" | "charge_card_update_limit" | "charge_cards_update_limit" | "close_charge_card" | "lock_charge_card" | "lock_charge_card_card_user" | "lock_charge_cards" | "unlock_charge_card" | "unlock_charge_card_card_user" | "unlock_charge_cards" | "close_charge_cards" | "revoke_invite_charge_cards" | "charge_card_express_interest" | "create_schedule" | "save_schedule" | "delete_schedule" | "ignore_schedule" | "save_task_detail" | "fetch_task_detail" | "delete_task" | "archive_task" | "snooze_task" | "unsnooze_task" | "create_tag" | "delete_tag" | "update_charge_card" | "update_charge_card_name" | "update_charge_card_name_card_user" | "issue_charge_card" | "issue_charge_cards" | "notification_settings_saved" | "referral_invite_sent" | "notification_mark_as_read" | "mark_as_complete_schedule" | "cancel_journal_entry" | "settings_accounting_accounts_updated" | "create_card_setup" | "confirm_card_setup" | "add_card_payment_source" | "fetch_payment_sources" | "task_assigned_toast_notification" | "task_due_tomorrow_toast_notification" | "task_deleted_toast_notification" | "task_archived_toast_notification" | "task_overdue_toast_notification" | "task_overdue_toast_notification_creator" | "task_notification_count" | "task_activities_toast_notification" | "task_created_toast_notification" | "task_group_creation_success" | "task_group_deletion_success" | "task_group_update_success" | "primary_funding_account_updated" | "task_time_spent_validation" | "missing_receipts_attachment" | "flux_analysis_unreviewed" | "flux_analysis_reviewed" | "receipt_match" | "receipts_upload" | "receipts_bulk_match" | "billing_address_view" | "express_pay_submit" | "exclude_transaction" | "reconcile" | "save_reconcile_for_later" | "recon_transaction_categorize" | "recon_transaction_match" | "update_debit_card_pin_attempt" | "set_debit_card_pin" | "ai_cfo_create_session_and_submit" | "ai_cfo_chat_session_deleted" | "charge_card_auto_pay_enable" | "charge_card_auto_pay_disable" | "treasury_setup" | "treasury_accept_term" | "treasury_transfer_money" | "treasury_transfer_money_failed" | "auto_transfer_rule_create" | "auto_transfer_rule_update" | "auto_transfer_rule_delete" | "auto_transfer_rule_pause" | "auto_transfer_rule_resume" | "treasury_update_portfolio_allocation" | "send_email_magic_link_to_user" | "complete_profile_done" | "reports_resync" | "invalid_phone_number" | "transactions_categorized_updated_failed" | "transaction_categorized_updated_failed" | "transactionsCategorized_transactionsUpdated" | "transactionsCategorized_transactionUpdated" | "transactionCategorized_transactionsUpdated" | "transactionCategorized_transactionUpdated" | "transactionsCategorized_transactionsFailed" | "transactionsCategorized_transactionFailed" | "transactionCategorized_transactionsFailed" | "transactionCategorized_transactionFailed" | "transactionsUpdated_transactionsFailed" | "transactionsUpdated_transactionFailed" | "transactionUpdated_transactionsFailed" | "transactionUpdated_transactionFailed" | "transactionsCategorized_transactionsUpdated_transactionsFailed" | "transactionsCategorized_transactionsUpdated_transactionFailed" | "transactionsCategorized_transactionUpdated_transactionsFailed" | "transactionCategorized_transactionsUpdated_transactionsFailed" | "transactionsCategorized_transactionUpdated_transactionFailed" | "transactionCategorized_transactionsUpdated_transactionFailed" | "transactionCategorized_transactionUpdated_transactionsFailed" | "transactionCategorized_transactionUpdated_transactionFailed" | "accounting_classes_enabled_update" | "account_excluded_from_reconciliation" | "account_included_in_reconciliation" | "ai_accountant_trigger_job" | "oauth_consent_approve" | "oauth_consent_invalid_request" | "credit_agent_update_profile" | "credit_agent_save_macro" | "credit_agent_schedule_cron";
42
42
  messageText: "notification" | "failed" | "success";
43
43
  type: "error" | "success" | "info";
44
44
  variables: {
@@ -18,7 +18,19 @@ const cardPolicyDetailReducer_1 = require("./cardPolicyDetailReducer");
18
18
  * the page can render with the existing values pre-populated.
19
19
  * 3. Flip the view lifecycle to `Completed` / `Error`.
20
20
  */
21
- const fetchCardPolicyDetailEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(cardPolicyDetailReducer_1.fetchCardPolicyDetail.match), (0, operators_1.mergeMap)((action) => {
21
+ const fetchCardPolicyDetailEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(cardPolicyDetailReducer_1.fetchCardPolicyDetail.match),
22
+ // `exhaustMap` (rather than `mergeMap`) so a duplicate
23
+ // `fetchCardPolicyDetail` dispatched while the previous one is
24
+ // still in flight is dropped instead of producing a second HTTP
25
+ // request. The edit screen dispatches this on mount, and React 18
26
+ // StrictMode invokes its mount effect twice with a stale
27
+ // `detailFetchState` closure — without `exhaustMap` that yields
28
+ // two `/policy-templates/{id}` calls. (The screen also dedupes
29
+ // list→edit navigations via the entity-cached template, so the
30
+ // detail GET is only emitted on direct landings — there's no
31
+ // realistic cross-templateId scenario that needs cancel-and-retry
32
+ // semantics here.)
33
+ (0, operators_1.exhaustMap)((action) => {
22
34
  const templateId = action.payload;
23
35
  return zeniAPI
24
36
  .getJSON(`${zeniAPI.apiEndPoints.cardMicroServiceBaseUrl}/1.0/policy-templates/${templateId}`)
@@ -18,7 +18,17 @@ const cardPolicyListReducer_1 = require("./cardPolicyListReducer");
18
18
  * 2. `updateCardPolicyStats` — entity-level cached stats.
19
19
  * 3. `updateCardPolicyListFetchStatus` — view lifecycle + ordered IDs.
20
20
  */
21
- const fetchCardPolicyListEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(cardPolicyListReducer_1.fetchCardPolicyList.match), (0, operators_1.mergeMap)(() => {
21
+ const fetchCardPolicyListEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(cardPolicyListReducer_1.fetchCardPolicyList.match),
22
+ // `exhaustMap` (rather than `mergeMap`) so a duplicate
23
+ // `fetchCardPolicyList` dispatched while the previous one is still
24
+ // in flight is dropped instead of producing a second HTTP request.
25
+ // This guards against React 18 StrictMode, which mount-cleanup-
26
+ // remounts the screen's mount effect with a stale closure of
27
+ // `listFetchState`, causing the same effect to dispatch this
28
+ // action twice synchronously. Retry-after-error still works
29
+ // because the previous inner observable has completed by the time
30
+ // the user retries.
31
+ (0, operators_1.exhaustMap)(() => {
22
32
  return zeniAPI
23
33
  .getJSON(`${zeniAPI.apiEndPoints.cardMicroServiceBaseUrl}/1.0/policy-templates`)
24
34
  .pipe((0, operators_1.mergeMap)((response) => {
@@ -5,7 +5,15 @@ const rxjs_1 = require("rxjs");
5
5
  const operators_1 = require("rxjs/operators");
6
6
  const cardPolicyReducer_1 = require("../../../../../entity/cardPolicy/cardPolicyReducer");
7
7
  const responsePayload_1 = require("../../../../../responsePayload");
8
- const fetchCardPolicyMccCategoriesEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(cardPolicyReducer_1.fetchCardPolicyMccCategories.match), (0, operators_1.mergeMap)(() => {
8
+ const fetchCardPolicyMccCategoriesEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(cardPolicyReducer_1.fetchCardPolicyMccCategories.match),
9
+ // `exhaustMap` (rather than `mergeMap`) so a duplicate
10
+ // `fetchCardPolicyMccCategories` dispatched while the previous one
11
+ // is still in flight is dropped instead of producing a second HTTP
12
+ // request. Both the Create and Edit policy screens dispatch this
13
+ // on mount, and React 18 StrictMode invokes their mount effect
14
+ // twice with a stale `mccCategoriesFetchState` closure — without
15
+ // `exhaustMap` that yields two `/cards/mcc-codes` calls.
16
+ (0, operators_1.exhaustMap)(() => {
9
17
  return zeniAPI
10
18
  .getJSON(`${zeniAPI.apiEndPoints.cardMicroServiceBaseUrl}/1.0/cards/mcc-codes`)
11
19
  .pipe((0, operators_1.mergeMap)((response) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeniai/client-epic-state",
3
- "version": "5.0.65-betaRR3",
3
+ "version": "5.0.65-betaRR5",
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",