From 4051ae94d42d22b0d69eb1b6b7e946c200331439 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 09:45:06 +0100 Subject: [PATCH 01/42] Temporarily hide the help pane when centered modal is displayed --- src/hooks/useSidePane.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/hooks/useSidePane.ts b/src/hooks/useSidePane.ts index 74150028c04cb..0239e0791f3db 100644 --- a/src/hooks/useSidePane.ts +++ b/src/hooks/useSidePane.ts @@ -31,7 +31,9 @@ function useSidePane() { const [sidePaneNVP] = useOnyx(ONYXKEYS.NVP_SIDE_PANE); const [language] = useOnyx(ONYXKEYS.NVP_PREFERRED_LOCALE); - const [isAttachmentModalVisible = false] = useOnyx(ONYXKEYS.MODAL, {selector: (modal) => modal?.type === CONST.MODAL.MODAL_TYPE.CENTERED}); + const [isAttachmentModalVisible = false] = useOnyx(ONYXKEYS.MODAL, { + selector: (modal) => modal?.type === CONST.MODAL.MODAL_TYPE.CENTERED_SWIPABLE_TO_RIGHT || modal?.type === CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE, + }); const isLanguageUnsupported = language !== CONST.LOCALES.EN; const isPaneHidden = isSidePaneHidden(sidePaneNVP, isExtraLargeScreenWidth) || isLanguageUnsupported || isAttachmentModalVisible; From 2402bf81d925e50f4f2de922f3e682a9202ae6d8 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 09:46:25 +0100 Subject: [PATCH 02/42] Implement ExpandableHelp --- src/components/SidePane/ExpandableHelp.tsx | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/components/SidePane/ExpandableHelp.tsx diff --git a/src/components/SidePane/ExpandableHelp.tsx b/src/components/SidePane/ExpandableHelp.tsx new file mode 100644 index 0000000000000..e2b04c815ac48 --- /dev/null +++ b/src/components/SidePane/ExpandableHelp.tsx @@ -0,0 +1,31 @@ +import React, {useState} from 'react'; +import Text from '@components/Text'; +import type {ThemeStyles} from '@styles/index'; + +type ExpandableHelpProps = { + children: React.ReactNode; + styles: ThemeStyles; + moreText?: string; +}; + +function ExpandableHelp({children, styles, moreText = '(more)'}: ExpandableHelpProps) { + const [isExpanded, setIsExpanded] = useState(false); + + if (isExpanded) { + return children; + } + + return ( + setIsExpanded(true)} + > + {' '} + {moreText} + + ); +} + +ExpandableHelp.displayName = 'ExpandableHelp'; + +export default ExpandableHelp; From 10aa44204843b85ffada06801788b0b8bf710e6d Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 10:21:42 +0100 Subject: [PATCH 03/42] Move content to separate files --- src/components/SidePane/HelpContent/inbox.tsx | 84 +++++++++++++++++ .../SidePane/HelpContent/search.tsx | 89 +++++++++++++++++++ .../SidePane/HelpContent/settings/index.tsx | 43 +++++++++ .../HelpContent/settings/workspaces/index.tsx | 49 ++++++++++ .../settings/workspaces/policyID.tsx | 67 ++++++++++++++ 5 files changed, 332 insertions(+) create mode 100644 src/components/SidePane/HelpContent/inbox.tsx create mode 100644 src/components/SidePane/HelpContent/search.tsx create mode 100644 src/components/SidePane/HelpContent/settings/index.tsx create mode 100644 src/components/SidePane/HelpContent/settings/workspaces/index.tsx create mode 100644 src/components/SidePane/HelpContent/settings/workspaces/policyID.tsx diff --git a/src/components/SidePane/HelpContent/inbox.tsx b/src/components/SidePane/HelpContent/inbox.tsx new file mode 100644 index 0000000000000..8103a786514cd --- /dev/null +++ b/src/components/SidePane/HelpContent/inbox.tsx @@ -0,0 +1,84 @@ +import React from 'react'; +import {View} from 'react-native'; +import ExpandableHelp from '@components/SidePane/ExpandableHelp'; +import Text from '@components/Text'; +import TextLink from '@components/TextLink'; +import type {ThemeStyles} from '@styles/index'; +import CONST from '@src/CONST'; + +function Inbox({styles}: {styles: ThemeStyles}) { + return ( + <> + Inbox + + 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: + + + + {CONST.BULLET} Text - Rich text messages stored securely and delivered via web, app, email, or SMS. + + + {CONST.BULLET} Images & Documents - Insert photos, screenshots, movies, PDFs, or more, using copy/paste, drag/drop, or the + attach button. + + + {CONST.BULLET} Expenses - Share an expense in the chat, either to simply track and document it, or to submit for + reimbursement. + + + {CONST.BULLET} 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: + + + + {CONST.BULLET} React - Throw a ♥️😂🔥 like on anything! + + + {CONST.BULLET} Reply in thread - Go deeper by creating a new chat on any comment. + + + {CONST.BULLET} Mark unread - Flag it for reading later, at your convenience. + + + + + Composer + + Use the composer at the bottom to write new messages: + + + + {CONST.BULLET} Markdown - Format text using bold,{' '} + italics, and{' '} + more. + + + {CONST.BULLET} 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). + + + + + + ); +} + +export default Inbox; diff --git a/src/components/SidePane/HelpContent/search.tsx b/src/components/SidePane/HelpContent/search.tsx new file mode 100644 index 0000000000000..1abc2d4dcbd5c --- /dev/null +++ b/src/components/SidePane/HelpContent/search.tsx @@ -0,0 +1,89 @@ +import React from 'react'; +import Text from '@components/Text'; +import type {ThemeStyles} from '@styles/index'; +import CONST from '@src/CONST'; + +function Search({styles}: {styles: ThemeStyles}) { + return ( + <> + 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: + + {CONST.BULLET} Expense - Individual standalone expenses. + + + {CONST.BULLET} Expense reports - Groups of expenses processed in a batch. + + + {CONST.BULLET} Chats - Comments written by you and others. + + + {CONST.BULLET} Invoices - Expenses submitted to clients for payment. + + + {CONST.BULLET} 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: + + {CONST.BULLET} All - Everything in every state. + + + Expenses/Expense Reports/Invoices + + {CONST.BULLET} Draft - Only you can see that hasn't been shared yet. + + + {CONST.BULLET} Outstanding - Submitted to someone and awaiting action. + + + {CONST.BULLET} Approved - Approved, but awaiting payment. + + + {CONST.BULLET} Done - Fully processed, no further action needed. + + + {CONST.BULLET} Paid - Fully paid, no further action needed. + + + Chats + + {CONST.BULLET} Unread - Not seen yet by you. + + + {CONST.BULLET} Sent - Sent by you. + + + {CONST.BULLET} Attachments - Image, movie, or document. + + + {CONST.BULLET} Links - Hyperlinks. + + + {CONST.BULLET} Pinned - Highlighted by you as important. + + + Trips + + {CONST.BULLET} Current - Happening or in the future. + + + {CONST.BULLET} Past - Already happened. + + + Results + The core of the Reports page are the search results themselves. + {CONST.BULLET} Select a row to see additional options. + {CONST.BULLET} Tap on a row to see more detail. + + ); +} + +export default Search; diff --git a/src/components/SidePane/HelpContent/settings/index.tsx b/src/components/SidePane/HelpContent/settings/index.tsx new file mode 100644 index 0000000000000..dc822b5d53c81 --- /dev/null +++ b/src/components/SidePane/HelpContent/settings/index.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import Text from '@components/Text'; +import type {ThemeStyles} from '@styles/index'; +import CONST from '@src/CONST'; + +function Settings({styles}: {styles: ThemeStyles}) { + return ( + <> + Settings + Here is where you configure Expensify exactly to your specifications: + + + {CONST.BULLET} Profile - Configure how you appear to others. + + + {CONST.BULLET} Wallet - See and manage your credit cards and bank accounts. + + + {CONST.BULLET} Preferences - Adjust how the app works for you. + + + {CONST.BULLET} Security - Lock down how you and others access your account. + + + {CONST.BULLET} Workspaces - Organize expenses for yourself and share with others. + + + {CONST.BULLET} Subscriptions - Manage payment details and history. + + + {CONST.BULLET} Domains - Advanced security and corporate card configuration. + + + {CONST.BULLET} Switch to Expensify Classic - Battle-tested and reliable. + + + {CONST.BULLET} Save the World - Let Expensify.org help your favorite teacher! + + + ); +} + +export default Settings; diff --git a/src/components/SidePane/HelpContent/settings/workspaces/index.tsx b/src/components/SidePane/HelpContent/settings/workspaces/index.tsx new file mode 100644 index 0000000000000..fa3cd3ae01012 --- /dev/null +++ b/src/components/SidePane/HelpContent/settings/workspaces/index.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import Text from '@components/Text'; +import type {ThemeStyles} from '@styles/index'; +import CONST from '@src/CONST'; + +function Workspaces({styles}: {styles: ThemeStyles}) { + return ( + <> + Workspaces + Workspaces allow for a wide range of features, including: + + + {CONST.BULLET} Categorize and submit expenses. + + + {CONST.BULLET} Approve and reimburse expenses. + + + {CONST.BULLET} Sync with accounting packages. + + + {CONST.BULLET} Connect to company card feeds. + + + {CONST.BULLET} Manage Expensify Cards. + + + {CONST.BULLET} Chat with colleagues, partners, and clients. + + {CONST.BULLET} … and lots more! + + Workspace Variations + Workspaces come in two variations: + + + {CONST.BULLET} Collect workspaces start at $5/member, and include all the basics for running a small business. + + + {CONST.BULLET} Control workspaces start at $9/member, and provide advanced capabilities, more powerful accounting sync, and more + sophisticated approval flows. + + + Managing Workspaces + In general, you would create one Workspace for each company you manage. You can create and join as many workspaces as you like. + + ); +} + +export default Workspaces; diff --git a/src/components/SidePane/HelpContent/settings/workspaces/policyID.tsx b/src/components/SidePane/HelpContent/settings/workspaces/policyID.tsx new file mode 100644 index 0000000000000..50e0aa5b31c70 --- /dev/null +++ b/src/components/SidePane/HelpContent/settings/workspaces/policyID.tsx @@ -0,0 +1,67 @@ +import React from 'react'; +import Text from '@components/Text'; +import type {ThemeStyles} from '@styles/index'; +import CONST from '@src/CONST'; + +function PolicyID({styles}: {styles: ThemeStyles}) { + return ( + <> + 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: + + + {CONST.BULLET} Overview - Configure how it appears to others. + + + {CONST.BULLET} Members - Add/remove members and admins. + + + {CONST.BULLET} Workflows - Configure submission, approval, and reimbursement. + + + {CONST.BULLET} Categories - Group expenses into a chart of accounts. + + + {CONST.BULLET} Expensify Card - Issue native Expensify Cards to employees. + + + {CONST.BULLET} Accounting - Sync with external accounting systems. + + + Optional Features + + These can be enabled via More Features: + + + + {CONST.BULLET} Distance rates - Configure mileage reimbursement. + + + {CONST.BULLET} Company card - Connect and manage third-party corporate card feeds. + + + {CONST.BULLET} Per diem - Configure daily rates. + + + {CONST.BULLET} Rules - Customize expense violations and set policy. + + + {CONST.BULLET} Invoices - Collect revenue from customers. + + + {CONST.BULLET} Tags - Group expenses by project or client. + + + {CONST.BULLET} Taxes - Track VAT and other taxes. + + + Report Fields + Capture extra expense report information. + + ); +} + +export default PolicyID; From 88db2953749c413eca71696d2ddd14d553916052 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 10:21:58 +0100 Subject: [PATCH 04/42] Improve algorithm to show help content --- src/components/SidePane/getHelpContent.tsx | 430 +++------------------ 1 file changed, 60 insertions(+), 370 deletions(-) diff --git a/src/components/SidePane/getHelpContent.tsx b/src/components/SidePane/getHelpContent.tsx index 3541ef9fc072d..21b5d87b391b2 100644 --- a/src/components/SidePane/getHelpContent.tsx +++ b/src/components/SidePane/getHelpContent.tsx @@ -1,16 +1,20 @@ -/* 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 Text from '@components/Text'; import type {ThemeStyles} from '@styles/index'; -import CONST from '@src/CONST'; +import Inbox from './HelpContent/inbox'; +import Search from './HelpContent/search'; +import Settings from './HelpContent/settings'; +import Workspaces from './HelpContent/settings/workspaces'; +import PolicyID from './HelpContent/settings/workspaces/policyID'; + +type ContentComponent = (props: {styles: ThemeStyles}) => ReactNode; type HelpContent = { /** The content to display for this route */ - content?: (styles: ThemeStyles) => ReactNode; + content: ContentComponent; /** Any children routes that this route has */ children?: Record; @@ -19,343 +23,27 @@ type HelpContent = { isExact?: boolean; }; -const helpContentMap: Record = { - r: { - content: (styles: ThemeStyles) => ( - <> - Inbox - - 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: - - {CONST.BULLET} Text - Rich text messages stored securely and delivered via web, app, email, or SMS. - - - {CONST.BULLET} Images & Documents - Insert photos, screenshots, movies, PDFs, or more, using copy/paste, drag/drop, or the attach - button - - - {CONST.BULLET} Expenses - Share an expense in the chat, either to simply track and document it, or to submit for reimbursement. - - - {CONST.BULLET} 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: - - {CONST.BULLET} React - Throw a ♥️😂🔥 like on anything! - - - {CONST.BULLET} Reply in thread - Go deeper by creating a new chat on any comment. - - - {CONST.BULLET} Mark unread - Flag it for reading later, at your convenience. - - - Composer - Use the composer at the bottom to write new messages: - - {CONST.BULLET} Markdown - Format text using *bold*,{' '} - _italics_, and more. - - - {CONST.BULLET} Mention - Invite or tag anyone in the world to any chat by putting an @ in front of their email address or phone - number (eg, - @awong@marslink.web, or @415-867-5309). - - - {CONST.BULLET} Mention - Invite or tag anyone in the world to any chat by putting an @ in front of their email address or phone - number (eg, - @awong@marslink.web, or @415-867-5309). - - - ), - }, - home: { - content: (styles: ThemeStyles) => ( - <> - Inbox - - 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: - - {CONST.BULLET} Text - Rich text messages stored securely and delivered via web, app, email, or SMS. - - - {CONST.BULLET} Images & Documents - Insert photos, screenshots, movies, PDFs, or more, using copy/paste, drag/drop, or the attach - button - - - {CONST.BULLET} Expenses - Share an expense in the chat, either to simply track and document it, or to submit for reimbursement. - - - {CONST.BULLET} 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: - - {CONST.BULLET} React - Throw a ❤️😂🔥 or anything you like on anything! - - - {CONST.BULLET} Reply in thread - Go deeper by creating a new chat on any comment. - - - {CONST.BULLET} Mark unread - Flag it for reading later, at your convenience. - - - Composer - Use the composer at the bottom to write new messages: - - {CONST.BULLET} Markdown - Format text using *bold*,{' '} - _italics_, and more. - - - {CONST.BULLET} Mention - Invite or tag anyone in the world to any chat by putting an @ in front of their email address or phone - number (eg, - @awong@marslink.web, or @415-867-5309). - - - ), - }, - search: { - content: (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: - - {CONST.BULLET} Expense - Individual standalone expenses. - - - {CONST.BULLET} Expense reports - Groups of expenses processed in a batch. - - - {CONST.BULLET} Chats - Comments written by you and others. - - - {CONST.BULLET} Invoices - Expenses submitted to clients for payment. - - - {CONST.BULLET} 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: - - {CONST.BULLET} All - Everything in every state. - - - Expenses/Expense Reports/Invoices - - {CONST.BULLET} Draft - Only you can see that hasn't been shared yet. - - - {CONST.BULLET} Outstanding - Submitted to someone and awaiting action. - - - {CONST.BULLET} Approved - Approved, but awaiting payment. - - - {CONST.BULLET} Done - Fully processed, no further action needed. - - - {CONST.BULLET} Paid - Fully paid, no further action needed. - - - Chats - - {CONST.BULLET} Unread - Not seen yet by you. - - - {CONST.BULLET} Sent - Sent by you. - - - {CONST.BULLET} Attachments - Image, movie, or document. - - - {CONST.BULLET} Links - Hyperlinks. - - - {CONST.BULLET} Pinned - Highlighted by you as important. - - - Trips - - {CONST.BULLET} Current - Happening or in the future. - - - {CONST.BULLET} Past - Already happened. - - - Results - The core of the Reports page are the search results themselves. - {CONST.BULLET} Select a row to see additional options. - {CONST.BULLET} Tap on a row to see more detail. - - ), - }, - settings: { - content: (styles: ThemeStyles) => ( - <> - Settings - Here is where you configure Expensify exactly to your specifications: - - - {CONST.BULLET} Profile - Configure how you appear to others. - - - {CONST.BULLET} Wallet - See and manage your credit cards and bank accounts. - - - {CONST.BULLET} Preferences - Adjust how the app works for you. - - - {CONST.BULLET} Security - Lock down how you and others access your account. - - - {CONST.BULLET} Workspaces - Organize expenses for yourself and share with others. - - - {CONST.BULLET} Subscriptions - Manage payment details and history. - - - {CONST.BULLET} Domains - Advanced security and corporate card configuration. - - - {CONST.BULLET} Switch to Expensify Classic - Battle-tested and reliable. - - - {CONST.BULLET} Save the World - Let Expensify.org help your favorite teacher! - - - ), - children: { - workspaces: { - content: (styles: ThemeStyles) => ( - <> - Settings > Workspaces - Workspaces allow for a wide range of features, including: - - - {CONST.BULLET} Categorize and submit expenses. - - - {CONST.BULLET} Approve and reimburse expenses. - - - {CONST.BULLET} Sync with accounting packages. - - - {CONST.BULLET} Connect to company card feeds. - - - {CONST.BULLET} Manage Expensify Cards. - - - {CONST.BULLET} Chat with colleagues, partners, and clients. - - {CONST.BULLET} … and lots more! - - Workspace Variations - Workspaces come in two variations: - - - {CONST.BULLET} Collect workspaces start at $5/member, and include all the basics for running a small business. - - - {CONST.BULLET} Control workspaces start at $9/member, and provide advanced capabilities, more powerful accounting sync, and - more sophisticated approval flows. - - - Managing Workspaces - 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: ThemeStyles) => ( - <> - Workspaces - 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: - - - {CONST.BULLET} Overview - Configure how it appears to others. - - - {CONST.BULLET} Members - Add/remove members and admins. - - - {CONST.BULLET} Workflows - Configure submission, approval, and reimbursement. - - - {CONST.BULLET} Categories - Group expenses into a chart of accounts. - - - {CONST.BULLET} Expensify Card - Issue native Expensify Cards to employees. - - - {CONST.BULLET} Accounting - Sync with external accounting systems. - - - Optional Features - - These can be enabled via More Features: - - - - {CONST.BULLET} Distance rates - Configure mileage reimbursement. - - - {CONST.BULLET} Company card - Connect and manage third-party corporate card feeds. - - - {CONST.BULLET} Per diem - Configure daily rates. - - - {CONST.BULLET} Rules - Customize expense violations and set policy. - - - {CONST.BULLET} Invoices - Collect revenue from customers. - - - {CONST.BULLET} Tags - Group expenses by project or client. - - - {CONST.BULLET} Taxes - Track VAT and other taxes. - - - Report Fields - Capture extra expense report information. - - ), +const helpContentMap: HelpContent = { + content: () => null, + children: { + r: { + content: Inbox, + }, + home: { + content: Inbox, + }, + search: { + content: Search, + }, + settings: { + content: Settings, + children: { + workspaces: { + content: Workspaces, + children: { + ':policyID': { + content: PolicyID, + }, }, }, }, @@ -364,14 +52,20 @@ const helpContentMap: Record = { }; type DiagnosticDataProps = { + /** The styles to apply to the diagnostic data */ styles: ThemeStyles; + + /** The route that was attempted to be accessed */ route: string; - currentRoute?: string; + + /** Whether the route is an exact match */ isExactMatch?: boolean; + + /** Help content to display */ children?: ReactNode; }; -function DiagnosticData({styles, route, currentRoute, children, isExactMatch}: DiagnosticDataProps) { +function DiagnosticData({styles, route, children, isExactMatch}: DiagnosticDataProps) { const diagnosticTitle = isExactMatch ? 'Help content found for route:' : 'Missing help content for route:'; return ( @@ -384,55 +78,51 @@ function DiagnosticData({styles, route, currentRoute, children, isExactMatch}: D )} {diagnosticTitle} {route} - {!isExactMatch && !!currentRoute && ( - <> - Using content from: - {currentRoute} - - )} ); } function getHelpContent(styles: ThemeStyles, route: string, isProduction: boolean): ReactNode { - const [firstPart, ...routeParts] = route.substring(1).split('/'); - const currentRoute = [firstPart]; - let currentNode: HelpContent = helpContentMap[firstPart]; + const routeParts = route.substring(1).split('/'); + const helpContentComponents: ContentComponent[] = []; + let activeHelpContent = helpContentMap; let isExactMatch = true; for (const part of routeParts) { - if (currentNode?.children?.[part]) { - currentNode = currentNode.children[part]; - currentRoute.push(part); - isExactMatch = true; + if (activeHelpContent?.children?.[part]) { + activeHelpContent = activeHelpContent.children[part]; + helpContentComponents.push(activeHelpContent.content); } else { isExactMatch = false; break; } } - if (currentNode?.content) { - if (isProduction) { - return currentNode.content(styles); - } - + const content = helpContentComponents.reverse().map((HelpContentNode, index) => { return ( - - {currentNode.content(styles)} - + <> + + {index < helpContentComponents.length - 1 && } + ); + }); + + if (isProduction && content) { + return content; } return ( + isExactMatch={isExactMatch} + > + {content} + ); } From ad2607084784a1875fc45ff16f9ae22951bd228e Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 12:39:31 +0100 Subject: [PATCH 05/42] Fix scroll issue on native devices --- src/components/SidePane/Help/HelpContent.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/SidePane/Help/HelpContent.tsx b/src/components/SidePane/Help/HelpContent.tsx index 02c226f4e4433..78e8996efcdaa 100644 --- a/src/components/SidePane/Help/HelpContent.tsx +++ b/src/components/SidePane/Help/HelpContent.tsx @@ -1,8 +1,10 @@ import {findFocusedRoute} from '@react-navigation/native'; import React, {useEffect, useRef} from 'react'; +// Importing from the react-native-gesture-handler package instead of the `components/ScrollView` to fix scroll issue: +// https://github.com/react-native-modal/react-native-modal/issues/236 +import {ScrollView} from 'react-native-gesture-handler'; import HeaderGap from '@components/HeaderGap'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import ScrollView from '@components/ScrollView'; import getHelpContent from '@components/SidePane/getHelpContent'; import useEnvironment from '@hooks/useEnvironment'; import useLocalize from '@hooks/useLocalize'; From 8a8bb49a6c50e3e3160ff442c9bf1d754a91444d Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 13:30:26 +0100 Subject: [PATCH 06/42] Implement HelpBulletList --- src/components/SidePane/HelpBulletList.tsx | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/components/SidePane/HelpBulletList.tsx diff --git a/src/components/SidePane/HelpBulletList.tsx b/src/components/SidePane/HelpBulletList.tsx new file mode 100644 index 0000000000000..8d7eb5313bfcb --- /dev/null +++ b/src/components/SidePane/HelpBulletList.tsx @@ -0,0 +1,26 @@ +import type {ReactNode} from 'react'; +import React from 'react'; +import {View} from 'react-native'; +import Text from '@components/Text'; +import type {ThemeStyles} from '@styles/index'; +import CONST from '@src/CONST'; + +type HelpBulletListProps = { + styles: ThemeStyles; + items: ReactNode[]; +}; + +function HelpBulletList({items, styles}: HelpBulletListProps) { + return items.map((item, index) => ( + + {CONST.DOT_SEPARATOR} + {item} + + )); +} + +export default HelpBulletList; From fec00de3e0cd91a31ecd9e463c25328f20aafc62 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 13:30:36 +0100 Subject: [PATCH 07/42] Rename ExpandableHelp --- src/components/SidePane/ExpandableHelp.tsx | 31 ------------------- src/components/SidePane/HelpExpandable.tsx | 35 ++++++++++++++++++++++ 2 files changed, 35 insertions(+), 31 deletions(-) delete mode 100644 src/components/SidePane/ExpandableHelp.tsx create mode 100644 src/components/SidePane/HelpExpandable.tsx diff --git a/src/components/SidePane/ExpandableHelp.tsx b/src/components/SidePane/ExpandableHelp.tsx deleted file mode 100644 index e2b04c815ac48..0000000000000 --- a/src/components/SidePane/ExpandableHelp.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React, {useState} from 'react'; -import Text from '@components/Text'; -import type {ThemeStyles} from '@styles/index'; - -type ExpandableHelpProps = { - children: React.ReactNode; - styles: ThemeStyles; - moreText?: string; -}; - -function ExpandableHelp({children, styles, moreText = '(more)'}: ExpandableHelpProps) { - const [isExpanded, setIsExpanded] = useState(false); - - if (isExpanded) { - return children; - } - - return ( - setIsExpanded(true)} - > - {' '} - {moreText} - - ); -} - -ExpandableHelp.displayName = 'ExpandableHelp'; - -export default ExpandableHelp; diff --git a/src/components/SidePane/HelpExpandable.tsx b/src/components/SidePane/HelpExpandable.tsx new file mode 100644 index 0000000000000..32d5444ef7082 --- /dev/null +++ b/src/components/SidePane/HelpExpandable.tsx @@ -0,0 +1,35 @@ +import React, {useState} from 'react'; +import Text from '@components/Text'; +import type {ThemeStyles} from '@styles/index'; + +type HelpExpandableProps = { + children: React.ReactNode; + styles: ThemeStyles; + title?: string; + moreText?: string; +}; + +function HelpExpandable({children, styles, title, moreText = '(more)'}: HelpExpandableProps) { + const [isExpanded, setIsExpanded] = useState(false); + + return ( + <> + + {title}{' '} + {!isExpanded && ( + setIsExpanded(true)} + > + {moreText} + + )} + + {isExpanded && children} + + ); +} + +HelpExpandable.displayName = 'ExpandableHelp'; + +export default HelpExpandable; From 6ec3f410ed087b3f6b6238df5b02077254ca01e5 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 13:30:45 +0100 Subject: [PATCH 08/42] Rename inbox to chat --- src/components/SidePane/HelpContent/chat.tsx | 130 ++++++++++++++++++ src/components/SidePane/HelpContent/inbox.tsx | 84 ----------- src/components/SidePane/getHelpContent.tsx | 6 +- 3 files changed, 133 insertions(+), 87 deletions(-) create mode 100644 src/components/SidePane/HelpContent/chat.tsx delete mode 100644 src/components/SidePane/HelpContent/inbox.tsx diff --git a/src/components/SidePane/HelpContent/chat.tsx b/src/components/SidePane/HelpContent/chat.tsx new file mode 100644 index 0000000000000..5ccdd38befdc4 --- /dev/null +++ b/src/components/SidePane/HelpContent/chat.tsx @@ -0,0 +1,130 @@ +/* eslint-disable react/no-unescaped-entities */ +import React from 'react'; +import {View} from 'react-native'; +import BulletList from '@components/SidePane/HelpBulletList'; +import ExpandableHelp from '@components/SidePane/HelpExpandable'; +import Text from '@components/Text'; +import TextLink from '@components/TextLink'; +import type {ThemeStyles} from '@styles/index'; + +function Inbox({styles}: {styles: ThemeStyles}) { + return ( + <> + 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 + + + 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 + + + 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 + + + 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 + + Expense reports waiting on you, + Tasks assigned to you, + Chats that have mentioned you, + Anything you have pinned, + ]} + /> + + + Chats + + + Most Recent - Lists every chat, ordered by whichever was most recently active. + , + + Focus - Only lists chats with unread messages, sorted alphabetically. + , + ]} + /> + + + ); +} + +export default Inbox; diff --git a/src/components/SidePane/HelpContent/inbox.tsx b/src/components/SidePane/HelpContent/inbox.tsx deleted file mode 100644 index 8103a786514cd..0000000000000 --- a/src/components/SidePane/HelpContent/inbox.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import React from 'react'; -import {View} from 'react-native'; -import ExpandableHelp from '@components/SidePane/ExpandableHelp'; -import Text from '@components/Text'; -import TextLink from '@components/TextLink'; -import type {ThemeStyles} from '@styles/index'; -import CONST from '@src/CONST'; - -function Inbox({styles}: {styles: ThemeStyles}) { - return ( - <> - Inbox - - 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: - - - - {CONST.BULLET} Text - Rich text messages stored securely and delivered via web, app, email, or SMS. - - - {CONST.BULLET} Images & Documents - Insert photos, screenshots, movies, PDFs, or more, using copy/paste, drag/drop, or the - attach button. - - - {CONST.BULLET} Expenses - Share an expense in the chat, either to simply track and document it, or to submit for - reimbursement. - - - {CONST.BULLET} 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: - - - - {CONST.BULLET} React - Throw a ♥️😂🔥 like on anything! - - - {CONST.BULLET} Reply in thread - Go deeper by creating a new chat on any comment. - - - {CONST.BULLET} Mark unread - Flag it for reading later, at your convenience. - - - - - Composer - - Use the composer at the bottom to write new messages: - - - - {CONST.BULLET} Markdown - Format text using bold,{' '} - italics, and{' '} - more. - - - {CONST.BULLET} 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). - - - - - - ); -} - -export default Inbox; diff --git a/src/components/SidePane/getHelpContent.tsx b/src/components/SidePane/getHelpContent.tsx index 21b5d87b391b2..52d943030bfbd 100644 --- a/src/components/SidePane/getHelpContent.tsx +++ b/src/components/SidePane/getHelpContent.tsx @@ -4,7 +4,7 @@ import React from 'react'; import {View} from 'react-native'; import Text from '@components/Text'; import type {ThemeStyles} from '@styles/index'; -import Inbox from './HelpContent/inbox'; +import Chat from './HelpContent/chat'; import Search from './HelpContent/search'; import Settings from './HelpContent/settings'; import Workspaces from './HelpContent/settings/workspaces'; @@ -27,10 +27,10 @@ const helpContentMap: HelpContent = { content: () => null, children: { r: { - content: Inbox, + content: Chat, }, home: { - content: Inbox, + content: Chat, }, search: { content: Search, From 6e32022fda4b04fdc99efe14854a506e25e3a69a Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 13:33:07 +0100 Subject: [PATCH 09/42] Add remaining content to search --- .../SidePane/HelpContent/search.tsx | 149 ++++++++++-------- 1 file changed, 82 insertions(+), 67 deletions(-) diff --git a/src/components/SidePane/HelpContent/search.tsx b/src/components/SidePane/HelpContent/search.tsx index 1abc2d4dcbd5c..acd0ec7b3db8c 100644 --- a/src/components/SidePane/HelpContent/search.tsx +++ b/src/components/SidePane/HelpContent/search.tsx @@ -1,87 +1,102 @@ +/* eslint-disable react/no-unescaped-entities */ import React from 'react'; +import BulletList from '@components/SidePane/HelpBulletList'; +import ExpandableHelp from '@components/SidePane/HelpExpandable'; import Text from '@components/Text'; import type {ThemeStyles} from '@styles/index'; -import CONST from '@src/CONST'; function Search({styles}: {styles: ThemeStyles}) { return ( <> - Reports + 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: - - {CONST.BULLET} Expense - Individual standalone expenses. - - - {CONST.BULLET} Expense reports - Groups of expenses processed in a batch. - - - {CONST.BULLET} Chats - Comments written by you and others. - - - {CONST.BULLET} Invoices - Expenses submitted to clients for payment. - - - {CONST.BULLET} Trips - Travel expenses booked with Expensify Travel or scanned with SmartScan. - + + + 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: - - {CONST.BULLET} All - Everything in every state. - - - Expenses/Expense Reports/Invoices - - {CONST.BULLET} Draft - Only you can see that hasn't been shared yet. - - - {CONST.BULLET} Outstanding - Submitted to someone and awaiting action. - - - {CONST.BULLET} Approved - Approved, but awaiting payment. - - - {CONST.BULLET} Done - Fully processed, no further action needed. - - - {CONST.BULLET} Paid - Fully paid, no further action needed. - - - Chats - - {CONST.BULLET} Unread - Not seen yet by you. - - - {CONST.BULLET} Sent - Sent by you. - - - {CONST.BULLET} Attachments - Image, movie, or document. - - - {CONST.BULLET} Links - Hyperlinks. - - - {CONST.BULLET} Pinned - Highlighted by you as important. - - - Trips - - {CONST.BULLET} Current - Happening or in the future. - - - {CONST.BULLET} Past - Already happened. - + + 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. - {CONST.BULLET} Select a row to see additional options. - {CONST.BULLET} Tap on a row to see more detail. + + Select a row to see additional options., Tap on a row to see more detail.]} + /> + ); } From a64d85d1b9c30b3a67dd628ec6812510ca6ab5d8 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 14:00:27 +0100 Subject: [PATCH 10/42] Fix selecting text --- src/components/SidePane/Help/HelpContent.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/SidePane/Help/HelpContent.tsx b/src/components/SidePane/Help/HelpContent.tsx index 78e8996efcdaa..ca5e5dfc54b75 100644 --- a/src/components/SidePane/Help/HelpContent.tsx +++ b/src/components/SidePane/Help/HelpContent.tsx @@ -55,7 +55,12 @@ function HelpContent({closeSidePane}: HelpContentProps) { shouldShowCloseButton={isExtraLargeScreenWidth} shouldDisplayHelpButton={false} /> - {getHelpContent(styles, route, isProduction)} + + {getHelpContent(styles, route, isProduction)} + ); } From 818989ada4dbfbedef91342400566ca4affbd9e2 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 14:01:12 +0100 Subject: [PATCH 11/42] Add userSelectNone style to HelpBulletList items --- src/components/SidePane/HelpBulletList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SidePane/HelpBulletList.tsx b/src/components/SidePane/HelpBulletList.tsx index 8d7eb5313bfcb..cdea8ef810f3f 100644 --- a/src/components/SidePane/HelpBulletList.tsx +++ b/src/components/SidePane/HelpBulletList.tsx @@ -17,7 +17,7 @@ function HelpBulletList({items, styles}: HelpBulletListProps) { key={`bullet-list-item-${index}`} style={[styles.flexRow, styles.alignItemsStart, styles.mt2]} > - {CONST.DOT_SEPARATOR} + {CONST.DOT_SEPARATOR} {item} )); From a666d0c90cead50a06befc51b7efc58d68ab2c3b Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 14:20:56 +0100 Subject: [PATCH 12/42] Update margin style for bullet list items in HelpBulletList component --- src/components/SidePane/HelpBulletList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SidePane/HelpBulletList.tsx b/src/components/SidePane/HelpBulletList.tsx index cdea8ef810f3f..63bde1a278a8c 100644 --- a/src/components/SidePane/HelpBulletList.tsx +++ b/src/components/SidePane/HelpBulletList.tsx @@ -15,7 +15,7 @@ function HelpBulletList({items, styles}: HelpBulletListProps) { {CONST.DOT_SEPARATOR} {item} From baa32b3e590ff168605b206be6338a4dd1a4872d Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 14:21:04 +0100 Subject: [PATCH 13/42] Add containerStyle prop to HelpExpandable component for customizable styling --- src/components/SidePane/HelpExpandable.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/SidePane/HelpExpandable.tsx b/src/components/SidePane/HelpExpandable.tsx index 32d5444ef7082..06547e7eebd6e 100644 --- a/src/components/SidePane/HelpExpandable.tsx +++ b/src/components/SidePane/HelpExpandable.tsx @@ -1,19 +1,22 @@ import React, {useState} from 'react'; +import type {StyleProp, ViewStyle} from 'react-native'; +import {View} from 'react-native'; import Text from '@components/Text'; import type {ThemeStyles} from '@styles/index'; type HelpExpandableProps = { children: React.ReactNode; styles: ThemeStyles; + containerStyle?: StyleProp; title?: string; moreText?: string; }; -function HelpExpandable({children, styles, title, moreText = '(more)'}: HelpExpandableProps) { +function HelpExpandable({children, styles, containerStyle, title, moreText = '(more)'}: HelpExpandableProps) { const [isExpanded, setIsExpanded] = useState(false); return ( - <> + {title}{' '} {!isExpanded && ( @@ -26,7 +29,7 @@ function HelpExpandable({children, styles, title, moreText = '(more)'}: HelpExpa )} {isExpanded && children} - + ); } From 12e9f81a472b00afdf46288c6b779f446ecc6805 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 14:21:57 +0100 Subject: [PATCH 14/42] Add missing content to Settings --- .../SidePane/HelpContent/settings/index.tsx | 69 +++++++++++-------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/src/components/SidePane/HelpContent/settings/index.tsx b/src/components/SidePane/HelpContent/settings/index.tsx index dc822b5d53c81..0df4284c32e6e 100644 --- a/src/components/SidePane/HelpContent/settings/index.tsx +++ b/src/components/SidePane/HelpContent/settings/index.tsx @@ -1,41 +1,50 @@ import React from 'react'; +import BulletList from '@components/SidePane/HelpBulletList'; +import ExpandableHelp from '@components/SidePane/HelpExpandable'; import Text from '@components/Text'; import type {ThemeStyles} from '@styles/index'; -import CONST from '@src/CONST'; function Settings({styles}: {styles: ThemeStyles}) { return ( <> Settings - Here is where you configure Expensify exactly to your specifications: - - - {CONST.BULLET} Profile - Configure how you appear to others. - - - {CONST.BULLET} Wallet - See and manage your credit cards and bank accounts. - - - {CONST.BULLET} Preferences - Adjust how the app works for you. - - - {CONST.BULLET} Security - Lock down how you and others access your account. - - - {CONST.BULLET} Workspaces - Organize expenses for yourself and share with others. - - - {CONST.BULLET} Subscriptions - Manage payment details and history. - - - {CONST.BULLET} Domains - Advanced security and corporate card configuration. - - - {CONST.BULLET} Switch to Expensify Classic - Battle-tested and reliable. - - - {CONST.BULLET} Save the World - Let Expensify.org help your favorite teacher! - + + + 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! + , + ]} + /> + ); } From c2517e359316418a09cacc836730247ecd5fdd8e Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 14:22:05 +0100 Subject: [PATCH 15/42] Add missing content to Workspaces --- .../HelpContent/settings/workspaces/index.tsx | 85 ++++++++++++------- 1 file changed, 52 insertions(+), 33 deletions(-) diff --git a/src/components/SidePane/HelpContent/settings/workspaces/index.tsx b/src/components/SidePane/HelpContent/settings/workspaces/index.tsx index fa3cd3ae01012..446c61ae62f6e 100644 --- a/src/components/SidePane/HelpContent/settings/workspaces/index.tsx +++ b/src/components/SidePane/HelpContent/settings/workspaces/index.tsx @@ -1,47 +1,66 @@ +/* eslint-disable react/no-unescaped-entities */ import React from 'react'; +import BulletList from '@components/SidePane/HelpBulletList'; +import ExpandableHelp from '@components/SidePane/HelpExpandable'; import Text from '@components/Text'; import type {ThemeStyles} from '@styles/index'; -import CONST from '@src/CONST'; function Workspaces({styles}: {styles: ThemeStyles}) { return ( <> Workspaces - Workspaces allow for a wide range of features, including: - - {CONST.BULLET} Categorize and submit expenses. - - - {CONST.BULLET} Approve and reimburse expenses. - - - {CONST.BULLET} Sync with accounting packages. - - - {CONST.BULLET} Connect to company card feeds. - - - {CONST.BULLET} Manage Expensify Cards. - - - {CONST.BULLET} Chat with colleagues, partners, and clients. - - {CONST.BULLET} … and lots more! + + + 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!, + ]} + /> + - Workspace Variations - 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. + , + ]} + /> + - - {CONST.BULLET} Collect workspaces start at $5/member, and include all the basics for running a small business. - - - {CONST.BULLET} Control workspaces start at $9/member, and provide advanced capabilities, more powerful accounting sync, and more - sophisticated approval flows. - - - Managing Workspaces - In general, you would create one Workspace for each company you manage. You can create and join as many workspaces as you like. + In general you would create one Workspace for each company you manage. You can create and join as many workspaces as you like. ); } From ecc2ea3a09da349771f01010a0f61a58d8e1696d Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 14:22:13 +0100 Subject: [PATCH 16/42] Add missing content to Workspace --- .../settings/workspaces/policyID.tsx | 122 ++++++++++-------- 1 file changed, 69 insertions(+), 53 deletions(-) diff --git a/src/components/SidePane/HelpContent/settings/workspaces/policyID.tsx b/src/components/SidePane/HelpContent/settings/workspaces/policyID.tsx index 50e0aa5b31c70..68ea891551ace 100644 --- a/src/components/SidePane/HelpContent/settings/workspaces/policyID.tsx +++ b/src/components/SidePane/HelpContent/settings/workspaces/policyID.tsx @@ -1,67 +1,83 @@ +/* eslint-disable react/no-unescaped-entities */ import React from 'react'; +import BulletList from '@components/SidePane/HelpBulletList'; +import ExpandableHelp from '@components/SidePane/HelpExpandable'; import Text from '@components/Text'; import type {ThemeStyles} from '@styles/index'; -import CONST from '@src/CONST'; -function PolicyID({styles}: {styles: ThemeStyles}) { +function Workspace({styles}: {styles: ThemeStyles}) { return ( <> 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: + Default features + + + 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. + , + ]} + /> + - - {CONST.BULLET} Overview - Configure how it appears to others. - - - {CONST.BULLET} Members - Add/remove members and admins. - - - {CONST.BULLET} Workflows - Configure submission, approval, and reimbursement. - - - {CONST.BULLET} Categories - Group expenses into a chart of accounts. - - - {CONST.BULLET} Expensify Card - Issue native Expensify Cards to employees. - - - {CONST.BULLET} Accounting - Sync with external accounting systems. - - - Optional Features - - These can be enabled via More Features: - - - - {CONST.BULLET} Distance rates - Configure mileage reimbursement. - - - {CONST.BULLET} Company card - Connect and manage third-party corporate card feeds. - - - {CONST.BULLET} Per diem - Configure daily rates. - - - {CONST.BULLET} Rules - Customize expense violations and set policy. - - - {CONST.BULLET} Invoices - Collect revenue from customers. - - - {CONST.BULLET} Tags - Group expenses by project or client. - - - {CONST.BULLET} Taxes - Track VAT and other taxes. - - - Report Fields - Capture extra expense report information. + Optional 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. + , + ]} + /> + ); } -export default PolicyID; +export default Workspace; From 9c301658bf6f6cf98a52ceb2aef57a6904c82cc9 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 14:22:22 +0100 Subject: [PATCH 17/42] Fix margin style for Reports head --- src/components/SidePane/HelpContent/search.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SidePane/HelpContent/search.tsx b/src/components/SidePane/HelpContent/search.tsx index acd0ec7b3db8c..37b864d2c010f 100644 --- a/src/components/SidePane/HelpContent/search.tsx +++ b/src/components/SidePane/HelpContent/search.tsx @@ -8,7 +8,7 @@ import type {ThemeStyles} from '@styles/index'; function Search({styles}: {styles: ThemeStyles}) { return ( <> - Reports + Reports Virtually all data can be analyzed and reported upon in the Reports page. The major elements of this page include: Data type From e084f8d3486ca067c2372dbf628bedc86d4a0172 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 14:35:37 +0100 Subject: [PATCH 18/42] Add default text for routes with no content --- src/components/SidePane/getHelpContent.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/SidePane/getHelpContent.tsx b/src/components/SidePane/getHelpContent.tsx index 52d943030bfbd..28e3a3394a8b6 100644 --- a/src/components/SidePane/getHelpContent.tsx +++ b/src/components/SidePane/getHelpContent.tsx @@ -76,6 +76,7 @@ function DiagnosticData({styles, route, children, isExactMatch}: DiagnosticDataP )} + Diagnostic data (visible only on staging) {diagnosticTitle} {route} @@ -93,6 +94,9 @@ function getHelpContent(styles: ThemeStyles, route: string, isProduction: boolea activeHelpContent = activeHelpContent.children[part]; helpContentComponents.push(activeHelpContent.content); } else { + if (helpContentComponents.length === 0) { + helpContentComponents.push(() => We couldn't find any help content for this route.); + } isExactMatch = false; break; } @@ -111,7 +115,7 @@ function getHelpContent(styles: ThemeStyles, route: string, isProduction: boolea ); }); - if (isProduction && content) { + if (isProduction) { return content; } From 21ddd816497a14707276525b96533b48c4028b90 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 14:35:58 +0100 Subject: [PATCH 19/42] Fix lint --- src/components/SidePane/getHelpContent.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/SidePane/getHelpContent.tsx b/src/components/SidePane/getHelpContent.tsx index 28e3a3394a8b6..fa330d6a8e51b 100644 --- a/src/components/SidePane/getHelpContent.tsx +++ b/src/components/SidePane/getHelpContent.tsx @@ -95,6 +95,7 @@ function getHelpContent(styles: ThemeStyles, route: string, isProduction: boolea helpContentComponents.push(activeHelpContent.content); } else { if (helpContentComponents.length === 0) { + // eslint-disable-next-line react/no-unescaped-entities helpContentComponents.push(() => We couldn't find any help content for this route.); } isExactMatch = false; From 16f90ac65092aeeccefd34bf4e4bfc0aa2e3e6a0 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 14:42:09 +0100 Subject: [PATCH 20/42] Rename Help components --- src/components/HeaderWithBackButton/index.tsx | 2 +- src/components/Navigation/TopBar.tsx | 2 +- .../Search/SearchPageHeader/SearchPageHeaderInput.tsx | 2 +- .../SidePane/{ => HelpComponents}/HelpBulletList.tsx | 0 .../SidePane/{ => HelpComponents}/HelpButton.tsx | 0 .../SidePane/{Help => HelpComponents}/HelpContent.tsx | 2 +- .../SidePane/{ => HelpComponents}/HelpExpandable.tsx | 0 .../HelpOverlay.tsx} | 8 ++++---- src/components/SidePane/HelpContent/chat.tsx | 4 ++-- .../SidePane/{ => HelpContent}/getHelpContent.tsx | 10 +++++----- src/components/SidePane/HelpContent/search.tsx | 4 ++-- src/components/SidePane/HelpContent/settings/index.tsx | 4 ++-- .../SidePane/HelpContent/settings/workspaces/index.tsx | 4 ++-- .../HelpContent/settings/workspaces/policyID.tsx | 4 ++-- .../SidePane/{Help => HelpModal}/index.android.tsx | 2 +- .../SidePane/{Help => HelpModal}/index.ios.tsx | 2 +- src/components/SidePane/{Help => HelpModal}/index.tsx | 6 +++--- src/components/SidePane/{Help => HelpModal}/types.ts | 0 src/components/SidePane/index.tsx | 2 +- src/pages/home/HeaderView.tsx | 2 +- 20 files changed, 30 insertions(+), 30 deletions(-) rename src/components/SidePane/{ => HelpComponents}/HelpBulletList.tsx (100%) rename src/components/SidePane/{ => HelpComponents}/HelpButton.tsx (100%) rename src/components/SidePane/{Help => HelpComponents}/HelpContent.tsx (97%) rename src/components/SidePane/{ => HelpComponents}/HelpExpandable.tsx (100%) rename src/components/SidePane/{SidePaneOverlay.tsx => HelpComponents/HelpOverlay.tsx} (88%) rename src/components/SidePane/{ => HelpContent}/getHelpContent.tsx (93%) rename src/components/SidePane/{Help => HelpModal}/index.android.tsx (93%) rename src/components/SidePane/{Help => HelpModal}/index.ios.tsx (89%) rename src/components/SidePane/{Help => HelpModal}/index.tsx (94%) rename src/components/SidePane/{Help => HelpModal}/types.ts (100%) diff --git a/src/components/HeaderWithBackButton/index.tsx b/src/components/HeaderWithBackButton/index.tsx index 605d2f6d3938b..606bd3ecdf9ca 100755 --- a/src/components/HeaderWithBackButton/index.tsx +++ b/src/components/HeaderWithBackButton/index.tsx @@ -8,7 +8,7 @@ import * as Expensicons from '@components/Icon/Expensicons'; import PinButton from '@components/PinButton'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import SearchButton from '@components/Search/SearchRouter/SearchButton'; -import HelpButton from '@components/SidePane/HelpButton'; +import HelpButton from '@components/SidePane/HelpComponents/HelpButton'; import ThreeDotsMenu from '@components/ThreeDotsMenu'; import Tooltip from '@components/Tooltip'; import useLocalize from '@hooks/useLocalize'; diff --git a/src/components/Navigation/TopBar.tsx b/src/components/Navigation/TopBar.tsx index e6ff26c43d7ab..98d0c266ab6ab 100644 --- a/src/components/Navigation/TopBar.tsx +++ b/src/components/Navigation/TopBar.tsx @@ -5,7 +5,7 @@ import Breadcrumbs from '@components/Breadcrumbs'; import LoadingBar from '@components/LoadingBar'; import {PressableWithoutFeedback} from '@components/Pressable'; import SearchButton from '@components/Search/SearchRouter/SearchButton'; -import HelpButton from '@components/SidePane/HelpButton'; +import HelpButton from '@components/SidePane/HelpComponents/HelpButton'; import Text from '@components/Text'; import WorkspaceSwitcherButton from '@components/WorkspaceSwitcherButton'; import useLocalize from '@hooks/useLocalize'; diff --git a/src/components/Search/SearchPageHeader/SearchPageHeaderInput.tsx b/src/components/Search/SearchPageHeader/SearchPageHeaderInput.tsx index e922102ee0eba..b87e118014fa4 100644 --- a/src/components/Search/SearchPageHeader/SearchPageHeaderInput.tsx +++ b/src/components/Search/SearchPageHeader/SearchPageHeaderInput.tsx @@ -18,7 +18,7 @@ import type {SearchQueryJSON, SearchQueryString} from '@components/Search/types' import {isSearchQueryItem} from '@components/SelectionList/Search/SearchQueryListItem'; import type {SearchQueryItem} from '@components/SelectionList/Search/SearchQueryListItem'; import type {SelectionListHandle} from '@components/SelectionList/types'; -import HelpButton from '@components/SidePane/HelpButton'; +import HelpButton from '@components/SidePane/HelpComponents/HelpButton'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; diff --git a/src/components/SidePane/HelpBulletList.tsx b/src/components/SidePane/HelpComponents/HelpBulletList.tsx similarity index 100% rename from src/components/SidePane/HelpBulletList.tsx rename to src/components/SidePane/HelpComponents/HelpBulletList.tsx diff --git a/src/components/SidePane/HelpButton.tsx b/src/components/SidePane/HelpComponents/HelpButton.tsx similarity index 100% rename from src/components/SidePane/HelpButton.tsx rename to src/components/SidePane/HelpComponents/HelpButton.tsx diff --git a/src/components/SidePane/Help/HelpContent.tsx b/src/components/SidePane/HelpComponents/HelpContent.tsx similarity index 97% rename from src/components/SidePane/Help/HelpContent.tsx rename to src/components/SidePane/HelpComponents/HelpContent.tsx index ca5e5dfc54b75..b1b4eceb3f344 100644 --- a/src/components/SidePane/Help/HelpContent.tsx +++ b/src/components/SidePane/HelpComponents/HelpContent.tsx @@ -5,7 +5,7 @@ import React, {useEffect, useRef} from 'react'; import {ScrollView} from 'react-native-gesture-handler'; import HeaderGap from '@components/HeaderGap'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import getHelpContent from '@components/SidePane/getHelpContent'; +import getHelpContent from '@components/SidePane/HelpContent/getHelpContent'; import useEnvironment from '@hooks/useEnvironment'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; diff --git a/src/components/SidePane/HelpExpandable.tsx b/src/components/SidePane/HelpComponents/HelpExpandable.tsx similarity index 100% rename from src/components/SidePane/HelpExpandable.tsx rename to src/components/SidePane/HelpComponents/HelpExpandable.tsx diff --git a/src/components/SidePane/SidePaneOverlay.tsx b/src/components/SidePane/HelpComponents/HelpOverlay.tsx similarity index 88% rename from src/components/SidePane/SidePaneOverlay.tsx rename to src/components/SidePane/HelpComponents/HelpOverlay.tsx index a16cfb1b90846..339c6fef3d047 100644 --- a/src/components/SidePane/SidePaneOverlay.tsx +++ b/src/components/SidePane/HelpComponents/HelpOverlay.tsx @@ -5,7 +5,7 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; -type SidePaneOverlayProps = { +type HelpOverlayProps = { /** Whether the side pane is displayed over RHP */ isRHPVisible: boolean; @@ -15,7 +15,7 @@ type SidePaneOverlayProps = { const easing = Easing.bezier(0.76, 0.0, 0.24, 1.0).factory(); -function SidePaneOverlay({isRHPVisible, onBackdropPress}: SidePaneOverlayProps) { +function HelpOverlay({isRHPVisible, onBackdropPress}: HelpOverlayProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -51,6 +51,6 @@ function SidePaneOverlay({isRHPVisible, onBackdropPress}: SidePaneOverlayProps) ); } -SidePaneOverlay.displayName = 'SidePaneOverlay'; +HelpOverlay.displayName = 'HelpOverlay'; -export default SidePaneOverlay; +export default HelpOverlay; diff --git a/src/components/SidePane/HelpContent/chat.tsx b/src/components/SidePane/HelpContent/chat.tsx index 5ccdd38befdc4..091ba317debcd 100644 --- a/src/components/SidePane/HelpContent/chat.tsx +++ b/src/components/SidePane/HelpContent/chat.tsx @@ -1,8 +1,8 @@ /* eslint-disable react/no-unescaped-entities */ import React from 'react'; import {View} from 'react-native'; -import BulletList from '@components/SidePane/HelpBulletList'; -import ExpandableHelp from '@components/SidePane/HelpExpandable'; +import BulletList from '@components/SidePane/HelpComponents/HelpBulletList'; +import ExpandableHelp from '@components/SidePane/HelpComponents/HelpExpandable'; import Text from '@components/Text'; import TextLink from '@components/TextLink'; import type {ThemeStyles} from '@styles/index'; diff --git a/src/components/SidePane/getHelpContent.tsx b/src/components/SidePane/HelpContent/getHelpContent.tsx similarity index 93% rename from src/components/SidePane/getHelpContent.tsx rename to src/components/SidePane/HelpContent/getHelpContent.tsx index fa330d6a8e51b..c2d18d876c47f 100644 --- a/src/components/SidePane/getHelpContent.tsx +++ b/src/components/SidePane/HelpContent/getHelpContent.tsx @@ -4,11 +4,11 @@ import React from 'react'; import {View} from 'react-native'; import Text from '@components/Text'; import type {ThemeStyles} from '@styles/index'; -import Chat from './HelpContent/chat'; -import Search from './HelpContent/search'; -import Settings from './HelpContent/settings'; -import Workspaces from './HelpContent/settings/workspaces'; -import PolicyID from './HelpContent/settings/workspaces/policyID'; +import Chat from './chat'; +import Search from './search'; +import Settings from './settings'; +import Workspaces from './settings/workspaces'; +import PolicyID from './settings/workspaces/policyID'; type ContentComponent = (props: {styles: ThemeStyles}) => ReactNode; diff --git a/src/components/SidePane/HelpContent/search.tsx b/src/components/SidePane/HelpContent/search.tsx index 37b864d2c010f..9fd460fba5628 100644 --- a/src/components/SidePane/HelpContent/search.tsx +++ b/src/components/SidePane/HelpContent/search.tsx @@ -1,7 +1,7 @@ /* eslint-disable react/no-unescaped-entities */ import React from 'react'; -import BulletList from '@components/SidePane/HelpBulletList'; -import ExpandableHelp from '@components/SidePane/HelpExpandable'; +import BulletList from '@components/SidePane/HelpComponents/HelpBulletList'; +import ExpandableHelp from '@components/SidePane/HelpComponents/HelpExpandable'; import Text from '@components/Text'; import type {ThemeStyles} from '@styles/index'; diff --git a/src/components/SidePane/HelpContent/settings/index.tsx b/src/components/SidePane/HelpContent/settings/index.tsx index 0df4284c32e6e..c7f836b921199 100644 --- a/src/components/SidePane/HelpContent/settings/index.tsx +++ b/src/components/SidePane/HelpContent/settings/index.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import BulletList from '@components/SidePane/HelpBulletList'; -import ExpandableHelp from '@components/SidePane/HelpExpandable'; +import BulletList from '@components/SidePane/HelpComponents/HelpBulletList'; +import ExpandableHelp from '@components/SidePane/HelpComponents/HelpExpandable'; import Text from '@components/Text'; import type {ThemeStyles} from '@styles/index'; diff --git a/src/components/SidePane/HelpContent/settings/workspaces/index.tsx b/src/components/SidePane/HelpContent/settings/workspaces/index.tsx index 446c61ae62f6e..98755c7433d30 100644 --- a/src/components/SidePane/HelpContent/settings/workspaces/index.tsx +++ b/src/components/SidePane/HelpContent/settings/workspaces/index.tsx @@ -1,7 +1,7 @@ /* eslint-disable react/no-unescaped-entities */ import React from 'react'; -import BulletList from '@components/SidePane/HelpBulletList'; -import ExpandableHelp from '@components/SidePane/HelpExpandable'; +import BulletList from '@components/SidePane/HelpComponents/HelpBulletList'; +import ExpandableHelp from '@components/SidePane/HelpComponents/HelpExpandable'; import Text from '@components/Text'; import type {ThemeStyles} from '@styles/index'; diff --git a/src/components/SidePane/HelpContent/settings/workspaces/policyID.tsx b/src/components/SidePane/HelpContent/settings/workspaces/policyID.tsx index 68ea891551ace..26dbbf413eeb1 100644 --- a/src/components/SidePane/HelpContent/settings/workspaces/policyID.tsx +++ b/src/components/SidePane/HelpContent/settings/workspaces/policyID.tsx @@ -1,7 +1,7 @@ /* eslint-disable react/no-unescaped-entities */ import React from 'react'; -import BulletList from '@components/SidePane/HelpBulletList'; -import ExpandableHelp from '@components/SidePane/HelpExpandable'; +import BulletList from '@components/SidePane/HelpComponents/HelpBulletList'; +import ExpandableHelp from '@components/SidePane/HelpComponents/HelpExpandable'; import Text from '@components/Text'; import type {ThemeStyles} from '@styles/index'; diff --git a/src/components/SidePane/Help/index.android.tsx b/src/components/SidePane/HelpModal/index.android.tsx similarity index 93% rename from src/components/SidePane/Help/index.android.tsx rename to src/components/SidePane/HelpModal/index.android.tsx index 5518bf1875eef..4d41bebac6313 100644 --- a/src/components/SidePane/Help/index.android.tsx +++ b/src/components/SidePane/HelpModal/index.android.tsx @@ -2,8 +2,8 @@ import {useFocusEffect} from '@react-navigation/native'; import React, {useCallback} from 'react'; import {BackHandler} from 'react-native'; import Modal from '@components/Modal'; +import HelpContent from '@components/SidePane/HelpComponents/HelpContent'; import CONST from '@src/CONST'; -import HelpContent from './HelpContent'; import type HelpProps from './types'; function Help({isPaneHidden, closeSidePane}: HelpProps) { diff --git a/src/components/SidePane/Help/index.ios.tsx b/src/components/SidePane/HelpModal/index.ios.tsx similarity index 89% rename from src/components/SidePane/Help/index.ios.tsx rename to src/components/SidePane/HelpModal/index.ios.tsx index 613040f1251c0..d249bdf316a9c 100644 --- a/src/components/SidePane/Help/index.ios.tsx +++ b/src/components/SidePane/HelpModal/index.ios.tsx @@ -1,7 +1,7 @@ import React from 'react'; import Modal from '@components/Modal'; +import HelpContent from '@components/SidePane/HelpComponents/HelpContent'; import CONST from '@src/CONST'; -import HelpContent from './HelpContent'; import type HelpProps from './types'; function Help({isPaneHidden, closeSidePane}: HelpProps) { diff --git a/src/components/SidePane/Help/index.tsx b/src/components/SidePane/HelpModal/index.tsx similarity index 94% rename from src/components/SidePane/Help/index.tsx rename to src/components/SidePane/HelpModal/index.tsx index 659ad52ba4256..eb4d6cf0ca2bc 100644 --- a/src/components/SidePane/Help/index.tsx +++ b/src/components/SidePane/HelpModal/index.tsx @@ -6,14 +6,14 @@ import {useOnyx} from 'react-native-onyx'; // Modal from react-native can't be used here, as it would block interactions with the rest of the app import ModalPortal from 'react-native-web/dist/exports/Modal/ModalPortal'; import FocusTrapForModal from '@components/FocusTrap/FocusTrapForModal'; -import SidePaneOverlay from '@components/SidePane/SidePaneOverlay'; +import HelpContent from '@components/SidePane/HelpComponents/HelpContent'; +import HelpOverlay from '@components/SidePane/HelpComponents/HelpOverlay'; import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useSafeAreaPaddings from '@hooks/useSafeAreaPaddings'; import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import HelpContent from './HelpContent'; import type HelpProps from './types'; function Help({sidePaneTranslateX, closeSidePane, shouldHideSidePaneBackdrop}: HelpProps) { @@ -60,7 +60,7 @@ function Help({sidePaneTranslateX, closeSidePane, shouldHideSidePaneBackdrop}: H {!shouldHideSidePaneBackdrop && ( - diff --git a/src/components/SidePane/Help/types.ts b/src/components/SidePane/HelpModal/types.ts similarity index 100% rename from src/components/SidePane/Help/types.ts rename to src/components/SidePane/HelpModal/types.ts diff --git a/src/components/SidePane/index.tsx b/src/components/SidePane/index.tsx index 715135a33468e..73aeab56434fa 100644 --- a/src/components/SidePane/index.tsx +++ b/src/components/SidePane/index.tsx @@ -1,6 +1,6 @@ import React from 'react'; import useSidePane from '@hooks/useSidePane'; -import Help from './Help'; +import Help from './HelpModal'; function SidePane() { const {shouldHideSidePane, isPaneHidden, sidePaneTranslateX, shouldHideSidePaneBackdrop, closeSidePane} = useSidePane(); diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx index ec77fa771bf86..53e67ea950ade 100644 --- a/src/pages/home/HeaderView.tsx +++ b/src/pages/home/HeaderView.tsx @@ -16,7 +16,7 @@ import ParentNavigationSubtitle from '@components/ParentNavigationSubtitle'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import ReportHeaderSkeletonView from '@components/ReportHeaderSkeletonView'; import SearchButton from '@components/Search/SearchRouter/SearchButton'; -import HelpButton from '@components/SidePane/HelpButton'; +import HelpButton from '@components/SidePane/HelpComponents/HelpButton'; import SubscriptAvatar from '@components/SubscriptAvatar'; import TaskHeaderActionButton from '@components/TaskHeaderActionButton'; import Text from '@components/Text'; From 9e0e1b3936d746d0311c6106988ad3061dd00a14 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 20:36:00 +0100 Subject: [PATCH 21/42] Add HELP_TYPE constant to CONST --- src/CONST.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/CONST.ts b/src/CONST.ts index deb153eafa44c..b7b650d0fc5b3 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1374,6 +1374,16 @@ const CONST = { BILL: 'bill', }, CHAT_TYPE: chatTypes, + HELP_TYPE: { + ...chatTypes, + CHAT_CONCIERGE: 'concierge', + EXPENSE_REPORT: 'expenseReport', + EXPENSE: 'expense', + CHAT: 'chat', + IOU: 'iou', + TASK: 'task', + INVOICE: 'invoice', + }, WORKSPACE_CHAT_ROOMS: { ANNOUNCE: '#announce', ADMINS: '#admins', @@ -2659,6 +2669,14 @@ const CONST = { SCAN: 'scan', PER_DIEM: 'per-diem', }, + EXPENSE_TYPE: { + DISTANCE: 'distance', + MANUAL: 'manual', + SCAN: 'scan', + PER_DIEM: 'per-diem', + EXPENSIFY_CARD: 'expensifyCard', + PENDING_EXPENSIFY_CARD: 'pendingExpensifyCard', + }, REPORT_ACTION_TYPE: { PAY: 'pay', CREATE: 'create', From 4bca2d1f6ef4556e2f8a66d0176c30eea39ef8ef Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 20:36:09 +0100 Subject: [PATCH 22/42] Add getHelpPaneReportType function to determine help pane report type --- src/libs/ReportUtils.ts | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 571b5ec68f4a7..2e6ee54ba0c17 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2047,6 +2047,38 @@ function isMoneyRequestReport(reportOrID: OnyxInputOrEntry | SearchRepor return isIOUReport(report) || isExpenseReport(report); } +/** + * Determines the help pane report type based on the given report. + */ +function getHelpPaneReportType(report: OnyxEntry): ValueOf | undefined { + if (!report) { + return undefined; + } + + if (isConciergeChatReport(report)) { + return CONST.REPORT.HELP_TYPE.CHAT_CONCIERGE; + } + + if (report?.chatType) { + return getChatType(report); + } + + switch (report?.type) { + case CONST.REPORT.TYPE.EXPENSE: + return CONST.REPORT.HELP_TYPE.EXPENSE_REPORT; + case CONST.REPORT.TYPE.CHAT: + return CONST.REPORT.HELP_TYPE.CHAT; + case CONST.REPORT.TYPE.IOU: + return CONST.REPORT.HELP_TYPE.IOU; + case CONST.REPORT.TYPE.INVOICE: + return CONST.REPORT.HELP_TYPE.INVOICE; + case CONST.REPORT.TYPE.TASK: + return CONST.REPORT.HELP_TYPE.TASK; + default: + return undefined; + } +} + /** * Checks if a report contains only Non-Reimbursable transactions */ @@ -9717,6 +9749,7 @@ export { getIntegrationIcon, canBeExported, isExported, + getHelpPaneReportType, hasOnlyNonReimbursableTransactions, getReportLastMessage, getReportLastVisibleActionCreated, From 5e7cae79062bc8e81c54ebc6140266a070912dbd Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 20:36:45 +0100 Subject: [PATCH 23/42] Add getExpenseType function to determine the expense type of a transaction --- src/libs/TransactionUtils/index.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index deb9d96e66f36..1861d1b2b36d0 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -177,6 +177,25 @@ function getRequestType(transaction: OnyxEntry): IOURequestType { return CONST.IOU.REQUEST_TYPE.MANUAL; } +/** + * Determines the expense type of a given transaction. + */ +function getExpenseType(transaction: OnyxEntry): ValueOf | undefined { + if (!transaction) { + return undefined; + } + + if (isExpensifyCardTransaction(transaction)) { + if (isPending(transaction)) { + return CONST.IOU.EXPENSE_TYPE.PENDING_EXPENSIFY_CARD; + } + + return CONST.IOU.EXPENSE_TYPE.EXPENSIFY_CARD; + } + + return getRequestType(transaction); +} + function isManualRequest(transaction: Transaction): boolean { // This is used during the expense creation flow before the transaction has been saved to the server if (lodashHas(transaction, 'iouRequestType')) { @@ -1481,6 +1500,7 @@ export { getUpdatedTransaction, getDescription, getRequestType, + getExpenseType, isManualRequest, isScanRequest, getAmount, From 432bd36c2add5a3a4ef264151718642c8ff9c7bd Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 20:44:20 +0100 Subject: [PATCH 24/42] Enhance substituteRouteParameters to support route parameter overrides --- src/libs/SidePaneUtils.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libs/SidePaneUtils.ts b/src/libs/SidePaneUtils.ts index 1b9cd04cdc4c6..82965763e8705 100644 --- a/src/libs/SidePaneUtils.ts +++ b/src/libs/SidePaneUtils.ts @@ -2,11 +2,12 @@ * This function is used to substitute the route parameters in the route string with the actual parameter names * * Example: - * route: /workspaces/123/rules/456 - * params: {workspaceID: '123', ruleID: '456'} - * result: /workspaces/:workspaceID/rules/:ruleID + * route: /workspaces/123/rules/456/777 + * params: {workspaceID: '123', ruleID: '456', reportID: '777'} + * overrides: {reportID: 'expense'} + * result: /workspaces/:workspaceID/rules/:ruleID/expense */ -function substituteRouteParameters(route: string, params: Record): string { +function substituteRouteParameters(route: string, params: Record, overrides?: Record): string { let updatedRoute = route; function searchAndReplace(obj: Record) { @@ -20,8 +21,9 @@ function substituteRouteParameters(route: string, params: Record); } else if (typeof value === 'string') { + const keyOverride = overrides?.[key]; const regex = new RegExp(`\\b${value}\\b`, 'g'); - updatedRoute = updatedRoute.replace(regex, `:${key}`); + updatedRoute = updatedRoute.replace(regex, keyOverride ?? `:${key}`); } } } From 172cf09171f737b535f01eb38bd5f0d2c5f36bd5 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 20:44:28 +0100 Subject: [PATCH 25/42] Add tests for substituteRouteParameters to validate route parameter overrides --- tests/unit/SidePaneUtilsTest.ts | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/unit/SidePaneUtilsTest.ts b/tests/unit/SidePaneUtilsTest.ts index c94f42c6585e7..99f975df1e193 100644 --- a/tests/unit/SidePaneUtilsTest.ts +++ b/tests/unit/SidePaneUtilsTest.ts @@ -72,4 +72,39 @@ describe('substituteRouteParameters', () => { const params = {reportID: '321'}; expect(substituteRouteParameters(route, params)).toBe('/analysis/:reportID/report321/details'); }); + + test('should apply simple route parameter overrides', () => { + const route = '/workspaces/123/rules/456'; + const params = {workspaceID: '123', ruleID: '456'}; + const overrides = {workspaceID: 'workspace', ruleID: 'rule'}; + expect(substituteRouteParameters(route, params, overrides)).toBe('/workspaces/workspace/rules/rule'); + }); + + test('should apply overrides to repeated parameters in the route', () => { + const route = '/reports/123/report/123/details'; + const params = {id: '123'}; + const overrides = {id: 'expense'}; + expect(substituteRouteParameters(route, params, overrides)).toBe('/reports/expense/report/expense/details'); + }); + + test('should correctly replace a parameter with a multi-segment override', () => { + const route = '/transactions/555/audit/555/details'; + const params = {transactionID: '555'}; + const overrides = {transactionID: ':record/:subtype'}; + expect(substituteRouteParameters(route, params, overrides)).toBe('/transactions/:record/:subtype/audit/:record/:subtype/details'); + }); + + test('should prioritize overrides over default substitution', () => { + const route = '/projects/999/tasks/888'; + const params = {projectID: '999', taskID: '888'}; + const overrides = {taskID: 'task-replace'}; + expect(substituteRouteParameters(route, params, overrides)).toBe('/projects/:projectID/tasks/task-replace'); + }); + + test('should apply overrides even when override matches another route param', () => { + const route = '/workspaces/123/rules/456'; + const params = {workspaceID: '123', ruleID: '456'}; + const overrides = {workspaceID: '456', ruleID: 'rule'}; + expect(substituteRouteParameters(route, params, overrides)).toBe('/workspaces/rule/rules/rule'); + }); }); From d4bda5c76fa438a58dcd7f2b6c6313baefff550b Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 20:45:02 +0100 Subject: [PATCH 26/42] Refactor HelpContent to dynamically change :reportID into report types --- .../SidePane/HelpComponents/HelpContent.tsx | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/components/SidePane/HelpComponents/HelpContent.tsx b/src/components/SidePane/HelpComponents/HelpContent.tsx index b1b4eceb3f344..f1c8118df6f92 100644 --- a/src/components/SidePane/HelpComponents/HelpContent.tsx +++ b/src/components/SidePane/HelpComponents/HelpContent.tsx @@ -1,8 +1,9 @@ import {findFocusedRoute} from '@react-navigation/native'; -import React, {useEffect, useRef} from 'react'; +import React, {useEffect, useMemo, useRef} from 'react'; // Importing from the react-native-gesture-handler package instead of the `components/ScrollView` to fix scroll issue: // https://github.com/react-native-modal/react-native-modal/issues/236 import {ScrollView} from 'react-native-gesture-handler'; +import {useOnyx} from 'react-native-onyx'; import HeaderGap from '@components/HeaderGap'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import getHelpContent from '@components/SidePane/HelpContent/getHelpContent'; @@ -12,7 +13,12 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useRootNavigationState from '@hooks/useRootNavigationState'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; +import {getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils'; +import {getHelpPaneReportType} from '@libs/ReportUtils'; import {substituteRouteParameters} from '@libs/SidePaneUtils'; +import {getExpenseType} from '@libs/TransactionUtils'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; type HelpContentProps = { closeSidePane: (shouldUpdateNarrow?: boolean) => void; @@ -23,11 +29,23 @@ function HelpContent({closeSidePane}: HelpContentProps) { const {translate} = useLocalize(); const {isProduction} = useEnvironment(); const {isExtraLargeScreenWidth} = useResponsiveLayout(); - const route = useRootNavigationState((state) => { - const params = (findFocusedRoute(state)?.params as Record) ?? {}; - const activeRoute = Navigation.getActiveRouteWithoutParams(); - return substituteRouteParameters(activeRoute, params); + + const routeParams = useRootNavigationState((state) => (findFocusedRoute(state)?.params as Record) ?? {}); + const reportID = routeParams.reportID ?? CONST.DEFAULT_NUMBER_ID; + const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); + const [parentReportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.parentReportID ?? CONST.DEFAULT_NUMBER_ID}`, { + canEvict: false, }); + const parentReportAction = report?.parentReportActionID ? parentReportActions?.[report.parentReportActionID] : undefined; + const linkedTransactionID = useMemo(() => (isMoneyRequestAction(parentReportAction) ? getOriginalMessage(parentReportAction)?.IOUTransactionID : undefined), [parentReportAction]); + const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${linkedTransactionID ?? CONST.DEFAULT_NUMBER_ID}`); + + const route = useMemo(() => { + const expenseType = getExpenseType(transaction); + const overrides = {reportID: expenseType ? `:${CONST.REPORT.HELP_TYPE.EXPENSE}/:${expenseType}` : `:${getHelpPaneReportType(report)}`}; + const activeRoute = Navigation.getActiveRouteWithoutParams(); + return substituteRouteParameters(activeRoute, routeParams, overrides); + }, [transaction, report, routeParams]); const wasPreviousNarrowScreen = useRef(!isExtraLargeScreenWidth); useEffect(() => { From 0938d7b08b0e5ec6491ea44383ec885ef3d3ab0b Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 20:53:30 +0100 Subject: [PATCH 27/42] Add help content mappings for various report types and expense categories --- .../SidePane/HelpContent/getHelpContent.tsx | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/components/SidePane/HelpContent/getHelpContent.tsx b/src/components/SidePane/HelpContent/getHelpContent.tsx index c2d18d876c47f..0d18d55d6b95c 100644 --- a/src/components/SidePane/HelpContent/getHelpContent.tsx +++ b/src/components/SidePane/HelpContent/getHelpContent.tsx @@ -4,6 +4,7 @@ import React from 'react'; import {View} from 'react-native'; import Text from '@components/Text'; import type {ThemeStyles} from '@styles/index'; +import CONST from '@src/CONST'; import Chat from './chat'; import Search from './search'; import Settings from './settings'; @@ -28,6 +29,40 @@ const helpContentMap: HelpContent = { children: { r: { content: Chat, + children: { + [`:${CONST.REPORT.HELP_TYPE.POLICY_ADMINS}`]: { + content: () => null, + }, + [`:${CONST.REPORT.HELP_TYPE.POLICY_EXPENSE_CHAT}`]: { + content: () => null, + }, + [`:${CONST.REPORT.HELP_TYPE.CHAT_CONCIERGE}`]: { + content: () => null, + }, + [`:${CONST.REPORT.HELP_TYPE.EXPENSE_REPORT}`]: { + content: () => null, + }, + [`:${CONST.REPORT.HELP_TYPE.EXPENSE}`]: { + content: () => null, + children: { + [`:${CONST.IOU.EXPENSE_TYPE.DISTANCE}`]: { + content: () => null, + }, + [`:${CONST.IOU.EXPENSE_TYPE.MANUAL}`]: { + content: () => null, + }, + [`:${CONST.IOU.EXPENSE_TYPE.SCAN}`]: { + content: () => null, + }, + [`:${CONST.IOU.EXPENSE_TYPE.EXPENSIFY_CARD}`]: { + content: () => null, + }, + [`:${CONST.IOU.EXPENSE_TYPE.PENDING_EXPENSIFY_CARD}`]: { + content: () => null, + }, + }, + }, + }, }, home: { content: Chat, From 8bccc1c4b39cd6b8ac8fac63ddff48ca1e8b47bd Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 20:58:27 +0100 Subject: [PATCH 28/42] Move chat to index --- src/components/SidePane/HelpContent/{chat.tsx => chat/index.tsx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/components/SidePane/HelpContent/{chat.tsx => chat/index.tsx} (100%) diff --git a/src/components/SidePane/HelpContent/chat.tsx b/src/components/SidePane/HelpContent/chat/index.tsx similarity index 100% rename from src/components/SidePane/HelpContent/chat.tsx rename to src/components/SidePane/HelpContent/chat/index.tsx From 7b83975c1097bef7a2f63c59d8421b973beb2066 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 20:58:36 +0100 Subject: [PATCH 29/42] Add AdminsChatRoom component --- .../SidePane/HelpContent/chat/admins.tsx | 39 +++++++++++++++++++ .../SidePane/HelpContent/getHelpContent.tsx | 3 +- 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 src/components/SidePane/HelpContent/chat/admins.tsx diff --git a/src/components/SidePane/HelpContent/chat/admins.tsx b/src/components/SidePane/HelpContent/chat/admins.tsx new file mode 100644 index 0000000000000..4c64a80ad0ed1 --- /dev/null +++ b/src/components/SidePane/HelpContent/chat/admins.tsx @@ -0,0 +1,39 @@ +/* eslint-disable react/no-unescaped-entities */ +import React from 'react'; +import BulletList from '@components/SidePane/HelpComponents/HelpBulletList'; +import ExpandableHelp from '@components/SidePane/HelpComponents/HelpExpandable'; +import Text from '@components/Text'; +import type {ThemeStyles} from '@styles/index'; + +function AdminsChatRoom({styles}: {styles: ThemeStyles}) { + return ( + <> + #admins + + + 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. + , + ]} + /> + + + ); +} + +export default AdminsChatRoom; diff --git a/src/components/SidePane/HelpContent/getHelpContent.tsx b/src/components/SidePane/HelpContent/getHelpContent.tsx index 0d18d55d6b95c..70c90e9653ad2 100644 --- a/src/components/SidePane/HelpContent/getHelpContent.tsx +++ b/src/components/SidePane/HelpContent/getHelpContent.tsx @@ -6,6 +6,7 @@ import Text from '@components/Text'; import type {ThemeStyles} from '@styles/index'; import CONST from '@src/CONST'; import Chat from './chat'; +import AdminsChatRoom from './chat/admins'; import Search from './search'; import Settings from './settings'; import Workspaces from './settings/workspaces'; @@ -31,7 +32,7 @@ const helpContentMap: HelpContent = { content: Chat, children: { [`:${CONST.REPORT.HELP_TYPE.POLICY_ADMINS}`]: { - content: () => null, + content: AdminsChatRoom, }, [`:${CONST.REPORT.HELP_TYPE.POLICY_EXPENSE_CHAT}`]: { content: () => null, From 5773223dae63deae503c8986654284276a239231 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 21:00:35 +0100 Subject: [PATCH 30/42] Add Concierge component --- .../SidePane/HelpContent/chat/concierge.tsx | 18 ++++++++++++++++++ .../SidePane/HelpContent/getHelpContent.tsx | 7 ++++--- 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 src/components/SidePane/HelpContent/chat/concierge.tsx diff --git a/src/components/SidePane/HelpContent/chat/concierge.tsx b/src/components/SidePane/HelpContent/chat/concierge.tsx new file mode 100644 index 0000000000000..ec0b86e19d7a7 --- /dev/null +++ b/src/components/SidePane/HelpContent/chat/concierge.tsx @@ -0,0 +1,18 @@ +/* eslint-disable react/no-unescaped-entities */ +import React from 'react'; +import Text from '@components/Text'; +import type {ThemeStyles} from '@styles/index'; + +function Concierge({styles}: {styles: ThemeStyles}) { + return ( + <> + 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! + + + ); +} + +export default Concierge; diff --git a/src/components/SidePane/HelpContent/getHelpContent.tsx b/src/components/SidePane/HelpContent/getHelpContent.tsx index 70c90e9653ad2..ce65242835aa1 100644 --- a/src/components/SidePane/HelpContent/getHelpContent.tsx +++ b/src/components/SidePane/HelpContent/getHelpContent.tsx @@ -7,6 +7,7 @@ import type {ThemeStyles} from '@styles/index'; import CONST from '@src/CONST'; import Chat from './chat'; import AdminsChatRoom from './chat/admins'; +import Concierge from './chat/concierge'; import Search from './search'; import Settings from './settings'; import Workspaces from './settings/workspaces'; @@ -34,10 +35,10 @@ const helpContentMap: HelpContent = { [`:${CONST.REPORT.HELP_TYPE.POLICY_ADMINS}`]: { content: AdminsChatRoom, }, - [`:${CONST.REPORT.HELP_TYPE.POLICY_EXPENSE_CHAT}`]: { - content: () => null, - }, [`:${CONST.REPORT.HELP_TYPE.CHAT_CONCIERGE}`]: { + content: Concierge, + }, + [`:${CONST.REPORT.HELP_TYPE.POLICY_EXPENSE_CHAT}`]: { content: () => null, }, [`:${CONST.REPORT.HELP_TYPE.EXPENSE_REPORT}`]: { From b3c3600f6726770bb870d894af2b8c64431c1df9 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 21:04:05 +0100 Subject: [PATCH 31/42] Add WorkspaceChat component --- .../SidePane/HelpContent/chat/workspace.tsx | 36 +++++++++++++++++++ .../SidePane/HelpContent/getHelpContent.tsx | 3 +- 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 src/components/SidePane/HelpContent/chat/workspace.tsx diff --git a/src/components/SidePane/HelpContent/chat/workspace.tsx b/src/components/SidePane/HelpContent/chat/workspace.tsx new file mode 100644 index 0000000000000..d9c4d430fe354 --- /dev/null +++ b/src/components/SidePane/HelpContent/chat/workspace.tsx @@ -0,0 +1,36 @@ +/* eslint-disable react/no-unescaped-entities */ +import React from 'react'; +import BulletList from '@components/SidePane/HelpComponents/HelpBulletList'; +import ExpandableHelp from '@components/SidePane/HelpComponents/HelpExpandable'; +import Text from '@components/Text'; +import type {ThemeStyles} from '@styles/index'; + +function WorkspaceChat({styles}: {styles: ThemeStyles}) { + return ( + <> + Workspace + + + + 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. + + ); +} + +export default WorkspaceChat; diff --git a/src/components/SidePane/HelpContent/getHelpContent.tsx b/src/components/SidePane/HelpContent/getHelpContent.tsx index ce65242835aa1..c276ec9702184 100644 --- a/src/components/SidePane/HelpContent/getHelpContent.tsx +++ b/src/components/SidePane/HelpContent/getHelpContent.tsx @@ -8,6 +8,7 @@ import CONST from '@src/CONST'; import Chat from './chat'; import AdminsChatRoom from './chat/admins'; import Concierge from './chat/concierge'; +import WorkspaceChat from './chat/workspace'; import Search from './search'; import Settings from './settings'; import Workspaces from './settings/workspaces'; @@ -39,7 +40,7 @@ const helpContentMap: HelpContent = { content: Concierge, }, [`:${CONST.REPORT.HELP_TYPE.POLICY_EXPENSE_CHAT}`]: { - content: () => null, + content: WorkspaceChat, }, [`:${CONST.REPORT.HELP_TYPE.EXPENSE_REPORT}`]: { content: () => null, From 9fa6727f0c52d1546bec7066f9cdc60e41a3be08 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 21:06:05 +0100 Subject: [PATCH 32/42] Add ExpenseReportChat component --- .../HelpContent/chat/expenseReport.tsx | 30 +++++++++++++++++++ .../SidePane/HelpContent/getHelpContent.tsx | 3 +- 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 src/components/SidePane/HelpContent/chat/expenseReport.tsx diff --git a/src/components/SidePane/HelpContent/chat/expenseReport.tsx b/src/components/SidePane/HelpContent/chat/expenseReport.tsx new file mode 100644 index 0000000000000..39cfab55eebb1 --- /dev/null +++ b/src/components/SidePane/HelpContent/chat/expenseReport.tsx @@ -0,0 +1,30 @@ +/* eslint-disable react/no-unescaped-entities */ +import React from 'react'; +import BulletList from '@components/SidePane/HelpComponents/HelpBulletList'; +import ExpandableHelp from '@components/SidePane/HelpComponents/HelpExpandable'; +import Text from '@components/Text'; +import type {ThemeStyles} from '@styles/index'; + +function ExpenseReportChat({styles}: {styles: ThemeStyles}) { + return ( + <> + Expense Report + + 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. + + ); +} + +export default ExpenseReportChat; diff --git a/src/components/SidePane/HelpContent/getHelpContent.tsx b/src/components/SidePane/HelpContent/getHelpContent.tsx index c276ec9702184..833cca530ffca 100644 --- a/src/components/SidePane/HelpContent/getHelpContent.tsx +++ b/src/components/SidePane/HelpContent/getHelpContent.tsx @@ -8,6 +8,7 @@ import CONST from '@src/CONST'; import Chat from './chat'; import AdminsChatRoom from './chat/admins'; import Concierge from './chat/concierge'; +import ExpenseReportChat from './chat/expenseReport'; import WorkspaceChat from './chat/workspace'; import Search from './search'; import Settings from './settings'; @@ -43,7 +44,7 @@ const helpContentMap: HelpContent = { content: WorkspaceChat, }, [`:${CONST.REPORT.HELP_TYPE.EXPENSE_REPORT}`]: { - content: () => null, + content: ExpenseReportChat, }, [`:${CONST.REPORT.HELP_TYPE.EXPENSE}`]: { content: () => null, From fbe6aee04011b09288732bc143ba65b6cd387c3d Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 21:09:56 +0100 Subject: [PATCH 33/42] Add ExpenseChat component --- .../HelpContent/chat/expense/index.tsx | 43 +++++++++++++++++++ .../SidePane/HelpContent/getHelpContent.tsx | 3 +- 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 src/components/SidePane/HelpContent/chat/expense/index.tsx diff --git a/src/components/SidePane/HelpContent/chat/expense/index.tsx b/src/components/SidePane/HelpContent/chat/expense/index.tsx new file mode 100644 index 0000000000000..05b2621ab54f0 --- /dev/null +++ b/src/components/SidePane/HelpContent/chat/expense/index.tsx @@ -0,0 +1,43 @@ +/* eslint-disable react/no-unescaped-entities */ +import React from 'react'; +import BulletList from '@components/SidePane/HelpComponents/HelpBulletList'; +import ExpandableHelp from '@components/SidePane/HelpComponents/HelpExpandable'; +import Text from '@components/Text'; +import type {ThemeStyles} from '@styles/index'; + +function ExpenseChat({styles}: {styles: ThemeStyles}) { + return ( + <> + Expense + + + 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. + + ); +} + +export default ExpenseChat; diff --git a/src/components/SidePane/HelpContent/getHelpContent.tsx b/src/components/SidePane/HelpContent/getHelpContent.tsx index 833cca530ffca..9c9873bb6d5e7 100644 --- a/src/components/SidePane/HelpContent/getHelpContent.tsx +++ b/src/components/SidePane/HelpContent/getHelpContent.tsx @@ -8,6 +8,7 @@ import CONST from '@src/CONST'; import Chat from './chat'; import AdminsChatRoom from './chat/admins'; import Concierge from './chat/concierge'; +import ExpenseChat from './chat/expense'; import ExpenseReportChat from './chat/expenseReport'; import WorkspaceChat from './chat/workspace'; import Search from './search'; @@ -47,7 +48,7 @@ const helpContentMap: HelpContent = { content: ExpenseReportChat, }, [`:${CONST.REPORT.HELP_TYPE.EXPENSE}`]: { - content: () => null, + content: ExpenseChat, children: { [`:${CONST.IOU.EXPENSE_TYPE.DISTANCE}`]: { content: () => null, From 209d51f594e93e2f707313d143029984c9191e4d Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 21:12:58 +0100 Subject: [PATCH 34/42] Add ManualExpense component --- .../HelpContent/chat/expense/manual.tsx | 17 +++++++++++++++++ .../SidePane/HelpContent/getHelpContent.tsx | 6 ++---- 2 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 src/components/SidePane/HelpContent/chat/expense/manual.tsx diff --git a/src/components/SidePane/HelpContent/chat/expense/manual.tsx b/src/components/SidePane/HelpContent/chat/expense/manual.tsx new file mode 100644 index 0000000000000..e8bbde72251a8 --- /dev/null +++ b/src/components/SidePane/HelpContent/chat/expense/manual.tsx @@ -0,0 +1,17 @@ +/* eslint-disable react/no-unescaped-entities */ +import React from 'react'; +import Text from '@components/Text'; +import type {ThemeStyles} from '@styles/index'; + +function ManualExpense({styles}: {styles: ThemeStyles}) { + return ( + <> + 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. + + + ); +} + +export default ManualExpense; diff --git a/src/components/SidePane/HelpContent/getHelpContent.tsx b/src/components/SidePane/HelpContent/getHelpContent.tsx index 9c9873bb6d5e7..f8b1ee4f8c5f6 100644 --- a/src/components/SidePane/HelpContent/getHelpContent.tsx +++ b/src/components/SidePane/HelpContent/getHelpContent.tsx @@ -9,6 +9,7 @@ import Chat from './chat'; import AdminsChatRoom from './chat/admins'; import Concierge from './chat/concierge'; import ExpenseChat from './chat/expense'; +import ManualExpense from './chat/expense/manual'; import ExpenseReportChat from './chat/expenseReport'; import WorkspaceChat from './chat/workspace'; import Search from './search'; @@ -50,11 +51,8 @@ const helpContentMap: HelpContent = { [`:${CONST.REPORT.HELP_TYPE.EXPENSE}`]: { content: ExpenseChat, children: { - [`:${CONST.IOU.EXPENSE_TYPE.DISTANCE}`]: { - content: () => null, - }, [`:${CONST.IOU.EXPENSE_TYPE.MANUAL}`]: { - content: () => null, + content: ManualExpense, }, [`:${CONST.IOU.EXPENSE_TYPE.SCAN}`]: { content: () => null, From 530f9ee73155ab17435fe8ef7ec4f860bf6451bd Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 21:13:17 +0100 Subject: [PATCH 35/42] Add ScanExpense component --- .../SidePane/HelpContent/chat/expense/scan.tsx | 15 +++++++++++++++ .../SidePane/HelpContent/getHelpContent.tsx | 3 ++- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 src/components/SidePane/HelpContent/chat/expense/scan.tsx diff --git a/src/components/SidePane/HelpContent/chat/expense/scan.tsx b/src/components/SidePane/HelpContent/chat/expense/scan.tsx new file mode 100644 index 0000000000000..14af094c38c2c --- /dev/null +++ b/src/components/SidePane/HelpContent/chat/expense/scan.tsx @@ -0,0 +1,15 @@ +/* eslint-disable react/no-unescaped-entities */ +import React from 'react'; +import Text from '@components/Text'; +import type {ThemeStyles} from '@styles/index'; + +function ScanExpense({styles}: {styles: ThemeStyles}) { + return ( + <> + Scanned + A "scanned" expense was created by extracting the relevant details using the Concierge AI. + + ); +} + +export default ScanExpense; diff --git a/src/components/SidePane/HelpContent/getHelpContent.tsx b/src/components/SidePane/HelpContent/getHelpContent.tsx index f8b1ee4f8c5f6..9e5c18d7074ad 100644 --- a/src/components/SidePane/HelpContent/getHelpContent.tsx +++ b/src/components/SidePane/HelpContent/getHelpContent.tsx @@ -10,6 +10,7 @@ import AdminsChatRoom from './chat/admins'; import Concierge from './chat/concierge'; import ExpenseChat from './chat/expense'; import ManualExpense from './chat/expense/manual'; +import ScanExpense from './chat/expense/scan'; import ExpenseReportChat from './chat/expenseReport'; import WorkspaceChat from './chat/workspace'; import Search from './search'; @@ -55,7 +56,7 @@ const helpContentMap: HelpContent = { content: ManualExpense, }, [`:${CONST.IOU.EXPENSE_TYPE.SCAN}`]: { - content: () => null, + content: ScanExpense, }, [`:${CONST.IOU.EXPENSE_TYPE.EXPENSIFY_CARD}`]: { content: () => null, From 5bb5bac51e8a47ab5c192c6c173c576c815d62c4 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 21:13:45 +0100 Subject: [PATCH 36/42] Add ExpensifyCardExpense component to HelpContent --- .../HelpContent/chat/expense/expensifyCard.tsx | 16 ++++++++++++++++ .../SidePane/HelpContent/getHelpContent.tsx | 3 ++- 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 src/components/SidePane/HelpContent/chat/expense/expensifyCard.tsx diff --git a/src/components/SidePane/HelpContent/chat/expense/expensifyCard.tsx b/src/components/SidePane/HelpContent/chat/expense/expensifyCard.tsx new file mode 100644 index 0000000000000..ab6656536fe51 --- /dev/null +++ b/src/components/SidePane/HelpContent/chat/expense/expensifyCard.tsx @@ -0,0 +1,16 @@ +/* eslint-disable react/no-unescaped-entities */ +import React from 'react'; +import Text from '@components/Text'; +import type {ThemeStyles} from '@styles/index'; + +function ExpensifyCardExpense({styles}: {styles: ThemeStyles}) { + return ( + <> + 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. + + ); +} + +export default ExpensifyCardExpense; diff --git a/src/components/SidePane/HelpContent/getHelpContent.tsx b/src/components/SidePane/HelpContent/getHelpContent.tsx index 9e5c18d7074ad..3d5a2fec7df39 100644 --- a/src/components/SidePane/HelpContent/getHelpContent.tsx +++ b/src/components/SidePane/HelpContent/getHelpContent.tsx @@ -9,6 +9,7 @@ import Chat from './chat'; import AdminsChatRoom from './chat/admins'; import Concierge from './chat/concierge'; import ExpenseChat from './chat/expense'; +import ExpensifyCardExpense from './chat/expense/expensifyCard'; import ManualExpense from './chat/expense/manual'; import ScanExpense from './chat/expense/scan'; import ExpenseReportChat from './chat/expenseReport'; @@ -59,7 +60,7 @@ const helpContentMap: HelpContent = { content: ScanExpense, }, [`:${CONST.IOU.EXPENSE_TYPE.EXPENSIFY_CARD}`]: { - content: () => null, + content: ExpensifyCardExpense, }, [`:${CONST.IOU.EXPENSE_TYPE.PENDING_EXPENSIFY_CARD}`]: { content: () => null, From ccdf0031678b96a75889116d8e5b734e1b2f9461 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 20 Mar 2025 21:14:35 +0100 Subject: [PATCH 37/42] Add ExpensifyCardPendingExpense --- .../chat/expense/pendingExpensifyCard.tsx | 20 +++++++++++++++++++ .../SidePane/HelpContent/getHelpContent.tsx | 3 ++- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 src/components/SidePane/HelpContent/chat/expense/pendingExpensifyCard.tsx diff --git a/src/components/SidePane/HelpContent/chat/expense/pendingExpensifyCard.tsx b/src/components/SidePane/HelpContent/chat/expense/pendingExpensifyCard.tsx new file mode 100644 index 0000000000000..d416d1df2ddd3 --- /dev/null +++ b/src/components/SidePane/HelpContent/chat/expense/pendingExpensifyCard.tsx @@ -0,0 +1,20 @@ +/* eslint-disable react/no-unescaped-entities */ +import React from 'react'; +import Text from '@components/Text'; +import type {ThemeStyles} from '@styles/index'; + +function ExpensifyCardPendingExpense({styles}: {styles: ThemeStyles}) { + return ( + <> + 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. + + ); +} + +export default ExpensifyCardPendingExpense; diff --git a/src/components/SidePane/HelpContent/getHelpContent.tsx b/src/components/SidePane/HelpContent/getHelpContent.tsx index 3d5a2fec7df39..cf8e934e3ba54 100644 --- a/src/components/SidePane/HelpContent/getHelpContent.tsx +++ b/src/components/SidePane/HelpContent/getHelpContent.tsx @@ -11,6 +11,7 @@ import Concierge from './chat/concierge'; import ExpenseChat from './chat/expense'; import ExpensifyCardExpense from './chat/expense/expensifyCard'; import ManualExpense from './chat/expense/manual'; +import ExpensifyCardPendingExpense from './chat/expense/pendingExpensifyCard'; import ScanExpense from './chat/expense/scan'; import ExpenseReportChat from './chat/expenseReport'; import WorkspaceChat from './chat/workspace'; @@ -63,7 +64,7 @@ const helpContentMap: HelpContent = { content: ExpensifyCardExpense, }, [`:${CONST.IOU.EXPENSE_TYPE.PENDING_EXPENSIFY_CARD}`]: { - content: () => null, + content: ExpensifyCardPendingExpense, }, }, }, From 393df94360fbf314b9e3b5c93e3c65a2167655e8 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 21 Mar 2025 13:28:10 +0100 Subject: [PATCH 38/42] Add key prop to DiagnosticData component in HelpContent --- src/components/SidePane/HelpContent/getHelpContent.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/SidePane/HelpContent/getHelpContent.tsx b/src/components/SidePane/HelpContent/getHelpContent.tsx index cf8e934e3ba54..b3af376048f1b 100644 --- a/src/components/SidePane/HelpContent/getHelpContent.tsx +++ b/src/components/SidePane/HelpContent/getHelpContent.tsx @@ -163,6 +163,7 @@ function getHelpContent(styles: ThemeStyles, route: string, isProduction: boolea return ( Date: Fri, 21 Mar 2025 13:30:48 +0100 Subject: [PATCH 39/42] Add keyboard shortcut for DEBUG action in HelpModal --- src/components/SidePane/HelpModal/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/SidePane/HelpModal/index.tsx b/src/components/SidePane/HelpModal/index.tsx index eb4d6cf0ca2bc..1eefad9761eda 100644 --- a/src/components/SidePane/HelpModal/index.tsx +++ b/src/components/SidePane/HelpModal/index.tsx @@ -37,6 +37,7 @@ function Help({sidePaneTranslateX, closeSidePane, shouldHideSidePaneBackdrop}: H useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.SEARCH, onCloseSidePaneOnSmallScreens, {shouldBubble: true}); useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.NEW_CHAT, onCloseSidePaneOnSmallScreens, {shouldBubble: true}); useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.SHORTCUTS, onCloseSidePaneOnSmallScreens, {shouldBubble: true}); + useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.DEBUG, onCloseSidePaneOnSmallScreens, {shouldBubble: true}); // Web back button: push history state and close side pane on popstate useEffect(() => { From 613778561f5c0d54210b5b83f6dca02da171c9d6 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 21 Mar 2025 15:10:37 +0100 Subject: [PATCH 40/42] Fix onyx warning in an edge case --- src/components/SidePane/HelpComponents/HelpContent.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/SidePane/HelpComponents/HelpContent.tsx b/src/components/SidePane/HelpComponents/HelpContent.tsx index f1c8118df6f92..02ad6db4e5600 100644 --- a/src/components/SidePane/HelpComponents/HelpContent.tsx +++ b/src/components/SidePane/HelpComponents/HelpContent.tsx @@ -31,9 +31,10 @@ function HelpContent({closeSidePane}: HelpContentProps) { const {isExtraLargeScreenWidth} = useResponsiveLayout(); const routeParams = useRootNavigationState((state) => (findFocusedRoute(state)?.params as Record) ?? {}); - const reportID = routeParams.reportID ?? CONST.DEFAULT_NUMBER_ID; + const reportID = routeParams.reportID || CONST.DEFAULT_NUMBER_ID; const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); - const [parentReportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.parentReportID ?? CONST.DEFAULT_NUMBER_ID}`, { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const [parentReportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.parentReportID || CONST.DEFAULT_NUMBER_ID}`, { canEvict: false, }); const parentReportAction = report?.parentReportActionID ? parentReportActions?.[report.parentReportActionID] : undefined; From 858d2a263b949a16a792edecf0da935a99e9d543 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Sat, 22 Mar 2025 15:01:17 +0100 Subject: [PATCH 41/42] Update modal visibility logic in useSidePane hook to include CENTERED_SMALL type --- src/hooks/useSidePane.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/hooks/useSidePane.ts b/src/hooks/useSidePane.ts index 0239e0791f3db..d645f9bd55faa 100644 --- a/src/hooks/useSidePane.ts +++ b/src/hooks/useSidePane.ts @@ -31,11 +31,14 @@ function useSidePane() { const [sidePaneNVP] = useOnyx(ONYXKEYS.NVP_SIDE_PANE); const [language] = useOnyx(ONYXKEYS.NVP_PREFERRED_LOCALE); - const [isAttachmentModalVisible = false] = useOnyx(ONYXKEYS.MODAL, { - selector: (modal) => modal?.type === CONST.MODAL.MODAL_TYPE.CENTERED_SWIPABLE_TO_RIGHT || modal?.type === CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE, + const [isModalCenteredVisible = false] = useOnyx(ONYXKEYS.MODAL, { + selector: (modal) => + modal?.type === CONST.MODAL.MODAL_TYPE.CENTERED_SWIPABLE_TO_RIGHT || + modal?.type === CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE || + modal?.type === CONST.MODAL.MODAL_TYPE.CENTERED_SMALL, }); const isLanguageUnsupported = language !== CONST.LOCALES.EN; - const isPaneHidden = isSidePaneHidden(sidePaneNVP, isExtraLargeScreenWidth) || isLanguageUnsupported || isAttachmentModalVisible; + const isPaneHidden = isSidePaneHidden(sidePaneNVP, isExtraLargeScreenWidth) || isLanguageUnsupported || isModalCenteredVisible; const sidePaneWidth = shouldUseNarrowLayout ? windowWidth : variables.sideBarWidth; const shouldApplySidePaneOffset = isExtraLargeScreenWidth && !isPaneHidden; From 6e34704d406b684c40743c906258fe6d72d31a4b Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Sat, 22 Mar 2025 15:03:29 +0100 Subject: [PATCH 42/42] Remove keyboard shortcut for DEBUG action in HelpModal --- src/components/SidePane/HelpModal/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/SidePane/HelpModal/index.tsx b/src/components/SidePane/HelpModal/index.tsx index 1eefad9761eda..eb4d6cf0ca2bc 100644 --- a/src/components/SidePane/HelpModal/index.tsx +++ b/src/components/SidePane/HelpModal/index.tsx @@ -37,7 +37,6 @@ function Help({sidePaneTranslateX, closeSidePane, shouldHideSidePaneBackdrop}: H useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.SEARCH, onCloseSidePaneOnSmallScreens, {shouldBubble: true}); useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.NEW_CHAT, onCloseSidePaneOnSmallScreens, {shouldBubble: true}); useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.SHORTCUTS, onCloseSidePaneOnSmallScreens, {shouldBubble: true}); - useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.DEBUG, onCloseSidePaneOnSmallScreens, {shouldBubble: true}); // Web back button: push history state and close side pane on popstate useEffect(() => {