Skip to content

Commit 0e18a5d

Browse files
committed
Merge branch 'main' of github.com:Expensify/App into snyk-fix-b51ba1095f2dbeceede6adc51fe1f017
2 parents b11ed9e + e4cdb42 commit 0e18a5d

22 files changed

+178
-59
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
* [Expensify Code of Conduct](CODE_OF_CONDUCT.md)
2828
* [Contributor License Agreement](contributingGuides/CLA.md)
2929
* [React StrictMode](contributingGuides/STRICT_MODE.md)
30+
* [Left Hand Navigation(LHN)](contributingGuides/LEFT_HAND_NAVIGATION.md)
3031

3132
----
3233

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
## OVERVIEW
2+
3+
The Left Hand Navigation (LHN) is designed to show different types of reports based on their status, user settings, and specific conditions. Each report type has unique visual indicators and sorting rules to help users quickly identify and prioritize their tasks.
4+
5+
### Types of report displayed in the LHN
6+
7+
The following outlines the expected behavior regarding which reports are displayed in the LHN:
8+
9+
- The report currently being viewed by the user is highlighted as the active report in the LHN, making it easy for users to locate their focus point within the navigation.
10+
If a report has unresolved issues, like an unapproved expense or outstanding violations, it will display a red dot next to it, indicating urgent action is required. These reports are displayed at the top of the LHN list (under pinned chats) and sorted alphabetically by report name for easy access.
11+
- Reports that need user action, such as responding to a message that mentions them, completing an assigned task, or addressing an expense, will display a green dot next to them. Additionally, if a system or concierge message indicates a trial period has expired and a payment method is missing, it will prompt the user with a similar green dot. This visual indicator helps users quickly identify where their attention is required.
12+
- If a user has started drafting a comment in a report, a pencil icon as indicator appears next to it in the LHN, letting users know there is an incomplete draft. These reports are sorted alphabetically by report name.
13+
- Pinned reports are always displayed at the top of the LHN list and are sorted alphabetically by name, giving quick access to reports the user wants to keep top-of-mind.
14+
- When the user has focus mode enabled, unread chat messages will display in bold in the LHN. This also applies to reports where notifications are hidden. Unread chats in focus mode are sorted alphabetically by report name to help users locate them more easily.
15+
- Archived reports are displayed in the LHN when the user is in default mode. These reports are shown with an indication that they are archived and are sorted by the date of the last visible action, with the most recent appearing first.
16+
- Self-DM messages will now be displayed in LHN. This allows users who want to track their own notes or messages in the LHN to do so without needing to look elsewhere.
17+
18+
### Types of report excluded from the LHN
19+
20+
Certain reports are excluded from the LHN to avoid clutter and to focus on relevant content for the user:
21+
22+
- Reports that are explicitly marked as hidden.
23+
- Reports with no participants are not displayed, as they lack meaningful content.
24+
- If the user does not have permission to access a report (due to policy restrictions), it will not be shown.
25+
- Transaction threads that contain only one transaction are excluded.
26+
- If a report is an empty chat, unless it's a report user is actively looking at.
27+
- For users with domain-based email addresses, reports are hidden if the includeDomainEmail setting is disabled.
28+
- Reports with a parent message pending deletion.
29+
- When focus mode is enabled and there are no unread messages.
30+
31+
### Sorting priorities for displayed report groups
32+
33+
1. Pinned, RBR and attention-required (GBR) reports:
34+
- Always sorted alphabetically by report name.
35+
2. Error reports:
36+
- Sorted alphabetically by report name.
37+
3. Draft reports:
38+
- Sorted alphabetically by report name.
39+
4. Non-Archived reports:
40+
- In default mode, these are sorted by the lastVisibleActionCreated date, so the most recently updated reports appear first.
41+
- In focus mode, these reports are sorted alphabetically by name for quicker navigation.
42+
5. Archived eports:
43+
- In default mode, these are sorted by lastVisibleActionCreated, with recent reports displayed first.
44+
- In focus mode, archived reports are sorted alphabetically by name.

docs/articles/expensify-classic/connections/quickbooks-online/Quickbooks-Online-Troubleshooting.md

+14-14
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ This error occurs when the account applied as a category to the expense in Expen
1818
5. Click on the pencil icon on the right to check if you have "In multiple accounts" selected:
1919
6. If "In multiple accounts" is selected, go to Chart of Accounts and click Edit for the account in question.
2020
7. Check the billable option and select an income account within your chart of accounts
21-
8. Sync your QuickBooks Online connection in Settings > Workspaces > [workspace name] > Connections.
21+
8. Sync your QuickBooks Online connection in Settings > Workspaces > Workspace Name > Connections.
2222
9. Open the report and click the Export to button and then the QuickBooks Online option.
2323

2424
# ExpensiError QBO046: Feature Not Included in Subscription
@@ -44,11 +44,11 @@ QuickBooks Online requires all expenses exported from Expensify to use a categor
4444

4545
## How to fix it
4646

47-
1. Sync your QuickBooks Online connection in Expensify from Settings > Workspaces > [workspace name] > Connections, and click the **Sync Now** button.
47+
1. Sync your QuickBooks Online connection in Expensify from Settings > Workspaces > Workspace Name > Connections, and click the **Sync Now** button.
4848
2. Review the expenses on the report. If any appear with a red _Category no longer valid_ violation, recategorize the expense until all expenses are violation-free.
4949
3. Click the **Export t**o button and then the **QuickBooks Online** option.
5050
- If you receive the same error, continue.
51-
4. Note the categories used on the expenses and check the Settings > Workspaces > [workspace name] > Categories page to confirm the exact categories used on the report are enabled and connected to QuickBooks Online (you'll see a green QB icon next to all connected categories).
51+
4. Note the categories used on the expenses and check the Settings > Workspaces > Workspace Name > Categories page to confirm the exact categories used on the report are enabled and connected to QuickBooks Online (you'll see a green QB icon next to all connected categories).
5252
5. Confirm the categories used on the expenses in the report match exactly the accounts in your QuickBooks Online chart of accounts.
5353
6. If you make any changes in QuickBooks Online or in Expensify, always sync the connection and then try to export again.
5454

@@ -67,13 +67,13 @@ There are two different ways you can resolve this error.
6767
1. Log into QuickBooks Online.
6868
2. Access the Employee Records for your submitters.
6969
3. Edit the name to differentiate them from the name they have on their account in Expensify.
70-
4. Sync your QuickBooks Online connection in Settings > Workspaces > [workspace name] > Connections.
70+
4. Sync your QuickBooks Online connection in Settings > Workspaces > Workspace Name > Connections.
7171
5. Open the report and click the Export to button and then the QuickBooks Online option.
7272

7373
**Option 2**:
7474
1. Log into QuickBooks Online.
7575
2. Manually create all of your Vendor Records, making sure that the email matches the email address associated with the user in Expensify.
76-
- In this case, we recommend disabling _Automatically Create Entities_ under Settings > Workspaces > [workspace name] > Connections > Configure > Advanced, so that you will receive the correct error messages when a vendor record doesn't exist.
76+
- In this case, we recommend disabling _Automatically Create Entities_ under Settings > Workspaces > Workspace Name > Connections > Configure > Advanced, so that you will receive the correct error messages when a vendor record doesn't exist.
7777

7878
# ExpensiError QBO097: When You Use Accounts Payable, You Must Choose a Vendor in the Name Field
7979

@@ -84,8 +84,8 @@ This error occurs when you are exporting reimbursable expenses as Journal Entrie
8484
## How to fix it
8585

8686
There are three different ways you can resolve this error.
87-
- Select a different type of export for reimbursable expenses under Settings > Workspaces > [worksapce name] > Connections > Configure > Export tab.
88-
- Enable _Automatically Create Entities_ under Settings > Workspaces > [workspace name] > Connections > Configure > Advanced to create vendor records automatically.
87+
- Select a different type of export for reimbursable expenses under Settings > Workspaces > Worksapce Name > Connections > Configure > Export tab.
88+
- Enable _Automatically Create Entities_ under Settings > Workspaces > Workspace Name > Connections > Configure > Advanced to create vendor records automatically.
8989
- Manually create vendor records in QuickBooks Online for each employee.
9090

9191
# ExpensiError QBO099: Items marked as billable must have sales information checked
@@ -102,7 +102,7 @@ This error occurs when an Item category on an expense does not have sales inform
102102
4. Check the option for **Sales**.
103103
5. Select an income account.
104104
6. Save your changes.
105-
7. Sync your QuickBooks Online connection in Settings > Workspaces > [workspace name] > Connections.
105+
7. Sync your QuickBooks Online connection in Settings > Workspaces > Workspace Name > Connections.
106106
8. Open the report and click the **Export to** button and then the **QuickBooks Online** option.
107107

108108

@@ -116,7 +116,7 @@ _Note: This error message can also show up as, "QuickBooks Reconnect error: OAut
116116

117117
## How to fix it
118118

119-
1. Navigate to Settings > Workspaces > Groups > [workspace name] > Connections.
119+
1. Navigate to Settings > Workspaces > Groups > Workspace Name > Connections.
120120
2. Click the **Sync Now** button.
121121
3. In the pop-up window, click **Reconnect** and enter your current QuickBooks Online credentials.
122122

@@ -133,7 +133,7 @@ This error occurs when settings in QuickBooks Online are enabled to warn of dupl
133133
1. Log into QuickBooks Online.
134134
2. Navigate to Settings > Advanced.
135135
3. Under the Other Preferences section, make sure "Warn if duplicate bill number is used" is set to "Off"
136-
4. Sync your QuickBooks Online connection in Settings > Workspaces > [workspace name] > Connections.
136+
4. Sync your QuickBooks Online connection in Settings > Workspaces > Workspace Name > Connections.
137137
5. Open the report and click the **Export to** button and then the **QuickBooks Online** option.
138138

139139
# Export error: QuickBooks Online: The transaction needs to be in the same currency as the A/R and A/P accounts
@@ -152,12 +152,12 @@ You can find the correct Vendor record by exporting your QuickBooks Online vendo
152152
If you have multiple Vendors with different currencies with the same email, Expensify is likely trying to export to the wrong one.
153153

154154
1. Try removing the email address from the vendor in QuickBooks Online that you aren't trying to export to.
155-
2. Sync your QuickBooks Online connection in Settings > Workspaces > [workspace name] > Connections.
155+
2. Sync your QuickBooks Online connection in Settings > Workspaces > Workspace Name > Connections.
156156
3. Open the report and click the **Export to** button and then the **QuickBooks Online** option.
157157

158158
If this still fails, you'll need to confirm that the A/P account selected in Expensify is set to the correct currency for the export.
159159

160-
1. Navigate to Settings > Workspaces > [workspace name] > Connections.
160+
1. Navigate to Settings > Workspaces > Workspace Name > Connections.
161161
2. Under the Exports tab check that both A/P accounts are the correct currency.
162162

163163
# Why are company card expenses exported to the wrong account in QuickBooks Online?
@@ -174,9 +174,9 @@ It’s important to note that expenses imported from a card linked at the indivi
174174

175175
The user exporting the report must be a domain admin. You can check the history and comment section at the bottom of the report to see who exported the report.
176176

177-
If your reports are being exported automatically by Concierge, the user listed as the Preferred Exporter under Settings > Workspaces > [workspace name] > Connections > click **Configure** must also be a domain admin.
177+
If your reports are being exported automatically by Concierge, the user listed as the Preferred Exporter under Settings > Workspaces > Workspace Name > Connections > click **Configure** must also be a domain admin.
178178

179-
If the report exporter is not a domain admin, all company card expenses will export to the bank account set in Settings > Workspaces > [workspace name] > Connections > click **Configure** for non-reimbursable expenses.
179+
If the report exporter is not a domain admin, all company card expenses will export to the bank account set in Settings > Workspaces > Workspace Name > Connections > click **Configure** for non-reimbursable expenses.
180180

181181
**Has the company card been mapped under the correct workspace?**
182182

src/components/Button/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ function Button(
290290
<View style={[isContentCentered ? styles.justifyContentCenter : styles.justifyContentBetween, styles.flexRow]}>
291291
<View style={[styles.alignItemsCenter, styles.flexRow, styles.flexShrink1]}>
292292
{!!icon && (
293-
<View style={[large ? styles.mr2 : styles.mr1, !text && styles.mr0, iconStyles]}>
293+
<View style={[styles.mr2, !text && styles.mr0, iconStyles]}>
294294
<Icon
295295
src={icon}
296296
fill={isHovered ? iconHoverFill ?? defaultFill : iconFill ?? defaultFill}

src/components/MoneyRequestConfirmationList.tsx

+5-2
Original file line numberDiff line numberDiff line change
@@ -682,9 +682,12 @@ function MoneyRequestConfirmationList({
682682
useEffect(() => {
683683
let updatedTagsString = TransactionUtils.getTag(transaction);
684684
policyTagLists.forEach((tagList, index) => {
685-
const enabledTags = Object.values(tagList.tags).filter((tag) => tag.enabled);
686685
const isTagListRequired = tagList.required ?? false;
687-
if (!isTagListRequired || enabledTags.length !== 1 || TransactionUtils.getTag(transaction, index)) {
686+
if (!isTagListRequired) {
687+
return;
688+
}
689+
const enabledTags = Object.values(tagList.tags).filter((tag) => tag.enabled);
690+
if (enabledTags.length !== 1 || TransactionUtils.getTag(transaction, index)) {
688691
return;
689692
}
690693
updatedTagsString = IOUUtils.insertTagIntoTransactionTagsString(updatedTagsString, enabledTags.at(0)?.name ?? '', index);

src/components/Search/SearchStatusBar.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ function SearchStatusBar({queryJSON, onStatusChange}: SearchStatusBarProps) {
180180
const query = SearchQueryUtils.buildSearchQueryString({...queryJSON, status: item.status});
181181
Navigation.setParams({q: query});
182182
});
183-
const isActive = queryJSON.status === item.status;
183+
const isActive = Array.isArray(queryJSON.status) ? queryJSON.status.includes(item.status) : queryJSON.status === item.status;
184184
const isFirstItem = index === 0;
185185
const isLastItem = index === options.length - 1;
186186

src/components/Search/index.tsx

+6-2
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,11 @@ function Search({queryJSON, onSearchListScroll, contentContainerStyle}: SearchPr
191191

192192
// There's a race condition in Onyx which makes it return data from the previous Search, so in addition to checking that the data is loaded
193193
// we also need to check that the searchResults matches the type and status of the current search
194-
const isDataLoaded = searchResults?.data !== undefined && searchResults?.search?.type === type && searchResults?.search?.status === status;
194+
const isDataLoaded =
195+
searchResults?.data !== undefined && searchResults?.search?.type === type && Array.isArray(status)
196+
? searchResults?.search?.status === status.join(',')
197+
: searchResults?.search?.status === status;
198+
195199
const shouldShowLoadingState = !isOffline && !isDataLoaded;
196200
const shouldShowLoadingMoreItems = !shouldShowLoadingState && searchResults?.search?.isLoading && searchResults?.search?.offset > 0;
197201
const isSearchResultsEmpty = !searchResults?.data || SearchUIUtils.isSearchResultsEmpty(searchResults);
@@ -402,7 +406,7 @@ function Search({queryJSON, onSearchListScroll, contentContainerStyle}: SearchPr
402406
};
403407

404408
const shouldShowYear = SearchUIUtils.shouldShowYear(searchResults?.data);
405-
const shouldShowSorting = sortableSearchStatuses.includes(status);
409+
const shouldShowSorting = Array.isArray(status) ? status.some((s) => sortableSearchStatuses.includes(s)) : sortableSearchStatuses.includes(status);
406410

407411
return (
408412
<SelectionListWithModal<ReportListItemType | TransactionListItemType | ReportActionListItemType>

src/components/Search/types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ type ExpenseSearchStatus = ValueOf<typeof CONST.SEARCH.STATUS.EXPENSE>;
3030
type InvoiceSearchStatus = ValueOf<typeof CONST.SEARCH.STATUS.INVOICE>;
3131
type TripSearchStatus = ValueOf<typeof CONST.SEARCH.STATUS.TRIP>;
3232
type ChatSearchStatus = ValueOf<typeof CONST.SEARCH.STATUS.CHAT>;
33-
type SearchStatus = ExpenseSearchStatus | InvoiceSearchStatus | TripSearchStatus | ChatSearchStatus;
33+
type SearchStatus = ExpenseSearchStatus | InvoiceSearchStatus | TripSearchStatus | ChatSearchStatus | Array<ExpenseSearchStatus | InvoiceSearchStatus | TripSearchStatus | ChatSearchStatus>;
3434

3535
type SearchContext = {
3636
currentSearchHash: number;

src/components/TabSelector/TabIcon.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,14 @@ function TabIcon({icon, activeOpacity = 0, inactiveOpacity = 1}: TabIconProps) {
2525
<Icon
2626
src={icon}
2727
fill={theme.icon}
28+
small
2829
/>
2930
</Animated.View>
3031
<Animated.View style={[StyleSheet.absoluteFill, {opacity: activeOpacity}]}>
3132
<Icon
3233
src={icon}
3334
fill={theme.iconMenu}
35+
small
3436
/>
3537
</Animated.View>
3638
</>

src/libs/Firebase/index.native.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import crashlytics from '@react-native-firebase/crashlytics';
33
import perf from '@react-native-firebase/perf';
44
import * as Environment from '@libs/Environment/Environment';
5-
import type {Log, StartTrace, StopTrace, TraceMap} from './types';
5+
import type {FirebaseAttributes, Log, StartTrace, StopTrace, TraceMap} from './types';
66
import utils from './utils';
77

88
const traceMap: TraceMap = {};
@@ -17,7 +17,7 @@ const startTrace: StartTrace = (customEventName) => {
1717
return;
1818
}
1919

20-
const attributes = utils.getAttributes();
20+
const attributes: FirebaseAttributes = utils.getAttributes(['accountId', 'personalDetailsLength', 'reportActionsLength', 'reportsLength', 'policiesLength']);
2121

2222
perf()
2323
.startTrace(customEventName)

src/libs/Firebase/index.web.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {trace} from '@firebase/performance';
22
import * as Environment from '@libs/Environment/Environment';
33
import {firebasePerfWeb} from './firebaseWebConfig';
4-
import type {Log, StartTrace, StopTrace, TraceMap} from './types';
4+
import type {FirebaseAttributes, Log, StartTrace, StopTrace, TraceMap} from './types';
55
import utils from './utils';
66

77
const traceMap: TraceMap = {};
@@ -19,7 +19,7 @@ const startTrace: StartTrace = (customEventName) => {
1919

2020
const perfTrace = trace(firebasePerfWeb, customEventName);
2121

22-
const attributes = utils.getAttributes();
22+
const attributes: FirebaseAttributes = utils.getAttributes(['accountId', 'personalDetailsLength', 'reportActionsLength', 'reportsLength', 'policiesLength']);
2323

2424
Object.entries(attributes).forEach(([name, value]) => {
2525
perfTrace.putAttribute(name, value);

src/libs/Firebase/types.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ type TraceMap = Record<string, Trace>;
99
type StartTrace = (customEventName: string) => void;
1010
type StopTrace = (customEventName: string) => void;
1111
type Log = (action: string) => void;
12-
type FirebaseAttributes = {
12+
type PerfAttributes = {
1313
accountId: string;
1414
personalDetailsLength: string;
1515
reportsLength: string;
@@ -21,4 +21,7 @@ type FirebaseAttributes = {
2121
policyRole: string;
2222
};
2323

24-
export type {StartTrace, StopTrace, TraceMap, Log, FirebaseAttributes};
24+
// TODO confirm which attributes are required for Firebase
25+
type FirebaseAttributes = Pick<PerfAttributes, 'accountId' | 'personalDetailsLength' | 'reportActionsLength' | 'reportsLength' | 'policiesLength'>;
26+
27+
export type {StartTrace, StopTrace, TraceMap, Log, PerfAttributes, FirebaseAttributes};

0 commit comments

Comments
 (0)