Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
36 changes: 22 additions & 14 deletions src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import type {FlashListRef, ListRenderItemInfo} from '@shopify/flash-list';
import {FlashList} from '@shopify/flash-list';
import React, {useRef} from 'react';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import {PressableWithFeedback} from '@components/Pressable';
import SearchBar from '@components/SearchBar';
import TableRowSkeleton from '@components/Skeletons/TableRowSkeleton';
import Text from '@components/Text';
import useCardFeeds from '@hooks/useCardFeeds';
import useCardsList from '@hooks/useCardsList';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import useOnyx from '@hooks/useOnyx';
import usePolicy from '@hooks/usePolicy';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
Expand All @@ -29,17 +31,15 @@ import Navigation from '@navigation/Navigation';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {Card, CompanyCardFeed, CompanyCardFeedWithDomainID, WorkspaceCardsList} from '@src/types/onyx';
import type {Card, CompanyCardFeed, CompanyCardFeedWithDomainID} from '@src/types/onyx';
import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
import WorkspaceCompanyCardsFeedAddedEmptyPage from './WorkspaceCompanyCardsFeedAddedEmptyPage';
import WorkspaceCompanyCardsListRow from './WorkspaceCompanyCardsListRow';

type WorkspaceCompanyCardsListProps = {
/** Selected feed */
selectedFeed: CompanyCardFeedWithDomainID;

/** List of company cards */
cardsList: OnyxEntry<WorkspaceCardsList>;

/** Current policy id */
policyID: string;

Expand All @@ -53,17 +53,24 @@ type WorkspaceCompanyCardsListProps = {
shouldShowGBDisclaimer?: boolean;
};

function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignCard, isAssigningCardDisabled, shouldShowGBDisclaimer}: WorkspaceCompanyCardsListProps) {
function WorkspaceCompanyCardsList({selectedFeed, policyID, onAssignCard, isAssigningCardDisabled, shouldShowGBDisclaimer}: WorkspaceCompanyCardsListProps) {
const styles = useThemeStyles();
const {isOffline} = useNetwork();
const {translate, localeCompare} = useLocalize();
const listRef = useRef<FlashListRef<string>>(null);
const {shouldUseNarrowLayout, isMediumScreenWidth} = useResponsiveLayout();

const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false});
const [cardsList, cardsListMetadata] = useCardsList(selectedFeed);
const isLoadingCardsList = !isOffline && isLoadingOnyxValue(cardsListMetadata);
const [personalDetails, personalDetailsMetadata] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false});
const isLoadingPersonalDetails = !isOffline && isLoadingOnyxValue(personalDetailsMetadata);
const isLoadingCardsTableData = isLoadingCardsList || isLoadingPersonalDetails;

const [customCardNames] = useOnyx(ONYXKEYS.NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES, {canBeMissing: true});
const policy = usePolicy(policyID);

const {cardList, ...assignedCards} = cardsList ?? {};
const cardListTyped: Record<string, string> | undefined = (cardsList as {cardList?: Record<string, string>})?.cardList ?? {};
const assignedCards = Object.fromEntries(Object.entries(cardsList ?? {}).filter(([key]) => key !== 'cardList')) as Record<string, Card>;
const [cardFeeds] = useCardFeeds(policyID);

const companyFeeds = getCompanyFeeds(cardFeeds);
Expand All @@ -89,7 +96,7 @@ function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignC
const renderItem = ({item: cardName, index}: ListRenderItemInfo<string>) => {
const assignedCard = Object.values(assignedCards ?? {}).find((card) => card.cardName === cardName);

const customCardName = customCardNames?.[assignedCard?.cardID ?? CONST.DEFAULT_NUMBER_ID];
const customCardName = assignedCard?.cardID ? customCardNames?.[assignedCard.cardID] : undefined;

const isCardDeleted = assignedCard?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE;

Expand All @@ -101,7 +108,7 @@ function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignC
if (isPlaid) {
cardIdentifier = cardName;
} else if (isCommercial) {
const cardValue = cardList?.[cardName] ?? cardName;
const cardValue = cardListTyped?.[cardName] ?? cardName;
const digitsOnly = cardValue.replaceAll(/\D/g, '');
if (digitsOnly.length >= 10) {
const first6 = digitsOnly.substring(0, 6);
Expand All @@ -111,7 +118,7 @@ function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignC
cardIdentifier = cardValue;
}
} else {
cardIdentifier = cardList?.[cardName] ?? cardName;
cardIdentifier = cardListTyped?.[cardName] ?? cardName;
}
}

Expand Down Expand Up @@ -149,7 +156,7 @@ function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignC
>
{({hovered}) => (
<WorkspaceCompanyCardsListRow
cardholder={personalDetails?.[assignedCard?.accountID ?? CONST.DEFAULT_NUMBER_ID]}
cardholder={assignedCard?.accountID ? personalDetails?.[assignedCard.accountID] : undefined}
cardName={cardName}
selectedFeed={selectedFeed}
plaidIconUrl={plaidIconUrl}
Expand Down Expand Up @@ -213,7 +220,7 @@ function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignC
);

// Show empty state when there are no cards
if (!cards?.length) {
if (!cards?.length && !isLoadingCardsTableData) {
return (
<WorkspaceCompanyCardsFeedAddedEmptyPage
shouldShowGBDisclaimer={shouldShowGBDisclaimer}
Expand All @@ -227,10 +234,11 @@ function WorkspaceCompanyCardsList({selectedFeed, cardsList, policyID, onAssignC
<View style={styles.flex1}>
<FlashList
ref={listRef}
data={cards}
data={isLoadingCardsTableData ? [] : (cards ?? [])}
renderItem={renderItem}
keyExtractor={keyExtractor}
ListHeaderComponent={ListHeaderComponent}
ListEmptyComponent={!isOffline && isLoadingCardsTableData ? <TableRowSkeleton fixedNumItems={5} /> : undefined}
showsVerticalScrollIndicator={false}
keyboardShouldPersistTaps="handled"
contentContainerStyle={styles.flexGrow1}
Expand Down
22 changes: 10 additions & 12 deletions src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useCallback, useEffect, useState} from 'react';
import React, {useEffect, useState} from 'react';
import ActivityIndicator from '@components/ActivityIndicator';
import DecisionModal from '@components/DecisionModal';
import useAssignCard from '@hooks/useAssignCard';
Expand All @@ -20,7 +20,7 @@ import {openPolicyCompanyCardsFeed, openPolicyCompanyCardsPage} from '@userActio
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type SCREENS from '@src/SCREENS';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
import WorkspaceCompanyCardPageEmptyState from './WorkspaceCompanyCardPageEmptyState';
import WorkspaceCompanyCardsFeedPendingPage from './WorkspaceCompanyCardsFeedPendingPage';
import WorkspaceCompanyCardsList from './WorkspaceCompanyCardsList';
Expand All @@ -39,10 +39,10 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) {
const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {canBeMissing: false});
const workspaceAccountID = policy?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID;
const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true});
const [cardFeeds, , defaultFeed] = useCardFeeds(policyID);
const [cardFeeds] = useCardFeeds(policyID);
const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds);
const feed = selectedFeed ? getCompanyCardFeed(selectedFeed) : undefined;
const [cardsList] = useCardsList(selectedFeed);
const [cardsList, cardsListMetadata] = useCardsList(selectedFeed);
const [countryByIp] = useOnyx(ONYXKEYS.COUNTRY, {canBeMissing: false});
const hasNoAssignedCard = Object.keys(cardsList ?? {}).length === 0;

Expand All @@ -53,18 +53,17 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) {
const isFeedAdded = !isPending && !isNoFeed;
const [shouldShowOfflineModal, setShouldShowOfflineModal] = useState(false);
const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeedData);
const fetchCompanyCards = useCallback(() => {
openPolicyCompanyCardsPage(policyID, domainOrWorkspaceAccountID);
}, [policyID, domainOrWorkspaceAccountID]);

const {isOffline} = useNetwork({onReconnect: fetchCompanyCards});
const isLoading = !isOffline && (!cardFeeds || (!!defaultFeed?.isLoading && isEmptyObject(cardsList)));
const {isOffline} = useNetwork({
onReconnect: () => openPolicyCompanyCardsPage(policyID, domainOrWorkspaceAccountID),
});
const isLoading = !isOffline && (!cardFeeds || (isFeedAdded && isLoadingOnyxValue(cardsListMetadata)));
const isGB = countryByIp === CONST.COUNTRY.GB;
const shouldShowGBDisclaimer = isGB && isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS) && (isNoFeed || hasNoAssignedCard);

useEffect(() => {
fetchCompanyCards();
}, [fetchCompanyCards]);
openPolicyCompanyCardsPage(policyID, domainOrWorkspaceAccountID);
}, [policyID, domainOrWorkspaceAccountID]);
Copy link
Contributor

Choose a reason for hiding this comment

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

@carlosmiceli We still have linting here 👀

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, could you explain what you mean?

Copy link
Contributor

@hungvu193 hungvu193 Dec 18, 2025

Choose a reason for hiding this comment

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

It appears in here if you view it on github review mode 👀

Image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ahhh, I see! I'm a bit rusty with App PRs, thanks!


useEffect(() => {
if (isLoading || !feed || isPending) {
Expand Down Expand Up @@ -113,7 +112,6 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) {
{isFeedAdded && !isPending && (
<WorkspaceCompanyCardsList
selectedFeed={selectedFeed}
cardsList={cardsList}
shouldShowGBDisclaimer={shouldShowGBDisclaimer}
policyID={policyID}
onAssignCard={assignCard}
Expand Down
Loading