From 8d84f892dd17c2c86a06d25cb24b0500e4648694 Mon Sep 17 00:00:00 2001 From: sumo_slonik Date: Wed, 25 Jun 2025 11:06:19 +0200 Subject: [PATCH 01/10] working solution --- src/ROUTES.ts | 1040 ++++++++--------- .../helpers/getAdaptedStateFromPath.ts | 3 + .../Navigation/helpers/getStateFromPath.ts | 4 +- src/libs/Navigation/helpers/pathMap.ts | 26 + 4 files changed, 547 insertions(+), 526 deletions(-) create mode 100644 src/libs/Navigation/helpers/pathMap.ts diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 67f9ed4da2d9..aea115a9d633 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -40,7 +40,7 @@ const ROUTES = { // This route renders the list of reports. HOME: 'home', - WORKSPACES_LIST: {route: 'settings/workspaces', getRoute: (backTo?: string) => getUrlWithBackToParam('settings/workspaces', backTo)}, + WORKSPACES_LIST: {route: 'workspaces', getRoute: (backTo?: string) => getUrlWithBackToParam('workspaces', backTo)}, SEARCH_ROOT: { route: 'search', @@ -979,365 +979,364 @@ const ROUTES = { WORKSPACE_NEW: 'workspace/new', WORKSPACE_NEW_ROOM: 'workspace/new-room', WORKSPACE_INITIAL: { - route: 'settings/workspaces/:policyID', + route: 'workspaces/:policyID', getRoute: (policyID: string | undefined, backTo?: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_INITIAL route'); } - return `${getUrlWithBackToParam(`settings/workspaces/${policyID}`, backTo)}` as const; + return `${getUrlWithBackToParam(`workspaces/${policyID}`, backTo)}` as const; }, }, WORKSPACE_INVITE: { - route: 'settings/workspaces/:policyID/invite', - getRoute: (policyID: string, backTo?: string) => `${getUrlWithBackToParam(`settings/workspaces/${policyID}/invite`, backTo)}` as const, + route: 'workspaces/:policyID/invite', + getRoute: (policyID: string, backTo?: string) => `${getUrlWithBackToParam(`workspaces/${policyID}/invite`, backTo)}` as const, }, WORKSPACE_INVITE_MESSAGE: { - route: 'settings/workspaces/:policyID/invite-message', - getRoute: (policyID: string, backTo?: string) => `${getUrlWithBackToParam(`settings/workspaces/${policyID}/invite-message`, backTo)}` as const, + route: 'workspaces/:policyID/invite-message', + getRoute: (policyID: string, backTo?: string) => `${getUrlWithBackToParam(`workspaces/${policyID}/invite-message`, backTo)}` as const, }, WORKSPACE_INVITE_MESSAGE_ROLE: { - route: 'settings/workspaces/:policyID/invite-message/role', - getRoute: (policyID: string, backTo?: string) => `${getUrlWithBackToParam(`settings/workspaces/${policyID}/invite-message/role`, backTo)}` as const, + route: 'workspaces/:policyID/invite-message/role', + getRoute: (policyID: string, backTo?: string) => `${getUrlWithBackToParam(`workspaces/${policyID}/invite-message/role`, backTo)}` as const, }, WORKSPACE_OVERVIEW: { - route: 'settings/workspaces/:policyID/overview', + route: 'workspaces/:policyID/overview', getRoute: (policyID: string | undefined, backTo?: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_OVERVIEW route'); } - return getUrlWithBackToParam(`settings/workspaces/${policyID}/overview` as const, backTo); + return getUrlWithBackToParam(`workspaces/${policyID}/overview` as const, backTo); }, }, WORKSPACE_OVERVIEW_ADDRESS: { - route: 'settings/workspaces/:policyID/overview/address', + route: 'workspaces/:policyID/overview/address', getRoute: (policyID: string | undefined, backTo?: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_OVERVIEW_ADDRESS route'); } - return getUrlWithBackToParam(`settings/workspaces/${policyID}/overview/address` as const, backTo); + return getUrlWithBackToParam(`workspaces/${policyID}/overview/address` as const, backTo); }, }, WORKSPACE_OVERVIEW_PLAN: { - route: 'settings/workspaces/:policyID/overview/plan', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/overview/plan` as const, backTo), + route: 'workspaces/:policyID/overview/plan', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/overview/plan` as const, backTo), }, WORKSPACE_ACCOUNTING: { - route: 'settings/workspaces/:policyID/accounting', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting` as const, + route: 'workspaces/:policyID/accounting', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting` as const, }, WORKSPACE_OVERVIEW_CURRENCY: { - route: 'settings/workspaces/:policyID/overview/currency', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/overview/currency` as const, + route: 'workspaces/:policyID/overview/currency', + getRoute: (policyID: string) => `workspaces/${policyID}/overview/currency` as const, }, POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_EXPORT: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-online/export', + route: 'workspaces/:policyID/accounting/quickbooks-online/export', getRoute: (policyID: string | undefined, backTo?: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_EXPORT route'); } - return getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/quickbooks-online/export` as const, backTo, false); + return getUrlWithBackToParam(`workspaces/${policyID}/accounting/quickbooks-online/export` as const, backTo, false); }, }, POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_COMPANY_CARD_EXPENSE_ACCOUNT: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-online/export/company-card-expense-account', - getRoute: (policyID: string, backTo?: string) => - getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/quickbooks-online/export/company-card-expense-account` as const, backTo), + route: 'workspaces/:policyID/accounting/quickbooks-online/export/company-card-expense-account', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/accounting/quickbooks-online/export/company-card-expense-account` as const, backTo), }, POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_COMPANY_CARD_EXPENSE_ACCOUNT_SELECT: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-online/export/company-card-expense-account/account-select', + route: 'workspaces/:policyID/accounting/quickbooks-online/export/company-card-expense-account/account-select', getRoute: (policyID: string | undefined, backTo?: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_COMPANY_CARD_EXPENSE_ACCOUNT_SELECT route'); } - return getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/quickbooks-online/export/company-card-expense-account/account-select` as const, backTo); + return getUrlWithBackToParam(`workspaces/${policyID}/accounting/quickbooks-online/export/company-card-expense-account/account-select` as const, backTo); }, }, POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_NON_REIMBURSABLE_DEFAULT_VENDOR_SELECT: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-online/export/company-card-expense-account/default-vendor-select', + route: 'workspaces/:policyID/accounting/quickbooks-online/export/company-card-expense-account/default-vendor-select', getRoute: (policyID: string | undefined) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_NON_REIMBURSABLE_DEFAULT_VENDOR_SELECT route'); } - return `settings/workspaces/${policyID}/accounting/quickbooks-online/export/company-card-expense-account/default-vendor-select` as const; + return `workspaces/${policyID}/accounting/quickbooks-online/export/company-card-expense-account/default-vendor-select` as const; }, }, POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_COMPANY_CARD_EXPENSE_SELECT: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-online/export/company-card-expense-account/card-select', + route: 'workspaces/:policyID/accounting/quickbooks-online/export/company-card-expense-account/card-select', getRoute: (policyID: string | undefined, backTo?: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_COMPANY_CARD_EXPENSE_SELECT route'); } - return getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/quickbooks-online/export/company-card-expense-account/card-select` as const, backTo); + return getUrlWithBackToParam(`workspaces/${policyID}/accounting/quickbooks-online/export/company-card-expense-account/card-select` as const, backTo); }, }, POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECT: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-online/export/invoice-account-select', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/quickbooks-online/export/invoice-account-select` as const, backTo), + route: 'workspaces/:policyID/accounting/quickbooks-online/export/invoice-account-select', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/accounting/quickbooks-online/export/invoice-account-select` as const, backTo), }, POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_PREFERRED_EXPORTER: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-online/export/preferred-exporter', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/quickbooks-online/export/preferred-exporter` as const, backTo), + route: 'workspaces/:policyID/accounting/quickbooks-online/export/preferred-exporter', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/accounting/quickbooks-online/export/preferred-exporter` as const, backTo), }, POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_EXPORT_OUT_OF_POCKET_EXPENSES: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-online/export/out-of-pocket-expense', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/quickbooks-online/export/out-of-pocket-expense` as const, backTo), + route: 'workspaces/:policyID/accounting/quickbooks-online/export/out-of-pocket-expense', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/accounting/quickbooks-online/export/out-of-pocket-expense` as const, backTo), }, POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_EXPORT_OUT_OF_POCKET_EXPENSES_ACCOUNT_SELECT: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-online/export/out-of-pocket-expense/account-select', + route: 'workspaces/:policyID/accounting/quickbooks-online/export/out-of-pocket-expense/account-select', getRoute: (policyID: string, backTo?: string) => - getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/quickbooks-online/export/out-of-pocket-expense/account-select` as const, backTo), + getUrlWithBackToParam(`workspaces/${policyID}/accounting/quickbooks-online/export/out-of-pocket-expense/account-select` as const, backTo), }, POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_EXPORT_OUT_OF_POCKET_EXPENSES_SELECT: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-online/export/out-of-pocket-expense/entity-select', + route: 'workspaces/:policyID/accounting/quickbooks-online/export/out-of-pocket-expense/entity-select', getRoute: (policyID: string, backTo?: string) => - getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/quickbooks-online/export/out-of-pocket-expense/entity-select` as const, backTo), + getUrlWithBackToParam(`workspaces/${policyID}/accounting/quickbooks-online/export/out-of-pocket-expense/entity-select` as const, backTo), }, POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_EXPORT_DATE_SELECT: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-online/export/date-select', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/quickbooks-online/export/date-select` as const, backTo), + route: 'workspaces/:policyID/accounting/quickbooks-online/export/date-select', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/accounting/quickbooks-online/export/date-select` as const, backTo), }, POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_COMPANY_CARD_EXPENSE_ACCOUNT_SELECT: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-desktop/export/company-card-expense-account/account-select', + route: 'workspaces/:policyID/accounting/quickbooks-desktop/export/company-card-expense-account/account-select', getRoute: (policyID?: string, backTo?: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_COMPANY_CARD_EXPENSE_ACCOUNT_SELECT route'); } - return getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/quickbooks-desktop/export/company-card-expense-account/account-select` as const, backTo); + return getUrlWithBackToParam(`workspaces/${policyID}/accounting/quickbooks-desktop/export/company-card-expense-account/account-select` as const, backTo); }, }, POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_COMPANY_CARD_EXPENSE_SELECT: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-desktop/export/company-card-expense-account/card-select', + route: 'workspaces/:policyID/accounting/quickbooks-desktop/export/company-card-expense-account/card-select', getRoute: (policyID?: string, backTo?: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_COMPANY_CARD_EXPENSE_SELECT route'); } - return getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/quickbooks-desktop/export/company-card-expense-account/card-select` as const, backTo); + return getUrlWithBackToParam(`workspaces/${policyID}/accounting/quickbooks-desktop/export/company-card-expense-account/card-select` as const, backTo); }, }, POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_NON_REIMBURSABLE_DEFAULT_VENDOR_SELECT: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-desktop/export/company-card-expense-account/default-vendor-select', + route: 'workspaces/:policyID/accounting/quickbooks-desktop/export/company-card-expense-account/default-vendor-select', getRoute: (policyID?: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_NON_REIMBURSABLE_DEFAULT_VENDOR_SELECT route'); } - return `settings/workspaces/${policyID}/accounting/quickbooks-desktop/export/company-card-expense-account/default-vendor-select` as const; + return `workspaces/${policyID}/accounting/quickbooks-desktop/export/company-card-expense-account/default-vendor-select` as const; }, }, POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_COMPANY_CARD_EXPENSE_ACCOUNT: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-desktop/export/company-card-expense-account', + route: 'workspaces/:policyID/accounting/quickbooks-desktop/export/company-card-expense-account', getRoute: (policyID?: string, backTo?: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_COMPANY_CARD_EXPENSE_ACCOUNT route'); } - return getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/quickbooks-desktop/export/company-card-expense-account` as const, backTo); + return getUrlWithBackToParam(`workspaces/${policyID}/accounting/quickbooks-desktop/export/company-card-expense-account` as const, backTo); }, }, WORKSPACE_ACCOUNTING_QUICKBOOKS_DESKTOP_ADVANCED: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-desktop/advanced', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-desktop/advanced` as const, + route: 'workspaces/:policyID/accounting/quickbooks-desktop/advanced', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/quickbooks-desktop/advanced` as const, }, POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_EXPORT_DATE_SELECT: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-desktop/export/date-select', + route: 'workspaces/:policyID/accounting/quickbooks-desktop/export/date-select', getRoute: (policyID?: string, backTo?: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_EXPORT_DATE_SELECT route'); } - return getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/quickbooks-desktop/export/date-select` as const, backTo); + return getUrlWithBackToParam(`workspaces/${policyID}/accounting/quickbooks-desktop/export/date-select` as const, backTo); }, }, POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_PREFERRED_EXPORTER: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-desktop/export/preferred-exporter', + route: 'workspaces/:policyID/accounting/quickbooks-desktop/export/preferred-exporter', getRoute: (policyID?: string, backTo?: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_PREFERRED_EXPORTER route'); } - return getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/quickbooks-desktop/export/preferred-exporter` as const, backTo); + return getUrlWithBackToParam(`workspaces/${policyID}/accounting/quickbooks-desktop/export/preferred-exporter` as const, backTo); }, }, POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_EXPORT_OUT_OF_POCKET_EXPENSES: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-desktop/export/out-of-pocket-expense', + route: 'workspaces/:policyID/accounting/quickbooks-desktop/export/out-of-pocket-expense', getRoute: (policyID?: string, backTo?: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_EXPORT_OUT_OF_POCKET_EXPENSES route'); } - return getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/quickbooks-desktop/export/out-of-pocket-expense` as const, backTo); + return getUrlWithBackToParam(`workspaces/${policyID}/accounting/quickbooks-desktop/export/out-of-pocket-expense` as const, backTo); }, }, POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_EXPORT_OUT_OF_POCKET_EXPENSES_ACCOUNT_SELECT: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-desktop/export/out-of-pocket-expense/account-select', + route: 'workspaces/:policyID/accounting/quickbooks-desktop/export/out-of-pocket-expense/account-select', getRoute: (policyID?: string, backTo?: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_EXPORT_OUT_OF_POCKET_EXPENSES_ACCOUNT_SELECT route'); } - return getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/quickbooks-desktop/export/out-of-pocket-expense/account-select` as const, backTo); + return getUrlWithBackToParam(`workspaces/${policyID}/accounting/quickbooks-desktop/export/out-of-pocket-expense/account-select` as const, backTo); }, }, POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_EXPORT_OUT_OF_POCKET_EXPENSES_SELECT: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-desktop/export/out-of-pocket-expense/entity-select', + route: 'workspaces/:policyID/accounting/quickbooks-desktop/export/out-of-pocket-expense/entity-select', getRoute: (policyID?: string, backTo?: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_EXPORT_OUT_OF_POCKET_EXPENSES_SELECT route'); } - return getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/quickbooks-desktop/export/out-of-pocket-expense/entity-select` as const, backTo); + return getUrlWithBackToParam(`workspaces/${policyID}/accounting/quickbooks-desktop/export/out-of-pocket-expense/entity-select` as const, backTo); }, }, POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_EXPORT: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-desktop/export', + route: 'workspaces/:policyID/accounting/quickbooks-desktop/export', getRoute: (policyID: string | undefined, backTo?: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_EXPORT route'); } - return getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/quickbooks-desktop/export` as const, backTo, false); + return getUrlWithBackToParam(`workspaces/${policyID}/accounting/quickbooks-desktop/export` as const, backTo, false); }, }, POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_SETUP_MODAL: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-desktop/setup-modal', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-desktop/setup-modal` as const, + route: 'workspaces/:policyID/accounting/quickbooks-desktop/setup-modal', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/quickbooks-desktop/setup-modal` as const, }, POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_SETUP_REQUIRED_DEVICE_MODAL: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-desktop/setup-required-device', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-desktop/setup-required-device` as const, + route: 'workspaces/:policyID/accounting/quickbooks-desktop/setup-required-device', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/quickbooks-desktop/setup-required-device` as const, }, POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_TRIGGER_FIRST_SYNC: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-desktop/trigger-first-sync', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-desktop/trigger-first-sync` as const, + route: 'workspaces/:policyID/accounting/quickbooks-desktop/trigger-first-sync', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/quickbooks-desktop/trigger-first-sync` as const, }, POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_IMPORT: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-desktop/import', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-desktop/import` as const, + route: 'workspaces/:policyID/accounting/quickbooks-desktop/import', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/quickbooks-desktop/import` as const, }, POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_CHART_OF_ACCOUNTS: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-desktop/import/accounts', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-desktop/import/accounts` as const, + route: 'workspaces/:policyID/accounting/quickbooks-desktop/import/accounts', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/quickbooks-desktop/import/accounts` as const, }, POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_CLASSES: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-desktop/import/classes', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-desktop/import/classes` as const, + route: 'workspaces/:policyID/accounting/quickbooks-desktop/import/classes', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/quickbooks-desktop/import/classes` as const, }, POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_CLASSES_DISPLAYED_AS: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-desktop/import/classes/displayed_as', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-desktop/import/classes/displayed_as` as const, + route: 'workspaces/:policyID/accounting/quickbooks-desktop/import/classes/displayed_as', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/quickbooks-desktop/import/classes/displayed_as` as const, }, POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_CUSTOMERS: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-desktop/import/customers', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-desktop/import/customers` as const, + route: 'workspaces/:policyID/accounting/quickbooks-desktop/import/customers', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/quickbooks-desktop/import/customers` as const, }, POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_CUSTOMERS_DISPLAYED_AS: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-desktop/import/customers/displayed_as', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-desktop/import/customers/displayed_as` as const, + route: 'workspaces/:policyID/accounting/quickbooks-desktop/import/customers/displayed_as', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/quickbooks-desktop/import/customers/displayed_as` as const, }, POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_ITEMS: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-desktop/import/items', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-desktop/import/items` as const, + route: 'workspaces/:policyID/accounting/quickbooks-desktop/import/items', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/quickbooks-desktop/import/items` as const, }, WORKSPACE_OVERVIEW_NAME: { - route: 'settings/workspaces/:policyID/overview/name', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/overview/name` as const, + route: 'workspaces/:policyID/overview/name', + getRoute: (policyID: string) => `workspaces/${policyID}/overview/name` as const, }, WORKSPACE_OVERVIEW_DESCRIPTION: { - route: 'settings/workspaces/:policyID/overview/description', + route: 'workspaces/:policyID/overview/description', getRoute: (policyID: string | undefined) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_OVERVIEW_DESCRIPTION route'); } - return `settings/workspaces/${policyID}/overview/description` as const; + return `workspaces/${policyID}/overview/description` as const; }, }, WORKSPACE_OVERVIEW_SHARE: { - route: 'settings/workspaces/:policyID/overview/share', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/overview/share` as const, + route: 'workspaces/:policyID/overview/share', + getRoute: (policyID: string) => `workspaces/${policyID}/overview/share` as const, }, WORKSPACE_AVATAR: { - route: 'settings/workspaces/:policyID/avatar', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/avatar` as const, + route: 'workspaces/:policyID/avatar', + getRoute: (policyID: string) => `workspaces/${policyID}/avatar` as const, }, WORKSPACE_JOIN_USER: { - route: 'settings/workspaces/:policyID/join', - getRoute: (policyID: string, inviterEmail: string) => `settings/workspaces/${policyID}/join?email=${inviterEmail}` as const, + route: 'workspaces/:policyID/join', + getRoute: (policyID: string, inviterEmail: string) => `workspaces/${policyID}/join?email=${inviterEmail}` as const, }, WORKSPACE_SETTINGS_CURRENCY: { - route: 'settings/workspaces/:policyID/settings/currency', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/settings/currency` as const, + route: 'workspaces/:policyID/settings/currency', + getRoute: (policyID: string) => `workspaces/${policyID}/settings/currency` as const, }, WORKSPACE_WORKFLOWS: { - route: 'settings/workspaces/:policyID/workflows', + route: 'workspaces/:policyID/workflows', getRoute: (policyID: string | undefined) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_WORKFLOWS route'); } - return `settings/workspaces/${policyID}/workflows` as const; + return `workspaces/${policyID}/workflows` as const; }, }, WORKSPACE_WORKFLOWS_APPROVALS_NEW: { - route: 'settings/workspaces/:policyID/workflows/approvals/new', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/workflows/approvals/new` as const, + route: 'workspaces/:policyID/workflows/approvals/new', + getRoute: (policyID: string) => `workspaces/${policyID}/workflows/approvals/new` as const, }, WORKSPACE_WORKFLOWS_APPROVALS_EDIT: { - route: 'settings/workspaces/:policyID/workflows/approvals/:firstApproverEmail/edit', - getRoute: (policyID: string, firstApproverEmail: string) => `settings/workspaces/${policyID}/workflows/approvals/${encodeURIComponent(firstApproverEmail)}/edit` as const, + route: 'workspaces/:policyID/workflows/approvals/:firstApproverEmail/edit', + getRoute: (policyID: string, firstApproverEmail: string) => `workspaces/${policyID}/workflows/approvals/${encodeURIComponent(firstApproverEmail)}/edit` as const, }, WORKSPACE_WORKFLOWS_APPROVALS_EXPENSES_FROM: { - route: 'settings/workspaces/:policyID/workflows/approvals/expenses-from', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/workflows/approvals/expenses-from` as const, backTo), + route: 'workspaces/:policyID/workflows/approvals/expenses-from', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/workflows/approvals/expenses-from` as const, backTo), }, WORKSPACE_WORKFLOWS_APPROVALS_APPROVER: { - route: 'settings/workspaces/:policyID/workflows/approvals/approver', + route: 'workspaces/:policyID/workflows/approvals/approver', getRoute: (policyID: string, approverIndex: number, backTo?: string) => - getUrlWithBackToParam(`settings/workspaces/${policyID}/workflows/approvals/approver?approverIndex=${approverIndex}` as const, backTo), + getUrlWithBackToParam(`workspaces/${policyID}/workflows/approvals/approver?approverIndex=${approverIndex}` as const, backTo), }, WORKSPACE_WORKFLOWS_PAYER: { - route: 'settings/workspaces/:policyID/workflows/payer', - getRoute: (policyId: string) => `settings/workspaces/${policyId}/workflows/payer` as const, + route: 'workspaces/:policyID/workflows/payer', + getRoute: (policyId: string) => `workspaces/${policyId}/workflows/payer` as const, }, WORKSPACE_WORKFLOWS_AUTOREPORTING_FREQUENCY: { - route: 'settings/workspaces/:policyID/workflows/auto-reporting-frequency', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/workflows/auto-reporting-frequency` as const, + route: 'workspaces/:policyID/workflows/auto-reporting-frequency', + getRoute: (policyID: string) => `workspaces/${policyID}/workflows/auto-reporting-frequency` as const, }, WORKSPACE_WORKFLOWS_AUTOREPORTING_MONTHLY_OFFSET: { - route: 'settings/workspaces/:policyID/workflows/auto-reporting-frequency/monthly-offset', + route: 'workspaces/:policyID/workflows/auto-reporting-frequency/monthly-offset', getRoute: (policyID: string | undefined) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_WORKFLOWS_AUTOREPORTING_MONTHLY_OFFSET route'); } - return `settings/workspaces/${policyID}/workflows/auto-reporting-frequency/monthly-offset` as const; + return `workspaces/${policyID}/workflows/auto-reporting-frequency/monthly-offset` as const; }, }, WORKSPACE_INVOICES: { - route: 'settings/workspaces/:policyID/invoices', + route: 'workspaces/:policyID/invoices', getRoute: (policyID: string | undefined) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_INVOICES route'); } - return `settings/workspaces/${policyID}/invoices` as const; + return `workspaces/${policyID}/invoices` as const; }, }, WORKSPACE_INVOICES_COMPANY_NAME: { - route: 'settings/workspaces/:policyID/invoices/company-name', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/invoices/company-name` as const, + route: 'workspaces/:policyID/invoices/company-name', + getRoute: (policyID: string) => `workspaces/${policyID}/invoices/company-name` as const, }, WORKSPACE_INVOICES_COMPANY_WEBSITE: { - route: 'settings/workspaces/:policyID/invoices/company-website', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/invoices/company-website` as const, + route: 'workspaces/:policyID/invoices/company-website', + getRoute: (policyID: string) => `workspaces/${policyID}/invoices/company-website` as const, }, WORKSPACE_MEMBERS: { - route: 'settings/workspaces/:policyID/members', + route: 'workspaces/:policyID/members', getRoute: (policyID: string | undefined) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_MEMBERS route'); } - return `settings/workspaces/${policyID}/members` as const; + return `workspaces/${policyID}/members` as const; }, }, WORKSPACE_MEMBERS_IMPORT: { - route: 'settings/workspaces/:policyID/members/import', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/members/import` as const, + route: 'workspaces/:policyID/members/import', + getRoute: (policyID: string) => `workspaces/${policyID}/members/import` as const, }, WORKSPACE_MEMBERS_IMPORTED: { - route: 'settings/workspaces/:policyID/members/imported', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/members/imported` as const, + route: 'workspaces/:policyID/members/imported', + getRoute: (policyID: string) => `workspaces/${policyID}/members/imported` as const, }, POLICY_ACCOUNTING: { - route: 'settings/workspaces/:policyID/accounting', + route: 'workspaces/:policyID/accounting', getRoute: (policyID: string | undefined, newConnectionName?: ConnectionName, integrationToDisconnect?: ConnectionName, shouldDisconnectIntegrationBeforeConnecting?: boolean) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING route'); @@ -1353,564 +1352,557 @@ const ROUTES = { queryParams += `&shouldDisconnectIntegrationBeforeConnecting=${shouldDisconnectIntegrationBeforeConnecting}`; } } - return `settings/workspaces/${policyID}/accounting${queryParams}` as const; + return `workspaces/${policyID}/accounting${queryParams}` as const; }, }, WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_ADVANCED: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-online/advanced', + route: 'workspaces/:policyID/accounting/quickbooks-online/advanced', getRoute: (policyID: string | undefined) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_ADVANCED route'); } - return `settings/workspaces/${policyID}/accounting/quickbooks-online/advanced` as const; + return `workspaces/${policyID}/accounting/quickbooks-online/advanced` as const; }, }, WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_ACCOUNT_SELECTOR: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-online/account-selector', - getRoute: (policyID: string | undefined) => `settings/workspaces/${policyID}/accounting/quickbooks-online/account-selector` as const, + route: 'workspaces/:policyID/accounting/quickbooks-online/account-selector', + getRoute: (policyID: string | undefined) => `workspaces/${policyID}/accounting/quickbooks-online/account-selector` as const, }, WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECTOR: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-online/invoice-account-selector', - getRoute: (policyID: string | undefined) => `settings/workspaces/${policyID}/accounting/quickbooks-online/invoice-account-selector` as const, + route: 'workspaces/:policyID/accounting/quickbooks-online/invoice-account-selector', + getRoute: (policyID: string | undefined) => `workspaces/${policyID}/accounting/quickbooks-online/invoice-account-selector` as const, }, WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_AUTO_SYNC: { - route: 'settings/workspaces/:policyID/connections/quickbooks-online/advanced/autosync', - getRoute: (policyID: string | undefined, backTo?: string) => - getUrlWithBackToParam(`settings/workspaces/${policyID}/connections/quickbooks-online/advanced/autosync` as const, backTo), + route: 'workspaces/:policyID/connections/quickbooks-online/advanced/autosync', + getRoute: (policyID: string | undefined, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/connections/quickbooks-online/advanced/autosync` as const, backTo), }, WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_ACCOUNTING_METHOD: { - route: 'settings/workspaces/:policyID/connections/quickbooks-online/advanced/autosync/accounting-method', + route: 'workspaces/:policyID/connections/quickbooks-online/advanced/autosync/accounting-method', getRoute: (policyID: string | undefined, backTo?: string) => - getUrlWithBackToParam(`settings/workspaces/${policyID}/connections/quickbooks-online/advanced/autosync/accounting-method` as const, backTo), + getUrlWithBackToParam(`workspaces/${policyID}/connections/quickbooks-online/advanced/autosync/accounting-method` as const, backTo), }, WORKSPACE_ACCOUNTING_CARD_RECONCILIATION: { - route: 'settings/workspaces/:policyID/accounting/:connection/card-reconciliation', - getRoute: (policyID: string, connection?: ValueOf) => - `settings/workspaces/${policyID}/accounting/${connection as string}/card-reconciliation` as const, + route: 'workspaces/:policyID/accounting/:connection/card-reconciliation', + getRoute: (policyID: string, connection?: ValueOf) => `workspaces/${policyID}/accounting/${connection as string}/card-reconciliation` as const, }, WORKSPACE_ACCOUNTING_RECONCILIATION_ACCOUNT_SETTINGS: { - route: 'settings/workspaces/:policyID/accounting/:connection/card-reconciliation/account', + route: 'workspaces/:policyID/accounting/:connection/card-reconciliation/account', getRoute: (policyID: string | undefined, connection?: ValueOf) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_ACCOUNTING_RECONCILIATION_ACCOUNT_SETTINGS route'); } - return `settings/workspaces/${policyID}/accounting/${connection as string}/card-reconciliation/account` as const; + return `workspaces/${policyID}/accounting/${connection as string}/card-reconciliation/account` as const; }, }, WORKSPACE_CATEGORIES: { - route: 'settings/workspaces/:policyID/categories', + route: 'workspaces/:policyID/categories', getRoute: (policyID: string | undefined) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_CATEGORIES route'); } - return `settings/workspaces/${policyID}/categories` as const; + return `workspaces/${policyID}/categories` as const; }, }, WORKSPACE_CATEGORY_SETTINGS: { - route: 'settings/workspaces/:policyID/category/:categoryName', - getRoute: (policyID: string, categoryName: string) => `settings/workspaces/${policyID}/category/${encodeURIComponent(categoryName)}` as const, + route: 'workspaces/:policyID/category/:categoryName', + getRoute: (policyID: string, categoryName: string) => `workspaces/${policyID}/category/${encodeURIComponent(categoryName)}` as const, }, WORKSPACE_UPGRADE: { - route: 'settings/workspaces/:policyID?/upgrade/:featureName?', + route: 'workspaces/:policyID?/upgrade/:featureName?', getRoute: (policyID?: string, featureName?: string, backTo?: string) => - getUrlWithBackToParam( - policyID ? (`settings/workspaces/${policyID}/upgrade/${encodeURIComponent(featureName ?? '')}` as const) : (`settings/workspaces/upgrade` as const), - backTo, - ), + getUrlWithBackToParam(policyID ? (`workspaces/${policyID}/upgrade/${encodeURIComponent(featureName ?? '')}` as const) : (`workspaces/upgrade` as const), backTo), }, WORKSPACE_DOWNGRADE: { - route: 'settings/workspaces/:policyID?/downgrade/', - getRoute: (policyID?: string, backTo?: string) => - getUrlWithBackToParam(policyID ? (`settings/workspaces/${policyID}/downgrade/` as const) : (`settings/workspaces/downgrade` as const), backTo), + route: 'workspaces/:policyID?/downgrade/', + getRoute: (policyID?: string, backTo?: string) => getUrlWithBackToParam(policyID ? (`workspaces/${policyID}/downgrade/` as const) : (`workspaces/downgrade` as const), backTo), }, WORKSPACE_PAY_AND_DOWNGRADE: { - route: 'settings/workspaces/pay-and-downgrade/', - getRoute: (backTo?: string) => getUrlWithBackToParam(`settings/workspaces/pay-and-downgrade` as const, backTo), + route: 'workspaces/pay-and-downgrade/', + getRoute: (backTo?: string) => getUrlWithBackToParam(`workspaces/pay-and-downgrade` as const, backTo), }, WORKSPACE_CATEGORIES_SETTINGS: { - route: 'settings/workspaces/:policyID/categories/settings', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/categories/settings` as const, + route: 'workspaces/:policyID/categories/settings', + getRoute: (policyID: string) => `workspaces/${policyID}/categories/settings` as const, }, WORKSPACE_CATEGORIES_IMPORT: { - route: 'settings/workspaces/:policyID/categories/import', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/categories/import` as const, + route: 'workspaces/:policyID/categories/import', + getRoute: (policyID: string) => `workspaces/${policyID}/categories/import` as const, }, WORKSPACE_CATEGORIES_IMPORTED: { - route: 'settings/workspaces/:policyID/categories/imported', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/categories/imported` as const, + route: 'workspaces/:policyID/categories/imported', + getRoute: (policyID: string) => `workspaces/${policyID}/categories/imported` as const, }, WORKSPACE_CATEGORY_CREATE: { - route: 'settings/workspaces/:policyID/categories/new', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/categories/new` as const, + route: 'workspaces/:policyID/categories/new', + getRoute: (policyID: string) => `workspaces/${policyID}/categories/new` as const, }, WORKSPACE_CATEGORY_EDIT: { - route: 'settings/workspaces/:policyID/category/:categoryName/edit', - getRoute: (policyID: string, categoryName: string) => `settings/workspaces/${policyID}/category/${encodeURIComponent(categoryName)}/edit` as const, + route: 'workspaces/:policyID/category/:categoryName/edit', + getRoute: (policyID: string, categoryName: string) => `workspaces/${policyID}/category/${encodeURIComponent(categoryName)}/edit` as const, }, WORKSPACE_CATEGORY_PAYROLL_CODE: { - route: 'settings/workspaces/:policyID/category/:categoryName/payroll-code', - getRoute: (policyID: string, categoryName: string) => `settings/workspaces/${policyID}/category/${encodeURIComponent(categoryName)}/payroll-code` as const, + route: 'workspaces/:policyID/category/:categoryName/payroll-code', + getRoute: (policyID: string, categoryName: string) => `workspaces/${policyID}/category/${encodeURIComponent(categoryName)}/payroll-code` as const, }, WORKSPACE_CATEGORY_GL_CODE: { - route: 'settings/workspaces/:policyID/category/:categoryName/gl-code', - getRoute: (policyID: string, categoryName: string) => `settings/workspaces/${policyID}/category/${encodeURIComponent(categoryName)}/gl-code` as const, + route: 'workspaces/:policyID/category/:categoryName/gl-code', + getRoute: (policyID: string, categoryName: string) => `workspaces/${policyID}/category/${encodeURIComponent(categoryName)}/gl-code` as const, }, WORKSPACE_CATEGORY_DEFAULT_TAX_RATE: { - route: 'settings/workspaces/:policyID/category/:categoryName/tax-rate', - getRoute: (policyID: string, categoryName: string) => `settings/workspaces/${policyID}/category/${encodeURIComponent(categoryName)}/tax-rate` as const, + route: 'workspaces/:policyID/category/:categoryName/tax-rate', + getRoute: (policyID: string, categoryName: string) => `workspaces/${policyID}/category/${encodeURIComponent(categoryName)}/tax-rate` as const, }, WORKSPACE_CATEGORY_FLAG_AMOUNTS_OVER: { - route: 'settings/workspaces/:policyID/category/:categoryName/flag-amounts', - getRoute: (policyID: string, categoryName: string) => `settings/workspaces/${policyID}/category/${encodeURIComponent(categoryName)}/flag-amounts` as const, + route: 'workspaces/:policyID/category/:categoryName/flag-amounts', + getRoute: (policyID: string, categoryName: string) => `workspaces/${policyID}/category/${encodeURIComponent(categoryName)}/flag-amounts` as const, }, WORKSPACE_CATEGORY_DESCRIPTION_HINT: { - route: 'settings/workspaces/:policyID/category/:categoryName/description-hint', - getRoute: (policyID: string, categoryName: string) => `settings/workspaces/${policyID}/category/${encodeURIComponent(categoryName)}/description-hint` as const, + route: 'workspaces/:policyID/category/:categoryName/description-hint', + getRoute: (policyID: string, categoryName: string) => `workspaces/${policyID}/category/${encodeURIComponent(categoryName)}/description-hint` as const, }, WORKSPACE_CATEGORY_REQUIRE_RECEIPTS_OVER: { - route: 'settings/workspaces/:policyID/category/:categoryName/require-receipts-over', - getRoute: (policyID: string, categoryName: string) => `settings/workspaces/${policyID}/category/${encodeURIComponent(categoryName)}/require-receipts-over` as const, + route: 'workspaces/:policyID/category/:categoryName/require-receipts-over', + getRoute: (policyID: string, categoryName: string) => `workspaces/${policyID}/category/${encodeURIComponent(categoryName)}/require-receipts-over` as const, }, WORKSPACE_CATEGORY_APPROVER: { - route: 'settings/workspaces/:policyID/category/:categoryName/approver', - getRoute: (policyID: string, categoryName: string) => `settings/workspaces/${policyID}/category/${encodeURIComponent(categoryName)}/approver` as const, + route: 'workspaces/:policyID/category/:categoryName/approver', + getRoute: (policyID: string, categoryName: string) => `workspaces/${policyID}/category/${encodeURIComponent(categoryName)}/approver` as const, }, WORKSPACE_MORE_FEATURES: { - route: 'settings/workspaces/:policyID/more-features', + route: 'workspaces/:policyID/more-features', getRoute: (policyID: string | undefined) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_MORE_FEATURES route'); } - return `settings/workspaces/${policyID}/more-features` as const; + return `workspaces/${policyID}/more-features` as const; }, }, WORKSPACE_TAGS: { - route: 'settings/workspaces/:policyID/tags', + route: 'workspaces/:policyID/tags', getRoute: (policyID: string | undefined) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_TAGS route'); } - return `settings/workspaces/${policyID}/tags` as const; + return `workspaces/${policyID}/tags` as const; }, }, WORKSPACE_TAG_CREATE: { - route: 'settings/workspaces/:policyID/tags/new', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/tags/new` as const, + route: 'workspaces/:policyID/tags/new', + getRoute: (policyID: string) => `workspaces/${policyID}/tags/new` as const, }, WORKSPACE_TAGS_SETTINGS: { - route: 'settings/workspaces/:policyID/tags/settings', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/tags/settings` as const, + route: 'workspaces/:policyID/tags/settings', + getRoute: (policyID: string) => `workspaces/${policyID}/tags/settings` as const, }, WORKSPACE_EDIT_TAGS: { - route: 'settings/workspaces/:policyID/tags/:orderWeight/edit', - getRoute: (policyID: string, orderWeight: number, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/tags/${orderWeight}/edit` as const, backTo), + route: 'workspaces/:policyID/tags/:orderWeight/edit', + getRoute: (policyID: string, orderWeight: number, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/tags/${orderWeight}/edit` as const, backTo), }, WORKSPACE_TAG_EDIT: { - route: 'settings/workspaces/:policyID/tag/:orderWeight/:tagName/edit', - getRoute: (policyID: string, orderWeight: number, tagName: string) => `settings/workspaces/${policyID}/tag/${orderWeight}/${encodeURIComponent(tagName)}/edit` as const, + route: 'workspaces/:policyID/tag/:orderWeight/:tagName/edit', + getRoute: (policyID: string, orderWeight: number, tagName: string) => `workspaces/${policyID}/tag/${orderWeight}/${encodeURIComponent(tagName)}/edit` as const, }, WORKSPACE_TAG_SETTINGS: { - route: 'settings/workspaces/:policyID/tag/:orderWeight/:tagName', + route: 'workspaces/:policyID/tag/:orderWeight/:tagName', getRoute: (policyID: string, orderWeight: number, tagName: string, parentTagsFilter?: string) => { let queryParams = ''; if (parentTagsFilter) { queryParams += `?parentTagsFilter=${parentTagsFilter}`; } - return `settings/workspaces/${policyID}/tag/${orderWeight}/${encodeURIComponent(tagName)}${queryParams}` as const; + return `workspaces/${policyID}/tag/${orderWeight}/${encodeURIComponent(tagName)}${queryParams}` as const; }, }, WORKSPACE_TAG_APPROVER: { - route: 'settings/workspaces/:policyID/tag/:orderWeight/:tagName/approver', - getRoute: (policyID: string, orderWeight: number, tagName: string) => `settings/workspaces/${policyID}/tag/${orderWeight}/${encodeURIComponent(tagName)}/approver` as const, + route: 'workspaces/:policyID/tag/:orderWeight/:tagName/approver', + getRoute: (policyID: string, orderWeight: number, tagName: string) => `workspaces/${policyID}/tag/${orderWeight}/${encodeURIComponent(tagName)}/approver` as const, }, WORKSPACE_TAG_LIST_VIEW: { - route: 'settings/workspaces/:policyID/tag-list/:orderWeight', - getRoute: (policyID: string, orderWeight: number) => `settings/workspaces/${policyID}/tag-list/${orderWeight}` as const, + route: 'workspaces/:policyID/tag-list/:orderWeight', + getRoute: (policyID: string, orderWeight: number) => `workspaces/${policyID}/tag-list/${orderWeight}` as const, }, WORKSPACE_TAG_GL_CODE: { - route: 'settings/workspaces/:policyID/tag/:orderWeight/:tagName/gl-code', - getRoute: (policyID: string, orderWeight: number, tagName: string) => `settings/workspaces/${policyID}/tag/${orderWeight}/${encodeURIComponent(tagName)}/gl-code` as const, + route: 'workspaces/:policyID/tag/:orderWeight/:tagName/gl-code', + getRoute: (policyID: string, orderWeight: number, tagName: string) => `workspaces/${policyID}/tag/${orderWeight}/${encodeURIComponent(tagName)}/gl-code` as const, }, WORKSPACE_TAGS_IMPORT: { - route: 'settings/workspaces/:policyID/tags/import', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/tags/import` as const, + route: 'workspaces/:policyID/tags/import', + getRoute: (policyID: string) => `workspaces/${policyID}/tags/import` as const, }, WORKSPACE_MULTI_LEVEL_TAGS_IMPORT_SETTINGS: { - route: 'settings/workspaces/:policyID/tags/import/multi-level', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/tags/import/multi-level` as const, + route: 'workspaces/:policyID/tags/import/multi-level', + getRoute: (policyID: string) => `workspaces/${policyID}/tags/import/multi-level` as const, }, WORKSPACE_TAGS_IMPORT_OPTIONS: { - route: 'settings/workspaces/:policyID/tags/import/import-options', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/tags/import/import-options` as const, + route: 'workspaces/:policyID/tags/import/import-options', + getRoute: (policyID: string) => `workspaces/${policyID}/tags/import/import-options` as const, }, WORKSPACE_TAGS_IMPORTED: { - route: 'settings/workspaces/:policyID/tags/imported', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/tags/imported` as const, + route: 'workspaces/:policyID/tags/imported', + getRoute: (policyID: string) => `workspaces/${policyID}/tags/imported` as const, }, WORKSPACE_TAGS_IMPORTED_MULTI_LEVEL: { - route: 'settings/workspaces/:policyID/tags/imported/multi-level', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/tags/imported/multi-level` as const, + route: 'workspaces/:policyID/tags/imported/multi-level', + getRoute: (policyID: string) => `workspaces/${policyID}/tags/imported/multi-level` as const, }, WORKSPACE_TAXES: { - route: 'settings/workspaces/:policyID/taxes', + route: 'workspaces/:policyID/taxes', getRoute: (policyID: string | undefined) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_TAXES route'); } - return `settings/workspaces/${policyID}/taxes` as const; + return `workspaces/${policyID}/taxes` as const; }, }, WORKSPACE_TAXES_SETTINGS: { - route: 'settings/workspaces/:policyID/taxes/settings', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/taxes/settings` as const, + route: 'workspaces/:policyID/taxes/settings', + getRoute: (policyID: string) => `workspaces/${policyID}/taxes/settings` as const, }, WORKSPACE_TAXES_SETTINGS_WORKSPACE_CURRENCY_DEFAULT: { - route: 'settings/workspaces/:policyID/taxes/settings/workspace-currency', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/taxes/settings/workspace-currency` as const, + route: 'workspaces/:policyID/taxes/settings/workspace-currency', + getRoute: (policyID: string) => `workspaces/${policyID}/taxes/settings/workspace-currency` as const, }, WORKSPACE_TAXES_SETTINGS_FOREIGN_CURRENCY_DEFAULT: { - route: 'settings/workspaces/:policyID/taxes/settings/foreign-currency', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/taxes/settings/foreign-currency` as const, + route: 'workspaces/:policyID/taxes/settings/foreign-currency', + getRoute: (policyID: string) => `workspaces/${policyID}/taxes/settings/foreign-currency` as const, }, WORKSPACE_TAXES_SETTINGS_CUSTOM_TAX_NAME: { - route: 'settings/workspaces/:policyID/taxes/settings/tax-name', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/taxes/settings/tax-name` as const, + route: 'workspaces/:policyID/taxes/settings/tax-name', + getRoute: (policyID: string) => `workspaces/${policyID}/taxes/settings/tax-name` as const, }, WORKSPACE_MEMBER_DETAILS: { - route: 'settings/workspaces/:policyID/members/:accountID', - getRoute: (policyID: string, accountID: number) => `settings/workspaces/${policyID}/members/${accountID}` as const, + route: 'workspaces/:policyID/members/:accountID', + getRoute: (policyID: string, accountID: number) => `workspaces/${policyID}/members/${accountID}` as const, }, WORKSPACE_CUSTOM_FIELDS: { - route: 'settings/workspaces/:policyID/members/:accountID/:customFieldType', - getRoute: (policyID: string, accountID: number, customFieldType: CustomFieldType) => `/settings/workspaces/${policyID}/members/${accountID}/${customFieldType}` as const, + route: 'workspaces/:policyID/members/:accountID/:customFieldType', + getRoute: (policyID: string, accountID: number, customFieldType: CustomFieldType) => `/workspaces/${policyID}/members/${accountID}/${customFieldType}` as const, }, WORKSPACE_MEMBER_NEW_CARD: { - route: 'settings/workspaces/:policyID/members/:accountID/new-card', - getRoute: (policyID: string, accountID: number) => `settings/workspaces/${policyID}/members/${accountID}/new-card` as const, + route: 'workspaces/:policyID/members/:accountID/new-card', + getRoute: (policyID: string, accountID: number) => `workspaces/${policyID}/members/${accountID}/new-card` as const, }, WORKSPACE_MEMBER_ROLE_SELECTION: { - route: 'settings/workspaces/:policyID/members/:accountID/role-selection', - getRoute: (policyID: string, accountID: number) => `settings/workspaces/${policyID}/members/${accountID}/role-selection` as const, + route: 'workspaces/:policyID/members/:accountID/role-selection', + getRoute: (policyID: string, accountID: number) => `workspaces/${policyID}/members/${accountID}/role-selection` as const, }, WORKSPACE_OWNER_CHANGE_SUCCESS: { - route: 'settings/workspaces/:policyID/change-owner/:accountID/success', - getRoute: (policyID: string, accountID: number) => `settings/workspaces/${policyID}/change-owner/${accountID}/success` as const, + route: 'workspaces/:policyID/change-owner/:accountID/success', + getRoute: (policyID: string, accountID: number) => `workspaces/${policyID}/change-owner/${accountID}/success` as const, }, WORKSPACE_OWNER_CHANGE_ERROR: { - route: 'settings/workspaces/:policyID/change-owner/:accountID/failure', - getRoute: (policyID: string, accountID: number) => `settings/workspaces/${policyID}/change-owner/${accountID}/failure` as const, + route: 'workspaces/:policyID/change-owner/:accountID/failure', + getRoute: (policyID: string, accountID: number) => `workspaces/${policyID}/change-owner/${accountID}/failure` as const, }, WORKSPACE_OWNER_CHANGE_CHECK: { - route: 'settings/workspaces/:policyID/change-owner/:accountID/:error', + route: 'workspaces/:policyID/change-owner/:accountID/:error', getRoute: (policyID: string, accountID: number, error: ValueOf) => - `settings/workspaces/${policyID}/change-owner/${accountID}/${error as string}` as const, + `workspaces/${policyID}/change-owner/${accountID}/${error as string}` as const, }, WORKSPACE_TAX_CREATE: { - route: 'settings/workspaces/:policyID/taxes/new', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/taxes/new` as const, + route: 'workspaces/:policyID/taxes/new', + getRoute: (policyID: string) => `workspaces/${policyID}/taxes/new` as const, }, WORKSPACE_TAX_EDIT: { - route: 'settings/workspaces/:policyID/tax/:taxID', - getRoute: (policyID: string, taxID: string) => `settings/workspaces/${policyID}/tax/${encodeURIComponent(taxID)}` as const, + route: 'workspaces/:policyID/tax/:taxID', + getRoute: (policyID: string, taxID: string) => `workspaces/${policyID}/tax/${encodeURIComponent(taxID)}` as const, }, WORKSPACE_TAX_NAME: { - route: 'settings/workspaces/:policyID/tax/:taxID/name', - getRoute: (policyID: string, taxID: string) => `settings/workspaces/${policyID}/tax/${encodeURIComponent(taxID)}/name` as const, + route: 'workspaces/:policyID/tax/:taxID/name', + getRoute: (policyID: string, taxID: string) => `workspaces/${policyID}/tax/${encodeURIComponent(taxID)}/name` as const, }, WORKSPACE_TAX_VALUE: { - route: 'settings/workspaces/:policyID/tax/:taxID/value', - getRoute: (policyID: string, taxID: string) => `settings/workspaces/${policyID}/tax/${encodeURIComponent(taxID)}/value` as const, + route: 'workspaces/:policyID/tax/:taxID/value', + getRoute: (policyID: string, taxID: string) => `workspaces/${policyID}/tax/${encodeURIComponent(taxID)}/value` as const, }, WORKSPACE_TAX_CODE: { - route: 'settings/workspaces/:policyID/tax/:taxID/tax-code', - getRoute: (policyID: string, taxID: string) => `settings/workspaces/${policyID}/tax/${encodeURIComponent(taxID)}/tax-code` as const, + route: 'workspaces/:policyID/tax/:taxID/tax-code', + getRoute: (policyID: string, taxID: string) => `workspaces/${policyID}/tax/${encodeURIComponent(taxID)}/tax-code` as const, }, WORKSPACE_REPORT_FIELDS: { - route: 'settings/workspaces/:policyID/reportFields', + route: 'workspaces/:policyID/reportFields', getRoute: (policyID: string | undefined) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_REPORT_FIELDS route'); } - return `settings/workspaces/${policyID}/reportFields` as const; + return `workspaces/${policyID}/reportFields` as const; }, }, WORKSPACE_CREATE_REPORT_FIELD: { - route: 'settings/workspaces/:policyID/reportFields/new', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/reportFields/new` as const, + route: 'workspaces/:policyID/reportFields/new', + getRoute: (policyID: string) => `workspaces/${policyID}/reportFields/new` as const, }, WORKSPACE_REPORT_FIELDS_SETTINGS: { - route: 'settings/workspaces/:policyID/reportFields/:reportFieldID/edit', - getRoute: (policyID: string, reportFieldID: string) => `settings/workspaces/${policyID}/reportFields/${encodeURIComponent(reportFieldID)}/edit` as const, + route: 'workspaces/:policyID/reportFields/:reportFieldID/edit', + getRoute: (policyID: string, reportFieldID: string) => `workspaces/${policyID}/reportFields/${encodeURIComponent(reportFieldID)}/edit` as const, }, WORKSPACE_REPORT_FIELDS_LIST_VALUES: { - route: 'settings/workspaces/:policyID/reportFields/listValues/:reportFieldID?', - getRoute: (policyID: string, reportFieldID?: string) => `settings/workspaces/${policyID}/reportFields/listValues/${reportFieldID ? encodeURIComponent(reportFieldID) : ''}` as const, + route: 'workspaces/:policyID/reportFields/listValues/:reportFieldID?', + getRoute: (policyID: string, reportFieldID?: string) => `workspaces/${policyID}/reportFields/listValues/${reportFieldID ? encodeURIComponent(reportFieldID) : ''}` as const, }, WORKSPACE_REPORT_FIELDS_ADD_VALUE: { - route: 'settings/workspaces/:policyID/reportFields/addValue/:reportFieldID?', - getRoute: (policyID: string, reportFieldID?: string) => `settings/workspaces/${policyID}/reportFields/addValue/${reportFieldID ? encodeURIComponent(reportFieldID) : ''}` as const, + route: 'workspaces/:policyID/reportFields/addValue/:reportFieldID?', + getRoute: (policyID: string, reportFieldID?: string) => `workspaces/${policyID}/reportFields/addValue/${reportFieldID ? encodeURIComponent(reportFieldID) : ''}` as const, }, WORKSPACE_REPORT_FIELDS_VALUE_SETTINGS: { - route: 'settings/workspaces/:policyID/reportFields/:valueIndex/:reportFieldID?', + route: 'workspaces/:policyID/reportFields/:valueIndex/:reportFieldID?', getRoute: (policyID: string, valueIndex: number, reportFieldID?: string) => - `settings/workspaces/${policyID}/reportFields/${valueIndex}/${reportFieldID ? encodeURIComponent(reportFieldID) : ''}` as const, + `workspaces/${policyID}/reportFields/${valueIndex}/${reportFieldID ? encodeURIComponent(reportFieldID) : ''}` as const, }, WORKSPACE_REPORT_FIELDS_EDIT_VALUE: { - route: 'settings/workspaces/:policyID/reportFields/new/:valueIndex/edit', - getRoute: (policyID: string, valueIndex: number) => `settings/workspaces/${policyID}/reportFields/new/${valueIndex}/edit` as const, + route: 'workspaces/:policyID/reportFields/new/:valueIndex/edit', + getRoute: (policyID: string, valueIndex: number) => `workspaces/${policyID}/reportFields/new/${valueIndex}/edit` as const, }, WORKSPACE_EDIT_REPORT_FIELDS_INITIAL_VALUE: { - route: 'settings/workspaces/:policyID/reportFields/:reportFieldID/edit/initialValue', - getRoute: (policyID: string, reportFieldID: string) => `settings/workspaces/${policyID}/reportFields/${encodeURIComponent(reportFieldID)}/edit/initialValue` as const, + route: 'workspaces/:policyID/reportFields/:reportFieldID/edit/initialValue', + getRoute: (policyID: string, reportFieldID: string) => `workspaces/${policyID}/reportFields/${encodeURIComponent(reportFieldID)}/edit/initialValue` as const, }, WORKSPACE_COMPANY_CARDS: { - route: 'settings/workspaces/:policyID/company-cards', + route: 'workspaces/:policyID/company-cards', getRoute: (policyID: string | undefined) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_COMPANY_CARDS route'); } - return `settings/workspaces/${policyID}/company-cards` as const; + return `workspaces/${policyID}/company-cards` as const; }, }, WORKSPACE_COMPANY_CARDS_BANK_CONNECTION: { - route: 'settings/workspaces/:policyID/company-cards/:bankName/bank-connection', + route: 'workspaces/:policyID/company-cards/:bankName/bank-connection', getRoute: (policyID: string | undefined, bankName: string, backTo: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_COMPANY_CARDS_BANK_CONNECTION route'); } - return getUrlWithBackToParam(`settings/workspaces/${policyID}/company-cards/${bankName}/bank-connection`, backTo); + return getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${bankName}/bank-connection`, backTo); }, }, WORKSPACE_COMPANY_CARDS_ADD_NEW: { - route: 'settings/workspaces/:policyID/company-cards/add-card-feed', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/company-cards/add-card-feed`, backTo), + route: 'workspaces/:policyID/company-cards/add-card-feed', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/add-card-feed`, backTo), }, WORKSPACE_COMPANY_CARDS_SELECT_FEED: { - route: 'settings/workspaces/:policyID/company-cards/select-feed', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/company-cards/select-feed` as const, + route: 'workspaces/:policyID/company-cards/select-feed', + getRoute: (policyID: string) => `workspaces/${policyID}/company-cards/select-feed` as const, }, WORKSPACE_COMPANY_CARDS_ASSIGN_CARD: { - route: 'settings/workspaces/:policyID/company-cards/:feed/assign-card', - getRoute: (policyID: string, feed: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/company-cards/${feed}/assign-card`, backTo), + route: 'workspaces/:policyID/company-cards/:feed/assign-card', + getRoute: (policyID: string, feed: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${feed}/assign-card`, backTo), }, WORKSPACE_COMPANY_CARDS_TRANSACTION_START_DATE: { - route: 'settings/workspaces/:policyID/company-cards/:feed/assign-card/transaction-start-date', - getRoute: (policyID: string, feed: string, backTo?: string) => - getUrlWithBackToParam(`settings/workspaces/${policyID}/company-cards/${feed}/assign-card/transaction-start-date`, backTo), + route: 'workspaces/:policyID/company-cards/:feed/assign-card/transaction-start-date', + getRoute: (policyID: string, feed: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${feed}/assign-card/transaction-start-date`, backTo), }, WORKSPACE_COMPANY_CARD_DETAILS: { - route: 'settings/workspaces/:policyID/company-cards/:bank/:cardID', - getRoute: (policyID: string, cardID: string, bank: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/company-cards/${bank}/${cardID}`, backTo), + route: 'workspaces/:policyID/company-cards/:bank/:cardID', + getRoute: (policyID: string, cardID: string, bank: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${bank}/${cardID}`, backTo), }, WORKSPACE_COMPANY_CARD_NAME: { - route: 'settings/workspaces/:policyID/company-cards/:bank/:cardID/edit/name', - getRoute: (policyID: string, cardID: string, bank: string) => `settings/workspaces/${policyID}/company-cards/${bank}/${cardID}/edit/name` as const, + route: 'workspaces/:policyID/company-cards/:bank/:cardID/edit/name', + getRoute: (policyID: string, cardID: string, bank: string) => `workspaces/${policyID}/company-cards/${bank}/${cardID}/edit/name` as const, }, WORKSPACE_COMPANY_CARD_EXPORT: { - route: 'settings/workspaces/:policyID/company-cards/:bank/:cardID/edit/export', + route: 'workspaces/:policyID/company-cards/:bank/:cardID/edit/export', getRoute: (policyID: string, cardID: string, bank: string, backTo?: string) => - getUrlWithBackToParam(`settings/workspaces/${policyID}/company-cards/${bank}/${cardID}/edit/export`, backTo, false), + getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${bank}/${cardID}/edit/export`, backTo, false), }, WORKSPACE_EXPENSIFY_CARD: { - route: 'settings/workspaces/:policyID/expensify-card', + route: 'workspaces/:policyID/expensify-card', getRoute: (policyID: string | undefined) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_EXPENSIFY_CARD route'); } - return `settings/workspaces/${policyID}/expensify-card` as const; + return `workspaces/${policyID}/expensify-card` as const; }, }, WORKSPACE_EXPENSIFY_CARD_DETAILS: { - route: 'settings/workspaces/:policyID/expensify-card/:cardID', - getRoute: (policyID: string, cardID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/expensify-card/${cardID}`, backTo), + route: 'workspaces/:policyID/expensify-card/:cardID', + getRoute: (policyID: string, cardID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/expensify-card/${cardID}`, backTo), }, EXPENSIFY_CARD_DETAILS: { route: 'settings/:policyID/expensify-card/:cardID', getRoute: (policyID: string, cardID: string, backTo?: string) => getUrlWithBackToParam(`settings/${policyID}/expensify-card/${cardID}`, backTo), }, WORKSPACE_EXPENSIFY_CARD_NAME: { - route: 'settings/workspaces/:policyID/expensify-card/:cardID/edit/name', - getRoute: (policyID: string, cardID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/expensify-card/${cardID}/edit/name`, backTo), + route: 'workspaces/:policyID/expensify-card/:cardID/edit/name', + getRoute: (policyID: string, cardID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/expensify-card/${cardID}/edit/name`, backTo), }, EXPENSIFY_CARD_NAME: { route: 'settings/:policyID/expensify-card/:cardID/edit/name', getRoute: (policyID: string, cardID: string, backTo?: string) => getUrlWithBackToParam(`settings/${policyID}/expensify-card/${cardID}/edit/name`, backTo), }, WORKSPACE_EXPENSIFY_CARD_LIMIT: { - route: 'settings/workspaces/:policyID/expensify-card/:cardID/edit/limit', - getRoute: (policyID: string, cardID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/expensify-card/${cardID}/edit/limit`, backTo), + route: 'workspaces/:policyID/expensify-card/:cardID/edit/limit', + getRoute: (policyID: string, cardID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/expensify-card/${cardID}/edit/limit`, backTo), }, EXPENSIFY_CARD_LIMIT: { route: 'settings/:policyID/expensify-card/:cardID/edit/limit', getRoute: (policyID: string, cardID: string, backTo?: string) => getUrlWithBackToParam(`settings/${policyID}/expensify-card/${cardID}/edit/limit`, backTo), }, WORKSPACE_EXPENSIFY_CARD_LIMIT_TYPE: { - route: 'settings/workspaces/:policyID/expensify-card/:cardID/edit/limit-type', - getRoute: (policyID: string, cardID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/expensify-card/${cardID}/edit/limit-type`, backTo), + route: 'workspaces/:policyID/expensify-card/:cardID/edit/limit-type', + getRoute: (policyID: string, cardID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/expensify-card/${cardID}/edit/limit-type`, backTo), }, EXPENSIFY_CARD_LIMIT_TYPE: { route: 'settings/:policyID/expensify-card/:cardID/edit/limit-type', getRoute: (policyID: string, cardID: string, backTo?: string) => getUrlWithBackToParam(`settings/${policyID}/expensify-card/${cardID}/edit/limit-type`, backTo), }, WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW: { - route: 'settings/workspaces/:policyID/expensify-card/issue-new', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/expensify-card/issue-new`, backTo), + route: 'workspaces/:policyID/expensify-card/issue-new', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/expensify-card/issue-new`, backTo), }, WORKSPACE_EXPENSIFY_CARD_BANK_ACCOUNT: { - route: 'settings/workspaces/:policyID/expensify-card/choose-bank-account', + route: 'workspaces/:policyID/expensify-card/choose-bank-account', getRoute: (policyID: string | undefined) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_EXPENSIFY_CARD_BANK_ACCOUNT route'); } - return `settings/workspaces/${policyID}/expensify-card/choose-bank-account` as const; + return `workspaces/${policyID}/expensify-card/choose-bank-account` as const; }, }, WORKSPACE_EXPENSIFY_CARD_SETTINGS: { - route: 'settings/workspaces/:policyID/expensify-card/settings', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/expensify-card/settings` as const, + route: 'workspaces/:policyID/expensify-card/settings', + getRoute: (policyID: string) => `workspaces/${policyID}/expensify-card/settings` as const, }, WORKSPACE_EXPENSIFY_CARD_SETTINGS_ACCOUNT: { - route: 'settings/workspaces/:policyID/expensify-card/settings/account', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/expensify-card/settings/account`, backTo), + route: 'workspaces/:policyID/expensify-card/settings/account', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/expensify-card/settings/account`, backTo), }, WORKSPACE_EXPENSIFY_CARD_SELECT_FEED: { - route: 'settings/workspaces/:policyID/expensify-card/select-feed', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/expensify-card/select-feed`, backTo), + route: 'workspaces/:policyID/expensify-card/select-feed', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/expensify-card/select-feed`, backTo), }, WORKSPACE_EXPENSIFY_CARD_SETTINGS_FREQUENCY: { - route: 'settings/workspaces/:policyID/expensify-card/settings/frequency', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/expensify-card/settings/frequency` as const, + route: 'workspaces/:policyID/expensify-card/settings/frequency', + getRoute: (policyID: string) => `workspaces/${policyID}/expensify-card/settings/frequency` as const, }, WORKSPACE_COMPANY_CARDS_SETTINGS: { - route: 'settings/workspaces/:policyID/company-cards/settings', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/company-cards/settings` as const, + route: 'workspaces/:policyID/company-cards/settings', + getRoute: (policyID: string) => `workspaces/${policyID}/company-cards/settings` as const, }, WORKSPACE_COMPANY_CARDS_SETTINGS_FEED_NAME: { - route: 'settings/workspaces/:policyID/company-cards/settings/feed-name', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/company-cards/settings/feed-name` as const, + route: 'workspaces/:policyID/company-cards/settings/feed-name', + getRoute: (policyID: string) => `workspaces/${policyID}/company-cards/settings/feed-name` as const, }, WORKSPACE_RULES: { - route: 'settings/workspaces/:policyID/rules', + route: 'workspaces/:policyID/rules', getRoute: (policyID: string | undefined) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_RULES route'); } - return `settings/workspaces/${policyID}/rules` as const; + return `workspaces/${policyID}/rules` as const; }, }, WORKSPACE_DISTANCE_RATES: { - route: 'settings/workspaces/:policyID/distance-rates', + route: 'workspaces/:policyID/distance-rates', getRoute: (policyID: string | undefined) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_DISTANCE_RATES route'); } - return `settings/workspaces/${policyID}/distance-rates` as const; + return `workspaces/${policyID}/distance-rates` as const; }, }, WORKSPACE_CREATE_DISTANCE_RATE: { - route: 'settings/workspaces/:policyID/distance-rates/new', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/distance-rates/new` as const, + route: 'workspaces/:policyID/distance-rates/new', + getRoute: (policyID: string) => `workspaces/${policyID}/distance-rates/new` as const, }, WORKSPACE_DISTANCE_RATES_SETTINGS: { - route: 'settings/workspaces/:policyID/distance-rates/settings', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/distance-rates/settings` as const, + route: 'workspaces/:policyID/distance-rates/settings', + getRoute: (policyID: string) => `workspaces/${policyID}/distance-rates/settings` as const, }, WORKSPACE_DISTANCE_RATE_DETAILS: { - route: 'settings/workspaces/:policyID/distance-rates/:rateID', - getRoute: (policyID: string, rateID: string) => `settings/workspaces/${policyID}/distance-rates/${rateID}` as const, + route: 'workspaces/:policyID/distance-rates/:rateID', + getRoute: (policyID: string, rateID: string) => `workspaces/${policyID}/distance-rates/${rateID}` as const, }, WORKSPACE_DISTANCE_RATE_EDIT: { - route: 'settings/workspaces/:policyID/distance-rates/:rateID/edit', - getRoute: (policyID: string, rateID: string) => `settings/workspaces/${policyID}/distance-rates/${rateID}/edit` as const, + route: 'workspaces/:policyID/distance-rates/:rateID/edit', + getRoute: (policyID: string, rateID: string) => `workspaces/${policyID}/distance-rates/${rateID}/edit` as const, }, WORKSPACE_DISTANCE_RATE_TAX_RECLAIMABLE_ON_EDIT: { - route: 'settings/workspaces/:policyID/distance-rates/:rateID/tax-reclaimable/edit', - getRoute: (policyID: string, rateID: string) => `settings/workspaces/${policyID}/distance-rates/${rateID}/tax-reclaimable/edit` as const, + route: 'workspaces/:policyID/distance-rates/:rateID/tax-reclaimable/edit', + getRoute: (policyID: string, rateID: string) => `workspaces/${policyID}/distance-rates/${rateID}/tax-reclaimable/edit` as const, }, WORKSPACE_DISTANCE_RATE_TAX_RATE_EDIT: { - route: 'settings/workspaces/:policyID/distance-rates/:rateID/tax-rate/edit', - getRoute: (policyID: string, rateID: string) => `settings/workspaces/${policyID}/distance-rates/${rateID}/tax-rate/edit` as const, + route: 'workspaces/:policyID/distance-rates/:rateID/tax-rate/edit', + getRoute: (policyID: string, rateID: string) => `workspaces/${policyID}/distance-rates/${rateID}/tax-rate/edit` as const, }, WORKSPACE_PER_DIEM: { - route: 'settings/workspaces/:policyID/per-diem', + route: 'workspaces/:policyID/per-diem', getRoute: (policyID: string | undefined, backTo?: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_PER_DIEM route'); } - return getUrlWithBackToParam(`settings/workspaces/${policyID}/per-diem`, backTo); + return getUrlWithBackToParam(`workspaces/${policyID}/per-diem`, backTo); }, }, WORKSPACE_PER_DIEM_IMPORT: { - route: 'settings/workspaces/:policyID/per-diem/import', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/per-diem/import` as const, + route: 'workspaces/:policyID/per-diem/import', + getRoute: (policyID: string) => `workspaces/${policyID}/per-diem/import` as const, }, WORKSPACE_PER_DIEM_IMPORTED: { - route: 'settings/workspaces/:policyID/per-diem/imported', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/per-diem/imported` as const, + route: 'workspaces/:policyID/per-diem/imported', + getRoute: (policyID: string) => `workspaces/${policyID}/per-diem/imported` as const, }, WORKSPACE_PER_DIEM_SETTINGS: { - route: 'settings/workspaces/:policyID/per-diem/settings', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/per-diem/settings` as const, + route: 'workspaces/:policyID/per-diem/settings', + getRoute: (policyID: string) => `workspaces/${policyID}/per-diem/settings` as const, }, WORKSPACE_PER_DIEM_DETAILS: { - route: 'settings/workspaces/:policyID/per-diem/details/:rateID/:subRateID', - getRoute: (policyID: string, rateID: string, subRateID: string) => `settings/workspaces/${policyID}/per-diem/details/${rateID}/${subRateID}` as const, + route: 'workspaces/:policyID/per-diem/details/:rateID/:subRateID', + getRoute: (policyID: string, rateID: string, subRateID: string) => `workspaces/${policyID}/per-diem/details/${rateID}/${subRateID}` as const, }, WORKSPACE_PER_DIEM_EDIT_DESTINATION: { - route: 'settings/workspaces/:policyID/per-diem/edit/destination/:rateID/:subRateID', - getRoute: (policyID: string, rateID: string, subRateID: string) => `settings/workspaces/${policyID}/per-diem/edit/destination/${rateID}/${subRateID}` as const, + route: 'workspaces/:policyID/per-diem/edit/destination/:rateID/:subRateID', + getRoute: (policyID: string, rateID: string, subRateID: string) => `workspaces/${policyID}/per-diem/edit/destination/${rateID}/${subRateID}` as const, }, WORKSPACE_PER_DIEM_EDIT_SUBRATE: { - route: 'settings/workspaces/:policyID/per-diem/edit/subrate/:rateID/:subRateID', - getRoute: (policyID: string, rateID: string, subRateID: string) => `settings/workspaces/${policyID}/per-diem/edit/subrate/${rateID}/${subRateID}` as const, + route: 'workspaces/:policyID/per-diem/edit/subrate/:rateID/:subRateID', + getRoute: (policyID: string, rateID: string, subRateID: string) => `workspaces/${policyID}/per-diem/edit/subrate/${rateID}/${subRateID}` as const, }, WORKSPACE_PER_DIEM_EDIT_AMOUNT: { - route: 'settings/workspaces/:policyID/per-diem/edit/amount/:rateID/:subRateID', - getRoute: (policyID: string, rateID: string, subRateID: string) => `settings/workspaces/${policyID}/per-diem/edit/amount/${rateID}/${subRateID}` as const, + route: 'workspaces/:policyID/per-diem/edit/amount/:rateID/:subRateID', + getRoute: (policyID: string, rateID: string, subRateID: string) => `workspaces/${policyID}/per-diem/edit/amount/${rateID}/${subRateID}` as const, }, WORKSPACE_PER_DIEM_EDIT_CURRENCY: { - route: 'settings/workspaces/:policyID/per-diem/edit/currency/:rateID/:subRateID', - getRoute: (policyID: string, rateID: string, subRateID: string) => `settings/workspaces/${policyID}/per-diem/edit/currency/${rateID}/${subRateID}` as const, + route: 'workspaces/:policyID/per-diem/edit/currency/:rateID/:subRateID', + getRoute: (policyID: string, rateID: string, subRateID: string) => `workspaces/${policyID}/per-diem/edit/currency/${rateID}/${subRateID}` as const, }, RULES_CUSTOM_NAME: { - route: 'settings/workspaces/:policyID/rules/name', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/rules/name` as const, + route: 'workspaces/:policyID/rules/name', + getRoute: (policyID: string) => `workspaces/${policyID}/rules/name` as const, }, RULES_AUTO_APPROVE_REPORTS_UNDER: { - route: 'settings/workspaces/:policyID/rules/auto-approve', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/rules/auto-approve` as const, + route: 'workspaces/:policyID/rules/auto-approve', + getRoute: (policyID: string) => `workspaces/${policyID}/rules/auto-approve` as const, }, RULES_RANDOM_REPORT_AUDIT: { - route: 'settings/workspaces/:policyID/rules/audit', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/rules/audit` as const, + route: 'workspaces/:policyID/rules/audit', + getRoute: (policyID: string) => `workspaces/${policyID}/rules/audit` as const, }, RULES_AUTO_PAY_REPORTS_UNDER: { - route: 'settings/workspaces/:policyID/rules/auto-pay', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/rules/auto-pay` as const, + route: 'workspaces/:policyID/rules/auto-pay', + getRoute: (policyID: string) => `workspaces/${policyID}/rules/auto-pay` as const, }, RULES_RECEIPT_REQUIRED_AMOUNT: { - route: 'settings/workspaces/:policyID/rules/receipt-required-amount', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/rules/receipt-required-amount` as const, + route: 'workspaces/:policyID/rules/receipt-required-amount', + getRoute: (policyID: string) => `workspaces/${policyID}/rules/receipt-required-amount` as const, }, RULES_MAX_EXPENSE_AMOUNT: { - route: 'settings/workspaces/:policyID/rules/max-expense-amount', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/rules/max-expense-amount` as const, + route: 'workspaces/:policyID/rules/max-expense-amount', + getRoute: (policyID: string) => `workspaces/${policyID}/rules/max-expense-amount` as const, }, RULES_MAX_EXPENSE_AGE: { - route: 'settings/workspaces/:policyID/rules/max-expense-age', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/rules/max-expense-age` as const, + route: 'workspaces/:policyID/rules/max-expense-age', + getRoute: (policyID: string) => `workspaces/${policyID}/rules/max-expense-age` as const, }, RULES_BILLABLE_DEFAULT: { - route: 'settings/workspaces/:policyID/rules/billable', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/rules/billable` as const, + route: 'workspaces/:policyID/rules/billable', + getRoute: (policyID: string) => `workspaces/${policyID}/rules/billable` as const, }, RULES_PROHIBITED_DEFAULT: { - route: 'settings/workspaces/:policyID/rules/prohibited', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/rules/prohibited` as const, + route: 'workspaces/:policyID/rules/prohibited', + getRoute: (policyID: string) => `workspaces/${policyID}/rules/prohibited` as const, }, RULES_CUSTOM: { - route: 'settings/workspaces/:policyID/rules/custom', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/rules/custom` as const, + route: 'workspaces/:policyID/rules/custom', + getRoute: (policyID: string) => `workspaces/${policyID}/rules/custom` as const, }, // Referral program promotion REFERRAL_DETAILS_MODAL: { @@ -2106,111 +2098,111 @@ const ROUTES = { getRoute: (threadReportID: string, backTo?: string) => getUrlWithBackToParam(`r/${threadReportID}/duplicates/confirm` as const, backTo), }, POLICY_ACCOUNTING_XERO_IMPORT: { - route: 'settings/workspaces/:policyID/accounting/xero/import', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/xero/import` as const, + route: 'workspaces/:policyID/accounting/xero/import', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/xero/import` as const, }, POLICY_ACCOUNTING_XERO_CHART_OF_ACCOUNTS: { - route: 'settings/workspaces/:policyID/accounting/xero/import/accounts', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/xero/import/accounts` as const, + route: 'workspaces/:policyID/accounting/xero/import/accounts', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/xero/import/accounts` as const, }, POLICY_ACCOUNTING_XERO_ORGANIZATION: { - route: 'settings/workspaces/:policyID/accounting/xero/organization/:currentOrganizationID', + route: 'workspaces/:policyID/accounting/xero/organization/:currentOrganizationID', getRoute: (policyID: string | undefined, currentOrganizationID: string | undefined) => { if (!policyID || !currentOrganizationID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_XERO_ORGANIZATION route'); } - return `settings/workspaces/${policyID}/accounting/xero/organization/${currentOrganizationID}` as const; + return `workspaces/${policyID}/accounting/xero/organization/${currentOrganizationID}` as const; }, }, POLICY_ACCOUNTING_XERO_TRACKING_CATEGORIES: { - route: 'settings/workspaces/:policyID/accounting/xero/import/tracking-categories', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/xero/import/tracking-categories` as const, + route: 'workspaces/:policyID/accounting/xero/import/tracking-categories', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/xero/import/tracking-categories` as const, }, POLICY_ACCOUNTING_XERO_TRACKING_CATEGORIES_MAP: { - route: 'settings/workspaces/:policyID/accounting/xero/import/tracking-categories/mapping/:categoryId/:categoryName', + route: 'workspaces/:policyID/accounting/xero/import/tracking-categories/mapping/:categoryId/:categoryName', getRoute: (policyID: string, categoryId: string, categoryName: string) => - `settings/workspaces/${policyID}/accounting/xero/import/tracking-categories/mapping/${categoryId}/${encodeURIComponent(categoryName)}` as const, + `workspaces/${policyID}/accounting/xero/import/tracking-categories/mapping/${categoryId}/${encodeURIComponent(categoryName)}` as const, }, POLICY_ACCOUNTING_XERO_CUSTOMER: { - route: 'settings/workspaces/:policyID/accounting/xero/import/customers', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/xero/import/customers` as const, + route: 'workspaces/:policyID/accounting/xero/import/customers', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/xero/import/customers` as const, }, POLICY_ACCOUNTING_XERO_TAXES: { - route: 'settings/workspaces/:policyID/accounting/xero/import/taxes', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/xero/import/taxes` as const, + route: 'workspaces/:policyID/accounting/xero/import/taxes', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/xero/import/taxes` as const, }, POLICY_ACCOUNTING_XERO_EXPORT: { - route: 'settings/workspaces/:policyID/accounting/xero/export', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/xero/export` as const, backTo, false), + route: 'workspaces/:policyID/accounting/xero/export', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/accounting/xero/export` as const, backTo, false), }, POLICY_ACCOUNTING_XERO_PREFERRED_EXPORTER_SELECT: { - route: 'settings/workspaces/:policyID/connections/xero/export/preferred-exporter/select', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/connections/xero/export/preferred-exporter/select` as const, backTo), + route: 'workspaces/:policyID/connections/xero/export/preferred-exporter/select', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/connections/xero/export/preferred-exporter/select` as const, backTo), }, POLICY_ACCOUNTING_XERO_EXPORT_PURCHASE_BILL_DATE_SELECT: { - route: 'settings/workspaces/:policyID/accounting/xero/export/purchase-bill-date-select', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/xero/export/purchase-bill-date-select` as const, backTo), + route: 'workspaces/:policyID/accounting/xero/export/purchase-bill-date-select', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/accounting/xero/export/purchase-bill-date-select` as const, backTo), }, POLICY_ACCOUNTING_XERO_EXPORT_BANK_ACCOUNT_SELECT: { - route: 'settings/workspaces/:policyID/accounting/xero/export/bank-account-select', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/xero/export/bank-account-select` as const, backTo), + route: 'workspaces/:policyID/accounting/xero/export/bank-account-select', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/accounting/xero/export/bank-account-select` as const, backTo), }, POLICY_ACCOUNTING_XERO_ADVANCED: { - route: 'settings/workspaces/:policyID/accounting/xero/advanced', + route: 'workspaces/:policyID/accounting/xero/advanced', getRoute: (policyID: string | undefined) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_XERO_ADVANCED route'); } - return `settings/workspaces/${policyID}/accounting/xero/advanced` as const; + return `workspaces/${policyID}/accounting/xero/advanced` as const; }, }, POLICY_ACCOUNTING_XERO_BILL_STATUS_SELECTOR: { - route: 'settings/workspaces/:policyID/accounting/xero/export/purchase-bill-status-selector', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/xero/export/purchase-bill-status-selector` as const, backTo), + route: 'workspaces/:policyID/accounting/xero/export/purchase-bill-status-selector', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/accounting/xero/export/purchase-bill-status-selector` as const, backTo), }, POLICY_ACCOUNTING_XERO_INVOICE_SELECTOR: { - route: 'settings/workspaces/:policyID/accounting/xero/advanced/invoice-account-selector', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/xero/advanced/invoice-account-selector` as const, + route: 'workspaces/:policyID/accounting/xero/advanced/invoice-account-selector', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/xero/advanced/invoice-account-selector` as const, }, POLICY_ACCOUNTING_XERO_BILL_PAYMENT_ACCOUNT_SELECTOR: { - route: 'settings/workspaces/:policyID/accounting/xero/advanced/bill-payment-account-selector', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/xero/advanced/bill-payment-account-selector` as const, + route: 'workspaces/:policyID/accounting/xero/advanced/bill-payment-account-selector', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/xero/advanced/bill-payment-account-selector` as const, }, POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_IMPORT: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import` as const, + route: 'workspaces/:policyID/accounting/quickbooks-online/import', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/quickbooks-online/import` as const, }, POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_CHART_OF_ACCOUNTS: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import/accounts', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import/accounts` as const, + route: 'workspaces/:policyID/accounting/quickbooks-online/import/accounts', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/quickbooks-online/import/accounts` as const, }, POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_CLASSES: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import/classes', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import/classes` as const, + route: 'workspaces/:policyID/accounting/quickbooks-online/import/classes', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/quickbooks-online/import/classes` as const, }, POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_CLASSES_DISPLAYED_AS: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import/classes/displayed-as', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import/classes/displayed-as` as const, + route: 'workspaces/:policyID/accounting/quickbooks-online/import/classes/displayed-as', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/quickbooks-online/import/classes/displayed-as` as const, }, POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_CUSTOMERS: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import/customers', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import/customers` as const, + route: 'workspaces/:policyID/accounting/quickbooks-online/import/customers', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/quickbooks-online/import/customers` as const, }, POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_CUSTOMERS_DISPLAYED_AS: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import/customers/displayed-as', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import/customers/displayed-as` as const, + route: 'workspaces/:policyID/accounting/quickbooks-online/import/customers/displayed-as', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/quickbooks-online/import/customers/displayed-as` as const, }, POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_LOCATIONS: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import/locations', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import/locations` as const, + route: 'workspaces/:policyID/accounting/quickbooks-online/import/locations', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/quickbooks-online/import/locations` as const, }, POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_LOCATIONS_DISPLAYED_AS: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import/locations/displayed-as', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import/locations/displayed-as` as const, + route: 'workspaces/:policyID/accounting/quickbooks-online/import/locations/displayed-as', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/quickbooks-online/import/locations/displayed-as` as const, }, POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_TAXES: { - route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import/taxes', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import/taxes` as const, + route: 'workspaces/:policyID/accounting/quickbooks-online/import/taxes', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/quickbooks-online/import/taxes` as const, }, RESTRICTED_ACTION: { route: 'restricted-action/workspace/:policyID', @@ -2218,298 +2210,296 @@ const ROUTES = { }, MISSING_PERSONAL_DETAILS: 'missing-personal-details', POLICY_ACCOUNTING_NETSUITE_SUBSIDIARY_SELECTOR: { - route: 'settings/workspaces/:policyID/accounting/netsuite/subsidiary-selector', + route: 'workspaces/:policyID/accounting/netsuite/subsidiary-selector', getRoute: (policyID: string | undefined) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_NETSUITE_SUBSIDIARY_SELECTOR route'); } - return `settings/workspaces/${policyID}/accounting/netsuite/subsidiary-selector` as const; + return `workspaces/${policyID}/accounting/netsuite/subsidiary-selector` as const; }, }, POLICY_ACCOUNTING_NETSUITE_EXISTING_CONNECTIONS: { - route: 'settings/workspaces/:policyID/accounting/netsuite/existing-connections', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/netsuite/existing-connections` as const, + route: 'workspaces/:policyID/accounting/netsuite/existing-connections', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/netsuite/existing-connections` as const, }, POLICY_ACCOUNTING_NETSUITE_TOKEN_INPUT: { - route: 'settings/workspaces/:policyID/accounting/netsuite/token-input', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/netsuite/token-input` as const, + route: 'workspaces/:policyID/accounting/netsuite/token-input', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/netsuite/token-input` as const, }, POLICY_ACCOUNTING_NETSUITE_IMPORT: { - route: 'settings/workspaces/:policyID/accounting/netsuite/import', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/netsuite/import` as const, + route: 'workspaces/:policyID/accounting/netsuite/import', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/netsuite/import` as const, }, POLICY_ACCOUNTING_NETSUITE_IMPORT_MAPPING: { - route: 'settings/workspaces/:policyID/accounting/netsuite/import/mapping/:importField', + route: 'workspaces/:policyID/accounting/netsuite/import/mapping/:importField', getRoute: (policyID: string, importField: TupleToUnion) => - `settings/workspaces/${policyID}/accounting/netsuite/import/mapping/${importField as string}` as const, + `workspaces/${policyID}/accounting/netsuite/import/mapping/${importField as string}` as const, }, POLICY_ACCOUNTING_NETSUITE_IMPORT_CUSTOM_FIELD_MAPPING: { - route: 'settings/workspaces/:policyID/accounting/netsuite/import/custom/:importCustomField', + route: 'workspaces/:policyID/accounting/netsuite/import/custom/:importCustomField', getRoute: (policyID: string, importCustomField: ValueOf) => - `settings/workspaces/${policyID}/accounting/netsuite/import/custom/${importCustomField as string}` as const, + `workspaces/${policyID}/accounting/netsuite/import/custom/${importCustomField as string}` as const, }, POLICY_ACCOUNTING_NETSUITE_IMPORT_CUSTOM_FIELD_VIEW: { - route: 'settings/workspaces/:policyID/accounting/netsuite/import/custom/:importCustomField/view/:valueIndex', + route: 'workspaces/:policyID/accounting/netsuite/import/custom/:importCustomField/view/:valueIndex', getRoute: (policyID: string, importCustomField: ValueOf, valueIndex: number) => - `settings/workspaces/${policyID}/accounting/netsuite/import/custom/${importCustomField as string}/view/${valueIndex}` as const, + `workspaces/${policyID}/accounting/netsuite/import/custom/${importCustomField as string}/view/${valueIndex}` as const, }, POLICY_ACCOUNTING_NETSUITE_IMPORT_CUSTOM_FIELD_EDIT: { - route: 'settings/workspaces/:policyID/accounting/netsuite/import/custom/:importCustomField/edit/:valueIndex/:fieldName', + route: 'workspaces/:policyID/accounting/netsuite/import/custom/:importCustomField/edit/:valueIndex/:fieldName', getRoute: (policyID: string, importCustomField: ValueOf, valueIndex: number, fieldName: string) => - `settings/workspaces/${policyID}/accounting/netsuite/import/custom/${importCustomField as string}/edit/${valueIndex}/${fieldName}` as const, + `workspaces/${policyID}/accounting/netsuite/import/custom/${importCustomField as string}/edit/${valueIndex}/${fieldName}` as const, }, POLICY_ACCOUNTING_NETSUITE_IMPORT_CUSTOM_LIST_ADD: { - route: 'settings/workspaces/:policyID/accounting/netsuite/import/custom-list/new', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/netsuite/import/custom-list/new` as const, + route: 'workspaces/:policyID/accounting/netsuite/import/custom-list/new', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/netsuite/import/custom-list/new` as const, }, POLICY_ACCOUNTING_NETSUITE_IMPORT_CUSTOM_SEGMENT_ADD: { - route: 'settings/workspaces/:policyID/accounting/netsuite/import/custom-segment/new', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/netsuite/import/custom-segment/new` as const, + route: 'workspaces/:policyID/accounting/netsuite/import/custom-segment/new', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/netsuite/import/custom-segment/new` as const, }, POLICY_ACCOUNTING_NETSUITE_IMPORT_CUSTOMERS_OR_PROJECTS: { - route: 'settings/workspaces/:policyID/accounting/netsuite/import/customer-projects', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/netsuite/import/customer-projects` as const, + route: 'workspaces/:policyID/accounting/netsuite/import/customer-projects', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/netsuite/import/customer-projects` as const, }, POLICY_ACCOUNTING_NETSUITE_IMPORT_CUSTOMERS_OR_PROJECTS_SELECT: { - route: 'settings/workspaces/:policyID/accounting/netsuite/import/customer-projects/select', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/netsuite/import/customer-projects/select` as const, + route: 'workspaces/:policyID/accounting/netsuite/import/customer-projects/select', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/netsuite/import/customer-projects/select` as const, }, POLICY_ACCOUNTING_NETSUITE_EXPORT: { - route: 'settings/workspaces/:policyID/connections/netsuite/export/', + route: 'workspaces/:policyID/connections/netsuite/export/', getRoute: (policyID: string | undefined, backTo?: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_NETSUITE_EXPORT route'); } - return getUrlWithBackToParam(`settings/workspaces/${policyID}/connections/netsuite/export/` as const, backTo, false); + return getUrlWithBackToParam(`workspaces/${policyID}/connections/netsuite/export/` as const, backTo, false); }, }, POLICY_ACCOUNTING_NETSUITE_PREFERRED_EXPORTER_SELECT: { - route: 'settings/workspaces/:policyID/connections/netsuite/export/preferred-exporter/select', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/connections/netsuite/export/preferred-exporter/select` as const, backTo), + route: 'workspaces/:policyID/connections/netsuite/export/preferred-exporter/select', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/connections/netsuite/export/preferred-exporter/select` as const, backTo), }, POLICY_ACCOUNTING_NETSUITE_DATE_SELECT: { - route: 'settings/workspaces/:policyID/connections/netsuite/export/date/select', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/connections/netsuite/export/date/select` as const, backTo), + route: 'workspaces/:policyID/connections/netsuite/export/date/select', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/connections/netsuite/export/date/select` as const, backTo), }, POLICY_ACCOUNTING_NETSUITE_EXPORT_EXPENSES: { - route: 'settings/workspaces/:policyID/connections/netsuite/export/expenses/:expenseType', + route: 'workspaces/:policyID/connections/netsuite/export/expenses/:expenseType', getRoute: (policyID: string | undefined, expenseType: ValueOf, backTo?: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_NETSUITE_EXPORT_EXPENSES route'); } - return getUrlWithBackToParam(`settings/workspaces/${policyID}/connections/netsuite/export/expenses/${expenseType as string}` as const, backTo); + return getUrlWithBackToParam(`workspaces/${policyID}/connections/netsuite/export/expenses/${expenseType as string}` as const, backTo); }, }, POLICY_ACCOUNTING_NETSUITE_EXPORT_EXPENSES_DESTINATION_SELECT: { - route: 'settings/workspaces/:policyID/connections/netsuite/export/expenses/:expenseType/destination/select', + route: 'workspaces/:policyID/connections/netsuite/export/expenses/:expenseType/destination/select', getRoute: (policyID: string, expenseType: ValueOf, backTo?: string) => - getUrlWithBackToParam(`settings/workspaces/${policyID}/connections/netsuite/export/expenses/${expenseType as string}/destination/select` as const, backTo), + getUrlWithBackToParam(`workspaces/${policyID}/connections/netsuite/export/expenses/${expenseType as string}/destination/select` as const, backTo), }, POLICY_ACCOUNTING_NETSUITE_EXPORT_EXPENSES_VENDOR_SELECT: { - route: 'settings/workspaces/:policyID/connections/netsuite/export/expenses/:expenseType/vendor/select', + route: 'workspaces/:policyID/connections/netsuite/export/expenses/:expenseType/vendor/select', getRoute: (policyID: string, expenseType: ValueOf, backTo?: string) => - getUrlWithBackToParam(`settings/workspaces/${policyID}/connections/netsuite/export/expenses/${expenseType as string}/vendor/select` as const, backTo), + getUrlWithBackToParam(`workspaces/${policyID}/connections/netsuite/export/expenses/${expenseType as string}/vendor/select` as const, backTo), }, POLICY_ACCOUNTING_NETSUITE_EXPORT_EXPENSES_PAYABLE_ACCOUNT_SELECT: { - route: 'settings/workspaces/:policyID/connections/netsuite/export/expenses/:expenseType/payable-account/select', + route: 'workspaces/:policyID/connections/netsuite/export/expenses/:expenseType/payable-account/select', getRoute: (policyID: string, expenseType: ValueOf, backTo?: string) => - getUrlWithBackToParam(`settings/workspaces/${policyID}/connections/netsuite/export/expenses/${expenseType as string}/payable-account/select` as const, backTo), + getUrlWithBackToParam(`workspaces/${policyID}/connections/netsuite/export/expenses/${expenseType as string}/payable-account/select` as const, backTo), }, POLICY_ACCOUNTING_NETSUITE_EXPORT_EXPENSES_JOURNAL_POSTING_PREFERENCE_SELECT: { - route: 'settings/workspaces/:policyID/connections/netsuite/export/expenses/:expenseType/journal-posting-preference/select', + route: 'workspaces/:policyID/connections/netsuite/export/expenses/:expenseType/journal-posting-preference/select', getRoute: (policyID: string, expenseType: ValueOf, backTo?: string) => - getUrlWithBackToParam(`settings/workspaces/${policyID}/connections/netsuite/export/expenses/${expenseType as string}/journal-posting-preference/select` as const, backTo), + getUrlWithBackToParam(`workspaces/${policyID}/connections/netsuite/export/expenses/${expenseType as string}/journal-posting-preference/select` as const, backTo), }, POLICY_ACCOUNTING_NETSUITE_RECEIVABLE_ACCOUNT_SELECT: { - route: 'settings/workspaces/:policyID/connections/netsuite/export/receivable-account/select', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/connections/netsuite/export/receivable-account/select` as const, backTo), + route: 'workspaces/:policyID/connections/netsuite/export/receivable-account/select', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/connections/netsuite/export/receivable-account/select` as const, backTo), }, POLICY_ACCOUNTING_NETSUITE_INVOICE_ITEM_PREFERENCE_SELECT: { - route: 'settings/workspaces/:policyID/connections/netsuite/export/invoice-item-preference/select', + route: 'workspaces/:policyID/connections/netsuite/export/invoice-item-preference/select', getRoute: (policyID: string | undefined, backTo?: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_NETSUITE_INVOICE_ITEM_PREFERENCE_SELECT route'); } - return getUrlWithBackToParam(`settings/workspaces/${policyID}/connections/netsuite/export/invoice-item-preference/select` as const, backTo); + return getUrlWithBackToParam(`workspaces/${policyID}/connections/netsuite/export/invoice-item-preference/select` as const, backTo); }, }, POLICY_ACCOUNTING_NETSUITE_INVOICE_ITEM_SELECT: { - route: 'settings/workspaces/:policyID/connections/netsuite/export/invoice-item-preference/invoice-item/select', + route: 'workspaces/:policyID/connections/netsuite/export/invoice-item-preference/invoice-item/select', getRoute: (policyID: string | undefined) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_NETSUITE_INVOICE_ITEM_SELECT route'); } - return `settings/workspaces/${policyID}/connections/netsuite/export/invoice-item-preference/invoice-item/select` as const; + return `workspaces/${policyID}/connections/netsuite/export/invoice-item-preference/invoice-item/select` as const; }, }, POLICY_ACCOUNTING_NETSUITE_TAX_POSTING_ACCOUNT_SELECT: { - route: 'settings/workspaces/:policyID/connections/netsuite/export/tax-posting-account/select', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/connections/netsuite/export/tax-posting-account/select` as const, + route: 'workspaces/:policyID/connections/netsuite/export/tax-posting-account/select', + getRoute: (policyID: string) => `workspaces/${policyID}/connections/netsuite/export/tax-posting-account/select` as const, }, POLICY_ACCOUNTING_NETSUITE_PROVINCIAL_TAX_POSTING_ACCOUNT_SELECT: { - route: 'settings/workspaces/:policyID/connections/netsuite/export/provincial-tax-posting-account/select', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/connections/netsuite/export/provincial-tax-posting-account/select` as const, + route: 'workspaces/:policyID/connections/netsuite/export/provincial-tax-posting-account/select', + getRoute: (policyID: string) => `workspaces/${policyID}/connections/netsuite/export/provincial-tax-posting-account/select` as const, }, POLICY_ACCOUNTING_NETSUITE_ADVANCED: { - route: 'settings/workspaces/:policyID/connections/netsuite/advanced/', + route: 'workspaces/:policyID/connections/netsuite/advanced/', getRoute: (policyID: string | undefined) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_NETSUITE_ADVANCED route'); } - return `settings/workspaces/${policyID}/connections/netsuite/advanced/` as const; + return `workspaces/${policyID}/connections/netsuite/advanced/` as const; }, }, POLICY_ACCOUNTING_NETSUITE_REIMBURSEMENT_ACCOUNT_SELECT: { - route: 'settings/workspaces/:policyID/connections/netsuite/advanced/reimbursement-account/select', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/connections/netsuite/advanced/reimbursement-account/select` as const, + route: 'workspaces/:policyID/connections/netsuite/advanced/reimbursement-account/select', + getRoute: (policyID: string) => `workspaces/${policyID}/connections/netsuite/advanced/reimbursement-account/select` as const, }, POLICY_ACCOUNTING_NETSUITE_COLLECTION_ACCOUNT_SELECT: { - route: 'settings/workspaces/:policyID/connections/netsuite/advanced/collection-account/select', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/connections/netsuite/advanced/collection-account/select` as const, + route: 'workspaces/:policyID/connections/netsuite/advanced/collection-account/select', + getRoute: (policyID: string) => `workspaces/${policyID}/connections/netsuite/advanced/collection-account/select` as const, }, POLICY_ACCOUNTING_NETSUITE_EXPENSE_REPORT_APPROVAL_LEVEL_SELECT: { - route: 'settings/workspaces/:policyID/connections/netsuite/advanced/expense-report-approval-level/select', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/connections/netsuite/advanced/expense-report-approval-level/select` as const, + route: 'workspaces/:policyID/connections/netsuite/advanced/expense-report-approval-level/select', + getRoute: (policyID: string) => `workspaces/${policyID}/connections/netsuite/advanced/expense-report-approval-level/select` as const, }, POLICY_ACCOUNTING_NETSUITE_VENDOR_BILL_APPROVAL_LEVEL_SELECT: { - route: 'settings/workspaces/:policyID/connections/netsuite/advanced/vendor-bill-approval-level/select', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/connections/netsuite/advanced/vendor-bill-approval-level/select` as const, + route: 'workspaces/:policyID/connections/netsuite/advanced/vendor-bill-approval-level/select', + getRoute: (policyID: string) => `workspaces/${policyID}/connections/netsuite/advanced/vendor-bill-approval-level/select` as const, }, POLICY_ACCOUNTING_NETSUITE_JOURNAL_ENTRY_APPROVAL_LEVEL_SELECT: { - route: 'settings/workspaces/:policyID/connections/netsuite/advanced/journal-entry-approval-level/select', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/connections/netsuite/advanced/journal-entry-approval-level/select` as const, + route: 'workspaces/:policyID/connections/netsuite/advanced/journal-entry-approval-level/select', + getRoute: (policyID: string) => `workspaces/${policyID}/connections/netsuite/advanced/journal-entry-approval-level/select` as const, }, POLICY_ACCOUNTING_NETSUITE_APPROVAL_ACCOUNT_SELECT: { - route: 'settings/workspaces/:policyID/connections/netsuite/advanced/approval-account/select', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/connections/netsuite/advanced/approval-account/select` as const, + route: 'workspaces/:policyID/connections/netsuite/advanced/approval-account/select', + getRoute: (policyID: string) => `workspaces/${policyID}/connections/netsuite/advanced/approval-account/select` as const, }, POLICY_ACCOUNTING_NETSUITE_CUSTOM_FORM_ID: { - route: 'settings/workspaces/:policyID/connections/netsuite/advanced/custom-form-id/:expenseType', + route: 'workspaces/:policyID/connections/netsuite/advanced/custom-form-id/:expenseType', getRoute: (policyID: string, expenseType: ValueOf) => - `settings/workspaces/${policyID}/connections/netsuite/advanced/custom-form-id/${expenseType as string}` as const, + `workspaces/${policyID}/connections/netsuite/advanced/custom-form-id/${expenseType as string}` as const, }, POLICY_ACCOUNTING_NETSUITE_AUTO_SYNC: { - route: 'settings/workspaces/:policyID/connections/netsuite/advanced/autosync', + route: 'workspaces/:policyID/connections/netsuite/advanced/autosync', getRoute: (policyID: string | undefined, backTo?: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_NETSUITE_AUTO_SYNC route'); } - return getUrlWithBackToParam(`settings/workspaces/${policyID}/connections/netsuite/advanced/autosync` as const, backTo); + return getUrlWithBackToParam(`workspaces/${policyID}/connections/netsuite/advanced/autosync` as const, backTo); }, }, POLICY_ACCOUNTING_NETSUITE_ACCOUNTING_METHOD: { - route: 'settings/workspaces/:policyID/connections/netsuite/advanced/autosync/accounting-method', + route: 'workspaces/:policyID/connections/netsuite/advanced/autosync/accounting-method', getRoute: (policyID: string | undefined, backTo?: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_NETSUITE_ACCOUNTING_METHOD route'); } - return getUrlWithBackToParam(`settings/workspaces/${policyID}/connections/netsuite/advanced/autosync/accounting-method` as const, backTo); + return getUrlWithBackToParam(`workspaces/${policyID}/connections/netsuite/advanced/autosync/accounting-method` as const, backTo); }, }, POLICY_ACCOUNTING_SAGE_INTACCT_PREREQUISITES: { - route: 'settings/workspaces/:policyID/accounting/sage-intacct/prerequisites', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/sage-intacct/prerequisites` as const, backTo), + route: 'workspaces/:policyID/accounting/sage-intacct/prerequisites', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/accounting/sage-intacct/prerequisites` as const, backTo), }, POLICY_ACCOUNTING_SAGE_INTACCT_ENTER_CREDENTIALS: { - route: 'settings/workspaces/:policyID/accounting/sage-intacct/enter-credentials', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/enter-credentials` as const, + route: 'workspaces/:policyID/accounting/sage-intacct/enter-credentials', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/sage-intacct/enter-credentials` as const, }, POLICY_ACCOUNTING_SAGE_INTACCT_EXISTING_CONNECTIONS: { - route: 'settings/workspaces/:policyID/accounting/sage-intacct/existing-connections', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/existing-connections` as const, + route: 'workspaces/:policyID/accounting/sage-intacct/existing-connections', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/sage-intacct/existing-connections` as const, }, POLICY_ACCOUNTING_SAGE_INTACCT_ENTITY: { - route: 'settings/workspaces/:policyID/accounting/sage-intacct/entity', + route: 'workspaces/:policyID/accounting/sage-intacct/entity', getRoute: (policyID: string | undefined) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_SAGE_INTACCT_ENTITY route'); } - return `settings/workspaces/${policyID}/accounting/sage-intacct/entity` as const; + return `workspaces/${policyID}/accounting/sage-intacct/entity` as const; }, }, POLICY_ACCOUNTING_SAGE_INTACCT_IMPORT: { - route: 'settings/workspaces/:policyID/accounting/sage-intacct/import', - getRoute: (policyID: string | undefined) => `settings/workspaces/${policyID}/accounting/sage-intacct/import` as const, + route: 'workspaces/:policyID/accounting/sage-intacct/import', + getRoute: (policyID: string | undefined) => `workspaces/${policyID}/accounting/sage-intacct/import` as const, }, POLICY_ACCOUNTING_SAGE_INTACCT_TOGGLE_MAPPINGS: { - route: 'settings/workspaces/:policyID/accounting/sage-intacct/import/toggle-mapping/:mapping', - getRoute: (policyID: string | undefined, mapping: SageIntacctMappingName) => - `settings/workspaces/${policyID}/accounting/sage-intacct/import/toggle-mapping/${mapping as string}` as const, + route: 'workspaces/:policyID/accounting/sage-intacct/import/toggle-mapping/:mapping', + getRoute: (policyID: string | undefined, mapping: SageIntacctMappingName) => `workspaces/${policyID}/accounting/sage-intacct/import/toggle-mapping/${mapping as string}` as const, }, POLICY_ACCOUNTING_SAGE_INTACCT_MAPPINGS_TYPE: { - route: 'settings/workspaces/:policyID/accounting/sage-intacct/import/mapping-type/:mapping', - getRoute: (policyID: string, mapping: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/import/mapping-type/${mapping}` as const, + route: 'workspaces/:policyID/accounting/sage-intacct/import/mapping-type/:mapping', + getRoute: (policyID: string, mapping: string) => `workspaces/${policyID}/accounting/sage-intacct/import/mapping-type/${mapping}` as const, }, POLICY_ACCOUNTING_SAGE_INTACCT_IMPORT_TAX: { - route: 'settings/workspaces/:policyID/accounting/sage-intacct/import/tax', - getRoute: (policyID: string | undefined) => `settings/workspaces/${policyID}/accounting/sage-intacct/import/tax` as const, + route: 'workspaces/:policyID/accounting/sage-intacct/import/tax', + getRoute: (policyID: string | undefined) => `workspaces/${policyID}/accounting/sage-intacct/import/tax` as const, }, POLICY_ACCOUNTING_SAGE_INTACCT_IMPORT_TAX_MAPPING: { - route: 'settings/workspaces/:policyID/accounting/sage-intacct/import/tax/mapping', - getRoute: (policyID: string | undefined) => `settings/workspaces/${policyID}/accounting/sage-intacct/import/tax/mapping` as const, + route: 'workspaces/:policyID/accounting/sage-intacct/import/tax/mapping', + getRoute: (policyID: string | undefined) => `workspaces/${policyID}/accounting/sage-intacct/import/tax/mapping` as const, }, POLICY_ACCOUNTING_SAGE_INTACCT_USER_DIMENSIONS: { - route: 'settings/workspaces/:policyID/accounting/sage-intacct/import/user-dimensions', - getRoute: (policyID: string | undefined) => `settings/workspaces/${policyID}/accounting/sage-intacct/import/user-dimensions` as const, + route: 'workspaces/:policyID/accounting/sage-intacct/import/user-dimensions', + getRoute: (policyID: string | undefined) => `workspaces/${policyID}/accounting/sage-intacct/import/user-dimensions` as const, }, POLICY_ACCOUNTING_SAGE_INTACCT_ADD_USER_DIMENSION: { - route: 'settings/workspaces/:policyID/accounting/sage-intacct/import/add-user-dimension', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/import/add-user-dimension` as const, + route: 'workspaces/:policyID/accounting/sage-intacct/import/add-user-dimension', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/sage-intacct/import/add-user-dimension` as const, }, POLICY_ACCOUNTING_SAGE_INTACCT_EDIT_USER_DIMENSION: { - route: 'settings/workspaces/:policyID/accounting/sage-intacct/import/edit-user-dimension/:dimensionName', - getRoute: (policyID: string, dimensionName: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/import/edit-user-dimension/${dimensionName}` as const, + route: 'workspaces/:policyID/accounting/sage-intacct/import/edit-user-dimension/:dimensionName', + getRoute: (policyID: string, dimensionName: string) => `workspaces/${policyID}/accounting/sage-intacct/import/edit-user-dimension/${dimensionName}` as const, }, POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT: { - route: 'settings/workspaces/:policyID/accounting/sage-intacct/export', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/sage-intacct/export` as const, backTo, false), + route: 'workspaces/:policyID/accounting/sage-intacct/export', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/accounting/sage-intacct/export` as const, backTo, false), }, POLICY_ACCOUNTING_SAGE_INTACCT_PREFERRED_EXPORTER: { - route: 'settings/workspaces/:policyID/accounting/sage-intacct/export/preferred-exporter', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/sage-intacct/export/preferred-exporter` as const, backTo), + route: 'workspaces/:policyID/accounting/sage-intacct/export/preferred-exporter', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/accounting/sage-intacct/export/preferred-exporter` as const, backTo), }, POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT_DATE: { - route: 'settings/workspaces/:policyID/accounting/sage-intacct/export/date', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/sage-intacct/export/date` as const, backTo), + route: 'workspaces/:policyID/accounting/sage-intacct/export/date', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/accounting/sage-intacct/export/date` as const, backTo), }, POLICY_ACCOUNTING_SAGE_INTACCT_REIMBURSABLE_EXPENSES: { - route: 'settings/workspaces/:policyID/accounting/sage-intacct/export/reimbursable', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/sage-intacct/export/reimbursable` as const, backTo), + route: 'workspaces/:policyID/accounting/sage-intacct/export/reimbursable', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/accounting/sage-intacct/export/reimbursable` as const, backTo), }, POLICY_ACCOUNTING_SAGE_INTACCT_NON_REIMBURSABLE_EXPENSES: { - route: 'settings/workspaces/:policyID/accounting/sage-intacct/export/nonreimbursable', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/sage-intacct/export/nonreimbursable` as const, backTo), + route: 'workspaces/:policyID/accounting/sage-intacct/export/nonreimbursable', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/accounting/sage-intacct/export/nonreimbursable` as const, backTo), }, POLICY_ACCOUNTING_SAGE_INTACCT_REIMBURSABLE_DESTINATION: { - route: 'settings/workspaces/:policyID/accounting/sage-intacct/export/reimbursable/destination', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/sage-intacct/export/reimbursable/destination` as const, backTo), + route: 'workspaces/:policyID/accounting/sage-intacct/export/reimbursable/destination', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/accounting/sage-intacct/export/reimbursable/destination` as const, backTo), }, POLICY_ACCOUNTING_SAGE_INTACCT_NON_REIMBURSABLE_DESTINATION: { - route: 'settings/workspaces/:policyID/accounting/sage-intacct/export/nonreimbursable/destination', - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/sage-intacct/export/nonreimbursable/destination` as const, backTo), + route: 'workspaces/:policyID/accounting/sage-intacct/export/nonreimbursable/destination', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/accounting/sage-intacct/export/nonreimbursable/destination` as const, backTo), }, POLICY_ACCOUNTING_SAGE_INTACCT_DEFAULT_VENDOR: { - route: 'settings/workspaces/:policyID/accounting/sage-intacct/export/:reimbursable/default-vendor', + route: 'workspaces/:policyID/accounting/sage-intacct/export/:reimbursable/default-vendor', getRoute: (policyID: string, reimbursable: string, backTo?: string) => - getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/sage-intacct/export/${reimbursable}/default-vendor` as const, backTo), + getUrlWithBackToParam(`workspaces/${policyID}/accounting/sage-intacct/export/${reimbursable}/default-vendor` as const, backTo), }, POLICY_ACCOUNTING_SAGE_INTACCT_NON_REIMBURSABLE_CREDIT_CARD_ACCOUNT: { - route: 'settings/workspaces/:policyID/accounting/sage-intacct/export/nonreimbursable/credit-card-account', - getRoute: (policyID: string, backTo?: string) => - getUrlWithBackToParam(`settings/workspaces/${policyID}/accounting/sage-intacct/export/nonreimbursable/credit-card-account` as const, backTo), + route: 'workspaces/:policyID/accounting/sage-intacct/export/nonreimbursable/credit-card-account', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/accounting/sage-intacct/export/nonreimbursable/credit-card-account` as const, backTo), }, POLICY_ACCOUNTING_SAGE_INTACCT_ADVANCED: { - route: 'settings/workspaces/:policyID/accounting/sage-intacct/advanced', + route: 'workspaces/:policyID/accounting/sage-intacct/advanced', getRoute: (policyID: string | undefined) => { if (!policyID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_SAGE_INTACCT_ADVANCED route'); } - return `settings/workspaces/${policyID}/accounting/sage-intacct/advanced` as const; + return `workspaces/${policyID}/accounting/sage-intacct/advanced` as const; }, }, POLICY_ACCOUNTING_SAGE_INTACCT_PAYMENT_ACCOUNT: { - route: 'settings/workspaces/:policyID/accounting/sage-intacct/advanced/payment-account', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/advanced/payment-account` as const, + route: 'workspaces/:policyID/accounting/sage-intacct/advanced/payment-account', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/sage-intacct/advanced/payment-account` as const, }, ADD_UNREPORTED_EXPENSE: { route: 'search/r/:reportID/add-unreported-expense/:backToReport?', diff --git a/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts b/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts index 13cd5db0cb95..f8001b09636d 100644 --- a/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts +++ b/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts @@ -14,6 +14,7 @@ import SCREENS from '@src/SCREENS'; import type {Report} from '@src/types/onyx'; import getParamsFromRoute from './getParamsFromRoute'; import {isFullScreenName} from './isNavigatorName'; +import getBestMatchingPath from './pathMap'; import replacePathInNestedState from './replacePathInNestedState'; let allReports: OnyxCollection; @@ -226,6 +227,8 @@ function getAdaptedState(state: PartialState { let normalizedPath = !path.startsWith('/') ? `/${path}` : path; + normalizedPath = getBestMatchingPath(normalizedPath) ?? normalizedPath; + // Bing search results still link to /signin when searching for “Expensify”, but the /signin route no longer exists in our repo, so we redirect it to the home page to avoid showing a Not Found page. if (normalizedPath === CONST.SIGNIN_ROUTE) { normalizedPath = '/'; diff --git a/src/libs/Navigation/helpers/getStateFromPath.ts b/src/libs/Navigation/helpers/getStateFromPath.ts index b784b2322f74..9051a1aae1f0 100644 --- a/src/libs/Navigation/helpers/getStateFromPath.ts +++ b/src/libs/Navigation/helpers/getStateFromPath.ts @@ -2,13 +2,15 @@ import type {NavigationState, PartialState} from '@react-navigation/native'; import {getStateFromPath as RNGetStateFromPath} from '@react-navigation/native'; import {linkingConfig} from '@libs/Navigation/linkingConfig'; import type {Route} from '@src/ROUTES'; +import getBestMatchingPath from './pathMap'; /** * @param path - The path to parse * @returns - It's possible that there is no navigation action for the given path */ function getStateFromPath(path: Route): PartialState { - const normalizedPath = !path.startsWith('/') ? `/${path}` : path; + let normalizedPath = !path.startsWith('/') ? `/${path}` : path; + normalizedPath = getBestMatchingPath(normalizedPath) ?? normalizedPath; // This function is used in the linkTo function where we want to use default getStateFromPath function. const state = RNGetStateFromPath(normalizedPath, linkingConfig.config); diff --git a/src/libs/Navigation/helpers/pathMap.ts b/src/libs/Navigation/helpers/pathMap.ts new file mode 100644 index 000000000000..c89d76e552bd --- /dev/null +++ b/src/libs/Navigation/helpers/pathMap.ts @@ -0,0 +1,26 @@ +const pathMap: Record = { + // eslint-disable-next-line @typescript-eslint/naming-convention + '/settings/workspaces/*': '/workspaces/', +}; + +function getBestMatchingPath(path: string) { + let bestMatch; + let maxLength = -1; + + for (const pattern of Object.keys(pathMap)) { + const regexStr = `^${pattern.replace('*', '.*')}`; + const regex = new RegExp(regexStr); + + if (regex.test(path) && pattern.length > maxLength) { + bestMatch = pattern; + maxLength = pattern.length; + } + } + if (!bestMatch) { + return bestMatch; + } + + const finalRegexp = bestMatch?.replace('*', '') ?? ''; + return path.replace(finalRegexp, pathMap[bestMatch]); +} +export default getBestMatchingPath; From 39d6bb77b58114ad082bb37f19c853d35c425144 Mon Sep 17 00:00:00 2001 From: sumo_slonik Date: Thu, 26 Jun 2025 10:24:18 +0200 Subject: [PATCH 02/10] renaming and add test --- .../helpers/getAdaptedStateFromPath.ts | 4 ++-- .../{pathMap.ts => getMatchingNewRoute.ts} | 13 +++++------- .../Navigation/helpers/getStateFromPath.ts | 8 +++---- tests/navigation/getMatchingNewRouteTest.ts | 21 +++++++++++++++++++ 4 files changed, 32 insertions(+), 14 deletions(-) rename src/libs/Navigation/helpers/{pathMap.ts => getMatchingNewRoute.ts} (54%) create mode 100644 tests/navigation/getMatchingNewRouteTest.ts diff --git a/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts b/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts index f8001b09636d..faf5e5f2632c 100644 --- a/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts +++ b/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts @@ -12,9 +12,9 @@ import NAVIGATORS from '@src/NAVIGATORS'; import ONYXKEYS from '@src/ONYXKEYS'; import SCREENS from '@src/SCREENS'; import type {Report} from '@src/types/onyx'; +import getMatchingNewRoute from './getMatchingNewRoute'; import getParamsFromRoute from './getParamsFromRoute'; import {isFullScreenName} from './isNavigatorName'; -import getBestMatchingPath from './pathMap'; import replacePathInNestedState from './replacePathInNestedState'; let allReports: OnyxCollection; @@ -227,7 +227,7 @@ function getAdaptedState(state: PartialState { let normalizedPath = !path.startsWith('/') ? `/${path}` : path; - normalizedPath = getBestMatchingPath(normalizedPath) ?? normalizedPath; + normalizedPath = getMatchingNewRoute(normalizedPath) ?? normalizedPath; // Bing search results still link to /signin when searching for “Expensify”, but the /signin route no longer exists in our repo, so we redirect it to the home page to avoid showing a Not Found page. if (normalizedPath === CONST.SIGNIN_ROUTE) { diff --git a/src/libs/Navigation/helpers/pathMap.ts b/src/libs/Navigation/helpers/getMatchingNewRoute.ts similarity index 54% rename from src/libs/Navigation/helpers/pathMap.ts rename to src/libs/Navigation/helpers/getMatchingNewRoute.ts index c89d76e552bd..6f202d2af8c1 100644 --- a/src/libs/Navigation/helpers/pathMap.ts +++ b/src/libs/Navigation/helpers/getMatchingNewRoute.ts @@ -1,13 +1,10 @@ -const pathMap: Record = { - // eslint-disable-next-line @typescript-eslint/naming-convention - '/settings/workspaces/*': '/workspaces/', -}; +import oldRoutes from '@navigation/linkingConfig/OldRoutes'; -function getBestMatchingPath(path: string) { +function getMatchingNewRoute(path: string) { let bestMatch; let maxLength = -1; - for (const pattern of Object.keys(pathMap)) { + for (const pattern of Object.keys(oldRoutes)) { const regexStr = `^${pattern.replace('*', '.*')}`; const regex = new RegExp(regexStr); @@ -21,6 +18,6 @@ function getBestMatchingPath(path: string) { } const finalRegexp = bestMatch?.replace('*', '') ?? ''; - return path.replace(finalRegexp, pathMap[bestMatch]); + return path.replace(finalRegexp, oldRoutes[bestMatch]); } -export default getBestMatchingPath; +export default getMatchingNewRoute; diff --git a/src/libs/Navigation/helpers/getStateFromPath.ts b/src/libs/Navigation/helpers/getStateFromPath.ts index 9051a1aae1f0..b58c8a38a6dc 100644 --- a/src/libs/Navigation/helpers/getStateFromPath.ts +++ b/src/libs/Navigation/helpers/getStateFromPath.ts @@ -2,18 +2,18 @@ import type {NavigationState, PartialState} from '@react-navigation/native'; import {getStateFromPath as RNGetStateFromPath} from '@react-navigation/native'; import {linkingConfig} from '@libs/Navigation/linkingConfig'; import type {Route} from '@src/ROUTES'; -import getBestMatchingPath from './pathMap'; +import getMatchingNewRoute from './getMatchingNewRoute'; /** * @param path - The path to parse * @returns - It's possible that there is no navigation action for the given path */ function getStateFromPath(path: Route): PartialState { - let normalizedPath = !path.startsWith('/') ? `/${path}` : path; - normalizedPath = getBestMatchingPath(normalizedPath) ?? normalizedPath; + const normalizedPath = !path.startsWith('/') ? `/${path}` : path; + const normalizedPathAfterRedirection = getMatchingNewRoute(normalizedPath) ?? normalizedPath; // This function is used in the linkTo function where we want to use default getStateFromPath function. - const state = RNGetStateFromPath(normalizedPath, linkingConfig.config); + const state = RNGetStateFromPath(normalizedPathAfterRedirection, linkingConfig.config); if (!state) { throw new Error('Failed to parse the path to a navigation state.'); diff --git a/tests/navigation/getMatchingNewRouteTest.ts b/tests/navigation/getMatchingNewRouteTest.ts new file mode 100644 index 000000000000..19c95d0baafc --- /dev/null +++ b/tests/navigation/getMatchingNewRouteTest.ts @@ -0,0 +1,21 @@ +import getMatchingNewRoute from '@navigation/helpers/getMatchingNewRoute'; + +describe('getBestMatchingPath', () => { + it('returns mapped base path when input matches the exact pattern', () => { + expect(getMatchingNewRoute('/settings/workspaces/')).toBe('/workspaces/'); + }); + + it('returns mapped path when input matches the pattern and have more content', () => { + expect(getMatchingNewRoute('/settings/workspaces/anything/more')).toBe('/workspaces/anything/more'); + }); + + it('returns undefined when input does not match any pattern - similar prefix but different ending', () => { + expect(getMatchingNewRoute('/settings/anything/')).toBe(undefined); + }); + + it('returns undefined when input is unrelated to any pattern', () => { + expect(getMatchingNewRoute('/anything/workspaces/')).toBe(undefined); + expect(getMatchingNewRoute('/anything/anything/')).toBe(undefined); + expect(getMatchingNewRoute('/anything/anything/anything')).toBe(undefined); + }); +}); From 7fb6b3fae2fa1455c29435da0deaa09a5b0b73d2 Mon Sep 17 00:00:00 2001 From: sumo_slonik Date: Thu, 26 Jun 2025 10:28:50 +0200 Subject: [PATCH 03/10] add description of function --- src/libs/Navigation/helpers/getMatchingNewRoute.ts | 10 ++++++++++ src/libs/Navigation/linkingConfig/OldRoutes.ts | 6 ++++++ 2 files changed, 16 insertions(+) create mode 100644 src/libs/Navigation/linkingConfig/OldRoutes.ts diff --git a/src/libs/Navigation/helpers/getMatchingNewRoute.ts b/src/libs/Navigation/helpers/getMatchingNewRoute.ts index 6f202d2af8c1..efa8f9621764 100644 --- a/src/libs/Navigation/helpers/getMatchingNewRoute.ts +++ b/src/libs/Navigation/helpers/getMatchingNewRoute.ts @@ -1,5 +1,15 @@ import oldRoutes from '@navigation/linkingConfig/OldRoutes'; +/** + * Maps an old route path to its corresponding new route based on the `oldRoutes` map. + * It finds the best matching pattern (with wildcard `*` support) and replaces the matched + * part of the path with the new route value. + * + * @param path - The input URL path to match and transform. + * @returns The new route path if a match is found, otherwise `undefined`. + * + * Related issue: https://github.com/Expensify/App/issues/64968 + */ function getMatchingNewRoute(path: string) { let bestMatch; let maxLength = -1; diff --git a/src/libs/Navigation/linkingConfig/OldRoutes.ts b/src/libs/Navigation/linkingConfig/OldRoutes.ts new file mode 100644 index 000000000000..ca56605dd3e3 --- /dev/null +++ b/src/libs/Navigation/linkingConfig/OldRoutes.ts @@ -0,0 +1,6 @@ +const oldRoutes: Record = { + // eslint-disable-next-line @typescript-eslint/naming-convention + '/settings/workspaces/*': '/workspaces/', +}; + +export default oldRoutes; From e599ae8edf57ecb6a456780d33c2293fe4bec791 Mon Sep 17 00:00:00 2001 From: sumo_slonik Date: Thu, 26 Jun 2025 12:33:33 +0200 Subject: [PATCH 04/10] fix pattern --- src/libs/Navigation/helpers/linkTo/index.ts | 2 ++ src/libs/Navigation/linkingConfig/OldRoutes.ts | 2 ++ tests/navigation/getMatchingNewRouteTest.ts | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/src/libs/Navigation/helpers/linkTo/index.ts b/src/libs/Navigation/helpers/linkTo/index.ts index 65c743a0d595..b758bb668d2a 100644 --- a/src/libs/Navigation/helpers/linkTo/index.ts +++ b/src/libs/Navigation/helpers/linkTo/index.ts @@ -6,6 +6,7 @@ import getStateFromPath from '@libs/Navigation/helpers/getStateFromPath'; import normalizePath from '@libs/Navigation/helpers/normalizePath'; import {linkingConfig} from '@libs/Navigation/linkingConfig'; import {shallowCompare} from '@libs/ObjectUtils'; +import getMatchingNewRoute from '@navigation/helpers/getMatchingNewRoute'; import type {NavigationPartialRoute, ReportsSplitNavigatorParamList, RootNavigatorParamList, StackNavigationAction} from '@navigation/types'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; @@ -56,6 +57,7 @@ export default function linkTo(navigation: NavigationContainerRef; const normalizedPath = normalizePath(path) as Route; + const normalizedPathAfterRedirection = (getMatchingNewRoute(normalizedPath) ?? normalizedPath) as Route; // This is the state generated with the default getStateFromPath function. // It won't include the whole state that will be generated for this path but the focused route will be correct. diff --git a/src/libs/Navigation/linkingConfig/OldRoutes.ts b/src/libs/Navigation/linkingConfig/OldRoutes.ts index ca56605dd3e3..8f31423ee871 100644 --- a/src/libs/Navigation/linkingConfig/OldRoutes.ts +++ b/src/libs/Navigation/linkingConfig/OldRoutes.ts @@ -1,6 +1,8 @@ const oldRoutes: Record = { // eslint-disable-next-line @typescript-eslint/naming-convention '/settings/workspaces/*': '/workspaces/', + // eslint-disable-next-line @typescript-eslint/naming-convention + '/settings/workspaces': '/workspaces', }; export default oldRoutes; diff --git a/tests/navigation/getMatchingNewRouteTest.ts b/tests/navigation/getMatchingNewRouteTest.ts index 19c95d0baafc..5426a1d7b272 100644 --- a/tests/navigation/getMatchingNewRouteTest.ts +++ b/tests/navigation/getMatchingNewRouteTest.ts @@ -5,6 +5,10 @@ describe('getBestMatchingPath', () => { expect(getMatchingNewRoute('/settings/workspaces/')).toBe('/workspaces/'); }); + it('returns mapped base path when input matches the exact pattern', () => { + expect(getMatchingNewRoute('/settings/workspaces')).toBe('/workspaces'); + }); + it('returns mapped path when input matches the pattern and have more content', () => { expect(getMatchingNewRoute('/settings/workspaces/anything/more')).toBe('/workspaces/anything/more'); }); From a5a201a0ede655bccf910e23453933e9b72c313b Mon Sep 17 00:00:00 2001 From: sumo_slonik Date: Thu, 26 Jun 2025 13:38:19 +0200 Subject: [PATCH 05/10] fix pattern --- src/libs/Navigation/helpers/linkTo/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/helpers/linkTo/index.ts b/src/libs/Navigation/helpers/linkTo/index.ts index b758bb668d2a..843e7723045f 100644 --- a/src/libs/Navigation/helpers/linkTo/index.ts +++ b/src/libs/Navigation/helpers/linkTo/index.ts @@ -62,7 +62,7 @@ export default function linkTo(navigation: NavigationContainerRef>; + const stateFromPath = getStateFromPath(normalizedPathAfterRedirection) as PartialState>; const currentState = navigation.getRootState() as NavigationState; const focusedRouteFromPath = findFocusedRoute(stateFromPath); From a3842d169822df95bd3c6d9799faa53ba3ca186a Mon Sep 17 00:00:00 2001 From: sumo_slonik Date: Fri, 27 Jun 2025 13:22:21 +0200 Subject: [PATCH 06/10] fix additional paths --- contributingGuides/NAVIGATION.md | 4 +-- contributingGuides/NAVIGATION_TESTS.md | 8 ++--- contributingGuides/STYLE.md | 6 ++-- .../settings/workspaces/:policyId/index.md | 33 ------------------- .../workspaces/:policyID/index.md | 0 help/ref/{settings => }/workspaces/index.md | 0 6 files changed, 9 insertions(+), 42 deletions(-) delete mode 100644 help/ref/settings/workspaces/:policyId/index.md rename help/ref/{settings => }/workspaces/:policyID/index.md (100%) rename help/ref/{settings => }/workspaces/index.md (100%) diff --git a/contributingGuides/NAVIGATION.md b/contributingGuides/NAVIGATION.md index d4eb414cefe1..ec0855e57c87 100644 --- a/contributingGuides/NAVIGATION.md +++ b/contributingGuides/NAVIGATION.md @@ -503,7 +503,7 @@ In Expensify, we use an extended implementation of this function because: Here are examples how the state is generated based on route: -- `settings/workspaces/1/overview` +- `workspaces/1/overview` ```json { @@ -536,7 +536,7 @@ Here are examples how the state is generated based on route: "params": { "policyID": "1" }, - "path": "/settings/workspaces/1/overview", + "path": "workspaces/1/overview", "key": "Workspace_Overview-key" } ] diff --git a/contributingGuides/NAVIGATION_TESTS.md b/contributingGuides/NAVIGATION_TESTS.md index c49220fba5cb..6b5d535303ab 100644 --- a/contributingGuides/NAVIGATION_TESTS.md +++ b/contributingGuides/NAVIGATION_TESTS.md @@ -33,11 +33,11 @@ 1. Open any workspace settings (Settings → Workspaces → Select any workspace) 2. Click the Settings button on the bottom tab. -3. Verify that the Workspace list is displayed (`/settings/workspaces`) +3. Verify that the Workspace list is displayed (`/workspaces`) 4. Select any workspace again. 5. Reload the page. 6. Click the Settings button on the bottom tab. -7. Verify that the Workspace list is displayed (`/settings/workspaces`) +7. Verify that the Workspace list is displayed (`/workspaces/`) #### The last visited screen in the settings tab is saved when switching between tabs @@ -52,7 +52,7 @@ #### Going up to the workspace list page after refreshing on the workspace settings and pressing the up button -1. Open the workspace settings from the deep link (use a link in format: `/settings/workspaces/:policyID:/profile`) +1. Open the workspace settings from the deep link (use a link in format: `/workspaces/:policyID:/profile`) 2. Click the app’s back button. 3. Verify if the workspace list is displayed. @@ -241,4 +241,4 @@ Linked issue: https://github.com/Expensify/App/issues/50177 11. Go back. 12. Verify you are navigated back to the employee size step. 13. Go back. -14. Verify you are navigated back to the Purpose step. \ No newline at end of file +14. Verify you are navigated back to the Purpose step. diff --git a/contributingGuides/STYLE.md b/contributingGuides/STYLE.md index 34466a6d84eb..d27ed05572db 100644 --- a/contributingGuides/STYLE.md +++ b/contributingGuides/STYLE.md @@ -535,13 +535,13 @@ We need to change the `getRoute()` `policyID` argument type to allow `undefined` ```diff WORKSPACE_PROFILE_ADDRESS: { - route: 'settings/workspaces/:policyID/profile/address', -- getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/profile/address` as const, backTo), + route: 'workspaces/:policyID/profile/address', +- getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/profile/address` as const, backTo), + getRoute: (policyID: string | undefined, backTo?: string) => { + if (!policyID) { + Log.warn("Invalid policyID is used to build the WORKSPACE_PROFILE_ADDRESS route") + } -+ return getUrlWithBackToParam(`settings/workspaces/${policyID}/profile/address` as const, backTo); ++ return getUrlWithBackToParam(`workspaces/${policyID}/profile/address` as const, backTo); + }, }, ``` diff --git a/help/ref/settings/workspaces/:policyId/index.md b/help/ref/settings/workspaces/:policyId/index.md deleted file mode 100644 index 469f92423df0..000000000000 --- a/help/ref/settings/workspaces/:policyId/index.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -layout: product -title: Expensify Chat ---- - -# Workspace - -This is where you configure all the settings of the many features associated with your workspace. - -## Default features - -Here are the features that are enabled by default: - -- **Overview** - Configure how it appears to others. -- **Members** - Add/remove members and admins. -- **Workflows** - Configure submission, approval, and reimbursement. -- **Categories** - Group expenses into a chart of accounts. -- **Expensify Card** - Issue native Expensify Cards to employees. -- **Accounting** - Sync with external accounting systems. - -## Optional features - -These can be enabled via More Features: - -- **Distance rates** - Configure mileage reimbursement. -- **Company card** - Connect and manage third-party corporate card feeds. -- **Per diem** - Configure daily rates. -- **Rules** - Customize expense violations and set policy. -- **Invoices** - Collect revenue from customers. -- **Tags** - Group expenses by project or client. -- **Taxes** - Track VAT and other taxes. -- **Report fields** - Capture extra expense report information. - diff --git a/help/ref/settings/workspaces/:policyID/index.md b/help/ref/workspaces/:policyID/index.md similarity index 100% rename from help/ref/settings/workspaces/:policyID/index.md rename to help/ref/workspaces/:policyID/index.md diff --git a/help/ref/settings/workspaces/index.md b/help/ref/workspaces/index.md similarity index 100% rename from help/ref/settings/workspaces/index.md rename to help/ref/workspaces/index.md From 2ff839c0b086bacaceae57aea7585268670e434d Mon Sep 17 00:00:00 2001 From: sumo_slonik Date: Fri, 27 Jun 2025 13:44:01 +0200 Subject: [PATCH 07/10] fix additional paths --- help/ref/{settings => }/workspaces/:policyID/accounting/index.md | 0 help/ref/{settings => }/workspaces/:policyID/members/index.md | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename help/ref/{settings => }/workspaces/:policyID/accounting/index.md (100%) rename help/ref/{settings => }/workspaces/:policyID/members/index.md (100%) diff --git a/help/ref/settings/workspaces/:policyID/accounting/index.md b/help/ref/workspaces/:policyID/accounting/index.md similarity index 100% rename from help/ref/settings/workspaces/:policyID/accounting/index.md rename to help/ref/workspaces/:policyID/accounting/index.md diff --git a/help/ref/settings/workspaces/:policyID/members/index.md b/help/ref/workspaces/:policyID/members/index.md similarity index 100% rename from help/ref/settings/workspaces/:policyID/members/index.md rename to help/ref/workspaces/:policyID/members/index.md From 3af0a69f6b5e675053acfa735f4cc420ac4351e3 Mon Sep 17 00:00:00 2001 From: sumo_slonik Date: Fri, 27 Jun 2025 15:15:56 +0200 Subject: [PATCH 08/10] regenerate help md --- .../SidePanel/HelpContent/helpContentMap.tsx | 980 +++++++++++------- 1 file changed, 595 insertions(+), 385 deletions(-) diff --git a/src/components/SidePanel/HelpContent/helpContentMap.tsx b/src/components/SidePanel/HelpContent/helpContentMap.tsx index 57ff9b2d5157..e3d60399c698 100644 --- a/src/components/SidePanel/HelpContent/helpContentMap.tsx +++ b/src/components/SidePanel/HelpContent/helpContentMap.tsx @@ -23,82 +23,247 @@ type HelpContent = { }; const helpContentMap: HelpContent = { - content: () => null, children: { r: { + children: { + ':expense': { + children: { + ':manual': { + content: ({styles}: {styles: ThemeStyles}) => ( + + Manual + + A “manual” expense has had all its details specified by the workspace member. It was not imported from any system, or scanned from a receipt. + + + ), + }, + ':pendingExpensifyCard': { + content: ({styles}: {styles: ThemeStyles}) => ( + + Expensify Card (pending) + + A “pending” Expensify Card expense represents a purchase that was recently made on the card, but has not yet “posted” – meaning, it has not been + formally recognized as a final, complete transaction. + + Any changes made to this expense will be preserved when the expense posts, typically 2-7 days later. + Pending transactions cannot be approved, as the final expense amount will not be confirmed until it posts. + + ), + }, + ':expensifyCard': { + content: ({styles}: {styles: ThemeStyles}) => ( + + Expensify Card + An “Expensify Card” expense corresponds to a “posted” (meaning, finalized by the bank) purchase. + + Expensify Card expenses cannot be reimbursed as they are centrally paid by the bank account linked to the workspace. + + + ), + }, + ':scan': { + content: ({styles}: {styles: ThemeStyles}) => ( + + Scanned + A “scanned” expense was created by extracting the relevant details using the Concierge AI. + + ), + }, + }, + content: ({styles}: {styles: ThemeStyles}) => ( + + Expense + Every expense gets a dedicated chat to discuss that specific expense. The expense consists of: + + Receipt – Attach a photo or document to this expense. + , + + Amount – The financial total of this transaction. + , + + Description – A general explanation of what this expense was for. + , + + Merchant – The business this purchase was made at. + , + + Date – The day on which the purchase was made. + , + ]} + /> + + The expense chat is shared with everyone in the approval flow, and will maintain an audit trail of all historical changes. + + + ), + }, + ':policyAdmins': { + content: ({styles}: {styles: ThemeStyles}) => ( + + #admins + + Every workspace automatically receives a special #admins chat room. Every admin is automatically added to this room as a member. The #admins room is used for + several purposes: + + + + Talking with Concierge, your setup specialist, or your account manager – When you first create the workspace, + Concierge and a setup specialist will be added. Feel free to ask any setup questions you have about how to configure the workspace, onboard your + team, connect your accounting, or anything else you might need. + + , + + + Monitoring workspace changes – Every #admins room shows an audit trail of any configuration changes or + significant events happening inside the workspace. + + , + + + Chatting with other admins – The #admins room is a useful space for workspace admins to chat with each other + about anything, whether or not it relates to Expensify. + + , + ]} + /> + + ), + }, + ':expenseReport': { + content: ({styles}: {styles: ThemeStyles}) => ( + + Expense Report + Every expense report gets a dedicated chat to discuss expenses, approvals, or anything you like. The expense report chat: + Is shared with everyone in the approval flow configured inside the workspace., + Will maintain an audit trail of all historical workflow actions (i.e., approvals)., + ]} + /> + + Press the attach button to add more expenses, or press the header for more options. Press on any expense to go deeper. + + + ), + }, + ':policyExpenseChat': { + content: ({styles}: {styles: ThemeStyles}) => ( + + Workspace + + Every workspace member gets a special chat between them and all workspace admins. This is a good place for workspace members to ask questions about expense + policy, for workspace admins to explain changes, or for any “formal” conversation to occur between members and admins. Press the attach button to: + + + Create expense – This will submit an expense to the workspace for reimbursement. + , + + Split expense – This will split an expense between the member and the workspace (e.g., for a business meal that + brings a spouse). + , + ]} + /> + All past expense reports are processed here and stored for historical reference. + + ), + }, + ':concierge': { + content: ({styles}: {styles: ThemeStyles}) => ( + + Concierge + + Concierge is available 24/7 to answer any question you have about anything — whether that’s how to get set up, how to fix a problem, or general best + practices. Concierge is a bot, but it’s really smart and can escalate you to a human whenever you want. Say hi — it’s friendly! + + + ), + }, + }, content: ({styles}: {styles: ThemeStyles}) => ( - <> - Chat - - Chat is the foundation of New Expensify. Every expense, expense report, workspace, or member has an associated "chat", which you can use to record additional details, + + Chat + + Chat is the foundation of New Expensify. Every expense, expense report, workspace, or member has an associated “chat”, which you can use to record additional details, or collaborate with others. Every chat has the following components: - Header - + This shows who you are chatting with (or what you are chatting about). You can press the header for more details on the chat, or additional actions to take upon it. - Comments - The core of the chat are its comments, which come in many forms: + The core of the chat are its comments, which come in many forms: - Text - Rich text messages stored securely and delivered via web, app, email, or SMS. + Text – Rich text messages stored securely and delivered via web, app, email, or SMS. , - Images & Documents - Insert photos, screenshots, movies, PDFs, or more, using copy/paste, drag/drop, or the attach + Images & Documents – Insert photos, screenshots, movies, PDFs, or more, using copy/paste, drag/drop, or the attach button. , - Expenses - Share an expense in the chat, either to simply track and document it, or to submit for reimbursement. + Expenses – Share an expense in the chat, either to simply track and document it, or to submit for reimbursement. , - Tasks - Record a task, and optionally assign it to someone (or yourself!). + Tasks – Record a task, and optionally assign it to someone (or yourself!). , ]} /> - Actions - Hover (or long press) on a comment to see additional options, including: + Hover (or long press) on a comment to see additional options, including: - React - Throw a ♥️😂🔥 like on anything! + React – Throw a ♥️😂🔥 like on anything! , - Reply in thread - Go deeper by creating a new chat on any comment. + Reply in thread – Go deeper by creating a new chat on any comment. , - Mark unread - Flag it for reading later, at your convenience. + Mark unread – Flag it for reading later, at your convenience. , ]} /> - Composer - Use the composer at the bottom to write new messages: + Use the composer at the bottom to write new messages: - Markdown - Format text using bold, italics, - and more. + Markdown – Format text using bold, italics, + and{' '} + + more + + . , - Mention - Invite or tag anyone in the world to any chat by putting an @ in front of their email address or phone number + Mention – Invite or tag anyone in the world to any chat by putting an @ in front of their email address or phone number (e.g., @awong@marslink.web, or @415-867-5309). , ]} /> - - Inbox - The Inbox is a prioritized "to do" list, highlighting exactly what you need to do next. It consists of: - + Inbox + The Inbox is a prioritized “to do” list, highlighting exactly what you need to do next. It consists of: Priorities - At the top of the Inbox are the most important tasks you should do first, which include: + At the top of the Inbox are the most important tasks you should do first, which include: Anything you have pinned, ]} /> - Chats - Beneath the priorities are a list of chats (with unread chats highlighted in bold), in one of two view modes: + Beneath the priorities are a list of chats (with unread chats highlighted in bold), in one of two view modes: - Most Recent - Lists every chat, ordered by whichever was most recently active. + Most Recent – Lists every chat, ordered by whichever was most recently active. , - Focus - Only lists chats with unread messages, sorted alphabetically. + Focus – Only lists chats with unread messages, sorted alphabetically. , ]} /> - + ), + }, + settings: { + content: ({styles}: {styles: ThemeStyles}) => ( + + Settings + Here is where you configure Expensify exactly to your specifications: + + Profile - Configure how you appear to others. + , + + Wallet - See and manage your credit cards and bank accounts. + , + + Preferences - Adjust how the app works for you. + , + + Security - Lock down how you and others access your account. + , + + Workspaces - Organize expenses for yourself and share with others. + , + + Subscriptions - Manage payment details and history. + , + + Domains - Advanced security and corporate card configuration. + , + + Switch to Expensify Classic - Battle tested and reliable. + , + + Save the World - Let Expensify.org help your favorite teacher! + , + ]} + /> + + ), + }, + workspaces: { children: { - ':policyAdmins': { + ':policyID': { + children: { + members: { + content: ({styles}: {styles: ThemeStyles}) => ( + + Workspace Members + Manage team access, roles, and permissions for your workspace. + Member Roles + + Admin + + Full workspace control and settings access, + Add/remove members and change roles, + Set up integrations and payment methods, + Approve and pay expenses, + ]} + /> + + Member + + Submit expenses and create reports, + Participate in workspace chats, + View assigned expenses and reports, + ]} + /> + + Auditor + + View all workspace reports (read-only), + Add comments but cannot modify expenses, + No approval or payment permissions, + ]} + /> + Adding Members + + + Alternative: Share workspace URL or QR code from{' '} + Settings > Profile > Share + + Managing Members + + Change Role: + + + + Remove Member: + + + + Bulk Actions: + + Select multiple members with checkboxes, + Use dropdown to remove or modify multiple members, + ]} + /> + Transfer Ownership + + Related Links + + + Managing Workspace Members + + , + + + Add Approvals + + , + ]} + /> + + ), + }, + accounting: { + content: ({styles}: {styles: ThemeStyles}) => ( + + Accounting Integrations + Connect your workspace to accounting software to sync expenses and streamline financial management. + Supported Integrations + + QuickBooks Online + + Real-time expense sync, + Category and vendor mapping, + Tax rate synchronization, + ]} + /> + + QuickBooks Desktop + + File-based import/export, + Chart of accounts import, + Custom field mapping, + ]} + /> + + Xero + + Automatic report sync, + Tracking category import, + Tax rate management, + ]} + /> + + NetSuite + + Advanced multi-entity support, + Custom dimension mapping, + Automated bill payments, + ]} + /> + + Sage Intacct + + Department/class tracking, + Multi-currency support, + Advanced approval workflows, + ]} + /> + Getting Started + + What Gets Synced + + From your accounting system: + + Chart of accounts (as categories), + Classes, departments, locations (as tags), + Tax rates and customers, + Vendors and bill payment accounts, + ]} + /> + + To your accounting system: + + Approved expense reports, + Company card transactions, + Vendor bills and journal entries, + Payment records and reconciliation data, + ]} + /> + Related Links + + + Connect to QuickBooks Online + + , + + + Connect to Xero + + , + + + Connect to NetSuite + + , + ]} + /> + + ), + }, + }, content: ({styles}: {styles: ThemeStyles}) => ( - <> - #admins - - Every workspace automatically receives a special #admins chat room. Every admin is automatically added to this room as a member. The #admins room is used for - several purposes: - + + Workspace + This is where you configure all the settings of the many features associated with your workspace. + Default features + Here are the features that are enabled by default: - Talking with Concierge, your setup specialist, or your account manager - When you first create the workspace, - Concierge and a setup specialist will be added. Feel free to ask any setup questions you have about how to configure the workspace, onboard your team, - connect your accounting, or anything else you might need. - , - - Monitoring workspace changes - Every #admins room shows an audit trail of any configuration changes or - significant events happening inside the workspace. + Overview - Configure how it appears to others. , - Chatting with other admins - The #admins room is a useful space for workspace admins to chat with each other - about anything, whether or not it relates to Expensify. + Members - Add/remove members and admins. , - ]} - /> - - ), - }, - ':concierge': { - content: ({styles}: {styles: ThemeStyles}) => ( - <> - Concierge - - Concierge is available 24/7 to answer any question you have about anything, whether that's how to get set up, how to fix a problem, or general best practices. - Concierge is a bot, but is really smart, and can escalate you to a human whenever you want. Say hi, it's friendly! - - - ), - }, - ':policyExpenseChat': { - content: ({styles}: {styles: ThemeStyles}) => ( - <> - Workspace - - - Every workspace member gets a special chat between them and all workspace admins. This is a good place for workspace members to ask questions about expense - policy, for workspace admins to explain changes, or for any "formal" conversation to occur between members and admins. Press the attach button to: - - - Create expense - This will submit an expense to the workspace for reimbursement. + Workflows - Configure submission, approval, and reimbursement. , - Split expense - This will split an expense between the member and the workspace (e.g., for a business meal that - brings a spouse). - , - ]} - /> - - All past expense reports are processed here and stored for historical reference. - - ), - }, - ':expenseReport': { - content: ({styles}: {styles: ThemeStyles}) => ( - <> - Expense Report - Every expense report gets a dedicated chat to discuss expenses, approvals, or anything you like. The expense report chat: - Is shared with everyone in the approval flow configured inside the workspace., - Will maintain an audit trail of all historical workflow actions (i.e., approvals)., + Categories - Group expenses into a chart of accounts. + , + + Expensify Card - Issue native Expensify Cards to employees. + , + + Accounting - Sync with external accounting systems. + , ]} /> - - - Press the attach button to add more expenses, or press the header for more options. Press on any expense to go deeper. - - - ), - }, - ':expense': { - content: ({styles}: {styles: ThemeStyles}) => ( - <> - Expense - Every expense gets a dedicated chat to discuss that specific expense. The expense consists of: + Optional features + These can be enabled via More Features: - Receipt - Attach a photo or document to this expense. + Distance rates - Configure mileage reimbursement. + , + + Company card - Connect and manage third-party corporate card feeds. + , + + Per diem - Configure daily rates. , - Amount - The financial total of this transaction. + Rules - Customize expense violations and set policy. , - Description - A general explanation of what this expense was for. + Invoices - Collect revenue from customers. , - Merchant - The business this purchase was made at. + Tags - Group expenses by project or client. , - Date - The day on which the purchase was made. + Taxes - Track VAT and other taxes. + , + + Report fields - Capture extra expense report information. , ]} /> - - - The expense chat is shared with everyone in the approval flow, and will maintain an audit trail of all historical changes. - - + ), - children: { - ':manual': { - content: ({styles}: {styles: ThemeStyles}) => ( - <> - Manual - - A "manual" expense has had all its details specified by the workspace member. It was not imported from any system, or scanned from a receipt. - - - ), - }, - ':scan': { - content: ({styles}: {styles: ThemeStyles}) => ( - <> - Scanned - A "scanned" expense was created by extracting the relevant details using the Concierge AI. - - ), - }, - ':expensifyCard': { - content: ({styles}: {styles: ThemeStyles}) => ( - <> - Expensify Card - An "Expensify Card" expense corresponds to a "posted" (meaning, finalized by the bank) purchase. - - Expensify Card expenses cannot be reimbursed as they are centrally paid by the bank account linked to the workspace. - - - ), - }, - ':pendingExpensifyCard': { - content: ({styles}: {styles: ThemeStyles}) => ( - <> - Expensify Card (pending) - - A "pending" Expensify Card expense represents a purchase that was recently made on the card, but has not yet "posted" – meaning, it has not been - formally recognized as a final, complete transaction. - - Any changes made to this expense will be preserved when the expense posts, typically 2-7 days later. - - Pending transactions cannot be approved, as the final expense amount will not be confirmed until it posts. - - - ), - }, - }, }, }, - }, - home: { content: ({styles}: {styles: ThemeStyles}) => ( - <> - Chat - - Chat is the foundation of New Expensify. Every expense, expense report, workspace, or member has an associated "chat", which you can use to record additional details, - or collaborate with others. Every chat has the following components: - - - Header - - This shows who you are chatting with (or what you are chatting about). You can press the header for more details on the chat, or additional actions to take upon it. - - - Comments - The core of the chat are its comments, which come in many forms: + + Workspaces + Workspaces allow for a wide range of features, including: - Text - Rich text messages stored securely and delivered via web, app, email, or SMS. - , - - Images & Documents - Insert photos, screenshots, movies, PDFs, or more, using copy/paste, drag/drop, or the attach - button. - , - - Expenses - Share an expense in the chat, either to simply track and document it, or to submit for reimbursement. - , - - Tasks - Record a task, and optionally assign it to someone (or yourself!). + Categorize and submit expenses , - ]} - /> - - Actions - Hover (or long press) on a comment to see additional options, including: - - React - Throw a ♥️😂🔥 like on anything! + Approve and reimburse expenses , - Reply in thread - Go deeper by creating a new chat on any comment. + Sync with accounting packages , - Mark unread - Flag it for reading later, at your convenience. + Connect to company card feeds , - ]} - /> - - Composer - Use the composer at the bottom to write new messages: - - Markdown - Format text using bold, italics, - and more. + Manage Expensify Cards , - Mention - Invite or tag anyone in the world to any chat by putting an @ in front of their email address or phone number - (e.g., @awong@marslink.web, or @415-867-5309). + Chat with colleagues, partners, and clients , + … and lots more!, ]} /> - - - Inbox - The Inbox is a prioritized "to do" list, highlighting exactly what you need to do next. It consists of: - - Priorities - At the top of the Inbox are the most important tasks you should do first, which include: - Expense reports waiting on you, - Tasks assigned to you, - Chats that have mentioned you, - Anything you have pinned, - ]} - /> - - Chats - Beneath the priorities are a list of chats (with unread chats highlighted in bold), in one of two view modes: + Workspaces come in two variations: - Most Recent - Lists every chat, ordered by whichever was most recently active. + Collect workspaces start at $5/member, and include all the basics for running a small business. , - Focus - Only lists chats with unread messages, sorted alphabetically. + Control workspaces start at $9/member, and provide advanced capabilities, more powerful accounting sync, and more + sophisticated approval flows. , ]} /> - + + In general you would create one Workspace for each company you manage. You can create and join as many workspaces as you like. + + ), }, search: { content: ({styles}: {styles: ThemeStyles}) => ( - <> - Reports - Virtually all data can be analyzed and reported upon in the Reports page. The major elements of this page include: - + + Reports + Virtually all data can be analyzed and reported upon in the Reports page. The major elements of this page include: Data type - Start first by choosing the type of data you want to analyze, which can be: + Start first by choosing the type of data you want to analyze, which can be: , ]} /> - Search - A quick method of narrowing the results by keyword or more. - + A quick method of narrowing the results by keyword or more. State filter - Simple methods to filter the results by "state", including: + Simple methods to filter the results by “state”, including: All, + + All + , <> Expenses/Expense/Invoices reports: + Draft - Only you can see that hasn't been shared yet., + Draft - Only you can see that hasn’t been shared yet., Outstanding - Submitted to someone and awaiting action., Approved - Approved, but awaiting payment., Done - Fully processed, no further action needed., @@ -443,6 +711,7 @@ const helpContentMap: HelpContent = { , <> Chats: + , <> Trips: + Current - Happening or in the future., Past - Already happened.]} @@ -463,177 +733,117 @@ const helpContentMap: HelpContent = { , ]} /> - Results - The core of the Reports page are the search results themselves. + The core of the Reports page are the search results themselves. Select a row to see additional options., Tap on a row to see more detail.]} /> - + ), }, - settings: { + home: { content: ({styles}: {styles: ThemeStyles}) => ( - <> - Settings - Here is where you configure Expensify exactly to your specifications: + + Chat + + Chat is the foundation of New Expensify. Every expense, expense report, workspace, or member has an associated “chat”, which you can use to record additional details, + or collaborate with others. Every chat has the following components: + + Header + + This shows who you are chatting with (or what you are chatting about). You can press the header for more details on the chat, or additional actions to take upon it. + + Comments + The core of the chat are its comments, which come in many forms: - Profile - Configure how you appear to others. + Text - Rich text messages stored securely and delivered via web, app, email, or SMS. , - Wallet - See and manage your credit cards and bank accounts. + Images & Documents - Insert photos, screenshots, movies, PDFs, or more, using copy/paste, drag/drop, or the attach + button. , - Preferences - Adjust how the app works for you. + Expenses - Share an expense in the chat, either to simply track and document it, or to submit for reimbursement. , - Security - Lock down how you and others access your account. + Tasks - Record a task, and optionally assign it to someone (or yourself!). , + ]} + /> + Actions + Hover (or long press) on a comment to see additional options, including: + - Workspaces - Organize expenses for yourself and share with others. + React - Throw a ♥️😂🔥 like on anything! , - Subscriptions - Manage payment details and history. + Reply in thread - Go deeper by creating a new chat on any comment. , - Domains - Advanced security and corporate card configuration. + Mark unread - Flag it for reading later, at your convenience. , + ]} + /> + Composer + Use the composer at the bottom to write new messages: + - Switch to Expensify Classic - Battle tested and reliable. + Markdown - Format text using bold, italics, + and{' '} + + more + + . , - Save the World - Let Expensify.org help your favorite teacher! + Mention - Invite or tag anyone in the world to any chat by putting an @ in front of their email address or phone number + (e.g., @awong@marslink.web, or @415-867-5309). , ]} /> - - ), - children: { - workspaces: { - content: ({styles}: {styles: ThemeStyles}) => ( - <> - Workspaces - Workspaces allow for a wide range of features, including: - - Categorize and submit expenses - , - - Approve and reimburse expenses - , - - Sync with accounting packages - , - - Connect to company card feeds - , - - Manage Expensify Cards - , - - Chat with colleagues, partners, and clients - , - … and lots more!, - ]} - /> - - Workspaces come in two variations: - - Collect workspaces start at $5/member, and include all the basics for running a small business. - , - - Control workspaces start at $9/member, and provide advanced capabilities, more powerful accounting sync, and more - sophisticated approval flows. - , - ]} - /> + Inbox + The Inbox is a prioritized “to do” list, highlighting exactly what you need to do next. It consists of: + Priorities + At the top of the Inbox are the most important tasks you should do first, which include: + Expense reports waiting on you, + Tasks assigned to you, + Chats that have mentioned you, + Anything you have pinned, + ]} + /> + Chats + Beneath the priorities are a list of chats (with unread chats highlighted in bold), in one of two view modes: + - In general you would create one Workspace for each company you manage. You can create and join as many workspaces as you like. - - - ), - children: { - ':policyID': { - content: ({styles}: {styles: ThemeStyles}) => ( - <> - Workspace - This is where you configure all the settings of the many features associated with your workspace. - - Default features - Here are the features that are enabled by default: - - Overview - Configure how it appears to others. - , - - Members - Add/remove members and admins. - , - - Workflows - Configure submission, approval, and reimbursement. - , - - Categories - Group expenses into a chart of accounts. - , - - Expensify Card - Issue native Expensify Cards to employees. - , - - Accounting - Sync with external accounting systems. - , - ]} - /> - - Optional features - These can be enabled via More Features: - - Distance rates - Configure mileage reimbursement. - , - - Company card - Connect and manage third-party corporate card feeds. - , - - Per diem - Configure daily rates. - , - - Rules - Customize expense violations and set policy. - , - - Invoices - Collect revenue from customers. - , - - Tags - Group expenses by project or client. - , - - Taxes - Track VAT and other taxes. - , - - Report fields - Capture extra expense report information. - , - ]} - /> - - ), - }, - }, - }, - }, + Most Recent - Lists every chat, ordered by whichever was most recently active. + , + + Focus - Only lists chats with unread messages, sorted alphabetically. + , + ]} + /> + + ), }, }, + content: () => null, }; export default helpContentMap; From 6b7a6ee3b30c9a7c95a34825d2d2e26651ab50fa Mon Sep 17 00:00:00 2001 From: sumo_slonik Date: Thu, 3 Jul 2025 11:38:58 +0200 Subject: [PATCH 09/10] helpContentMap regenerate --- .../SidePanel/HelpContent/helpContentMap.tsx | 1985 ++++++++--------- 1 file changed, 946 insertions(+), 1039 deletions(-) diff --git a/src/components/SidePanel/HelpContent/helpContentMap.tsx b/src/components/SidePanel/HelpContent/helpContentMap.tsx index b77e8a4aa916..ccbb77214802 100644 --- a/src/components/SidePanel/HelpContent/helpContentMap.tsx +++ b/src/components/SidePanel/HelpContent/helpContentMap.tsx @@ -1,13 +1,12 @@ /* eslint-disable react/no-unescaped-entities */ - /* eslint-disable @typescript-eslint/naming-convention */ import type {ReactNode} from 'react'; import React from 'react'; import {View} from 'react-native'; +import type {ThemeStyles} from '@styles/index'; import BulletList from '@components/SidePanel/HelpComponents/HelpBulletList'; import Text from '@components/Text'; import TextLink from '@components/TextLink'; -import type {ThemeStyles} from '@styles/index'; type ContentComponent = (props: {styles: ThemeStyles}) => ReactNode; @@ -23,1063 +22,971 @@ type HelpContent = { }; const helpContentMap: HelpContent = { - children: { - home: { - content: ({styles}: {styles: ThemeStyles}) => ( - - Chat - - Chat is the foundation of New Expensify. Every expense, expense report, workspace, or member has an associated “chat”, which you can use to record additional details, - or collaborate with others. Every chat has the following components: - - Header - - This shows who you are chatting with (or what you are chatting about). You can press the header for more details on the chat, or additional actions to take upon it. - - Comments - The core of the chat are its comments, which come in many forms: - - Text - Rich text messages stored securely and delivered via web, app, email, or SMS. - , - - Images & Documents - Insert photos, screenshots, movies, PDFs, or more, using copy/paste, drag/drop, or the attach - button. - , - - Expenses - Share an expense in the chat, either to simply track and document it, or to submit for reimbursement. - , - - Tasks - Record a task, and optionally assign it to someone (or yourself!). - , - ]} - /> - Actions - Hover (or long press) on a comment to see additional options, including: - - React - Throw a ♥️😂🔥 like on anything! - , - - Reply in thread - Go deeper by creating a new chat on any comment. - , - - Mark unread - Flag it for reading later, at your convenience. - , - ]} - /> - Composer - Use the composer at the bottom to write new messages: - - Markdown - Format text using bold, italics, - and{' '} - - more - - . - , - - Mention - Invite or tag anyone in the world to any chat by putting an @ in front of their email address or phone number - (e.g., @awong@marslink.web, or @415-867-5309). - , - ]} - /> - - Inbox - The Inbox is a prioritized “to do” list, highlighting exactly what you need to do next. It consists of: - Priorities - At the top of the Inbox are the most important tasks you should do first, which include: - Expense reports waiting on you, - Tasks assigned to you, - Chats that have mentioned you, - Anything you have pinned, - ]} - /> - Chats - Beneath the priorities are a list of chats (with unread chats highlighted in bold), in one of two view modes: - - Most Recent - Lists every chat, ordered by whichever was most recently active. - , - - Focus - Only lists chats with unread messages, sorted alphabetically. - , - ]} - /> - - ), - }, - ':action': { - children: { - ':iouType': { - children: { - start: { - children: { - ':transactionID': { - children: { - ':reportID': { - children: { - distance: { - children: { - ':backToReport': { - content: ({styles}: {styles: ThemeStyles}) => ( - - Distance Expense - - Easily track mileage costs using Expensify’s built-in map feature. Create and submit distance-based expenses right - from the web, desktop, or mobile app. - - - - Create distance expenses: Click the green + button and choose Create - expense, then select Distance. Enter your starting point and destination. You can also add stops if - needed. - - , - - - Submit for approval: Choose your workspace and confirm the distance, - amount, and date. Add optional notes or categories, then click Create expense to submit the mileage - expense for approval. - - , - - - Log a round-trip: To log a round-trip, use the same location for both - start and finish, and include any stops along the way. - - , - ]} - /> - - ), - }, - }, - }, - scan: { - children: { - ':backToReport': { - content: ({styles}: {styles: ThemeStyles}) => ( - - Scan Receipt - SmartScan automatically extracts expense details from receipt images. - How to Scan - - What SmartScan Detects - - Amount and currency - , - - Merchant name and location - , - - Date of purchase - , - - Tax information (when visible) - , - - Category suggestions based on merchant type - , - ]} - /> - Supported Receipt Types - - Photos - Take with your device camera - , - - Email receipts - Forward to receipts@expensify.com - , - - PDF receipts - Upload from your device - , - - Screenshots - From apps or websites - , - ]} - /> - Tips for Best Results - Ensure receipt text is clear and readable, - Include the full receipt in the image, - Good lighting improves accuracy, - Straight angles work better than tilted photos, - ]} - /> - After Scanning - Review extracted details for accuracy, - Add description, category, or tags as needed, - SmartScan learns from your corrections, - ]} - /> - Related Links - - - Create an Expense - - , - - - Free Features in Expensify - - , - ]} - /> - - ), - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - r: { - children: { - ':concierge': { - content: ({styles}: {styles: ThemeStyles}) => ( - - Concierge - - Concierge is available 24/7 to answer any question you have about anything — whether that’s how to get set up, how to fix a problem, or general best - practices. Concierge is a bot, but it’s really smart and can escalate you to a human whenever you want. Say hi — it’s friendly! - - - ), - }, - ':expense': { - children: { - ':scan': { - content: ({styles}: {styles: ThemeStyles}) => ( - - Scanned - A “scanned” expense was created by extracting the relevant details using the Concierge AI. - - ), - }, - ':manual': { - content: ({styles}: {styles: ThemeStyles}) => ( + children: { + ":action": { + children: { + ":iouType": { + children: { + start: { + children: { + ":transactionID": { + children: { + ":reportID": { + children: { + scan: { + children: { + ":backToReport": { + content: ({styles}: {styles: ThemeStyles}) => ( - Manual - - A “manual” expense has had all its details specified by the workspace member. It was not imported from any system, or scanned from a receipt. - - - ), - }, - ':pendingExpensifyCard': { - content: ({styles}: {styles: ThemeStyles}) => ( - - Expensify Card (pending) - - A “pending” Expensify Card expense represents a purchase that was recently made on the card, but has not yet “posted” – meaning, it has not been - formally recognized as a final, complete transaction. - - Any changes made to this expense will be preserved when the expense posts, typically 2-7 days later. - Pending transactions cannot be approved, as the final expense amount will not be confirmed until it posts. + Scan Receipt + SmartScan automatically extracts expense details from receipt images. + How to Scan + + + + + + + + + What SmartScan Detects + Amount and currency, + Merchant name and location, + Date of purchase, + Tax information (when visible), + Category suggestions based on merchant type + ]} + /> + Supported Receipt Types + Photos - Take with your device camera, + Email receipts - Forward to receipts@expensify.com, + PDF receipts - Upload from your device, + Screenshots - From apps or websites + ]} + /> + Tips for Best Results + Ensure receipt text is clear and readable, + Include the full receipt in the image, + Good lighting improves accuracy, + Straight angles work better than tilted photos + ]} + /> + After Scanning + Review extracted details for accuracy, + Add description, category, or tags as needed, + SmartScan learns from your corrections + ]} + /> + Related Links + Create an Expense, + Free Features in Expensify + ]} + /> - ), + ), + }, + }, }, - ':expensifyCard': { - content: ({styles}: {styles: ThemeStyles}) => ( + distance: { + children: { + ":backToReport": { + content: ({styles}: {styles: ThemeStyles}) => ( - Expensify Card - An “Expensify Card” expense corresponds to a “posted” (meaning, finalized by the bank) purchase. - - Expensify Card expenses cannot be reimbursed as they are centrally paid by the bank account linked to the workspace. - + Distance Expense + Easily track mileage costs using Expensify’s built-in map feature. Create and submit distance-based expenses right from the web, desktop, or mobile app. + + Create distance expenses: Click the green + button and choose Create expense, then select Distance. Enter your starting point and destination. You can also add stops if needed. + , + + Submit for approval: Choose your workspace and confirm the distance, amount, and date. Add optional notes or categories, then click Create expense to submit the mileage expense for approval. + , + + Log a round-trip: To log a round-trip, use the same location for both start and finish, and include any stops along the way. + + ]} + /> - ), + ), + }, + }, }, + }, }, - content: ({styles}: {styles: ThemeStyles}) => ( - - Expense - Every expense gets a dedicated chat to discuss that specific expense. The expense consists of: - - Receipt – Attach a photo or document to this expense. - , - - Amount – The financial total of this transaction. - , - - Description – A general explanation of what this expense was for. - , - - Merchant – The business this purchase was made at. - , - - Date – The day on which the purchase was made. - , - ]} - /> - - The expense chat is shared with everyone in the approval flow, and will maintain an audit trail of all historical changes. - - - ), - }, - ':policyAdmins': { - content: ({styles}: {styles: ThemeStyles}) => ( - - #admins - - Every workspace automatically receives a special #admins chat room. Every admin is automatically added to this room as a member. The #admins room is used for - several purposes: - - - - Talking with Concierge, your setup specialist, or your account manager – When you first create the workspace, - Concierge and a setup specialist will be added. Feel free to ask any setup questions you have about how to configure the workspace, onboard your - team, connect your accounting, or anything else you might need. - - , - - - Monitoring workspace changes – Every #admins room shows an audit trail of any configuration changes or - significant events happening inside the workspace. - - , - - - Chatting with other admins – The #admins room is a useful space for workspace admins to chat with each other - about anything, whether or not it relates to Expensify. - - , - ]} - /> - - ), - }, - ':policyExpenseChat': { - content: ({styles}: {styles: ThemeStyles}) => ( - - Workspace - - Every workspace member gets a special chat between them and all workspace admins. This is a good place for workspace members to ask questions about expense - policy, for workspace admins to explain changes, or for any “formal” conversation to occur between members and admins. Press the attach button to: - - - Create expense – This will submit an expense to the workspace for reimbursement. - , - - Split expense – This will split an expense between the member and the workspace (e.g., for a business meal that - brings a spouse). - , - ]} - /> - All past expense reports are processed here and stored for historical reference. - - ), - }, - ':policyAnnounce': { - content: ({styles}: {styles: ThemeStyles}) => ( - - Announce Room (#announce) - - The #announce room is a chat space available to all workspace members. It’s perfect for sharing company-wide updates, policy changes, or event reminders. The - #announce room is accessible from your Inbox in the left-hand menu. - - - - Post company-wide announcements: All members can post in #announce by default, making it easy to communicate - across the workspace. - - , - - - Restrict posting to admins: Workspace admins can limit posting to admins only. Open the #announce room, click - the room header, select Settings, and change Who can post to Admins only. - - , - - - Everyone can read messages: Even if posting is limited to admins, all workspace members can still view - messages in the #announce room. - - , - ]} - /> - - ), - }, - ':expenseReport': { - content: ({styles}: {styles: ThemeStyles}) => ( - - Expense Report - Every expense report gets a dedicated chat to discuss expenses, approvals, or anything you like. The expense report chat: - Is shared with everyone in the approval flow configured inside the workspace., - Will maintain an audit trail of all historical workflow actions (i.e., approvals)., - ]} - /> - - Press the attach button to add more expenses, or press the header for more options. Press on any expense to go deeper. - - - ), + }, }, + }, + }, + }, + }, + }, + }, + r: { + children: { + ":concierge": { + content: ({styles}: {styles: ThemeStyles}) => ( + + Concierge + Concierge is available 24/7 to answer any question you have about anything — whether that’s how to get set up, how to fix a problem, or general best practices. + Concierge is a bot, but it’s really smart and can escalate you to a human whenever you want. Say hi — it’s friendly! + + ), + }, + ":expense": { + children: { + ":scan": { + content: ({styles}: {styles: ThemeStyles}) => ( + + Scanned + A “scanned” expense was created by extracting the relevant details using the Concierge AI. + + ), }, - content: ({styles}: {styles: ThemeStyles}) => ( + ":manual": { + content: ({styles}: {styles: ThemeStyles}) => ( - Chat - - Chat is the foundation of New Expensify. Every expense, expense report, workspace, or member has an associated “chat”, which you can use to record additional details, - or collaborate with others. Every chat has the following components: - - Header - - This shows who you are chatting with (or what you are chatting about). You can press the header for more details on the chat, or additional actions to take upon it. - - Comments - The core of the chat are its comments, which come in many forms: - - Text – Rich text messages stored securely and delivered via web, app, email, or SMS. - , - - Images & Documents – Insert photos, screenshots, movies, PDFs, or more, using copy/paste, drag/drop, or the attach - button. - , - - Expenses – Share an expense in the chat, either to simply track and document it, or to submit for reimbursement. - , - - Tasks – Record a task, and optionally assign it to someone (or yourself!). - , - ]} - /> - Actions - Hover (or long press) on a comment to see additional options, including: - - React – Throw a ♥️😂🔥 like on anything! - , - - Reply in thread – Go deeper by creating a new chat on any comment. - , - - Mark unread – Flag it for reading later, at your convenience. - , - ]} - /> - Composer - Use the composer at the bottom to write new messages: - - Markdown – Format text using bold, italics, - and{' '} - - more - - . - , - - Mention – Invite or tag anyone in the world to any chat by putting an @ in front of their email address or phone number - (e.g., @awong@marslink.web, or @415-867-5309). - , - ]} - /> - - Inbox - The Inbox is a prioritized “to do” list, highlighting exactly what you need to do next. It consists of: - Priorities - At the top of the Inbox are the most important tasks you should do first, which include: - Expense reports waiting on you, - Tasks assigned to you, - Chats that have mentioned you, - Anything you have pinned, - ]} - /> - Chats - Beneath the priorities are a list of chats (with unread chats highlighted in bold), in one of two view modes: - - Most Recent – Lists every chat, ordered by whichever was most recently active. - , - - Focus – Only lists chats with unread messages, sorted alphabetically. - , - ]} - /> + Manual + A “manual” expense has had all its details specified by the workspace member. It was not imported from any system, or scanned from a receipt. - ), - }, - settings: { - children: { - workspaces: { - children: { - ':policyID': { - children: { - accounting: { - content: ({styles}: {styles: ThemeStyles}) => ( - - Accounting Integrations - Connect your workspace to accounting software to sync expenses and streamline financial management. - Supported Integrations - - QuickBooks Online - - Real-time expense sync, - Category and vendor mapping, - Tax rate synchronization, - ]} - /> - - QuickBooks Desktop - - File-based import/export, - Chart of accounts import, - Custom field mapping, - ]} - /> - - Xero - - Automatic report sync, - Tracking category import, - Tax rate management, - ]} - /> - - NetSuite - - Advanced multi-entity support, - Custom dimension mapping, - Automated bill payments, - ]} - /> - - Sage Intacct - - Department/class tracking, - Multi-currency support, - Advanced approval workflows, - ]} - /> - Getting Started - - What Gets Synced - - From your accounting system: - - Chart of accounts (as categories), - Classes, departments, locations (as tags), - Tax rates and customers, - Vendors and bill payment accounts, - ]} - /> - - To your accounting system: - - Approved expense reports, - Company card transactions, - Vendor bills and journal entries, - Payment records and reconciliation data, - ]} - /> - Related Links - - - Connect to QuickBooks Online - - , - - - Connect to Xero - - , - - - Connect to NetSuite - - , - ]} - /> - - ), - }, - members: { - content: ({styles}: {styles: ThemeStyles}) => ( - - Workspace Members - Manage team access, roles, and permissions for your workspace. - Member Roles - - Admin - - Full workspace control and settings access, - Add/remove members and change roles, - Set up integrations and payment methods, - Approve and pay expenses, - ]} - /> - - Member - - Submit expenses and create reports, - Participate in workspace chats, - View assigned expenses and reports, - ]} - /> - - Auditor - - View all workspace reports (read-only), - Add comments but cannot modify expenses, - No approval or payment permissions, - ]} - /> - Adding Members - - - Alternative: Share workspace URL or QR code from{' '} - Settings > Profile > Share - - Managing Members - - Change Role: - - - - Remove Member: - - - - Bulk Actions: - - Select multiple members with checkboxes, - Use dropdown to remove or modify multiple members, - ]} - /> - Transfer Ownership - - Related Links - - - Managing Workspace Members - - , - - - Add Approvals - - , - ]} - /> - - ), - }, - }, - content: ({styles}: {styles: ThemeStyles}) => ( - - Workspace - This is where you configure all the settings of the many features associated with your workspace. - Default features - Here are the features that are enabled by default: - - Overview - Configure how it appears to others. - , - - Members - Add/remove members and admins. - , - - Workflows - Configure submission, approval, and reimbursement. - , - - Categories - Group expenses into a chart of accounts. - , - - Expensify Card - Issue native Expensify Cards to employees. - , - - Accounting - Sync with external accounting systems. - , - ]} - /> - Optional features - These can be enabled via More Features: - - Distance rates - Configure mileage reimbursement. - , - - Company card - Connect and manage third-party corporate card feeds. - , - - Per diem - Configure daily rates. - , - - Rules - Customize expense violations and set policy. - , - - Invoices - Collect revenue from customers. - , - - Tags - Group expenses by project or client. - , - - Taxes - Track VAT and other taxes. - , - - Report fields - Capture extra expense report information. - , - ]} - /> - - ), - }, - }, - content: ({styles}: {styles: ThemeStyles}) => ( - - Workspaces - Workspaces allow for a wide range of features, including: - - Categorize and submit expenses - , - - Approve and reimburse expenses - , - - Sync with accounting packages - , - - Connect to company card feeds - , - - Manage Expensify Cards - , - - Chat with colleagues, partners, and clients - , - … and lots more!, - ]} - /> - Workspaces come in two variations: - - Collect workspaces start at $5/member, and include all the basics for running a small business. - , - - Control workspaces start at $9/member, and provide advanced capabilities, more powerful accounting sync, and more - sophisticated approval flows. - , - ]} - /> - - In general you would create one Workspace for each company you manage. You can create and join as many workspaces as you like. - - - ), - }, + ), }, - content: ({styles}: {styles: ThemeStyles}) => ( + ":pendingExpensifyCard": { + content: ({styles}: {styles: ThemeStyles}) => ( - Settings - Here is where you configure Expensify exactly to your specifications: - - Profile - Configure how you appear to others. - , - - Wallet - See and manage your credit cards and bank accounts. - , - - Preferences - Adjust how the app works for you. - , - - Security - Lock down how you and others access your account. - , - - Workspaces - Organize expenses for yourself and share with others. - , - - Subscriptions - Manage payment details and history. - , - - Domains - Advanced security and corporate card configuration. - , - - Switch to Expensify Classic - Battle tested and reliable. - , - - Save the World - Let Expensify.org help your favorite teacher! - , - ]} - /> + Expensify Card (pending) + A “pending” Expensify Card expense represents a purchase that was recently made on the card, but has not yet “posted” – meaning, it has not been formally recognized as a final, complete transaction. + Any changes made to this expense will be preserved when the expense posts, typically 2-7 days later. + Pending transactions cannot be approved, as the final expense amount will not be confirmed until it posts. - ), - }, - search: { - content: ({styles}: {styles: ThemeStyles}) => ( + ), + }, + ":expensifyCard": { + content: ({styles}: {styles: ThemeStyles}) => ( - Reports - Virtually all data can be analyzed and reported upon in the Reports page. The major elements of this page include: - Data type - Start first by choosing the type of data you want to analyze, which can be: - - Expense - Individual standalone expenses. - , - - Expense reports - Groups of expenses processed in a batch. - , - - Chats - Comments written by you and others. - , - - Invoices - Expenses submitted to clients for payment. - , - - Trips - Travel expenses booked with Expensify Travel or scanned with SmartScan. - , - ]} - /> - Search - A quick method of narrowing the results by keyword or more. - State filter - Simple methods to filter the results by “state”, including: - - All - , - <> - Expenses/Expense/Invoices reports: - - Draft - Only you can see that hasn’t been shared yet., - Outstanding - Submitted to someone and awaiting action., - Approved - Approved, but awaiting payment., - Done - Fully processed, no further action needed., - Paid - Fully paid, no further action needed., - ]} - /> - , - <> - Chats: - - Unread - Not seen yet by you., - Sent - Sent by you., - Attachments - Image, movie, or document., - Links - Hyperlinks., - Pinned - Highlighted by you as important., - ]} - /> - , - <> - Trips: - - Current - Happening or in the future., Past - Already happened.]} - /> - , - ]} - /> - Results - The core of the Reports page are the search results themselves. - Select a row to see additional options., Tap on a row to see more detail.]} - /> + Expensify Card + An “Expensify Card” expense corresponds to a “posted” (meaning, finalized by the bank) purchase. + Expensify Card expenses cannot be reimbursed as they are centrally paid by the bank account linked to the workspace. - ), + ), + }, + }, + content: ({styles}: {styles: ThemeStyles}) => ( + + Expense + Every expense gets a dedicated chat to discuss that specific expense. The expense consists of: + Receipt – Attach a photo or document to this expense., + Amount – The financial total of this transaction., + Description – A general explanation of what this expense was for., + Merchant – The business this purchase was made at., + Date – The day on which the purchase was made. + ]} + /> + The expense chat is shared with everyone in the approval flow, and will maintain an audit trail of all historical changes. + + ), }, - new: { - children: { - task: { - content: ({styles}: {styles: ThemeStyles}) => ( - - Tasks - - Keep conversations organized by letting you create actionable to-dos directly within a chat. You can assign them to yourself or others in both 1:1 and group - chats. - - - - Create a task: In any chat, click the + button next to the message field and select Assign a task. Add a - title (required) and an optional description, and choose an assignee from chat participants. You can also leave it unassigned to track it - yourself. - - , - - - Use tasks to stay on top of action items: Tasks are great for follow-ups like “Submit expense report,” “Share - slide deck,” or “Update mileage rate.” They’re perfect for 1:1 check-ins, project updates, or organizing next steps after a team discussion. - - , - - - Edit and manage tasks: Task creators and assignees can comment, edit the title or description, reassign the - task, or mark it as complete. Just click the task to update any details. - - , - - - Tasks stay visible: Each task is shared in the chat where it’s created. When completed, it will be clearly - marked in the chat and can be reopened if needed. - - , - ]} - /> - - ), - }, + ":policyAdmins": { + content: ({styles}: {styles: ThemeStyles}) => ( + + #admins + Every workspace automatically receives a special #admins chat room. Every admin is automatically added to this room as a member. The #admins room is used for several purposes: + + Talking with Concierge, your setup specialist, or your account manager – When you first create the workspace, Concierge and a setup specialist will be added. Feel free to ask any setup questions you have about how to configure the workspace, onboard your team, connect your accounting, or anything else you might need. + , + + Monitoring workspace changes – Every #admins room shows an audit trail of any configuration changes or significant events happening inside the workspace. + , + + Chatting with other admins – The #admins room is a useful space for workspace admins to chat with each other about anything, whether or not it relates to Expensify. + + ]} + /> + + ), + }, + ":expenseReport": { + content: ({styles}: {styles: ThemeStyles}) => ( + + Expense Report + Every expense report gets a dedicated chat to discuss expenses, approvals, or anything you like. The expense report chat: + Is shared with everyone in the approval flow configured inside the workspace., + Will maintain an audit trail of all historical workflow actions (i.e., approvals). + ]} + /> + Press the attach button to add more expenses, or press the header for more options. Press on any expense to go deeper. + + ), + }, + ":policyExpenseChat": { + content: ({styles}: {styles: ThemeStyles}) => ( + + Workspace + Every workspace member gets a special chat between them and all workspace admins. This is a good place for workspace members to ask questions about expense policy, for workspace admins to explain changes, or for any “formal” conversation to occur between members and admins. Press the attach button to: + Create expense – This will submit an expense to the workspace for reimbursement., + Split expense – This will split an expense between the member and the workspace (e.g., for a business meal that brings a spouse). + ]} + /> + All past expense reports are processed here and stored for historical reference. + + ), + }, + ":policyAnnounce": { + content: ({styles}: {styles: ThemeStyles}) => ( + + Announce Room (#announce) + The #announce room is a chat space available to all workspace members. It’s perfect for sharing company-wide updates, policy changes, or event reminders. The #announce room is accessible from your Inbox in the left-hand menu. + + Post company-wide announcements: All members can post in #announce by default, making it easy to communicate across the workspace. + , + + Restrict posting to admins: Workspace admins can limit posting to admins only. Open the #announce room, click the room header, select Settings, and change Who can post to Admins only. + , + + Everyone can read messages: Even if posting is limited to admins, all workspace members can still view messages in the #announce room. + + ]} + /> + + ), + }, + }, + content: ({styles}: {styles: ThemeStyles}) => ( + + Chat + Chat is the foundation of New Expensify. Every expense, expense report, workspace, or member has an associated “chat”, which you can use to record additional details, or collaborate with others. Every chat has the following components: + Header + This shows who you are chatting with (or what you are chatting about). You can press the header for more details on the chat, or additional actions to take upon it. + Comments + The core of the chat are its comments, which come in many forms: + Text – Rich text messages stored securely and delivered via web, app, email, or SMS., + Images & Documents – Insert photos, screenshots, movies, PDFs, or more, using copy/paste, drag/drop, or the attach button., + Expenses – Share an expense in the chat, either to simply track and document it, or to submit for reimbursement., + Tasks – Record a task, and optionally assign it to someone (or yourself!). + ]} + /> + Actions + Hover (or long press) on a comment to see additional options, including: + React – Throw a ♥️😂🔥 like on anything!, + Reply in thread – Go deeper by creating a new chat on any comment., + Mark unread – Flag it for reading later, at your convenience. + ]} + /> + Composer + Use the composer at the bottom to write new messages: + Markdown – Format text using bold, italics, and more., + Mention – Invite or tag anyone in the world to any chat by putting an @ in front of their email address or phone number (e.g., @awong@marslink.web, or @415-867-5309). + ]} + /> + + Inbox + The Inbox is a prioritized “to do” list, highlighting exactly what you need to do next. It consists of: + Priorities + At the top of the Inbox are the most important tasks you should do first, which include: + Expense reports waiting on you, + Tasks assigned to you, + Chats that have mentioned you, + Anything you have pinned + ]} + /> + Chats + Beneath the priorities are a list of chats (with unread chats highlighted in bold), in one of two view modes: + Most Recent – Lists every chat, ordered by whichever was most recently active., + Focus – Only lists chats with unread messages, sorted alphabetically. + ]} + /> + + ), + }, + settings: { + children: { + preferences: { + content: ({styles}: {styles: ThemeStyles}) => ( + + Preferences + Customize your Expensify experience with these preference settings: + Theme + Change the app’s appearance to suit your preference: + Dark Mode - Easy on the eyes in low-light environments, + Light Mode - Bright, clean interface for well-lit spaces, + Use Device Settings - Automatically match your device’s theme + ]} + /> + To change your theme: + + + + + + Language + Expensify supports multiple languages including: + English, Español, Deutsch, Français, Italiano, + 日本語, Nederlands, Polski, Português (BR), + 中文 (简体) + ]} + /> + To change your language: + + + + + + + Notifications + Control how and when you receive updates: + Receive relevant feature updates and Expensify news, + Mute all sounds from Expensify + ]} + /> + To manage notifications: + + + + + Payment Currency + Set your default currency for expense tracking and reimbursements. + + Note: Preference changes only affect your personal account view. Workspace members must update their own settings individually. + + ), + }, + wallet: { + content: ({styles}: {styles: ThemeStyles}) => ( + + Business Bank Accounts + Connect a verified business bank account to unlock payment features like reimbursements, bill pay, invoice collections, and Expensify Card issuance. Supported currencies: USD, CAD, GBP, EUR, and AUD. + Getting Started + Enable Payment Features + + + + + + + Connect Your Bank Account + + + + + + + + + What You Can Do + Once your account is verified, you’ll be able to: + Reimburse employees via ACH, + Pay vendors and suppliers, + Issue Expensify Cards to your team, + Collect invoice payments from clients + ]} + /> + Sharing Access + + + + + + + + Heads up: Your bank account must be fully verified before any payment features go live. The process usually takes 1–2 business days. + + Personal Bank Accounts + Add your personal bank account to get reimbursed or paid — no paper checks, no waiting around. Expensify supports banks in over 190 countries. + Adding a Personal Bank Account + + + + + + + What You Can Do + Get reimbursed for expense reports, + Receive invoice payments, + Use multi-currency support to get paid in your local currency + ]} + /> + + Heads up: Personal accounts are for receiving funds only. If you want to send payments or issue Expensify Cards, you’ll need to connect a verified business bank account. + + ), + }, + }, + content: ({styles}: {styles: ThemeStyles}) => ( + + Settings + Here is where you configure Expensify exactly to your specifications: + Profile - Configure how you appear to others., + Wallet - See and manage your credit cards and bank accounts., + Preferences - Adjust how the app works for you., + Security - Lock down how you and others access your account., + Workspaces - Organize expenses for yourself and share with others., + Subscriptions - Manage payment details and history., + Domains - Advanced security and corporate card configuration., + Switch to Expensify Classic - Battle tested and reliable., + Save the World - Let Expensify.org help your favorite teacher! + ]} + /> + + ), + }, + workspaces: { + children: { + ":policyID": { + children: { + accounting: { + content: ({styles}: {styles: ThemeStyles}) => ( + + Accounting Integrations + Connect your workspace to accounting software to sync expenses and streamline financial management. + Supported Integrations + QuickBooks Online + Real-time expense sync, + Category and vendor mapping, + Tax rate synchronization + ]} + /> + QuickBooks Desktop + File-based import/export, + Chart of accounts import, + Custom field mapping + ]} + /> + Xero + Automatic report sync, + Tracking category import, + Tax rate management + ]} + /> + NetSuite + Advanced multi-entity support, + Custom dimension mapping, + Automated bill payments + ]} + /> + Sage Intacct + Department/class tracking, + Multi-currency support, + Advanced approval workflows + ]} + /> + Getting Started + + + + + + + + What Gets Synced + From your accounting system: + Chart of accounts (as categories), + Classes, departments, locations (as tags), + Tax rates and customers, + Vendors and bill payment accounts + ]} + /> + To your accounting system: + Approved expense reports, + Company card transactions, + Vendor bills and journal entries, + Payment records and reconciliation data + ]} + /> + Related Links + Connect to QuickBooks Online, + Connect to Xero, + Connect to NetSuite + ]} + /> + + ), + }, + members: { + content: ({styles}: {styles: ThemeStyles}) => ( + + Workspace Members + Manage team access, roles, and permissions for your workspace. + Member Roles + Admin + Full workspace control and settings access, + Add/remove members and change roles, + Set up integrations and payment methods, + Approve and pay expenses + ]} + /> + Member + Submit expenses and create reports, + Participate in workspace chats, + View assigned expenses and reports + ]} + /> + Auditor + View all workspace reports (read-only), + Add comments but cannot modify expenses, + No approval or payment permissions + ]} + /> + Adding Members + + + + + + + + + Alternative: Share workspace URL or QR code from Settings > Profile > Share + Managing Members + Change Role: + + + + + + Remove Member: + + + + + + Bulk Actions: + Select multiple members with checkboxes, + Use dropdown to remove or modify multiple members + ]} + /> + Transfer Ownership + + + + + + + Related Links + Managing Workspace Members, + Add Approvals + ]} + /> + + ), }, + }, + content: ({styles}: {styles: ThemeStyles}) => ( + + Workspace + This is where you configure all the settings of the many features associated with your workspace. + Default features + Here are the features that are enabled by default: + Overview - Configure how it appears to others., + Members - Add/remove members and admins., + Workflows - Configure submission, approval, and reimbursement., + Categories - Group expenses into a chart of accounts., + Expensify Card - Issue native Expensify Cards to employees., + Accounting - Sync with external accounting systems. + ]} + /> + Optional features + These can be enabled via More Features: + Distance rates - Configure mileage reimbursement., + Company card - Connect and manage third-party corporate card feeds., + Per diem - Configure daily rates., + Rules - Customize expense violations and set policy., + Invoices - Collect revenue from customers., + Tags - Group expenses by project or client., + Taxes - Track VAT and other taxes., + Report fields - Capture extra expense report information. + ]} + /> + + ), }, + }, + content: ({styles}: {styles: ThemeStyles}) => ( + + Workspaces + Workspaces help you manage company expenses, enforce policies, and integrate with accounting software. Each workspace has its own rules, settings, and features. + Creating a Workspace + To create a new workspace: + + + + + + + Your first workspace includes: + Free 30-day trial, + Access to Setup Specialist via #admins chat room, + Help from Concierge in your Inbox + ]} + /> + Managing Members + To invite team members: + + + + + + + Member vs Admin roles: + Members can submit their own reports and approve assigned reports, + Admins can approve all workspace reports, view all reports, and edit workspace settings + ]} + /> + To assign admin roles: + + + + + + Key Features + Categories - Organize and track expenses (imported automatically if connected to accounting software) + Approval Workflows - Automate expense report reviews: + Toggle Add Approvals on under Workflows, + Set a default first approver for all expenses, + Create custom workflows for specific members + ]} + /> + Accounting Integrations - Connect to: + QuickBooks Online, + Xero, + NetSuite, + Sage Intacct + ]} + /> + Additional Features (enable via More Features): + Expensify Cards for company spending, + Distance tracking for mileage, + Tags for detailed expense coding, + Company card connections + ]} + /> + Workspace Settings + Access all workspace configuration from the Workspaces tab: + Overview - Name, currency, description, and sharing options, + Members - Invite, remove, and manage member roles, + Categories - Add and organize expense categories, + Workflows - Set up approval and payment processes, + More Features - Enable additional workspace capabilities + ]} + /> + + Tip: Use the Share option on your workspace profile to get an invite link or QR code for easy member onboarding. + + ), }, - content: () => null, -}; + search: { + content: ({styles}: {styles: ThemeStyles}) => ( + + Reports + Virtually all data can be analyzed and reported upon in the Reports page. The major elements of this page include: + Data type + Start first by choosing the type of data you want to analyze, which can be: + Expense - Individual standalone expenses., + Expense reports - Groups of expenses processed in a batch., + Chats - Comments written by you and others., + Invoices - Expenses submitted to clients for payment., + Trips - Travel expenses booked with Expensify Travel or scanned with SmartScan. + ]} + /> + Search + A quick method of narrowing the results by keyword or more. + State filter + Simple methods to filter the results by “state”, including: + All, + <> + Expenses/Expense/Invoices reports: + + + Draft - Only you can see that hasn’t been shared yet., + Outstanding - Submitted to someone and awaiting action., + Approved - Approved, but awaiting payment., + Done - Fully processed, no further action needed., + Paid - Fully paid, no further action needed. + ]} + /> + + + , + <> + Chats: + + + Unread - Not seen yet by you., + Sent - Sent by you., + Attachments - Image, movie, or document., + Links - Hyperlinks., + Pinned - Highlighted by you as important. + ]} + /> + + + , + <> + Trips: + + + Current - Happening or in the future., + Past - Already happened. + ]} + /> + + + + ]} + /> + Results + The core of the Reports page are the search results themselves. + Select a row to see additional options., + Tap on a row to see more detail. + ]} + /> + + ), + }, + new: { + children: { + task: { + content: ({styles}: {styles: ThemeStyles}) => ( + + Tasks + Keep conversations organized by letting you create actionable to-dos directly within a chat. You can assign them to yourself or others in both 1:1 and group chats. + + Create a task: In any chat, click the + button next to the message field and select Assign a task. Add a title (required) and an optional description, and choose an assignee from chat participants. You can also leave it unassigned to track it yourself. + , + + Use tasks to stay on top of action items: Tasks are great for follow-ups like “Submit expense report,” “Share slide deck,” or “Update mileage rate.” They’re perfect for 1:1 check-ins, project updates, or organizing next steps after a team discussion. + , + + Edit and manage tasks: Task creators and assignees can comment, edit the title or description, reassign the task, or mark it as complete. Just click the task to update any details. + , + + Tasks stay visible: Each task is shared in the chat where it’s created. When completed, it will be clearly marked in the chat and can be reopened if needed. + + ]} + /> + + ), + }, + }, + }, + home: { + content: ({styles}: {styles: ThemeStyles}) => ( + + Navigating Expensify + Get familiar with Expensify’s intuitive navigation system designed for easy access to all your tools. + Left-hand Navigation Bar + The vertical left-hand bar is your main navigation hub: + Expensify logo - Click to return to your Inbox (homepage), + Inbox - Your personalized dashboard with action items and reminders, + Reports - Access all your expense reports and filtering tools, + Workspaces - Manage company and personal workspace settings, + Account - Personal settings, profile, and preferences, + Global Create button - Quick access to create reports, expenses, invoices, and chats + ]} + /> + Inbox Overview + Your Inbox serves as the homepage and shows: + Smart reminders to submit, approve, or reconcile expenses, + Real-time updates on recent actions and flagged reports, + List of chats with other employees in your organization, + Personalized action items based on your role and activity + ]} + /> + Chat Features + Every expense, report, or workspace has an associated chat for collaboration: + Text messages with rich formatting support, + Images & Documents via copy/paste, drag/drop, or attach button, + Expenses to track and submit for reimbursement, + Tasks to assign and manage work items, + Mentions to invite anyone by email or phone number + ]} + /> + Reports Section + The Reports tab consolidates filtering and reporting: + Use the Workspace filter inside the Filters menu to refine results, + Apply filters and queries that update automatically, + View all expense reports across your workspaces + ]} + /> + Quick Actions + Use the green Create button to quickly: + Start a new chat or conversation, + Create an expense report, + Add an expense or receipt, + Create a task or invoice, + Submit expenses for approval + ]} + /> + + Tip: Navigation is consistent across web, mobile, and desktop versions of Expensify. + + ), + }, + }, + content: () => null, +} export default helpContentMap; export type {ContentComponent}; From 08ab6fbd74ed440e3c85c59a9f285592def4ecb8 Mon Sep 17 00:00:00 2001 From: sumo_slonik Date: Thu, 3 Jul 2025 16:22:38 +0200 Subject: [PATCH 10/10] fix checks --- .../SidePanel/HelpContent/helpContentMap.tsx | 2182 ++++++++++------- 1 file changed, 1241 insertions(+), 941 deletions(-) diff --git a/src/components/SidePanel/HelpContent/helpContentMap.tsx b/src/components/SidePanel/HelpContent/helpContentMap.tsx index ccbb77214802..79228e32923f 100644 --- a/src/components/SidePanel/HelpContent/helpContentMap.tsx +++ b/src/components/SidePanel/HelpContent/helpContentMap.tsx @@ -1,12 +1,13 @@ /* eslint-disable react/no-unescaped-entities */ + /* eslint-disable @typescript-eslint/naming-convention */ import type {ReactNode} from 'react'; import React from 'react'; import {View} from 'react-native'; -import type {ThemeStyles} from '@styles/index'; import BulletList from '@components/SidePanel/HelpComponents/HelpBulletList'; import Text from '@components/Text'; import TextLink from '@components/TextLink'; +import type {ThemeStyles} from '@styles/index'; type ContentComponent = (props: {styles: ThemeStyles}) => ReactNode; @@ -22,971 +23,1270 @@ type HelpContent = { }; const helpContentMap: HelpContent = { - children: { - ":action": { - children: { - ":iouType": { - children: { - start: { - children: { - ":transactionID": { - children: { - ":reportID": { - children: { - scan: { - children: { - ":backToReport": { - content: ({styles}: {styles: ThemeStyles}) => ( + children: { + ':action': { + children: { + ':iouType': { + children: { + start: { + children: { + ':transactionID': { + children: { + ':reportID': { + children: { + scan: { + children: { + ':backToReport': { + content: ({styles}: {styles: ThemeStyles}) => ( + + Scan Receipt + SmartScan automatically extracts expense details from receipt images. + How to Scan + + What SmartScan Detects + + Amount and currency + , + + Merchant name and location + , + + Date of purchase + , + + Tax information (when visible) + , + + Category suggestions based on merchant type + , + ]} + /> + Supported Receipt Types + + Photos - Take with your device camera + , + + Email receipts - Forward to receipts@expensify.com + , + + PDF receipts - Upload from your device + , + + Screenshots - From apps or websites + , + ]} + /> + Tips for Best Results + Ensure receipt text is clear and readable, + Include the full receipt in the image, + Good lighting improves accuracy, + Straight angles work better than tilted photos, + ]} + /> + After Scanning + Review extracted details for accuracy, + Add description, category, or tags as needed, + SmartScan learns from your corrections, + ]} + /> + Related Links + + + Create an Expense + + , + + + Free Features in Expensify + + , + ]} + /> + + ), + }, + }, + }, + distance: { + children: { + ':backToReport': { + content: ({styles}: {styles: ThemeStyles}) => ( + + Distance Expense + + Easily track mileage costs using Expensify’s built-in map feature. Create and submit distance-based expenses right + from the web, desktop, or mobile app. + + + + Create distance expenses: Click the green + button and choose Create + expense, then select Distance. Enter your starting point and destination. You can also add stops if + needed. + + , + + + Submit for approval: Choose your workspace and confirm the distance, + amount, and date. Add optional notes or categories, then click Create expense to submit the mileage + expense for approval. + + , + + + Log a round-trip: To log a round-trip, use the same location for both + start and finish, and include any stops along the way. + + , + ]} + /> + + ), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + r: { + children: { + ':concierge': { + content: ({styles}: {styles: ThemeStyles}) => ( + + Concierge + + Concierge is available 24/7 to answer any question you have about anything — whether that’s how to get set up, how to fix a problem, or general best + practices. Concierge is a bot, but it’s really smart and can escalate you to a human whenever you want. Say hi — it’s friendly! + + + ), + }, + ':expense': { + children: { + ':scan': { + content: ({styles}: {styles: ThemeStyles}) => ( - Scan Receipt - SmartScan automatically extracts expense details from receipt images. - How to Scan - - - - - - - - - What SmartScan Detects - Amount and currency, - Merchant name and location, - Date of purchase, - Tax information (when visible), - Category suggestions based on merchant type - ]} - /> - Supported Receipt Types - Photos - Take with your device camera, - Email receipts - Forward to receipts@expensify.com, - PDF receipts - Upload from your device, - Screenshots - From apps or websites - ]} - /> - Tips for Best Results - Ensure receipt text is clear and readable, - Include the full receipt in the image, - Good lighting improves accuracy, - Straight angles work better than tilted photos - ]} - /> - After Scanning - Review extracted details for accuracy, - Add description, category, or tags as needed, - SmartScan learns from your corrections - ]} - /> - Related Links - Create an Expense, - Free Features in Expensify - ]} - /> + Scanned + A “scanned” expense was created by extracting the relevant details using the Concierge AI. - ), - }, - }, + ), }, - distance: { - children: { - ":backToReport": { - content: ({styles}: {styles: ThemeStyles}) => ( + ':manual': { + content: ({styles}: {styles: ThemeStyles}) => ( - Distance Expense - Easily track mileage costs using Expensify’s built-in map feature. Create and submit distance-based expenses right from the web, desktop, or mobile app. - - Create distance expenses: Click the green + button and choose Create expense, then select Distance. Enter your starting point and destination. You can also add stops if needed. - , - - Submit for approval: Choose your workspace and confirm the distance, amount, and date. Add optional notes or categories, then click Create expense to submit the mileage expense for approval. - , - - Log a round-trip: To log a round-trip, use the same location for both start and finish, and include any stops along the way. - - ]} - /> + Manual + + A “manual” expense has had all its details specified by the workspace member. It was not imported from any system, or scanned from a receipt. + - ), - }, - }, + ), + }, + ':pendingExpensifyCard': { + content: ({styles}: {styles: ThemeStyles}) => ( + + Expensify Card (pending) + + A “pending” Expensify Card expense represents a purchase that was recently made on the card, but has not yet “posted” – meaning, it has not been + formally recognized as a final, complete transaction. + + Any changes made to this expense will be preserved when the expense posts, typically 2-7 days later. + Pending transactions cannot be approved, as the final expense amount will not be confirmed until it posts. + + ), + }, + ':expensifyCard': { + content: ({styles}: {styles: ThemeStyles}) => ( + + Expensify Card + An “Expensify Card” expense corresponds to a “posted” (meaning, finalized by the bank) purchase. + + Expensify Card expenses cannot be reimbursed as they are centrally paid by the bank account linked to the workspace. + + + ), }, - }, }, - }, + content: ({styles}: {styles: ThemeStyles}) => ( + + Expense + Every expense gets a dedicated chat to discuss that specific expense. The expense consists of: + + Receipt – Attach a photo or document to this expense. + , + + Amount – The financial total of this transaction. + , + + Description – A general explanation of what this expense was for. + , + + Merchant – The business this purchase was made at. + , + + Date – The day on which the purchase was made. + , + ]} + /> + + The expense chat is shared with everyone in the approval flow, and will maintain an audit trail of all historical changes. + + + ), + }, + ':policyAdmins': { + content: ({styles}: {styles: ThemeStyles}) => ( + + #admins + + Every workspace automatically receives a special #admins chat room. Every admin is automatically added to this room as a member. The #admins room is used for + several purposes: + + + + Talking with Concierge, your setup specialist, or your account manager – When you first create the workspace, + Concierge and a setup specialist will be added. Feel free to ask any setup questions you have about how to configure the workspace, onboard your + team, connect your accounting, or anything else you might need. + + , + + + Monitoring workspace changes – Every #admins room shows an audit trail of any configuration changes or + significant events happening inside the workspace. + + , + + + Chatting with other admins – The #admins room is a useful space for workspace admins to chat with each other + about anything, whether or not it relates to Expensify. + + , + ]} + /> + + ), + }, + ':expenseReport': { + content: ({styles}: {styles: ThemeStyles}) => ( + + Expense Report + Every expense report gets a dedicated chat to discuss expenses, approvals, or anything you like. The expense report chat: + Is shared with everyone in the approval flow configured inside the workspace., + Will maintain an audit trail of all historical workflow actions (i.e., approvals)., + ]} + /> + + Press the attach button to add more expenses, or press the header for more options. Press on any expense to go deeper. + + + ), + }, + ':policyExpenseChat': { + content: ({styles}: {styles: ThemeStyles}) => ( + + Workspace + + Every workspace member gets a special chat between them and all workspace admins. This is a good place for workspace members to ask questions about expense + policy, for workspace admins to explain changes, or for any “formal” conversation to occur between members and admins. Press the attach button to: + + + Create expense – This will submit an expense to the workspace for reimbursement. + , + + Split expense – This will split an expense between the member and the workspace (e.g., for a business meal that + brings a spouse). + , + ]} + /> + All past expense reports are processed here and stored for historical reference. + + ), + }, + ':policyAnnounce': { + content: ({styles}: {styles: ThemeStyles}) => ( + + Announce Room (#announce) + + The #announce room is a chat space available to all workspace members. It’s perfect for sharing company-wide updates, policy changes, or event reminders. The + #announce room is accessible from your Inbox in the left-hand menu. + + + + Post company-wide announcements: All members can post in #announce by default, making it easy to communicate + across the workspace. + + , + + + Restrict posting to admins: Workspace admins can limit posting to admins only. Open the #announce room, click + the room header, select Settings, and change Who can post to Admins only. + + , + + + Everyone can read messages: Even if posting is limited to admins, all workspace members can still view + messages in the #announce room. + + , + ]} + /> + + ), }, - }, - }, - }, - }, - }, - }, - r: { - children: { - ":concierge": { - content: ({styles}: {styles: ThemeStyles}) => ( - - Concierge - Concierge is available 24/7 to answer any question you have about anything — whether that’s how to get set up, how to fix a problem, or general best practices. - Concierge is a bot, but it’s really smart and can escalate you to a human whenever you want. Say hi — it’s friendly! - - ), - }, - ":expense": { - children: { - ":scan": { - content: ({styles}: {styles: ThemeStyles}) => ( - - Scanned - A “scanned” expense was created by extracting the relevant details using the Concierge AI. - - ), }, - ":manual": { - content: ({styles}: {styles: ThemeStyles}) => ( + content: ({styles}: {styles: ThemeStyles}) => ( - Manual - A “manual” expense has had all its details specified by the workspace member. It was not imported from any system, or scanned from a receipt. + Chat + + Chat is the foundation of New Expensify. Every expense, expense report, workspace, or member has an associated “chat”, which you can use to record additional details, + or collaborate with others. Every chat has the following components: + + Header + + This shows who you are chatting with (or what you are chatting about). You can press the header for more details on the chat, or additional actions to take upon it. + + Comments + The core of the chat are its comments, which come in many forms: + + Text – Rich text messages stored securely and delivered via web, app, email, or SMS. + , + + Images & Documents – Insert photos, screenshots, movies, PDFs, or more, using copy/paste, drag/drop, or the attach + button. + , + + Expenses – Share an expense in the chat, either to simply track and document it, or to submit for reimbursement. + , + + Tasks – Record a task, and optionally assign it to someone (or yourself!). + , + ]} + /> + Actions + Hover (or long press) on a comment to see additional options, including: + + React – Throw a ♥️😂🔥 like on anything! + , + + Reply in thread – Go deeper by creating a new chat on any comment. + , + + Mark unread – Flag it for reading later, at your convenience. + , + ]} + /> + Composer + Use the composer at the bottom to write new messages: + + Markdown – Format text using bold, italics, + and{' '} + + more + + . + , + + Mention – Invite or tag anyone in the world to any chat by putting an @ in front of their email address or phone number + (e.g., @awong@marslink.web, or @415-867-5309). + , + ]} + /> + + Inbox + The Inbox is a prioritized “to do” list, highlighting exactly what you need to do next. It consists of: + Priorities + At the top of the Inbox are the most important tasks you should do first, which include: + Expense reports waiting on you, + Tasks assigned to you, + Chats that have mentioned you, + Anything you have pinned, + ]} + /> + Chats + Beneath the priorities are a list of chats (with unread chats highlighted in bold), in one of two view modes: + + Most Recent – Lists every chat, ordered by whichever was most recently active. + , + + Focus – Only lists chats with unread messages, sorted alphabetically. + , + ]} + /> - ), + ), + }, + settings: { + children: { + preferences: { + content: ({styles}: {styles: ThemeStyles}) => ( + + Preferences + Customize your Expensify experience with these preference settings: + Theme + Change the app’s appearance to suit your preference: + + Dark Mode - Easy on the eyes in low-light environments + , + + Light Mode - Bright, clean interface for well-lit spaces + , + + Use Device Settings - Automatically match your device’s theme + , + ]} + /> + + To change your theme: + + + Language + Expensify supports multiple languages including: + English, Español, Deutsch, Français, Italiano, + 日本語, Nederlands, Polski, Português (BR), + 中文 (简体), + ]} + /> + + To change your language: + + + Notifications + Control how and when you receive updates: + + Receive relevant feature updates and Expensify news + , + + Mute all sounds from Expensify + , + ]} + /> + + To manage notifications: + + + Payment Currency + Set your default currency for expense tracking and reimbursements. + + + Note: Preference changes only affect your personal account view. Workspace members must update their own settings + individually. + + + ), + }, + wallet: { + content: ({styles}: {styles: ThemeStyles}) => ( + + Business Bank Accounts + + Connect a verified business bank account to unlock payment features like reimbursements, bill pay, invoice collections, and Expensify Card issuance. Supported + currencies: USD, CAD, GBP, EUR, and AUD. + + Getting Started + Enable Payment Features + + Connect Your Bank Account + + What You Can Do + Once your account is verified, you’ll be able to: + + Reimburse employees via ACH + , + + Pay vendors and suppliers + , + + Issue Expensify Cards to your team + , + + Collect invoice payments from clients + , + ]} + /> + Sharing Access + + + Heads up: Your bank account must be fully verified before any payment features go live. The process usually takes 1–2 + business days. + + + Personal Bank Accounts + + Add your personal bank account to get reimbursed or paid — no paper checks, no waiting around. Expensify supports banks in over 190 countries. + + Adding a Personal Bank Account + + What You Can Do + + Get reimbursed for expense reports + , + + Receive invoice payments + , + + Use multi-currency support to get paid in your local currency + , + ]} + /> + + + Heads up: Personal accounts are for receiving funds only. If you want to send payments or issue Expensify Cards, you’ll + need to connect a verified business bank account. + + + ), + }, }, - ":pendingExpensifyCard": { - content: ({styles}: {styles: ThemeStyles}) => ( + content: ({styles}: {styles: ThemeStyles}) => ( - Expensify Card (pending) - A “pending” Expensify Card expense represents a purchase that was recently made on the card, but has not yet “posted” – meaning, it has not been formally recognized as a final, complete transaction. - Any changes made to this expense will be preserved when the expense posts, typically 2-7 days later. - Pending transactions cannot be approved, as the final expense amount will not be confirmed until it posts. + Settings + Here is where you configure Expensify exactly to your specifications: + + Profile - Configure how you appear to others. + , + + Wallet - See and manage your credit cards and bank accounts. + , + + Preferences - Adjust how the app works for you. + , + + Security - Lock down how you and others access your account. + , + + Workspaces - Organize expenses for yourself and share with others. + , + + Subscriptions - Manage payment details and history. + , + + Domains - Advanced security and corporate card configuration. + , + + Switch to Expensify Classic - Battle tested and reliable. + , + + Save the World - Let Expensify.org help your favorite teacher! + , + ]} + /> - ), + ), + }, + workspaces: { + children: { + ':policyID': { + children: { + accounting: { + content: ({styles}: {styles: ThemeStyles}) => ( + + Accounting Integrations + Connect your workspace to accounting software to sync expenses and streamline financial management. + Supported Integrations + + QuickBooks Online + + Real-time expense sync, + Category and vendor mapping, + Tax rate synchronization, + ]} + /> + + QuickBooks Desktop + + File-based import/export, + Chart of accounts import, + Custom field mapping, + ]} + /> + + Xero + + Automatic report sync, + Tracking category import, + Tax rate management, + ]} + /> + + NetSuite + + Advanced multi-entity support, + Custom dimension mapping, + Automated bill payments, + ]} + /> + + Sage Intacct + + Department/class tracking, + Multi-currency support, + Advanced approval workflows, + ]} + /> + Getting Started + + What Gets Synced + + From your accounting system: + + Chart of accounts (as categories), + Classes, departments, locations (as tags), + Tax rates and customers, + Vendors and bill payment accounts, + ]} + /> + + To your accounting system: + + Approved expense reports, + Company card transactions, + Vendor bills and journal entries, + Payment records and reconciliation data, + ]} + /> + Related Links + + + Connect to QuickBooks Online + + , + + + Connect to Xero + + , + + + Connect to NetSuite + + , + ]} + /> + + ), + }, + members: { + content: ({styles}: {styles: ThemeStyles}) => ( + + Workspace Members + Manage team access, roles, and permissions for your workspace. + Member Roles + + Admin + + Full workspace control and settings access, + Add/remove members and change roles, + Set up integrations and payment methods, + Approve and pay expenses, + ]} + /> + + Member + + Submit expenses and create reports, + Participate in workspace chats, + View assigned expenses and reports, + ]} + /> + + Auditor + + View all workspace reports (read-only), + Add comments but cannot modify expenses, + No approval or payment permissions, + ]} + /> + Adding Members + + + Alternative: Share workspace URL or QR code from{' '} + Settings > Profile > Share + + Managing Members + + Change Role: + + + + Remove Member: + + + + Bulk Actions: + + Select multiple members with checkboxes, + Use dropdown to remove or modify multiple members, + ]} + /> + Transfer Ownership + + Related Links + + + Managing Workspace Members + + , + + + Add Approvals + + , + ]} + /> + + ), + }, + }, + content: ({styles}: {styles: ThemeStyles}) => ( + + Workspace + This is where you configure all the settings of the many features associated with your workspace. + Default features + Here are the features that are enabled by default: + + Overview - Configure how it appears to others. + , + + Members - Add/remove members and admins. + , + + Workflows - Configure submission, approval, and reimbursement. + , + + Categories - Group expenses into a chart of accounts. + , + + Expensify Card - Issue native Expensify Cards to employees. + , + + Accounting - Sync with external accounting systems. + , + ]} + /> + Optional features + These can be enabled via More Features: + + Distance rates - Configure mileage reimbursement. + , + + Company card - Connect and manage third-party corporate card feeds. + , + + Per diem - Configure daily rates. + , + + Rules - Customize expense violations and set policy. + , + + Invoices - Collect revenue from customers. + , + + Tags - Group expenses by project or client. + , + + Taxes - Track VAT and other taxes. + , + + Report fields - Capture extra expense report information. + , + ]} + /> + + ), + }, }, - ":expensifyCard": { - content: ({styles}: {styles: ThemeStyles}) => ( + content: ({styles}: {styles: ThemeStyles}) => ( - Expensify Card - An “Expensify Card” expense corresponds to a “posted” (meaning, finalized by the bank) purchase. - Expensify Card expenses cannot be reimbursed as they are centrally paid by the bank account linked to the workspace. + Workspaces + + Workspaces help you manage company expenses, enforce policies, and integrate with accounting software. Each workspace has its own rules, settings, and features. + + Creating a Workspace + + To create a new workspace: + + + + Your first workspace includes: + + Free 30-day trial, + Access to Setup Specialist via #admins chat room, + Help from Concierge in your Inbox, + ]} + /> + Managing Members + + To invite team members: + + + + Member vs Admin roles: + + + Members can submit their own reports and approve assigned reports + , + + Admins can approve all workspace reports, view all reports, and edit workspace settings + , + ]} + /> + + To assign admin roles: + + + Key Features + + Categories - Organize and track expenses (imported automatically if connected to accounting software) + + + Approval Workflows - Automate expense report reviews: + + + Toggle Add Approvals on under Workflows + , + Set a default first approver for all expenses, + Create custom workflows for specific members, + ]} + /> + + Accounting Integrations - Connect to: + + QuickBooks Online, + Xero, + NetSuite, + Sage Intacct, + ]} + /> + + Additional Features (enable via More Features): + + Expensify Cards for company spending, + Distance tracking for mileage, + Tags for detailed expense coding, + Company card connections, + ]} + /> + Workspace Settings + + Access all workspace configuration from the Workspaces tab: + + + Overview - Name, currency, description, and sharing options + , + + Members - Invite, remove, and manage member roles + , + + Categories - Add and organize expense categories + , + + Workflows - Set up approval and payment processes + , + + More Features - Enable additional workspace capabilities + , + ]} + /> + + + Tip: Use the Share option on your workspace profile to get an invite link or QR code for + easy member onboarding. + - ), - }, - }, - content: ({styles}: {styles: ThemeStyles}) => ( - - Expense - Every expense gets a dedicated chat to discuss that specific expense. The expense consists of: - Receipt – Attach a photo or document to this expense., - Amount – The financial total of this transaction., - Description – A general explanation of what this expense was for., - Merchant – The business this purchase was made at., - Date – The day on which the purchase was made. - ]} - /> - The expense chat is shared with everyone in the approval flow, and will maintain an audit trail of all historical changes. - - ), - }, - ":policyAdmins": { - content: ({styles}: {styles: ThemeStyles}) => ( - - #admins - Every workspace automatically receives a special #admins chat room. Every admin is automatically added to this room as a member. The #admins room is used for several purposes: - - Talking with Concierge, your setup specialist, or your account manager – When you first create the workspace, Concierge and a setup specialist will be added. Feel free to ask any setup questions you have about how to configure the workspace, onboard your team, connect your accounting, or anything else you might need. - , - - Monitoring workspace changes – Every #admins room shows an audit trail of any configuration changes or significant events happening inside the workspace. - , - - Chatting with other admins – The #admins room is a useful space for workspace admins to chat with each other about anything, whether or not it relates to Expensify. - - ]} - /> - - ), + ), }, - ":expenseReport": { - content: ({styles}: {styles: ThemeStyles}) => ( - - Expense Report - Every expense report gets a dedicated chat to discuss expenses, approvals, or anything you like. The expense report chat: - Is shared with everyone in the approval flow configured inside the workspace., - Will maintain an audit trail of all historical workflow actions (i.e., approvals). - ]} - /> - Press the attach button to add more expenses, or press the header for more options. Press on any expense to go deeper. - - ), - }, - ":policyExpenseChat": { - content: ({styles}: {styles: ThemeStyles}) => ( - - Workspace - Every workspace member gets a special chat between them and all workspace admins. This is a good place for workspace members to ask questions about expense policy, for workspace admins to explain changes, or for any “formal” conversation to occur between members and admins. Press the attach button to: - Create expense – This will submit an expense to the workspace for reimbursement., - Split expense – This will split an expense between the member and the workspace (e.g., for a business meal that brings a spouse). - ]} - /> - All past expense reports are processed here and stored for historical reference. - - ), - }, - ":policyAnnounce": { - content: ({styles}: {styles: ThemeStyles}) => ( - - Announce Room (#announce) - The #announce room is a chat space available to all workspace members. It’s perfect for sharing company-wide updates, policy changes, or event reminders. The #announce room is accessible from your Inbox in the left-hand menu. - - Post company-wide announcements: All members can post in #announce by default, making it easy to communicate across the workspace. - , - - Restrict posting to admins: Workspace admins can limit posting to admins only. Open the #announce room, click the room header, select Settings, and change Who can post to Admins only. - , - - Everyone can read messages: Even if posting is limited to admins, all workspace members can still view messages in the #announce room. - - ]} - /> - - ), - }, - }, - content: ({styles}: {styles: ThemeStyles}) => ( - - Chat - Chat is the foundation of New Expensify. Every expense, expense report, workspace, or member has an associated “chat”, which you can use to record additional details, or collaborate with others. Every chat has the following components: - Header - This shows who you are chatting with (or what you are chatting about). You can press the header for more details on the chat, or additional actions to take upon it. - Comments - The core of the chat are its comments, which come in many forms: - Text – Rich text messages stored securely and delivered via web, app, email, or SMS., - Images & Documents – Insert photos, screenshots, movies, PDFs, or more, using copy/paste, drag/drop, or the attach button., - Expenses – Share an expense in the chat, either to simply track and document it, or to submit for reimbursement., - Tasks – Record a task, and optionally assign it to someone (or yourself!). - ]} - /> - Actions - Hover (or long press) on a comment to see additional options, including: - React – Throw a ♥️😂🔥 like on anything!, - Reply in thread – Go deeper by creating a new chat on any comment., - Mark unread – Flag it for reading later, at your convenience. - ]} - /> - Composer - Use the composer at the bottom to write new messages: - Markdown – Format text using bold, italics, and more., - Mention – Invite or tag anyone in the world to any chat by putting an @ in front of their email address or phone number (e.g., @awong@marslink.web, or @415-867-5309). - ]} - /> - - Inbox - The Inbox is a prioritized “to do” list, highlighting exactly what you need to do next. It consists of: - Priorities - At the top of the Inbox are the most important tasks you should do first, which include: - Expense reports waiting on you, - Tasks assigned to you, - Chats that have mentioned you, - Anything you have pinned - ]} - /> - Chats - Beneath the priorities are a list of chats (with unread chats highlighted in bold), in one of two view modes: - Most Recent – Lists every chat, ordered by whichever was most recently active., - Focus – Only lists chats with unread messages, sorted alphabetically. - ]} - /> - - ), - }, - settings: { - children: { - preferences: { - content: ({styles}: {styles: ThemeStyles}) => ( - - Preferences - Customize your Expensify experience with these preference settings: - Theme - Change the app’s appearance to suit your preference: - Dark Mode - Easy on the eyes in low-light environments, - Light Mode - Bright, clean interface for well-lit spaces, - Use Device Settings - Automatically match your device’s theme - ]} - /> - To change your theme: - - - - - - Language - Expensify supports multiple languages including: - English, Español, Deutsch, Français, Italiano, - 日本語, Nederlands, Polski, Português (BR), - 中文 (简体) - ]} - /> - To change your language: - - - - - - - Notifications - Control how and when you receive updates: - Receive relevant feature updates and Expensify news, - Mute all sounds from Expensify - ]} - /> - To manage notifications: - - - - - Payment Currency - Set your default currency for expense tracking and reimbursements. - - Note: Preference changes only affect your personal account view. Workspace members must update their own settings individually. - - ), - }, - wallet: { - content: ({styles}: {styles: ThemeStyles}) => ( - - Business Bank Accounts - Connect a verified business bank account to unlock payment features like reimbursements, bill pay, invoice collections, and Expensify Card issuance. Supported currencies: USD, CAD, GBP, EUR, and AUD. - Getting Started - Enable Payment Features - - - - - - - Connect Your Bank Account - - - - - - - - - What You Can Do - Once your account is verified, you’ll be able to: - Reimburse employees via ACH, - Pay vendors and suppliers, - Issue Expensify Cards to your team, - Collect invoice payments from clients - ]} - /> - Sharing Access - - - - - - - - Heads up: Your bank account must be fully verified before any payment features go live. The process usually takes 1–2 business days. - - Personal Bank Accounts - Add your personal bank account to get reimbursed or paid — no paper checks, no waiting around. Expensify supports banks in over 190 countries. - Adding a Personal Bank Account - - - - - - - What You Can Do - Get reimbursed for expense reports, - Receive invoice payments, - Use multi-currency support to get paid in your local currency - ]} - /> - - Heads up: Personal accounts are for receiving funds only. If you want to send payments or issue Expensify Cards, you’ll need to connect a verified business bank account. - - ), - }, - }, - content: ({styles}: {styles: ThemeStyles}) => ( - - Settings - Here is where you configure Expensify exactly to your specifications: - Profile - Configure how you appear to others., - Wallet - See and manage your credit cards and bank accounts., - Preferences - Adjust how the app works for you., - Security - Lock down how you and others access your account., - Workspaces - Organize expenses for yourself and share with others., - Subscriptions - Manage payment details and history., - Domains - Advanced security and corporate card configuration., - Switch to Expensify Classic - Battle tested and reliable., - Save the World - Let Expensify.org help your favorite teacher! - ]} - /> - - ), - }, - workspaces: { - children: { - ":policyID": { - children: { - accounting: { - content: ({styles}: {styles: ThemeStyles}) => ( + search: { + content: ({styles}: {styles: ThemeStyles}) => ( - Accounting Integrations - Connect your workspace to accounting software to sync expenses and streamline financial management. - Supported Integrations - QuickBooks Online - Real-time expense sync, - Category and vendor mapping, - Tax rate synchronization - ]} - /> - QuickBooks Desktop - File-based import/export, - Chart of accounts import, - Custom field mapping - ]} - /> - Xero - Automatic report sync, - Tracking category import, - Tax rate management - ]} - /> - NetSuite - Advanced multi-entity support, - Custom dimension mapping, - Automated bill payments - ]} - /> - Sage Intacct - Department/class tracking, - Multi-currency support, - Advanced approval workflows - ]} - /> - Getting Started - - - - - - - - What Gets Synced - From your accounting system: - Chart of accounts (as categories), - Classes, departments, locations (as tags), - Tax rates and customers, - Vendors and bill payment accounts - ]} - /> - To your accounting system: - Approved expense reports, - Company card transactions, - Vendor bills and journal entries, - Payment records and reconciliation data - ]} - /> - Related Links - Connect to QuickBooks Online, - Connect to Xero, - Connect to NetSuite - ]} - /> + Reports + Virtually all data can be analyzed and reported upon in the Reports page. The major elements of this page include: + Data type + Start first by choosing the type of data you want to analyze, which can be: + + Expense - Individual standalone expenses. + , + + Expense reports - Groups of expenses processed in a batch. + , + + Chats - Comments written by you and others. + , + + Invoices - Expenses submitted to clients for payment. + , + + Trips - Travel expenses booked with Expensify Travel or scanned with SmartScan. + , + ]} + /> + Search + A quick method of narrowing the results by keyword or more. + State filter + Simple methods to filter the results by “state”, including: + + All + , + <> + Expenses/Expense/Invoices reports: + + Draft - Only you can see that hasn’t been shared yet., + Outstanding - Submitted to someone and awaiting action., + Approved - Approved, but awaiting payment., + Done - Fully processed, no further action needed., + Paid - Fully paid, no further action needed., + ]} + /> + , + <> + Chats: + + Unread - Not seen yet by you., + Sent - Sent by you., + Attachments - Image, movie, or document., + Links - Hyperlinks., + Pinned - Highlighted by you as important., + ]} + /> + , + <> + Trips: + + Current - Happening or in the future., Past - Already happened.]} + /> + , + ]} + /> + Results + The core of the Reports page are the search results themselves. + Select a row to see additional options., Tap on a row to see more detail.]} + /> - ), + ), + }, + new: { + children: { + task: { + content: ({styles}: {styles: ThemeStyles}) => ( + + Tasks + + Keep conversations organized by letting you create actionable to-dos directly within a chat. You can assign them to yourself or others in both 1:1 and group + chats. + + + + Create a task: In any chat, click the + button next to the message field and select Assign a task. Add a + title (required) and an optional description, and choose an assignee from chat participants. You can also leave it unassigned to track it + yourself. + + , + + + Use tasks to stay on top of action items: Tasks are great for follow-ups like “Submit expense report,” “Share + slide deck,” or “Update mileage rate.” They’re perfect for 1:1 check-ins, project updates, or organizing next steps after a team discussion. + + , + + + Edit and manage tasks: Task creators and assignees can comment, edit the title or description, reassign the + task, or mark it as complete. Just click the task to update any details. + + , + + + Tasks stay visible: Each task is shared in the chat where it’s created. When completed, it will be clearly + marked in the chat and can be reopened if needed. + + , + ]} + /> + + ), + }, }, - members: { - content: ({styles}: {styles: ThemeStyles}) => ( + }, + home: { + content: ({styles}: {styles: ThemeStyles}) => ( - Workspace Members - Manage team access, roles, and permissions for your workspace. - Member Roles - Admin - Full workspace control and settings access, - Add/remove members and change roles, - Set up integrations and payment methods, - Approve and pay expenses - ]} - /> - Member - Submit expenses and create reports, - Participate in workspace chats, - View assigned expenses and reports - ]} - /> - Auditor - View all workspace reports (read-only), - Add comments but cannot modify expenses, - No approval or payment permissions - ]} - /> - Adding Members - - - - - - - - - Alternative: Share workspace URL or QR code from Settings > Profile > Share - Managing Members - Change Role: - - - - - - Remove Member: - - - - - - Bulk Actions: - Select multiple members with checkboxes, - Use dropdown to remove or modify multiple members - ]} - /> - Transfer Ownership - - - - - - - Related Links - Managing Workspace Members, - Add Approvals - ]} - /> + Navigating Expensify + Get familiar with Expensify’s intuitive navigation system designed for easy access to all your tools. + Left-hand Navigation Bar + + The vertical left-hand bar is your main navigation hub: + + + Expensify logo - Click to return to your Inbox (homepage) + , + + Inbox - Your personalized dashboard with action items and reminders + , + + Reports - Access all your expense reports and filtering tools + , + + Workspaces - Manage company and personal workspace settings + , + + Account - Personal settings, profile, and preferences + , + + Global Create button - Quick access to create reports, expenses, invoices, and chats + , + ]} + /> + Inbox Overview + + Your Inbox serves as the homepage and shows: + + Smart reminders to submit, approve, or reconcile expenses, + Real-time updates on recent actions and flagged reports, + List of chats with other employees in your organization, + Personalized action items based on your role and activity, + ]} + /> + Chat Features + Every expense, report, or workspace has an associated chat for collaboration: + + Text messages with rich formatting support + , + + Images & Documents via copy/paste, drag/drop, or attach button + , + + Expenses to track and submit for reimbursement + , + + Tasks to assign and manage work items + , + + Mentions to invite anyone by email or phone number + , + ]} + /> + Reports Section + + The Reports tab consolidates filtering and reporting: + + + Use the Workspace filter inside the Filters menu to refine results + , + Apply filters and queries that update automatically, + View all expense reports across your workspaces, + ]} + /> + Quick Actions + + Use the green Create button to quickly: + + Start a new chat or conversation, + Create an expense report, + Add an expense or receipt, + Create a task or invoice, + Submit expenses for approval, + ]} + /> + + + Tip: Navigation is consistent across web, mobile, and desktop versions of Expensify. + - ), - }, - }, - content: ({styles}: {styles: ThemeStyles}) => ( - - Workspace - This is where you configure all the settings of the many features associated with your workspace. - Default features - Here are the features that are enabled by default: - Overview - Configure how it appears to others., - Members - Add/remove members and admins., - Workflows - Configure submission, approval, and reimbursement., - Categories - Group expenses into a chart of accounts., - Expensify Card - Issue native Expensify Cards to employees., - Accounting - Sync with external accounting systems. - ]} - /> - Optional features - These can be enabled via More Features: - Distance rates - Configure mileage reimbursement., - Company card - Connect and manage third-party corporate card feeds., - Per diem - Configure daily rates., - Rules - Customize expense violations and set policy., - Invoices - Collect revenue from customers., - Tags - Group expenses by project or client., - Taxes - Track VAT and other taxes., - Report fields - Capture extra expense report information. - ]} - /> - - ), - }, - }, - content: ({styles}: {styles: ThemeStyles}) => ( - - Workspaces - Workspaces help you manage company expenses, enforce policies, and integrate with accounting software. Each workspace has its own rules, settings, and features. - Creating a Workspace - To create a new workspace: - - - - - - - Your first workspace includes: - Free 30-day trial, - Access to Setup Specialist via #admins chat room, - Help from Concierge in your Inbox - ]} - /> - Managing Members - To invite team members: - - - - - - - Member vs Admin roles: - Members can submit their own reports and approve assigned reports, - Admins can approve all workspace reports, view all reports, and edit workspace settings - ]} - /> - To assign admin roles: - - - - - - Key Features - Categories - Organize and track expenses (imported automatically if connected to accounting software) - Approval Workflows - Automate expense report reviews: - Toggle Add Approvals on under Workflows, - Set a default first approver for all expenses, - Create custom workflows for specific members - ]} - /> - Accounting Integrations - Connect to: - QuickBooks Online, - Xero, - NetSuite, - Sage Intacct - ]} - /> - Additional Features (enable via More Features): - Expensify Cards for company spending, - Distance tracking for mileage, - Tags for detailed expense coding, - Company card connections - ]} - /> - Workspace Settings - Access all workspace configuration from the Workspaces tab: - Overview - Name, currency, description, and sharing options, - Members - Invite, remove, and manage member roles, - Categories - Add and organize expense categories, - Workflows - Set up approval and payment processes, - More Features - Enable additional workspace capabilities - ]} - /> - - Tip: Use the Share option on your workspace profile to get an invite link or QR code for easy member onboarding. - - ), - }, - search: { - content: ({styles}: {styles: ThemeStyles}) => ( - - Reports - Virtually all data can be analyzed and reported upon in the Reports page. The major elements of this page include: - Data type - Start first by choosing the type of data you want to analyze, which can be: - Expense - Individual standalone expenses., - Expense reports - Groups of expenses processed in a batch., - Chats - Comments written by you and others., - Invoices - Expenses submitted to clients for payment., - Trips - Travel expenses booked with Expensify Travel or scanned with SmartScan. - ]} - /> - Search - A quick method of narrowing the results by keyword or more. - State filter - Simple methods to filter the results by “state”, including: - All, - <> - Expenses/Expense/Invoices reports: - - - Draft - Only you can see that hasn’t been shared yet., - Outstanding - Submitted to someone and awaiting action., - Approved - Approved, but awaiting payment., - Done - Fully processed, no further action needed., - Paid - Fully paid, no further action needed. - ]} - /> - - - , - <> - Chats: - - - Unread - Not seen yet by you., - Sent - Sent by you., - Attachments - Image, movie, or document., - Links - Hyperlinks., - Pinned - Highlighted by you as important. - ]} - /> - - - , - <> - Trips: - - - Current - Happening or in the future., - Past - Already happened. - ]} - /> - - - - ]} - /> - Results - The core of the Reports page are the search results themselves. - Select a row to see additional options., - Tap on a row to see more detail. - ]} - /> - - ), - }, - new: { - children: { - task: { - content: ({styles}: {styles: ThemeStyles}) => ( - - Tasks - Keep conversations organized by letting you create actionable to-dos directly within a chat. You can assign them to yourself or others in both 1:1 and group chats. - - Create a task: In any chat, click the + button next to the message field and select Assign a task. Add a title (required) and an optional description, and choose an assignee from chat participants. You can also leave it unassigned to track it yourself. - , - - Use tasks to stay on top of action items: Tasks are great for follow-ups like “Submit expense report,” “Share slide deck,” or “Update mileage rate.” They’re perfect for 1:1 check-ins, project updates, or organizing next steps after a team discussion. - , - - Edit and manage tasks: Task creators and assignees can comment, edit the title or description, reassign the task, or mark it as complete. Just click the task to update any details. - , - - Tasks stay visible: Each task is shared in the chat where it’s created. When completed, it will be clearly marked in the chat and can be reopened if needed. - - ]} - /> - - ), + ), }, - }, - }, - home: { - content: ({styles}: {styles: ThemeStyles}) => ( - - Navigating Expensify - Get familiar with Expensify’s intuitive navigation system designed for easy access to all your tools. - Left-hand Navigation Bar - The vertical left-hand bar is your main navigation hub: - Expensify logo - Click to return to your Inbox (homepage), - Inbox - Your personalized dashboard with action items and reminders, - Reports - Access all your expense reports and filtering tools, - Workspaces - Manage company and personal workspace settings, - Account - Personal settings, profile, and preferences, - Global Create button - Quick access to create reports, expenses, invoices, and chats - ]} - /> - Inbox Overview - Your Inbox serves as the homepage and shows: - Smart reminders to submit, approve, or reconcile expenses, - Real-time updates on recent actions and flagged reports, - List of chats with other employees in your organization, - Personalized action items based on your role and activity - ]} - /> - Chat Features - Every expense, report, or workspace has an associated chat for collaboration: - Text messages with rich formatting support, - Images & Documents via copy/paste, drag/drop, or attach button, - Expenses to track and submit for reimbursement, - Tasks to assign and manage work items, - Mentions to invite anyone by email or phone number - ]} - /> - Reports Section - The Reports tab consolidates filtering and reporting: - Use the Workspace filter inside the Filters menu to refine results, - Apply filters and queries that update automatically, - View all expense reports across your workspaces - ]} - /> - Quick Actions - Use the green Create button to quickly: - Start a new chat or conversation, - Create an expense report, - Add an expense or receipt, - Create a task or invoice, - Submit expenses for approval - ]} - /> - - Tip: Navigation is consistent across web, mobile, and desktop versions of Expensify. - - ), }, - }, - content: () => null, -} + content: () => null, +}; export default helpContentMap; export type {ContentComponent};