Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NO QA] Feat: Pending state with no feed #48489

Merged
merged 31 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
eb1a18d
new asset
waterim Sep 3, 2024
d08cfcb
add support for reactNode for the subtitle
waterim Sep 3, 2024
de98cdc
new translations
waterim Sep 3, 2024
99d0bde
add openPolicyCompanyCardsPage API
waterim Sep 3, 2024
5920205
new pending page
waterim Sep 3, 2024
498cf13
update main page
waterim Sep 3, 2024
a681ef4
Merge remote-tracking branch 'upstream/main' into feat-47376-pending-…
waterim Sep 3, 2024
f62e749
Merge remote-tracking branch 'upstream/main' into feat-47376-pending-…
waterim Sep 9, 2024
9074672
change translations
waterim Sep 9, 2024
3cb9aa6
change name to WorkspaceCompanyCardsFeedPendingPage
waterim Sep 9, 2024
59f098c
update company card page and header buttons
waterim Sep 9, 2024
8f3d136
fix lint
waterim Sep 9, 2024
529343a
update stylings
waterim Sep 10, 2024
15846c0
fix for selected feeds
waterim Sep 10, 2024
7715f45
make selectedFeed as a single
waterim Sep 11, 2024
60143a0
Merge remote-tracking branch 'upstream/main' into feat-47376-pending-…
waterim Sep 11, 2024
232983f
fix scroll, fix container, fix header buttons show
waterim Sep 11, 2024
315adc4
add ? to translations
waterim Sep 12, 2024
1571fb6
Merge remote-tracking branch 'upstream/main' into feat-47376-pending-…
waterim Sep 12, 2024
809e8fc
remove Conciege from es translation
waterim Sep 12, 2024
1e07e3b
add new util function, remove usePolicy, change logic
waterim Sep 12, 2024
400e2be
remove toString
waterim Sep 12, 2024
0a4af4d
Merge remote-tracking branch 'upstream/main' into feat-47376-pending-…
waterim Sep 13, 2024
10d58c2
add translations
waterim Sep 16, 2024
a462c69
add new amex image
waterim Sep 16, 2024
32e9733
update all other icons
waterim Sep 16, 2024
886d2e5
remove amexBlue and update icon in the cardType component
waterim Sep 16, 2024
3980236
Merge remote-tracking branch 'upstream/main' into feat-47376-pending-…
waterim Sep 16, 2024
bcc3b4b
Merge remote-tracking branch 'upstream/main' into feat-47376-pending-…
waterim Sep 17, 2024
84228b4
update images
waterim Sep 17, 2024
72693cd
fix deprecated getParentReportAction
waterim Sep 17, 2024
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/components/EmptyStateComponent/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type MediaTypes = ValueOf<typeof CONST.EMPTY_STATE_MEDIA>;
type SharedProps<T> = {
SkeletonComponent: ValidSkeletons;
title: string;
subtitle: string;
subtitle: string | React.ReactNode;
buttonText?: string;
buttonAction?: () => void;
containerStyles?: StyleProp<ViewStyle>;
Expand Down
2 changes: 2 additions & 0 deletions src/components/Icon/Illustrations.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import AmexCompanyCards from '@assets/images/companyCards/amex.svg';
import CompanyCardsEmptyState from '@assets/images/companyCards/emptystate__card-pos.svg';
import MasterCardCompanyCards from '@assets/images/companyCards/mastercard.svg';
import CompanyCardsPendingState from '@assets/images/companyCards/pendingstate_laptop-with-hourglass-and-cards.svg';
import VisaCompanyCards from '@assets/images/companyCards/visa.svg';
import EmptyCardState from '@assets/images/emptystate__expensifycard.svg';
import ExpensifyCardIllustration from '@assets/images/expensifyCard/cardIllustration.svg';
Expand Down Expand Up @@ -235,5 +236,6 @@ export {
AmexCompanyCards,
MasterCardCompanyCards,
VisaCompanyCards,
CompanyCardsPendingState,
TurtleInShell,
};
2 changes: 2 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2994,6 +2994,8 @@ export default {
setTransactionLiabilityDescription: 'When enabled, cardholders can delete card transactions. New transactions will follow this rule.',
emptyAddedFeedTitle: 'Assign company cards',
emptyAddedFeedDescription: 'Get started by assigning your first card to a member.',
pendingFeedTitle: `We're reviewing your request...`,
pendingFeedDescription: `We're currently reviewing your feed details. Once that's done we'll reach out to you via`,
waterim marked this conversation as resolved.
Show resolved Hide resolved
},
workflows: {
title: 'Workflows',
Expand Down
2 changes: 2 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3043,6 +3043,8 @@ export default {
'Cuando está habilitada, los titulares de tarjetas pueden eliminar transacciones con tarjeta. Las transacciones nuevas seguirán esta regla.',
emptyAddedFeedTitle: 'Asignar tarjetas de empresa',
emptyAddedFeedDescription: 'Comienza asignando tu primera tarjeta a un miembro.',
pendingFeedTitle: `Estamos revisando tu solicitud...`,
pendingFeedDescription: `Actualmente estamos revisando los detalles de tu feed. Una vez hecho esto, nos pondremos en contacto contigo a través de Concierge.`,
},
workflows: {
title: 'Flujos de trabajo',
Expand Down
6 changes: 6 additions & 0 deletions src/libs/API/parameters/OpenPolicyCompanyCardsPageParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
type OpenPolicyCompanyCardsPageParams = {
policyID: string;
authToken: string | null | undefined;
};

export default OpenPolicyCompanyCardsPageParams;
2 changes: 2 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,7 @@ const READ_COMMANDS = {
OPEN_POLICY_TAXES_PAGE: 'OpenPolicyTaxesPage',
OPEN_POLICY_REPORT_FIELDS_PAGE: 'OpenPolicyReportFieldsPage',
OPEN_POLICY_EXPENSIFY_CARDS_PAGE: 'OpenPolicyExpensifyCardsPage',
OPEN_POLICY_COMPANY_CARDS_PAGE: 'OpenPolicyCompanyCardsPage',
OPEN_POLICY_EDIT_CARD_LIMIT_TYPE_PAGE: 'OpenPolicyEditCardLimitTypePage',
OPEN_WORKSPACE_INVITE_PAGE: 'OpenWorkspaceInvitePage',
OPEN_DRAFT_WORKSPACE_REQUEST: 'OpenDraftWorkspaceRequest',
Expand Down Expand Up @@ -852,6 +853,7 @@ type ReadCommandParameters = {
[READ_COMMANDS.OPEN_POLICY_MORE_FEATURES_PAGE]: Parameters.OpenPolicyMoreFeaturesPageParams;
[READ_COMMANDS.OPEN_POLICY_ACCOUNTING_PAGE]: Parameters.OpenPolicyAccountingPageParams;
[READ_COMMANDS.OPEN_POLICY_EXPENSIFY_CARDS_PAGE]: Parameters.OpenPolicyExpensifyCardsPageParams;
[READ_COMMANDS.OPEN_POLICY_COMPANY_CARDS_PAGE]: Parameters.OpenPolicyExpensifyCardsPageParams;
[READ_COMMANDS.OPEN_POLICY_EDIT_CARD_LIMIT_TYPE_PAGE]: Parameters.OpenPolicyEditCardLimitTypePageParams;
[READ_COMMANDS.OPEN_POLICY_PROFILE_PAGE]: Parameters.OpenPolicyProfilePageParams;
[READ_COMMANDS.OPEN_POLICY_INITIAL_PAGE]: Parameters.OpenPolicyInitialPageParams;
Expand Down
42 changes: 42 additions & 0 deletions src/libs/actions/Policy/Policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2109,6 +2109,47 @@ function openPolicyTaxesPage(policyID: string) {
API.read(READ_COMMANDS.OPEN_POLICY_TAXES_PAGE, params);
}

function openPolicyCompanyCardsPage(policyID: string, workspaceAccountID: number) {
const authToken = NetworkStore.getAuthToken();

const optimisticData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`,
value: {
isLoading: true,
},
},
];

const successData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`,
value: {
isLoading: false,
},
},
];

const failureData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`,
value: {
isLoading: false,
},
},
];

const params: OpenPolicyExpensifyCardsPageParams = {
policyID,
authToken,
};

API.read(READ_COMMANDS.OPEN_POLICY_COMPANY_CARDS_PAGE, params, {optimisticData, successData, failureData});
}

function openPolicyExpensifyCardsPage(policyID: string, workspaceAccountID: number) {
const authToken = NetworkStore.getAuthToken();

Expand Down Expand Up @@ -4716,6 +4757,7 @@ export {
setWorkspaceCompanyCardFeedName,
deleteWorkspaceCompanyCardFeed,
setWorkspaceCompanyCardTransactionLiability,
openPolicyCompanyCardsPage,
};

export type {NewCustomUnit};
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import EmptyStateComponent from '@components/EmptyStateComponent';
import * as Illustrations from '@components/Icon/Illustrations';
import TableListItemSkeleton from '@components/Skeletons/TableRowSkeleton';
import TextLink from '@components/TextLink';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import colors from '@styles/theme/colors';
import * as ReportInstance from '@userActions/Report';
import CONST from '@src/CONST';

function WorkspaceCompanyCardsFeedPendingPage() {
const {translate} = useLocalize();
const styles = useThemeStyles();

const subtitle = (
<>
{translate('workspace.moreFeatures.companyCards.pendingFeedDescription')}
<TextLink onPress={() => ReportInstance.navigateToConciergeChat()}> {CONST?.CONCIERGE_CHAT_NAME}</TextLink>
</>
);

return (
<EmptyStateComponent
SkeletonComponent={TableListItemSkeleton}
waterim marked this conversation as resolved.
Show resolved Hide resolved
headerMediaType={CONST.EMPTY_STATE_MEDIA.ILLUSTRATION}
headerMedia={Illustrations.CompanyCardsPendingState}
headerStyles={[styles.emptyStateCardIllustrationContainer, {backgroundColor: colors.ice800}]}
headerContentStyles={styles.emptyStateCardIllustration}
title={translate('workspace.moreFeatures.companyCards.pendingFeedTitle')}
subtitle={subtitle}
/>
);
}

WorkspaceCompanyCardsFeedPendingPage.displayName = 'WorkspaceCompanyCardsFeedPendingPage';

export default WorkspaceCompanyCardsFeedPendingPage;
Original file line number Diff line number Diff line change
@@ -1,48 +1,47 @@
import React from 'react';
import {View} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import Button from '@components/Button';
import CaretWrapper from '@components/CaretWrapper';
import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import {PressableWithFeedback} from '@components/Pressable';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import usePolicy from '@hooks/usePolicy';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import * as CardUtils from '@libs/CardUtils';
import Navigation from '@navigation/Navigation';
import variables from '@styles/variables';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {CardFeeds} from '@src/types/onyx';

const mockedFeeds = {
companyCardNicknames: {
cdfbmo: 'BMO MasterCard',
},
} as unknown as CardFeeds;

type WorkspaceCompanyCardsListHeaderButtonsProps = {
/** Current policy id */
policyID: string;

/** Currently selected feed */
selectedFeed: string;

/** Is pending state of the feed */
waterim marked this conversation as resolved.
Show resolved Hide resolved
isPending: boolean;
};

function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed}: WorkspaceCompanyCardsListHeaderButtonsProps) {
function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, isPending}: WorkspaceCompanyCardsListHeaderButtonsProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {shouldUseNarrowLayout} = useResponsiveLayout();
// TODO: use data form onyx instead of mocked one when API is implemented
// const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`);
const cardFeeds = mockedFeeds ?? {};
const policy = usePolicy(policyID);
const workspaceAccountID = policy?.workspaceAccountID ?? -1;
waterim marked this conversation as resolved.
Show resolved Hide resolved
const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID.toString()}`);
waterim marked this conversation as resolved.
Show resolved Hide resolved

return (
<View style={[styles.w100, styles.ph5, !shouldUseNarrowLayout ? [styles.pv2, styles.flexRow, styles.alignItemsCenter, styles.justifyContentBetween] : styles.pb2]}>
<PressableWithFeedback
onPress={() => Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_SELECT_FEED.getRoute(policyID))}
style={[styles.flexRow, styles.alignItemsCenter, styles.gap3, styles.ml4, shouldUseNarrowLayout && styles.mb3]}
accessibilityLabel={cardFeeds?.companyCardNicknames?.[selectedFeed]}
accessibilityLabel={cardFeeds?.companyCardNicknames?.[selectedFeed] ?? ''}
>
<Icon
src={CardUtils.getCardFeedIcon(selectedFeed)}
Expand All @@ -61,6 +60,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed}: Worksp
<Button
medium
success
isDisabled={isPending}
// TODO: navigate to Assign card flow when it's implemented
onPress={() => {}}
icon={Expensicons.Plus}
Expand Down
105 changes: 59 additions & 46 deletions src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,27 @@
import {useFocusEffect} from '@react-navigation/native';
import type {StackScreenProps} from '@react-navigation/stack';
import React from 'react';
import React, {useCallback} from 'react';
import {ActivityIndicator} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import * as Illustrations from '@components/Icon/Illustrations';
import useLocalize from '@hooks/useLocalize';
import usePolicy from '@hooks/usePolicy';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import type {FullScreenNavigatorParamList} from '@libs/Navigation/types';
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections';
import * as Policy from '@userActions/Policy/Policy';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type SCREENS from '@src/SCREENS';
import type {CardFeeds, WorkspaceCardsList} from '@src/types/onyx';
import type {WorkspaceCardsList} from '@src/types/onyx';
import WorkspaceCompanyCardPageEmptyState from './WorkspaceCompanyCardPageEmptyState';
import WorkspaceCompanyCardsFeedAddedEmptyPage from './WorkspaceCompanyCardsFeedAddedEmptyPage';
import WorkspaceCompanyCardsFeedPendingPage from './WorkspaceCompanyCardsFeedPendingPage';
import WorkspaceCompanyCardsList from './WorkspaceCompanyCardsList';
import WorkspaceCompanyCardsListHeaderButtons from './WorkspaceCompanyCardsListHeaderButtons';

const mockedFeeds: CardFeeds = {
companyCards: {
cdfbmo: {
pending: false,
asrEnabled: true,
forceReimbursable: 'force_no',
liabilityType: 'corporate',
preferredPolicy: '',
reportTitleFormat: '{report:card}{report:bank}{report:submit:from}{report:total}{report:enddate:MMMM}',
statementPeriodEndDay: 'LAST_DAY_OF_MONTH',
},
},
companyCardNicknames: {
cdfbmo: 'BMO MasterCard',
},
};

const mockedCards = {
id1: {
accountID: 885646,
Expand Down Expand Up @@ -108,47 +99,69 @@ type WorkspaceCompanyCardPageProps = StackScreenProps<FullScreenNavigatorParamLi

function WorkspaceCompanyCardPage({route}: WorkspaceCompanyCardPageProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const theme = useTheme();
const policyID = route.params.policyID;
// const workspaceAccountID = PolicyUtils.getWorkspaceAccountID(policyID);

// TODO: use data form onyx instead of mocked one when API is implemented
// const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`);
const cardFeeds = mockedFeeds;
const policy = usePolicy(policyID);
const workspaceAccountID = policy?.workspaceAccountID ?? -1;
waterim marked this conversation as resolved.
Show resolved Hide resolved
const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID.toString()}`);
waterim marked this conversation as resolved.
Show resolved Hide resolved
const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`);
const defaultFeed = Object.keys(cardFeeds?.companyCards ?? {})[0];
const selectedFeed = lastSelectedFeed ?? defaultFeed;

const fetchCompanyCards = useCallback(() => {
Policy.openPolicyCompanyCardsPage(policyID, workspaceAccountID);
}, [policyID, workspaceAccountID]);

useFocusEffect(fetchCompanyCards);
Copy link
Contributor

Choose a reason for hiding this comment

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

@waterim do we really need to call this api on focus, shouldn't it only call on the mount of the component? Bcoz I think other onyx data will be updated with the WRITE api responses.


const companyCards = cardFeeds?.companyCards;
const companyCardsLength = Object.keys(companyCards ?? {}).length;
const isNoFeed = companyCardsLength === 0;
const isFeedAdded = companyCardsLength > 0;
const isPending = companyCardsLength === 1 && Object.values(companyCards ?? {})[0].pending;
const isLoading = !cardFeeds || cardFeeds.isLoading;
waterim marked this conversation as resolved.
Show resolved Hide resolved

// TODO: use data form onyx instead of mocked one when API is implemented
// const [cardsList] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${selectedFeed}`);
const cardsList = mockedCards ?? {};

// TODO correct Onyx flag should be defined in separate PR for "Pending State with No Other Feeds"
const isFeedAdded = true;

return (
<AccessOrNotFoundWrapper
policyID={route.params.policyID}
featureName={CONST.POLICY.MORE_FEATURES.ARE_COMPANY_CARDS_ENABLED}
>
<WorkspacePageWithSections
shouldUseScrollView={!isFeedAdded}
icon={Illustrations.CompanyCard}
headerText={translate('workspace.common.companyCards')}
route={route}
guidesCallTaskID={CONST.GUIDES_CALL_TASK_IDS.WORKSPACE_COMPANY_CARDS}
shouldShowOfflineIndicatorInWideScreen
includeSafeAreaPaddingBottom
showLoadingAsFirstRender={false}
>
{isFeedAdded && (
<WorkspaceCompanyCardsListHeaderButtons
policyID={policyID}
selectedFeed={selectedFeed}
/>
)}
{!isFeedAdded && <WorkspaceCompanyCardPageEmptyState route={route} />}
{isFeedAdded && <WorkspaceCompanyCardsList cardsList={cardsList} />}
</WorkspacePageWithSections>
{isLoading && (
<ActivityIndicator
size={CONST.ACTIVITY_INDICATOR_SIZE.LARGE}
style={styles.flex1}
color={theme.spinner}
/>
)}
{!isLoading && (
<WorkspacePageWithSections
shouldUseScrollView={!isFeedAdded}
waterim marked this conversation as resolved.
Show resolved Hide resolved
icon={Illustrations.CompanyCard}
headerText={translate('workspace.common.companyCards')}
route={route}
guidesCallTaskID={CONST.GUIDES_CALL_TASK_IDS.WORKSPACE_COMPANY_CARDS}
shouldShowOfflineIndicatorInWideScreen
includeSafeAreaPaddingBottom
showLoadingAsFirstRender={false}
>
{isFeedAdded && (
<WorkspaceCompanyCardsListHeaderButtons
waterim marked this conversation as resolved.
Show resolved Hide resolved
policyID={policyID}
selectedFeed={selectedFeed}
isPending={isPending}
/>
)}
{isNoFeed && <WorkspaceCompanyCardPageEmptyState route={route} />}
{isFeedAdded && !isPending && <WorkspaceCompanyCardsFeedAddedEmptyPage />}
{isPending && <WorkspaceCompanyCardsFeedPendingPage />}
{isFeedAdded && !isPending && <WorkspaceCompanyCardsList cardsList={cardsList} />}
</WorkspacePageWithSections>
)}
</AccessOrNotFoundWrapper>
);
}
Expand Down
3 changes: 3 additions & 0 deletions src/types/onyx/CardFeeds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ type CardFeeds = {

/** User-friendly feed nicknames */
companyCardNicknames: Record<string, string>;

/** Whether we are loading the data via the API */
isLoading?: boolean;
};

export default CardFeeds;
Loading