@zeniai/client-epic-state 5.0.4-betaJK1 → 5.0.7-beta0ND

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 (49) hide show
  1. package/lib/coreEpics.js +2 -1
  2. package/lib/entity/accountRecon/accountReconState.d.ts +1 -1
  3. package/lib/entity/accountRecon/accountReconState.js +0 -1
  4. package/lib/entity/aiAccountantCustomer/aiAccountantCustomerPayload.d.ts +9 -1
  5. package/lib/entity/aiAccountantCustomer/aiAccountantCustomerPayload.js +16 -1
  6. package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
  7. package/lib/entity/snackbar/snackbarTypes.js +2 -0
  8. package/lib/entity/tenant/SessionManager.d.ts +38 -0
  9. package/lib/entity/tenant/SessionManager.js +171 -0
  10. package/lib/entity/tenant/epic/sessionHeartbeatEpic.d.ts +16 -0
  11. package/lib/entity/tenant/epic/sessionHeartbeatEpic.js +16 -0
  12. package/lib/entity/tenant/sessionTypes.d.ts +26 -0
  13. package/lib/entity/tenant/sessionTypes.js +12 -0
  14. package/lib/entity/tenant/tenantReducer.d.ts +5 -1
  15. package/lib/entity/tenant/tenantReducer.js +23 -2
  16. package/lib/entity/tenant/tenantState.d.ts +1 -0
  17. package/lib/entity/transaction/payloadTypes/transactionPayload.d.ts +2 -0
  18. package/lib/entity/transaction/payloadTypes/transactionPayload.js +18 -4
  19. package/lib/esm/coreEpics.js +2 -1
  20. package/lib/esm/entity/accountRecon/accountReconState.js +0 -1
  21. package/lib/esm/entity/aiAccountantCustomer/aiAccountantCustomerPayload.js +16 -1
  22. package/lib/esm/entity/snackbar/snackbarTypes.js +2 -0
  23. package/lib/esm/entity/tenant/SessionManager.js +167 -0
  24. package/lib/esm/entity/tenant/epic/sessionHeartbeatEpic.js +12 -0
  25. package/lib/esm/entity/tenant/sessionTypes.js +9 -0
  26. package/lib/esm/entity/tenant/tenantReducer.js +21 -1
  27. package/lib/esm/entity/transaction/payloadTypes/transactionPayload.js +18 -4
  28. package/lib/esm/index.js +5 -2
  29. package/lib/esm/view/expenseAutomationView/epics/accountRecon/excludeAccountFromReconciliationEpic.js +19 -0
  30. package/lib/esm/view/expenseAutomationView/epics/accountRecon/fetchReconciliationViewEpic.js +0 -2
  31. package/lib/esm/view/expenseAutomationView/epics/accountRecon/includeAccountInReconciliationEpic.js +21 -0
  32. package/lib/esm/view/expenseAutomationView/epics/accountRecon/initialiseLocalDataForSelectedAccountIdEpic.js +0 -1
  33. package/lib/esm/view/expenseAutomationView/epics/accountRecon/saveReconciliationReviewEpic.js +2 -29
  34. package/lib/esm/view/expenseAutomationView/epics/accountRecon/uploadAccountStatementDocumentAIHelper.js +1 -2
  35. package/lib/index.d.ts +5 -2
  36. package/lib/index.js +36 -30
  37. package/lib/tsconfig.typecheck.tsbuildinfo +1 -0
  38. package/lib/view/expenseAutomationView/epics/accountRecon/excludeAccountFromReconciliationEpic.d.ts +2 -1
  39. package/lib/view/expenseAutomationView/epics/accountRecon/excludeAccountFromReconciliationEpic.js +19 -0
  40. package/lib/view/expenseAutomationView/epics/accountRecon/fetchReconciliationViewEpic.js +0 -2
  41. package/lib/view/expenseAutomationView/epics/accountRecon/includeAccountInReconciliationEpic.d.ts +2 -1
  42. package/lib/view/expenseAutomationView/epics/accountRecon/includeAccountInReconciliationEpic.js +21 -0
  43. package/lib/view/expenseAutomationView/epics/accountRecon/initialiseLocalDataForSelectedAccountIdEpic.js +0 -1
  44. package/lib/view/expenseAutomationView/epics/accountRecon/saveReconciliationReviewEpic.js +2 -29
  45. package/lib/view/expenseAutomationView/epics/accountRecon/uploadAccountStatementDocumentAIHelper.d.ts +1 -1
  46. package/lib/view/expenseAutomationView/epics/accountRecon/uploadAccountStatementDocumentAIHelper.js +1 -2
  47. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
  48. package/lib/view/expenseAutomationView/types/reconciliationViewState.d.ts +1 -4
  49. package/package.json +1 -1
package/lib/coreEpics.js CHANGED
@@ -24,6 +24,7 @@ const resendVerifyDeviceOTPEpic_1 = require("./entity/tenant/epic/resendVerifyDe
24
24
  const saveExternalConnectionEpic_1 = require("./entity/tenant/epic/saveExternalConnectionEpic");
25
25
  const sendEmailMagicLinkToUserEpic_1 = require("./entity/tenant/epic/sendEmailMagicLinkToUserEpic");
26
26
  const signInUserEpic_1 = require("./entity/tenant/epic/signInUserEpic");
27
+ const sessionHeartbeatEpic_1 = require("./entity/tenant/epic/sessionHeartbeatEpic");
27
28
  const signOutUserEpic_1 = require("./entity/tenant/epic/signOutUserEpic");
28
29
  const verifyDeviceWithTwoFAEpic_1 = require("./entity/tenant/epic/verifyDeviceWithTwoFAEpic");
29
30
  // ── Toast Notification Epic ──────────────────────────────────────────
@@ -48,7 +49,7 @@ const verifyOtpEpic_1 = require("./view/twoFactorAuthentication/verifyOtpEpic");
48
49
  // RootActionType is only available in the dynamically imported epic.ts.
49
50
  const coreRootEpic = (0, redux_observable_1.combineEpics)(
50
51
  // Auth & tenant
51
- clearAllEpic_1.clearAllEpic, doMagicLinkSignInEpic_1.doMagicLinkSignInEpic, signInUserEpic_1.doSignInEpic, signOutUserEpic_1.doSignOutEpic, fetchActiveTenantEpic_1.fetchActiveTenantEpic, fetchAllTenantsEpic_1.fetchAllTenantsEpic, fetchExcludedResourcesEpic_1.fetchExcludedResourcesEpic, fetchExternalConnectionsEpic_1.fetchExternalConnectionsEpic, fetchSubscriptionSummaryForTenantEpic_1.fetchSubscriptionSummaryForTenantEpic, resendVerifyDeviceOTPEpic_1.resendVerifyDeviceOTPEpic, saveExternalConnectionEpic_1.saveExternalConnectionEpic, sendEmailMagicLinkToUserEpic_1.sendEmailMagicLinkToUserEpic, verifyDeviceWithTwoFAEpic_1.verifyDeviceWithTwoFAEpic,
52
+ clearAllEpic_1.clearAllEpic, doMagicLinkSignInEpic_1.doMagicLinkSignInEpic, signInUserEpic_1.doSignInEpic, signOutUserEpic_1.doSignOutEpic, sessionHeartbeatEpic_1.sessionHeartbeatEpic, fetchActiveTenantEpic_1.fetchActiveTenantEpic, fetchAllTenantsEpic_1.fetchAllTenantsEpic, fetchExcludedResourcesEpic_1.fetchExcludedResourcesEpic, fetchExternalConnectionsEpic_1.fetchExternalConnectionsEpic, fetchSubscriptionSummaryForTenantEpic_1.fetchSubscriptionSummaryForTenantEpic, resendVerifyDeviceOTPEpic_1.resendVerifyDeviceOTPEpic, saveExternalConnectionEpic_1.saveExternalConnectionEpic, sendEmailMagicLinkToUserEpic_1.sendEmailMagicLinkToUserEpic, verifyDeviceWithTwoFAEpic_1.verifyDeviceWithTwoFAEpic,
52
53
  // Toast
53
54
  pushToastNotificationEpic_1.pushToastNotificationEpic,
54
55
  // Collaboration
@@ -4,7 +4,7 @@ import { MonthYearPeriod } from '../../commonStateTypes/timePeriod';
4
4
  import { ZeniDate } from '../../zeniDayJS';
5
5
  import { MerchantType, TransactionDirectionType } from '../transaction/stateTypes/reconciliationTransaction';
6
6
  import { TransactionType } from '../transaction/stateTypes/transactionType';
7
- export declare const toRecommendedActionCodeType: (v: string) => "record_expense" | "record_deposit" | "record_transfer" | "record_bill_payment" | "record_cc_payment" | "record_cc_credit" | "record_journal_entry";
7
+ export declare const toRecommendedActionCodeType: (v: string) => "record_expense" | "record_deposit" | "record_transfer" | "record_bill_payment" | "record_cc_payment" | "record_cc_credit";
8
8
  export type RecommendedActionCodeType = ReturnType<typeof toRecommendedActionCodeType>;
9
9
  export declare const toReconciliationStatusCodeType: (v: string) => "pending" | "failed" | "in_progress" | "save_for_later" | "reconciled" | "reconciled_on_qbo";
10
10
  export type ReconciliationStatusCodeType = ReturnType<typeof toReconciliationStatusCodeType>;
@@ -10,7 +10,6 @@ const RECOMMENDED_ACTION_CODE = [
10
10
  'record_bill_payment', // Match
11
11
  'record_cc_payment', // Categorize
12
12
  'record_cc_credit', // Categorize
13
- 'record_journal_entry', // Journal Entry
14
13
  ];
15
14
  const toRecommendedActionCodeType = (v) => (0, stringToUnion_1.stringToUnion)(v, RECOMMENDED_ACTION_CODE);
16
15
  exports.toRecommendedActionCodeType = toRecommendedActionCodeType;
@@ -1,7 +1,15 @@
1
1
  import { AiAccountantCustomer, AiAccountantEnrollment, AiAccountantJob } from './aiAccountantCustomerState';
2
+ export interface AiAccountantModelAccuracyPayload {
3
+ accuracy: number;
4
+ model_id: string;
5
+ passed: boolean;
6
+ threshold: number;
7
+ training_status: string;
8
+ validated_at: string;
9
+ }
2
10
  export interface AiAccountantEnrollmentPayload {
3
11
  status: string;
4
- accuracy?: Record<string, number>;
12
+ accuracy?: Record<string, AiAccountantModelAccuracyPayload> | null;
5
13
  latest_training?: string;
6
14
  latest_transaction_sync?: string;
7
15
  offboarded_at?: string;
@@ -4,9 +4,24 @@ exports.toAiAccountantJob = exports.toAiAccountantCustomer = exports.toAiAccount
4
4
  const zeniDayJS_1 = require("../../zeniDayJS");
5
5
  const aiAccountantCustomerState_1 = require("./aiAccountantCustomerState");
6
6
  // ── Mapping functions (payload → state) ──
7
+ // Backend returns accuracy as a percent (0–100) inside a nested object; the UI
8
+ // layer treats it as a 0–1 fraction. Normalize here and drop malformed entries.
9
+ const toAccuracyByModel = (accuracyPayload) => {
10
+ if (accuracyPayload == null) {
11
+ return undefined;
12
+ }
13
+ const result = {};
14
+ Object.entries(accuracyPayload).forEach(([model, entry]) => {
15
+ const percent = entry?.accuracy;
16
+ if (typeof percent === 'number' && Number.isFinite(percent)) {
17
+ result[model] = percent / 100;
18
+ }
19
+ });
20
+ return Object.keys(result).length > 0 ? result : undefined;
21
+ };
7
22
  const toAiAccountantEnrollment = (payload) => ({
8
23
  status: (0, aiAccountantCustomerState_1.toAiAccountantEnrollmentStatus)(payload.status),
9
- accuracy: payload.accuracy,
24
+ accuracy: toAccuracyByModel(payload.accuracy),
10
25
  latestTraining: payload.latest_training != null
11
26
  ? (0, zeniDayJS_1.date)(payload.latest_training)
12
27
  : undefined,
@@ -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" | "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";
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" | "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";
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>;
@@ -205,6 +205,8 @@ const ALL_SNACKBAR_MESSAGE_SECTIONS = [
205
205
  'transactionCategorized_transactionUpdated_transactionsFailed',
206
206
  'transactionCategorized_transactionUpdated_transactionFailed',
207
207
  'accounting_classes_enabled_update',
208
+ 'account_excluded_from_reconciliation',
209
+ 'account_included_in_reconciliation',
208
210
  ];
209
211
  const toSnackbarMessageSection = (v) => (0, stringToUnion_1.stringToUnion)(v, ALL_SNACKBAR_MESSAGE_SECTIONS);
210
212
  exports.toSnackbarMessageSection = toSnackbarMessageSection;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * SessionManager — Central session lifecycle manager.
3
+ *
4
+ * Responsibilities:
5
+ * 1. Track user activity (mousemove, keydown, click, scroll, touch)
6
+ * 2. Send heartbeat at configurable intervals while user is active
7
+ * 3. Show warning popup after idle timeout
8
+ * 4. Auto-logout after warning countdown expires
9
+ */
10
+ import { SessionCallbacks, SessionConfig } from './sessionTypes';
11
+ export declare class SessionManager {
12
+ private config;
13
+ private callbacks;
14
+ /** Timestamps and timers */
15
+ private lastActivityTime;
16
+ private idleCheckInterval;
17
+ private heartbeatInterval;
18
+ private countdownInterval;
19
+ private activityDebounceTimer;
20
+ /** State */
21
+ private running;
22
+ private warningActive;
23
+ private secondsRemaining;
24
+ /** Bound handler for event listener cleanup. */
25
+ private boundOnActivity;
26
+ private boundOnVisibilityChange;
27
+ start(config: Partial<SessionConfig> | undefined, callbacks: SessionCallbacks): void;
28
+ stop(): void;
29
+ continueSession(): void;
30
+ isWarningActive(): boolean;
31
+ getSecondsRemaining(): number;
32
+ private onActivity;
33
+ private onVisibilityChange;
34
+ private checkIdle;
35
+ private startWarning;
36
+ private startHeartbeat;
37
+ private clearTimer;
38
+ }
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+ /**
3
+ * SessionManager — Central session lifecycle manager.
4
+ *
5
+ * Responsibilities:
6
+ * 1. Track user activity (mousemove, keydown, click, scroll, touch)
7
+ * 2. Send heartbeat at configurable intervals while user is active
8
+ * 3. Show warning popup after idle timeout
9
+ * 4. Auto-logout after warning countdown expires
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.SessionManager = void 0;
13
+ const sessionTypes_1 = require("./sessionTypes");
14
+ /** Events that indicate user activity. */
15
+ const ACTIVITY_EVENTS = [
16
+ 'mousemove',
17
+ 'mousedown',
18
+ 'keydown',
19
+ 'scroll',
20
+ 'touchstart',
21
+ 'click',
22
+ ];
23
+ /** Debounce interval for activity events (ms). */
24
+ const ACTIVITY_DEBOUNCE_MS = 1000;
25
+ class SessionManager {
26
+ constructor() {
27
+ this.config = sessionTypes_1.DEFAULT_SESSION_CONFIG;
28
+ this.callbacks = null;
29
+ /** Timestamps and timers */
30
+ this.lastActivityTime = 0;
31
+ this.idleCheckInterval = null;
32
+ this.heartbeatInterval = null;
33
+ this.countdownInterval = null;
34
+ this.activityDebounceTimer = null;
35
+ /** State */
36
+ this.running = false;
37
+ this.warningActive = false;
38
+ this.secondsRemaining = 0;
39
+ /** Bound handler for event listener cleanup. */
40
+ this.boundOnActivity = this.onActivity.bind(this);
41
+ this.boundOnVisibilityChange = this.onVisibilityChange.bind(this);
42
+ }
43
+ // ─── Public API ────────────────────────────────────────────
44
+ start(config = {}, callbacks) {
45
+ if (this.running) {
46
+ this.stop();
47
+ }
48
+ this.config = { ...sessionTypes_1.DEFAULT_SESSION_CONFIG, ...config };
49
+ this.callbacks = callbacks;
50
+ this.running = true;
51
+ this.warningActive = false;
52
+ this.lastActivityTime = Date.now();
53
+ if (!this.config.isAutoLogoutEnabled) {
54
+ return;
55
+ }
56
+ ACTIVITY_EVENTS.forEach((event) => {
57
+ document.addEventListener(event, this.boundOnActivity, {
58
+ capture: true,
59
+ passive: true,
60
+ });
61
+ });
62
+ document.addEventListener('visibilitychange', this.boundOnVisibilityChange);
63
+ this.startHeartbeat();
64
+ this.callbacks?.onHeartbeat();
65
+ this.idleCheckInterval = setInterval(() => this.checkIdle(), 1000);
66
+ }
67
+ stop() {
68
+ this.running = false;
69
+ this.warningActive = false;
70
+ ACTIVITY_EVENTS.forEach((event) => {
71
+ document.removeEventListener(event, this.boundOnActivity, {
72
+ capture: true,
73
+ });
74
+ });
75
+ document.removeEventListener('visibilitychange', this.boundOnVisibilityChange);
76
+ this.clearTimer('idleCheckInterval');
77
+ this.clearTimer('heartbeatInterval');
78
+ this.clearTimer('countdownInterval');
79
+ this.clearTimer('activityDebounceTimer');
80
+ }
81
+ continueSession() {
82
+ if (!this.running) {
83
+ return;
84
+ }
85
+ this.warningActive = false;
86
+ this.secondsRemaining = 0;
87
+ this.lastActivityTime = Date.now();
88
+ this.clearTimer('countdownInterval');
89
+ this.callbacks?.onHeartbeat();
90
+ this.callbacks?.onSessionExtended();
91
+ this.startHeartbeat();
92
+ }
93
+ isWarningActive() {
94
+ return this.warningActive;
95
+ }
96
+ getSecondsRemaining() {
97
+ return this.secondsRemaining;
98
+ }
99
+ // ─── Private ───────────────────────────────────────────────
100
+ onActivity() {
101
+ if (!this.running || this.warningActive) {
102
+ return;
103
+ }
104
+ if (this.activityDebounceTimer != null) {
105
+ return;
106
+ }
107
+ this.lastActivityTime = Date.now();
108
+ this.activityDebounceTimer = setTimeout(() => {
109
+ this.activityDebounceTimer = null;
110
+ }, ACTIVITY_DEBOUNCE_MS);
111
+ }
112
+ onVisibilityChange() {
113
+ if (!this.running) {
114
+ return;
115
+ }
116
+ if (document.visibilityState === 'visible') {
117
+ this.lastActivityTime = Date.now();
118
+ this.checkIdle();
119
+ }
120
+ }
121
+ checkIdle() {
122
+ if (!this.running || this.warningActive) {
123
+ return;
124
+ }
125
+ const idleMs = Date.now() - this.lastActivityTime;
126
+ const idleTimeoutMs = this.config.idleTimeoutMinutes * 60 * 1000;
127
+ if (idleMs >= idleTimeoutMs) {
128
+ this.startWarning();
129
+ }
130
+ }
131
+ startWarning() {
132
+ this.warningActive = true;
133
+ this.secondsRemaining = this.config.warningDurationSeconds;
134
+ this.clearTimer('heartbeatInterval');
135
+ this.callbacks?.onWarningStart(this.secondsRemaining);
136
+ this.countdownInterval = setInterval(() => {
137
+ this.secondsRemaining -= 1;
138
+ if (this.secondsRemaining <= 0) {
139
+ this.clearTimer('countdownInterval');
140
+ this.warningActive = false;
141
+ this.callbacks?.onAutoLogout();
142
+ }
143
+ else {
144
+ this.callbacks?.onWarningTick(this.secondsRemaining);
145
+ }
146
+ }, 1000);
147
+ }
148
+ startHeartbeat() {
149
+ this.clearTimer('heartbeatInterval');
150
+ const intervalMs = this.config.heartbeatIntervalMinutes * 60 * 1000;
151
+ this.heartbeatInterval = setInterval(() => {
152
+ if (!this.running || this.warningActive) {
153
+ return;
154
+ }
155
+ this.callbacks?.onHeartbeat();
156
+ }, intervalMs);
157
+ }
158
+ clearTimer(name) {
159
+ const timer = this[name];
160
+ if (timer != null) {
161
+ if (name === 'activityDebounceTimer') {
162
+ clearTimeout(timer);
163
+ }
164
+ else {
165
+ clearInterval(timer);
166
+ }
167
+ this[name] = null;
168
+ }
169
+ }
170
+ }
171
+ exports.SessionManager = SessionManager;
@@ -0,0 +1,16 @@
1
+ import { ActionsObservable, StateObservable } from 'redux-observable';
2
+ import { RootState } from '../../../reducer';
3
+ import { ZeniAPI } from '../../../zeniAPI';
4
+ import { sendSessionHeartbeat, sessionHeartbeatFailure, sessionHeartbeatSuccess } from '../tenantReducer';
5
+ export type ActionType = ReturnType<typeof sendSessionHeartbeat> | ReturnType<typeof sessionHeartbeatSuccess> | ReturnType<typeof sessionHeartbeatFailure>;
6
+ export declare const sessionHeartbeatEpic: (actions$: ActionsObservable<ActionType>, _: StateObservable<RootState>, zeniAPI: ZeniAPI) => import("rxjs").Observable<{
7
+ payload: {
8
+ expiresAt: string;
9
+ };
10
+ type: "tenant/sessionHeartbeatSuccess";
11
+ } | {
12
+ payload: {
13
+ error: import("../../../responsePayload").ZeniAPIStatus<Record<string, unknown>>;
14
+ };
15
+ type: "tenant/sessionHeartbeatFailure";
16
+ }>;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sessionHeartbeatEpic = void 0;
4
+ const rxjs_1 = require("rxjs");
5
+ const operators_1 = require("rxjs/operators");
6
+ const responsePayload_1 = require("../../../responsePayload");
7
+ const tenantReducer_1 = require("../tenantReducer");
8
+ const sessionHeartbeatEpic = (actions$, _, zeniAPI) => actions$.pipe((0, operators_1.filter)(tenantReducer_1.sendSessionHeartbeat.match), (0, operators_1.switchMap)(() => zeniAPI
9
+ .postAndGetJSON(`${zeniAPI.apiEndPoints.authMicroServiceBaseUrl}/1.0/sessions/heartbeat`)
10
+ .pipe((0, operators_1.mergeMap)((response) => {
11
+ if ((0, responsePayload_1.isSuccessResponse)(response)) {
12
+ return (0, rxjs_1.of)((0, tenantReducer_1.sessionHeartbeatSuccess)(response.data?.expiry ?? ''));
13
+ }
14
+ return (0, rxjs_1.of)((0, tenantReducer_1.sessionHeartbeatFailure)(response.status));
15
+ }), (0, operators_1.catchError)((error) => (0, rxjs_1.of)((0, tenantReducer_1.sessionHeartbeatFailure)((0, responsePayload_1.createZeniAPIStatus)('Heartbeat failed', 'Session heartbeat API call errored: ' + JSON.stringify(error))))))));
16
+ exports.sessionHeartbeatEpic = sessionHeartbeatEpic;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Session management types for idle timeout and heartbeat.
3
+ */
4
+ export interface SessionConfig {
5
+ /** Minutes between heartbeat API calls while user is active. Default: 10 */
6
+ heartbeatIntervalMinutes: number;
7
+ /** Minutes of inactivity before the warning popup appears. Default: 30 */
8
+ idleTimeoutMinutes: number;
9
+ /** Whether auto-logout is enabled. Default: true */
10
+ isAutoLogoutEnabled: boolean;
11
+ /** Seconds to count down in the warning popup before auto-logout. Default: 60 */
12
+ warningDurationSeconds: number;
13
+ }
14
+ export declare const DEFAULT_SESSION_CONFIG: SessionConfig;
15
+ export interface SessionCallbacks {
16
+ /** Called when the countdown reaches zero — app should sign out. */
17
+ onAutoLogout: () => void;
18
+ /** Called to send a heartbeat API request. */
19
+ onHeartbeat: () => void;
20
+ /** Called when the session is extended (heartbeat success or "Continue Session"). */
21
+ onSessionExtended: () => void;
22
+ /** Called when the warning countdown starts. */
23
+ onWarningStart: (secondsRemaining: number) => void;
24
+ /** Called every second during the countdown. */
25
+ onWarningTick: (secondsRemaining: number) => void;
26
+ }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ /**
3
+ * Session management types for idle timeout and heartbeat.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DEFAULT_SESSION_CONFIG = void 0;
7
+ exports.DEFAULT_SESSION_CONFIG = {
8
+ heartbeatIntervalMinutes: 10,
9
+ idleTimeoutMinutes: 30,
10
+ isAutoLogoutEnabled: true,
11
+ warningDurationSeconds: 60,
12
+ };
@@ -51,7 +51,11 @@ export declare const updateTenants: import("@reduxjs/toolkit").ActionCreatorWith
51
51
  accountLockTime?: string;
52
52
  accountUnlockTime?: string;
53
53
  isAccountLocked?: boolean;
54
- }, "tenant/sendEmailMagicLinkToUserFailure">, updateSignInState: import("@reduxjs/toolkit").ActionCreatorWithPayload<FetchState, "tenant/updateSignInState">, doSignOut: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"tenant/doSignOut">, signOutSuccess: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"tenant/signOutSuccess">, updateLoggedInUser: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[userPayload: LoggedInUserPayload, zeniSessionId: string, authProvider: AuthProvider], {
54
+ }, "tenant/sendEmailMagicLinkToUserFailure">, updateSignInState: import("@reduxjs/toolkit").ActionCreatorWithPayload<FetchState, "tenant/updateSignInState">, doSignOut: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"tenant/doSignOut">, signOutSuccess: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"tenant/signOutSuccess">, sendSessionHeartbeat: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"tenant/sendSessionHeartbeat">, sessionHeartbeatSuccess: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[expiresAt: string], {
55
+ expiresAt: string;
56
+ }, "tenant/sessionHeartbeatSuccess", never, never>, sessionHeartbeatFailure: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[error: ZeniAPIStatus<Record<string, unknown>>], {
57
+ error: ZeniAPIStatus<Record<string, unknown>>;
58
+ }, "tenant/sessionHeartbeatFailure", never, never>, updateLoggedInUser: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[userPayload: LoggedInUserPayload, zeniSessionId: string, authProvider: AuthProvider], {
55
59
  userPayload: LoggedInUserPayload;
56
60
  zeniSessionId: string;
57
61
  authProvider: AuthProvider;
@@ -4,7 +4,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  var _a;
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.toConnection = exports.toRoleResource = exports.resendVerifyDeviceOTPFailure = exports.resendVerifyDeviceOTPSuccess = exports.resendVerifyDeviceOTP = exports.verifyDeviceWithTwoFAFailure = exports.verifyDeviceWithTwoFASuccess = exports.verifyDeviceWithTwoFA = exports.updateTenantMasterTOSInfo = exports.updateTenantAccountingClassesEnabled = exports.trigger2FA = exports.resetSignInState = exports.updateTreasuryVideoViewedForLoggedInUser = exports.updateReferViewedForLoggedInUser = exports.updateTenantReimbursementInfo = exports.removeOnboardingTenant = exports.updateOnboardingTenants = exports.updateSubscriptionSummaryForTenantFailure = exports.updateSubscriptionSummaryForTenantSuccess = exports.fetchSubscriptionSummaryForTenant = exports.clearAll = exports.fetchExternalConnectionsSuccess = exports.fetchExternalConnectionsFailure = exports.saveExternalConnectionFailure = exports.saveExternalConnectionSuccess = exports.saveExternalConnection = exports.fetchExternalConnections = exports.updateLoggedInUser = exports.signOutSuccess = exports.doSignOut = exports.updateSignInState = exports.sendEmailMagicLinkToUserFailure = exports.sendEmailMagicLinkToUserSuccess = exports.sendEmailMagicLinkToUser = exports.magicLinkSignInFailure = exports.magicLinkSignInSuccess = exports.doMagicLinkSignIn = exports.doSignIn = exports.updateExcludedResourcesFailure = exports.updateExcludedResourcesSuccess = exports.fetchExcludedResources = exports.updateCurrentTenant = exports.updateTenantFailure = exports.updateTenantSuccess = exports.fetchActiveTenant = exports.updateTenantsFailure = exports.updateTenantsSuccess = exports.fetchAllTenants = exports.updateTenants = exports.initialState = void 0;
7
+ exports.resendVerifyDeviceOTPSuccess = exports.resendVerifyDeviceOTP = exports.verifyDeviceWithTwoFAFailure = exports.verifyDeviceWithTwoFASuccess = exports.verifyDeviceWithTwoFA = exports.updateTenantMasterTOSInfo = exports.updateTenantAccountingClassesEnabled = exports.trigger2FA = exports.resetSignInState = exports.updateTreasuryVideoViewedForLoggedInUser = exports.updateReferViewedForLoggedInUser = exports.updateTenantReimbursementInfo = exports.removeOnboardingTenant = exports.updateOnboardingTenants = exports.updateSubscriptionSummaryForTenantFailure = exports.updateSubscriptionSummaryForTenantSuccess = exports.fetchSubscriptionSummaryForTenant = exports.clearAll = exports.fetchExternalConnectionsSuccess = exports.fetchExternalConnectionsFailure = exports.saveExternalConnectionFailure = exports.saveExternalConnectionSuccess = exports.saveExternalConnection = exports.fetchExternalConnections = exports.updateLoggedInUser = exports.sessionHeartbeatFailure = exports.sessionHeartbeatSuccess = exports.sendSessionHeartbeat = exports.signOutSuccess = exports.doSignOut = exports.updateSignInState = exports.sendEmailMagicLinkToUserFailure = exports.sendEmailMagicLinkToUserSuccess = exports.sendEmailMagicLinkToUser = exports.magicLinkSignInFailure = exports.magicLinkSignInSuccess = exports.doMagicLinkSignIn = exports.doSignIn = exports.updateExcludedResourcesFailure = exports.updateExcludedResourcesSuccess = exports.fetchExcludedResources = exports.updateCurrentTenant = exports.updateTenantFailure = exports.updateTenantSuccess = exports.fetchActiveTenant = exports.updateTenantsFailure = exports.updateTenantsSuccess = exports.fetchAllTenants = exports.updateTenants = exports.initialState = void 0;
8
+ exports.toConnection = exports.toRoleResource = exports.resendVerifyDeviceOTPFailure = void 0;
8
9
  exports.mapConnectionsPayloadToState = mapConnectionsPayloadToState;
9
10
  const toolkit_1 = require("@reduxjs/toolkit");
10
11
  const js_base64_1 = require("js-base64");
@@ -436,6 +437,26 @@ const tenant = (0, toolkit_1.createSlice)({
436
437
  signOutSuccess(draft) {
437
438
  draft.loggedInUser = undefined;
438
439
  draft.currentTenantId = undefined;
440
+ draft.sessionExpiresAt = undefined;
441
+ },
442
+ sendSessionHeartbeat() {
443
+ // No state change needed — epic handles the API call
444
+ },
445
+ sessionHeartbeatSuccess: {
446
+ prepare(expiresAt) {
447
+ return { payload: { expiresAt } };
448
+ },
449
+ reducer(draft, action) {
450
+ draft.sessionExpiresAt = action.payload.expiresAt;
451
+ },
452
+ },
453
+ sessionHeartbeatFailure: {
454
+ prepare(error) {
455
+ return { payload: { error } };
456
+ },
457
+ reducer(draft, action) {
458
+ draft.error = action.payload.error;
459
+ },
439
460
  },
440
461
  fetchSubscriptionSummaryForTenant: {
441
462
  prepare(tenantId) {
@@ -543,7 +564,7 @@ const tenant = (0, toolkit_1.createSlice)({
543
564
  },
544
565
  },
545
566
  });
546
- _a = tenant.actions, exports.updateTenants = _a.updateTenants, exports.fetchAllTenants = _a.fetchAllTenants, exports.updateTenantsSuccess = _a.updateTenantsSuccess, exports.updateTenantsFailure = _a.updateTenantsFailure, exports.fetchActiveTenant = _a.fetchActiveTenant, exports.updateTenantSuccess = _a.updateTenantSuccess, exports.updateTenantFailure = _a.updateTenantFailure, exports.updateCurrentTenant = _a.updateCurrentTenant, exports.fetchExcludedResources = _a.fetchExcludedResources, exports.updateExcludedResourcesSuccess = _a.updateExcludedResourcesSuccess, exports.updateExcludedResourcesFailure = _a.updateExcludedResourcesFailure, exports.doSignIn = _a.doSignIn, exports.doMagicLinkSignIn = _a.doMagicLinkSignIn, exports.magicLinkSignInSuccess = _a.magicLinkSignInSuccess, exports.magicLinkSignInFailure = _a.magicLinkSignInFailure, exports.sendEmailMagicLinkToUser = _a.sendEmailMagicLinkToUser, exports.sendEmailMagicLinkToUserSuccess = _a.sendEmailMagicLinkToUserSuccess, exports.sendEmailMagicLinkToUserFailure = _a.sendEmailMagicLinkToUserFailure, exports.updateSignInState = _a.updateSignInState, exports.doSignOut = _a.doSignOut, exports.signOutSuccess = _a.signOutSuccess, exports.updateLoggedInUser = _a.updateLoggedInUser, exports.fetchExternalConnections = _a.fetchExternalConnections, exports.saveExternalConnection = _a.saveExternalConnection, exports.saveExternalConnectionSuccess = _a.saveExternalConnectionSuccess, exports.saveExternalConnectionFailure = _a.saveExternalConnectionFailure, exports.fetchExternalConnectionsFailure = _a.fetchExternalConnectionsFailure, exports.fetchExternalConnectionsSuccess = _a.fetchExternalConnectionsSuccess, exports.clearAll = _a.clearAll, exports.fetchSubscriptionSummaryForTenant = _a.fetchSubscriptionSummaryForTenant, exports.updateSubscriptionSummaryForTenantSuccess = _a.updateSubscriptionSummaryForTenantSuccess, exports.updateSubscriptionSummaryForTenantFailure = _a.updateSubscriptionSummaryForTenantFailure, exports.updateOnboardingTenants = _a.updateOnboardingTenants, exports.removeOnboardingTenant = _a.removeOnboardingTenant, exports.updateTenantReimbursementInfo = _a.updateTenantReimbursementInfo, exports.updateReferViewedForLoggedInUser = _a.updateReferViewedForLoggedInUser, exports.updateTreasuryVideoViewedForLoggedInUser = _a.updateTreasuryVideoViewedForLoggedInUser, exports.resetSignInState = _a.resetSignInState, exports.trigger2FA = _a.trigger2FA, exports.updateTenantAccountingClassesEnabled = _a.updateTenantAccountingClassesEnabled, exports.updateTenantMasterTOSInfo = _a.updateTenantMasterTOSInfo, exports.verifyDeviceWithTwoFA = _a.verifyDeviceWithTwoFA, exports.verifyDeviceWithTwoFASuccess = _a.verifyDeviceWithTwoFASuccess, exports.verifyDeviceWithTwoFAFailure = _a.verifyDeviceWithTwoFAFailure, exports.resendVerifyDeviceOTP = _a.resendVerifyDeviceOTP, exports.resendVerifyDeviceOTPSuccess = _a.resendVerifyDeviceOTPSuccess, exports.resendVerifyDeviceOTPFailure = _a.resendVerifyDeviceOTPFailure;
567
+ _a = tenant.actions, exports.updateTenants = _a.updateTenants, exports.fetchAllTenants = _a.fetchAllTenants, exports.updateTenantsSuccess = _a.updateTenantsSuccess, exports.updateTenantsFailure = _a.updateTenantsFailure, exports.fetchActiveTenant = _a.fetchActiveTenant, exports.updateTenantSuccess = _a.updateTenantSuccess, exports.updateTenantFailure = _a.updateTenantFailure, exports.updateCurrentTenant = _a.updateCurrentTenant, exports.fetchExcludedResources = _a.fetchExcludedResources, exports.updateExcludedResourcesSuccess = _a.updateExcludedResourcesSuccess, exports.updateExcludedResourcesFailure = _a.updateExcludedResourcesFailure, exports.doSignIn = _a.doSignIn, exports.doMagicLinkSignIn = _a.doMagicLinkSignIn, exports.magicLinkSignInSuccess = _a.magicLinkSignInSuccess, exports.magicLinkSignInFailure = _a.magicLinkSignInFailure, exports.sendEmailMagicLinkToUser = _a.sendEmailMagicLinkToUser, exports.sendEmailMagicLinkToUserSuccess = _a.sendEmailMagicLinkToUserSuccess, exports.sendEmailMagicLinkToUserFailure = _a.sendEmailMagicLinkToUserFailure, exports.updateSignInState = _a.updateSignInState, exports.doSignOut = _a.doSignOut, exports.signOutSuccess = _a.signOutSuccess, exports.sendSessionHeartbeat = _a.sendSessionHeartbeat, exports.sessionHeartbeatSuccess = _a.sessionHeartbeatSuccess, exports.sessionHeartbeatFailure = _a.sessionHeartbeatFailure, exports.updateLoggedInUser = _a.updateLoggedInUser, exports.fetchExternalConnections = _a.fetchExternalConnections, exports.saveExternalConnection = _a.saveExternalConnection, exports.saveExternalConnectionSuccess = _a.saveExternalConnectionSuccess, exports.saveExternalConnectionFailure = _a.saveExternalConnectionFailure, exports.fetchExternalConnectionsFailure = _a.fetchExternalConnectionsFailure, exports.fetchExternalConnectionsSuccess = _a.fetchExternalConnectionsSuccess, exports.clearAll = _a.clearAll, exports.fetchSubscriptionSummaryForTenant = _a.fetchSubscriptionSummaryForTenant, exports.updateSubscriptionSummaryForTenantSuccess = _a.updateSubscriptionSummaryForTenantSuccess, exports.updateSubscriptionSummaryForTenantFailure = _a.updateSubscriptionSummaryForTenantFailure, exports.updateOnboardingTenants = _a.updateOnboardingTenants, exports.removeOnboardingTenant = _a.removeOnboardingTenant, exports.updateTenantReimbursementInfo = _a.updateTenantReimbursementInfo, exports.updateReferViewedForLoggedInUser = _a.updateReferViewedForLoggedInUser, exports.updateTreasuryVideoViewedForLoggedInUser = _a.updateTreasuryVideoViewedForLoggedInUser, exports.resetSignInState = _a.resetSignInState, exports.trigger2FA = _a.trigger2FA, exports.updateTenantAccountingClassesEnabled = _a.updateTenantAccountingClassesEnabled, exports.updateTenantMasterTOSInfo = _a.updateTenantMasterTOSInfo, exports.verifyDeviceWithTwoFA = _a.verifyDeviceWithTwoFA, exports.verifyDeviceWithTwoFASuccess = _a.verifyDeviceWithTwoFASuccess, exports.verifyDeviceWithTwoFAFailure = _a.verifyDeviceWithTwoFAFailure, exports.resendVerifyDeviceOTP = _a.resendVerifyDeviceOTP, exports.resendVerifyDeviceOTPSuccess = _a.resendVerifyDeviceOTPSuccess, exports.resendVerifyDeviceOTPFailure = _a.resendVerifyDeviceOTPFailure;
547
568
  exports.default = tenant.reducer;
548
569
  /**
549
570
  * Converts tenants payload to Tenant and User State
@@ -45,6 +45,7 @@ export interface TenantState extends FetchedState {
45
45
  magicLinkCodeVerifier?: string;
46
46
  magicLinkEmail?: string;
47
47
  otpChallenge?: string;
48
+ sessionExpiresAt?: string;
48
49
  twoFAPhoneNumber?: string;
49
50
  }
50
51
  export interface TenantProductSettings {
@@ -21,6 +21,8 @@ export interface TransactionPayload extends TransactionIDPayload, AccountBasePay
21
21
  category_id?: string | null;
22
22
  category_integration_id?: string | null;
23
23
  category_name?: string | null;
24
+ class_id?: string | null;
25
+ class_name?: string | null;
24
26
  customer_id?: string | null;
25
27
  customer_name?: string | null;
26
28
  document_id?: string;
@@ -38,8 +38,6 @@ const toTransaction = (payload) => {
38
38
  const amount = (0, exports.getTransactionPayloadAmount)(payload);
39
39
  const recipientAmount = getTransactionPayloadRecipientAmount(payload);
40
40
  const lines = payload?.lines?.map((linePayload) => (0, transactionLinePayload_1.toLine)(linePayload, amount, recipientAmount));
41
- const firstLineWithClass = lines?.find((line) => 'class' in line &&
42
- line.class?.classId != null);
43
41
  return {
44
42
  ...(0, transactionIDPayload_1.toTransactionID)(payload),
45
43
  typeOfTransaction: 'supported',
@@ -97,6 +95,12 @@ const toTransaction = (payload) => {
97
95
  payload.category_integration_id !== ''
98
96
  ? { categoryIntegrationId: payload.category_integration_id }
99
97
  : {}),
98
+ ...(payload.class_id != null && payload.class_id !== ''
99
+ ? { classId: payload.class_id }
100
+ : {}),
101
+ ...(payload.class_name != null && payload.class_name !== ''
102
+ ? { className: payload.class_name }
103
+ : {}),
100
104
  ...(payload.category_name != null && payload.category_name !== ''
101
105
  ? { categoryName: payload.category_name }
102
106
  : {}),
@@ -105,8 +109,6 @@ const toTransaction = (payload) => {
105
109
  account: payloadHasAccountInfo(payload)
106
110
  ? (0, accountPayload_1.mapAccountBasePayloadToAccountBase)(payload)
107
111
  : undefined,
108
- classId: firstLineWithClass?.class?.classId,
109
- className: firstLineWithClass?.class?.className,
110
112
  syncToken: payload.sync_token?.toString(),
111
113
  paymentType: payload.payment_type ?? undefined,
112
114
  paymentTypeName: payload.payment_type_name ?? undefined,
@@ -176,6 +178,18 @@ const toTransactionPayload = (transaction) => {
176
178
  target_account_name: transaction.targetAccountName,
177
179
  };
178
180
  }
181
+ if (transaction.classId != null && transaction.classId !== '') {
182
+ transactionPayload = {
183
+ ...transactionPayload,
184
+ class_id: transaction.classId,
185
+ };
186
+ }
187
+ if (transaction.className != null && transaction.className !== '') {
188
+ transactionPayload = {
189
+ ...transactionPayload,
190
+ class_name: transaction.className,
191
+ };
192
+ }
179
193
  if (transaction.toFromAccountId != null &&
180
194
  transaction.toFromAccountId !== '') {
181
195
  transactionPayload = {
@@ -22,6 +22,7 @@ import { resendVerifyDeviceOTPEpic } from './entity/tenant/epic/resendVerifyDevi
22
22
  import { saveExternalConnectionEpic } from './entity/tenant/epic/saveExternalConnectionEpic';
23
23
  import { sendEmailMagicLinkToUserEpic } from './entity/tenant/epic/sendEmailMagicLinkToUserEpic';
24
24
  import { doSignInEpic } from './entity/tenant/epic/signInUserEpic';
25
+ import { sessionHeartbeatEpic } from './entity/tenant/epic/sessionHeartbeatEpic';
25
26
  import { doSignOutEpic } from './entity/tenant/epic/signOutUserEpic';
26
27
  import { verifyDeviceWithTwoFAEpic } from './entity/tenant/epic/verifyDeviceWithTwoFAEpic';
27
28
  // ── Toast Notification Epic ──────────────────────────────────────────
@@ -46,7 +47,7 @@ import { verifyOtpEpic } from './view/twoFactorAuthentication/verifyOtpEpic';
46
47
  // RootActionType is only available in the dynamically imported epic.ts.
47
48
  const coreRootEpic = combineEpics(
48
49
  // Auth & tenant
49
- clearAllEpic, doMagicLinkSignInEpic, doSignInEpic, doSignOutEpic, fetchActiveTenantEpic, fetchAllTenantsEpic, fetchExcludedResourcesEpic, fetchExternalConnectionsEpic, fetchSubscriptionSummaryForTenantEpic, resendVerifyDeviceOTPEpic, saveExternalConnectionEpic, sendEmailMagicLinkToUserEpic, verifyDeviceWithTwoFAEpic,
50
+ clearAllEpic, doMagicLinkSignInEpic, doSignInEpic, doSignOutEpic, sessionHeartbeatEpic, fetchActiveTenantEpic, fetchAllTenantsEpic, fetchExcludedResourcesEpic, fetchExternalConnectionsEpic, fetchSubscriptionSummaryForTenantEpic, resendVerifyDeviceOTPEpic, saveExternalConnectionEpic, sendEmailMagicLinkToUserEpic, verifyDeviceWithTwoFAEpic,
50
51
  // Toast
51
52
  pushToastNotificationEpic,
52
53
  // Collaboration
@@ -7,7 +7,6 @@ const RECOMMENDED_ACTION_CODE = [
7
7
  'record_bill_payment', // Match
8
8
  'record_cc_payment', // Categorize
9
9
  'record_cc_credit', // Categorize
10
- 'record_journal_entry', // Journal Entry
11
10
  ];
12
11
  export const toRecommendedActionCodeType = (v) => stringToUnion(v, RECOMMENDED_ACTION_CODE);
13
12
  const RECONCILIATION_STATUS_CODE = [
@@ -1,9 +1,24 @@
1
1
  import { date } from '../../zeniDayJS';
2
2
  import { toAiAccountantEnrollmentStatus, toAiAccountantJobStatus, toAiAccountantOperationType, } from './aiAccountantCustomerState';
3
3
  // ── Mapping functions (payload → state) ──
4
+ // Backend returns accuracy as a percent (0–100) inside a nested object; the UI
5
+ // layer treats it as a 0–1 fraction. Normalize here and drop malformed entries.
6
+ const toAccuracyByModel = (accuracyPayload) => {
7
+ if (accuracyPayload == null) {
8
+ return undefined;
9
+ }
10
+ const result = {};
11
+ Object.entries(accuracyPayload).forEach(([model, entry]) => {
12
+ const percent = entry?.accuracy;
13
+ if (typeof percent === 'number' && Number.isFinite(percent)) {
14
+ result[model] = percent / 100;
15
+ }
16
+ });
17
+ return Object.keys(result).length > 0 ? result : undefined;
18
+ };
4
19
  export const toAiAccountantEnrollment = (payload) => ({
5
20
  status: toAiAccountantEnrollmentStatus(payload.status),
6
- accuracy: payload.accuracy,
21
+ accuracy: toAccuracyByModel(payload.accuracy),
7
22
  latestTraining: payload.latest_training != null
8
23
  ? date(payload.latest_training)
9
24
  : undefined,
@@ -202,6 +202,8 @@ const ALL_SNACKBAR_MESSAGE_SECTIONS = [
202
202
  'transactionCategorized_transactionUpdated_transactionsFailed',
203
203
  'transactionCategorized_transactionUpdated_transactionFailed',
204
204
  'accounting_classes_enabled_update',
205
+ 'account_excluded_from_reconciliation',
206
+ 'account_included_in_reconciliation',
205
207
  ];
206
208
  export const toSnackbarMessageSection = (v) => stringToUnion(v, ALL_SNACKBAR_MESSAGE_SECTIONS);
207
209
  // Keeping this different from the message type, since we may not have messages in all instances