Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1291,6 +1291,15 @@ const ROUTES = {
return `settings/workspaces/${policyID}/company-cards` as const;
},
},
WORKSPACE_COMPANY_CARDS_BANK_CONNECTION: {
route: 'settings/workspaces/:policyID/company-cards/:bankName/bank-connection',
getRoute: (policyID: string | undefined, bankName: string, backTo: string) => {
if (!policyID) {
Log.warn('Invalid policyID is used to build the WORKSPACE_COMPANY_CARDS_BANK_CONNECTION route');
}
return getUrlWithBackToParam(`settings/workspaces/${policyID}/company-cards/${bankName}/bank-connection`, backTo);
},
},
WORKSPACE_COMPANY_CARDS_ADD_NEW: {
route: 'settings/workspaces/:policyID/company-cards/add-card-feed',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/company-cards/add-card-feed` as const,
Expand Down
1 change: 1 addition & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,7 @@ const SCREENS = {
COMPANY_CARDS: 'Workspace_CompanyCards',
COMPANY_CARDS_ASSIGN_CARD: 'Workspace_CompanyCards_AssignCard',
COMPANY_CARDS_SELECT_FEED: 'Workspace_CompanyCards_Select_Feed',
COMPANY_CARDS_BANK_CONNECTION: 'Workspace_CompanyCards_BankConnection',
COMPANY_CARDS_ADD_NEW: 'Workspace_CompanyCards_New',
COMPANY_CARDS_TYPE: 'Workspace_CompanyCards_Type',
COMPANY_CARDS_INSTRUCTIONS: 'Workspace_CompanyCards_Instructions',
Expand Down
30 changes: 4 additions & 26 deletions src/components/DotIndicatorMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import {isReceiptError} from '@libs/ErrorUtils';
import fileDownload from '@libs/fileDownload';
import * as Localize from '@libs/Localize';
import CONST from '@src/CONST';
import {translateLocal} from '@libs/Localize';
import type {ReceiptError} from '@src/types/onyx/Transaction';
import Icon from './Icon';
import * as Expensicons from './Icon/Expensicons';
Expand Down Expand Up @@ -61,38 +60,17 @@ function DotIndicatorMessage({messages = {}, style, type, textStyles}: DotIndica
key={index}
style={styles.offlineFeedback.text}
>
<Text style={[StyleUtils.getDotIndicatorTextStyles(isErrorMessage)]}>{Localize.translateLocal('iou.error.receiptFailureMessage')}</Text>
<Text style={[StyleUtils.getDotIndicatorTextStyles(isErrorMessage)]}>{translateLocal('iou.error.receiptFailureMessage')}</Text>
<TextLink
style={[StyleUtils.getDotIndicatorTextStyles(), styles.link]}
onPress={() => {
fileDownload(message.source, message.filename);
}}
>
{Localize.translateLocal('iou.error.saveFileMessage')}
{translateLocal('iou.error.saveFileMessage')}
</TextLink>

<Text style={[StyleUtils.getDotIndicatorTextStyles(isErrorMessage)]}>{Localize.translateLocal('iou.error.loseFileMessage')}</Text>
</Text>
);
}

if (message === CONST.COMPANY_CARDS.CONNECTION_ERROR) {
return (
<Text
key={index}
style={styles.offlineFeedback.text}
>
<Text style={[StyleUtils.getDotIndicatorTextStyles(isErrorMessage)]}>{Localize.translateLocal('workspace.companyCards.brokenConnectionErrorFirstPart')}</Text>
<TextLink
style={[StyleUtils.getDotIndicatorTextStyles(), styles.link]}
onPress={() => {
// TODO: re-navigate the user to the bank’s website to re-authenticate https://github.com/Expensify/App/issues/50448
}}
>
{Localize.translateLocal('workspace.companyCards.brokenConnectionErrorLink')}
</TextLink>

<Text style={[StyleUtils.getDotIndicatorTextStyles(isErrorMessage)]}>{Localize.translateLocal('workspace.companyCards.brokenConnectionErrorSecondPart')}</Text>
<Text style={[StyleUtils.getDotIndicatorTextStyles(isErrorMessage)]}>{translateLocal('iou.error.loseFileMessage')}</Text>
</Text>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,7 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
[SCREENS.WORKSPACE.INVOICES_COMPANY_WEBSITE]: () => require<ReactComponentModule>('../../../../pages/workspace/invoices/WorkspaceInvoicingDetailsWebsite').default,
[SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD]: () => require<ReactComponentModule>('../../../../pages/workspace/companyCards/assignCard/AssignCardFeedPage').default,
[SCREENS.WORKSPACE.COMPANY_CARDS_SELECT_FEED]: () => require<ReactComponentModule>('../../../../pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage').default,
[SCREENS.WORKSPACE.COMPANY_CARDS_BANK_CONNECTION]: () => require<ReactComponentModule>('../../../../pages/workspace/companyCards/addNew/BankConnection').default,
[SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW]: () => require<ReactComponentModule>('../../../../pages/workspace/companyCards/addNew/AddNewCardPage').default,
[SCREENS.WORKSPACE.COMPANY_CARD_DETAILS]: () => require<ReactComponentModule>('../../../../pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage').default,
[SCREENS.WORKSPACE.COMPANY_CARD_NAME]: () => require<ReactComponentModule>('../../../../pages/workspace/companyCards/WorkspaceCompanyCardEditCardNamePage').default,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial<Record<FullScreenName, string[]>> = {
SCREENS.WORKSPACE.COMPANY_CARDS_NAME,
SCREENS.WORKSPACE.COMPANY_CARDS_DETAILS,
SCREENS.WORKSPACE.COMPANY_CARDS_SELECT_FEED,
SCREENS.WORKSPACE.COMPANY_CARDS_BANK_CONNECTION,
SCREENS.WORKSPACE.COMPANY_CARDS_SETTINGS,
SCREENS.WORKSPACE.COMPANY_CARDS_SETTINGS_FEED_NAME,
SCREENS.WORKSPACE.COMPANY_CARDS_SETTINGS,
Expand Down
3 changes: 3 additions & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,9 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
[SCREENS.WORKSPACE.COMPANY_CARDS_SELECT_FEED]: {
path: ROUTES.WORKSPACE_COMPANY_CARDS_SELECT_FEED.route,
},
[SCREENS.WORKSPACE.COMPANY_CARDS_BANK_CONNECTION]: {
path: ROUTES.WORKSPACE_COMPANY_CARDS_BANK_CONNECTION.route,
},
[SCREENS.WORKSPACE.COMPANY_CARD_DETAILS]: {
path: ROUTES.WORKSPACE_COMPANY_CARD_DETAILS.route,
},
Expand Down
5 changes: 5 additions & 0 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,11 @@ type SettingsNavigatorParamList = {
[SCREENS.WORKSPACE.COMPANY_CARDS_SELECT_FEED]: {
policyID: string;
};
[SCREENS.WORKSPACE.COMPANY_CARDS_BANK_CONNECTION]: {
policyID: string;
bankName: string;
backTo: Routes;
};
[SCREENS.WORKSPACE.COMPANY_CARD_DETAILS]: {
policyID: string;
bank: CompanyCardFeed;
Expand Down
6 changes: 0 additions & 6 deletions src/libs/PolicyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import INPUT_IDS from '@src/types/form/NetSuiteCustomFieldForm';
import type {OnyxInputOrEntry, Policy, PolicyCategories, PolicyEmployeeList, PolicyTagLists, PolicyTags, Report, TaxRate} from '@src/types/onyx';
import type {CardFeedData} from '@src/types/onyx/CardFeeds';
import type {ErrorFields, PendingAction, PendingFields} from '@src/types/onyx/OnyxCommon';
import type {
ConnectionLastSync,
Expand Down Expand Up @@ -1146,10 +1145,6 @@ function getWorkflowApprovalsUnavailable(policy: OnyxEntry<Policy>) {
return policy?.approvalMode === CONST.POLICY.APPROVAL_MODE.OPTIONAL || !!policy?.errorFields?.approvalMode;
}

function hasPolicyFeedsError(feeds: Record<string, CardFeedData>, feedToSkip?: string): boolean {
return Object.entries(feeds).filter(([feedName, feedData]) => feedName !== feedToSkip && !!feedData.errors).length > 0;
}

function getAllPoliciesLength() {
return Object.keys(allPolicies ?? {}).length;
}
Expand Down Expand Up @@ -1233,7 +1228,6 @@ export {
goBackFromInvalidPolicy,
hasAccountingConnections,
shouldShowSyncError,
hasPolicyFeedsError,
shouldShowCustomUnitsError,
shouldShowEmployeeListError,
hasIntegrationAutoSync,
Expand Down
58 changes: 40 additions & 18 deletions src/libs/actions/CompanyCards.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type {OnyxEntry, OnyxUpdate} from 'react-native-onyx';
import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import * as API from '@libs/API';
import type {
Expand All @@ -18,10 +18,11 @@ import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReportUtils from '@libs/ReportUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Card, CardFeeds} from '@src/types/onyx';
import type {Card, CardFeeds, WorkspaceCardsList} from '@src/types/onyx';
import type {AssignCard, AssignCardData} from '@src/types/onyx/AssignCard';
import type {AddNewCardFeedData, AddNewCardFeedStep, CompanyCardFeed} from '@src/types/onyx/CardFeeds';
import type {OnyxData} from '@src/types/onyx/Request';
import {isEmptyObject} from '@src/types/utils/EmptyObject';

type AddNewCompanyCardFlowData = {
/** Step to be set in Onyx */
Expand Down Expand Up @@ -403,8 +404,6 @@ function unassignWorkspaceCompanyCard(workspaceAccountID: number, bankName: stri

function updateWorkspaceCompanyCard(workspaceAccountID: number, cardID: string, bankName: CompanyCardFeed) {
const authToken = NetworkStore.getAuthToken();
const optimisticFeedUpdates = {[bankName]: {errors: null}};
const failureFeedUpdates = {[bankName]: {errors: {error: CONST.COMPANY_CARDS.CONNECTION_ERROR}}};

const optimisticData: OnyxUpdate[] = [
{
Expand Down Expand Up @@ -437,13 +436,6 @@ function updateWorkspaceCompanyCard(workspaceAccountID: number, cardID: string,
},
},
},
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`,
value: {
settings: {companyCards: optimisticFeedUpdates},
},
},
];

const finallyData: OnyxUpdate[] = [
Expand Down Expand Up @@ -504,13 +496,6 @@ function updateWorkspaceCompanyCard(workspaceAccountID: number, cardID: string,
},
},
},
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`,
value: {
settings: {companyCards: failureFeedUpdates},
},
},
];

const parameters = {
Expand Down Expand Up @@ -740,6 +725,41 @@ function openPolicyCompanyCardsFeed(policyID: string, feed: CompanyCardFeed) {
API.read(READ_COMMANDS.OPEN_POLICY_COMPANY_CARDS_FEED, parameters);
}

/**
* Takes the list of cards divided by workspaces and feeds and returns the flattened non-Expensify cards related to the provided workspace
*
* @param allCardsList the list where cards split by workspaces and feeds and stored under `card_${workspaceAccountID}_${feedName}` keys
* @param workspaceAccountID the workspace account id we want to get cards for
*/
function flatAllCardsList(allCardsList: OnyxCollection<WorkspaceCardsList>, workspaceAccountID: number): Record<string, Card> | undefined {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this method would deserve some plain English explanation of what its used for and what is the expected output format (unit test possibly too)

if (!allCardsList) {
return;
}

return Object.entries(allCardsList).reduce((acc, [key, allCards]) => {
if (!key.includes(workspaceAccountID.toString()) || key.includes(CONST.EXPENSIFY_CARD.BANK)) {
return acc;
}
const {cardList, ...feedCards} = allCards ?? {};
Object.assign(acc, feedCards);
return acc;
}, {});
}

/**
* Check if any feed card has a broken connection

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Check if any feed card has a broken connection
* Check if any card from the provided feed(s) has a broken connection

*
* @param feedCards the list of the cards, related to one or several feeds
* @param [feedToExclude] the feed to ignore during the check, it's useful for checking broken connection error only in the feeds other than the selected one
*/
function checkIfFeedConnectionIsBroken(feedCards: Record<string, Card> | undefined, feedToExclude?: string): boolean {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add docs here as well and try to explain why the feedToExclude might be needed? Thanks!

if (!feedCards || isEmptyObject(feedCards)) {
return false;
}

return Object.values(feedCards).some((card) => card.bank !== feedToExclude && card.lastScrapeResult !== 200);
}

export {
setWorkspaceCompanyCardFeedName,
deleteWorkspaceCompanyCardFeed,
Expand All @@ -757,4 +777,6 @@ export {
clearAddNewCardFlow,
setAssignCardStepAndData,
clearAssignCardStepAndData,
checkIfFeedConnectionIsBroken,
flatAllCardsList,
};
4 changes: 2 additions & 2 deletions src/libs/actions/getCompanyCardBankConnection/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type CompanyCardBankConnection = {
isNewDot: string;
};

export default function getCompanyCardBankConnection(policyID?: string, bankName?: string, scrapeMinDate?: string) {
export default function getCompanyCardBankConnection(policyID?: string, bankName?: string) {
const bankConnection = Object.keys(CONST.COMPANY_CARDS.BANKS).find((key) => CONST.COMPANY_CARDS.BANKS[key as keyof typeof CONST.COMPANY_CARDS.BANKS] === bankName);

if (!bankName || !bankConnection || !policyID) {
Expand All @@ -23,7 +23,7 @@ export default function getCompanyCardBankConnection(policyID?: string, bankName
isNewDot: 'true',
domainName: PolicyUtils.getDomainNameForPolicy(policyID),
isCorporate: 'true',
scrapeMinDate: scrapeMinDate ?? '',
scrapeMinDate: '',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey @VickyStash do you remember why this was removed?

I'm looking at #84401

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, but I don't remember
Maybe, cause the scrapeMinDate wasn't passed to this function, but I'm not sure

};
const commandURL = getApiRoot({
shouldSkipWebProxy: true,
Expand Down
Loading
Loading