Skip to content

Commit

Permalink
Merge pull request #52871 from callstack-internal/chore/52820-workspa…
Browse files Browse the repository at this point in the history
…ce-cards-cleanup

Chore/52820 workspace cards cleanup
  • Loading branch information
mountiny authored Nov 22, 2024
2 parents 2166dff + 2570632 commit b3c8b56
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 18 deletions.
13 changes: 13 additions & 0 deletions src/libs/CardUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {TranslationPaths} from '@src/languages/types';
import type {OnyxValues} from '@src/ONYXKEYS';
import ONYXKEYS from '@src/ONYXKEYS';
import type {BankAccountList, Card, CardFeeds, CardList, CompanyCardFeed, PersonalDetailsList, WorkspaceCardsList} from '@src/types/onyx';
import type {FilteredCardList} from '@src/types/onyx/Card';
import type {CompanyCardNicknames, CompanyFeeds} from '@src/types/onyx/CardFeeds';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import type IconAsset from '@src/types/utils/IconAsset';
Expand Down Expand Up @@ -352,6 +353,16 @@ function getSelectedFeed(lastSelectedFeed: OnyxEntry<CompanyCardFeed>, cardFeeds
return lastSelectedFeed ?? defaultFeed;
}

function getFilteredCardList(list?: WorkspaceCardsList) {
const {cardList, ...cards} = list ?? {};
// We need to filter out cards which already has been assigned
return Object.fromEntries(Object.entries(cardList ?? {}).filter(([cardNumber]) => !Object.values(cards).find((card) => card.lastFourPAN && cardNumber.endsWith(card.lastFourPAN))));
}

function hasOnlyOneCardToAssign(list: FilteredCardList) {
return Object.keys(list).length === 1;
}

export {
isExpensifyCard,
isCorporateCard,
Expand All @@ -378,4 +389,6 @@ export {
getCorrectStepForSelectedBank,
getCustomOrFormattedFeedName,
removeExpensifyCardFromCompanyCards,
getFilteredCardList,
hasOnlyOneCardToAssign,
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import * as CardUtils from '@libs/CardUtils';
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
import * as PolicyUtils from '@libs/PolicyUtils';
import Navigation from '@navigation/Navigation';
import variables from '@styles/variables';
import * as CompanyCards from '@userActions/CompanyCards';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {CompanyCardFeed} from '@src/types/onyx';
import type {AssignCardData, AssignCardStep} from '@src/types/onyx/AssignCard';

type WorkspaceCompanyCardsListHeaderButtonsProps = {
/** Current policy id */
Expand All @@ -41,6 +45,36 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed}: Worksp
const isCustomFeed = CardUtils.isCustomFeed(selectedFeed);
const companyFeeds = CardUtils.getCompanyFeeds(cardFeeds);
const currentFeedData = companyFeeds?.[selectedFeed];
const policy = PolicyUtils.getPolicy(policyID);

const [list] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${selectedFeed}`);
const filteredCardList = CardUtils.getFilteredCardList(list);

const handleAssignCard = () => {
const data: Partial<AssignCardData> = {
bankName: selectedFeed,
};

let currentStep: AssignCardStep = CONST.COMPANY_CARD.STEP.ASSIGNEE;

if (Object.keys(policy?.employeeList ?? {}).length === 1) {
const userEmail = Object.keys(policy?.employeeList ?? {}).at(0) ?? '';
data.email = userEmail;
const personalDetails = PersonalDetailsUtils.getPersonalDetailByEmail(userEmail);
const memberName = personalDetails?.firstName ? personalDetails.firstName : personalDetails?.login;
data.cardName = `${memberName}'s card`;
currentStep = CONST.COMPANY_CARD.STEP.CARD;

if (CardUtils.hasOnlyOneCardToAssign(filteredCardList)) {
currentStep = CONST.COMPANY_CARD.STEP.TRANSACTION_START_DATE;
data.cardNumber = Object.keys(filteredCardList).at(0);
data.encryptedCardNumber = Object.values(filteredCardList).at(0);
}
}

CompanyCards.setAssignCardStepAndData({data, currentStep});
Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute(policyID, selectedFeed)));
};

return (
<OfflineWithFeedback
Expand Down Expand Up @@ -79,7 +113,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed}: Worksp
<Button
success
isDisabled={!currentFeedData || !!currentFeedData?.pending || !!currentFeedData?.errors}
onPress={() => Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute(policyID, selectedFeed))}
onPress={handleAssignCard}
icon={Expensicons.Plus}
text={translate('workspace.companyCards.assignCard')}
style={shouldChangeLayout && styles.flex1}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ function AssignCardFeedPage({route, policy}: AssignCardFeedPageProps) {
const policyID = policy?.id ?? '-1';

useEffect(() => {
CompanyCards.setAssignCardStepAndData({data: {bankName: feed}});
}, [feed]);
return () => {
CompanyCards.clearAssignCardStepAndData();
};
}, []);

switch (currentStep) {
case CONST.COMPANY_CARD.STEP.ASSIGNEE:
Expand All @@ -52,8 +54,6 @@ function AssignCardFeedPage({route, policy}: AssignCardFeedPageProps) {
default:
return <AssigneeStep policy={policy} />;
}

return <AssigneeStep policy={policy} />;
}

export default withPolicyAndFullscreenLoading(AssignCardFeedPage);
14 changes: 12 additions & 2 deletions src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import useDebouncedState from '@hooks/useDebouncedState';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import useThemeStyles from '@hooks/useThemeStyles';
import * as CardUtils from '@libs/CardUtils';
import {formatPhoneNumber} from '@libs/LocalePhoneNumber';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
Expand All @@ -35,6 +36,10 @@ function AssigneeStep({policy}: AssigneeStepProps) {
const styles = useThemeStyles();
const {isOffline} = useNetwork();
const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD);
const workspaceAccountID = PolicyUtils.getWorkspaceAccountID(policy?.id ?? '-1');

const [list] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${assignCard?.data?.bankName ?? ''}`);
const filteredCardList = CardUtils.getFilteredCardList(list);

const isEditing = assignCard?.isEditing;

Expand All @@ -57,8 +62,10 @@ function AssigneeStep({policy}: AssigneeStepProps) {
const personalDetail = PersonalDetailsUtils.getPersonalDetailByEmail(selectedMember);
const memberName = personalDetail?.firstName ? personalDetail.firstName : personalDetail?.login;

const nextStep = CardUtils.hasOnlyOneCardToAssign(filteredCardList) ? CONST.COMPANY_CARD.STEP.TRANSACTION_START_DATE : CONST.COMPANY_CARD.STEP.CARD;

CompanyCards.setAssignCardStepAndData({
currentStep: isEditing ? CONST.COMPANY_CARD.STEP.CONFIRMATION : CONST.COMPANY_CARD.STEP.CARD,
currentStep: isEditing ? CONST.COMPANY_CARD.STEP.CONFIRMATION : nextStep,
data: {
email: selectedMember,
cardName: `${memberName}'s card`,
Expand All @@ -69,7 +76,10 @@ function AssigneeStep({policy}: AssigneeStepProps) {

const handleBackButtonPress = () => {
if (isEditing) {
CompanyCards.setAssignCardStepAndData({currentStep: CONST.COMPANY_CARD.STEP.CONFIRMATION, isEditing: false});
CompanyCards.setAssignCardStepAndData({
currentStep: CONST.COMPANY_CARD.STEP.CONFIRMATION,
isEditing: false,
});
return;
}
Navigation.goBack();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,8 @@ function CardSelectionStep({feed, policyID}: CardSelectionStepProps) {

const isEditing = assignCard?.isEditing;
const assigneeDisplayName = PersonalDetailsUtils.getPersonalDetailByEmail(assignCard?.data?.email ?? '')?.displayName ?? '';
const {cardList, ...cards} = list ?? {};
// We need to filter out cards which already has been assigned
const filteredCardList = Object.fromEntries(
Object.entries(cardList ?? {}).filter(([cardNumber]) => !Object.values(cards).find((card) => card.lastFourPAN && cardNumber.endsWith(card.lastFourPAN))),
);
const filteredCardList = CardUtils.getFilteredCardList(list);

const [cardSelected, setCardSelected] = useState(assignCard?.data?.encryptedCardNumber ?? '');
const [shouldShowError, setShouldShowError] = useState(false);

Expand Down
27 changes: 22 additions & 5 deletions src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import type {CompanyCardFeed} from '@src/types/onyx';
import type {AssignCardData, AssignCardStep} from '@src/types/onyx/AssignCard';

type CardFeedListItem = ListItem & {
/** Card feed value */
Expand All @@ -47,8 +48,12 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew

const accountID = Number(route.params.accountID);
const memberLogin = personalDetails?.[accountID]?.login ?? '';
const memberName = personalDetails?.[accountID]?.firstName ? personalDetails?.[accountID]?.firstName : personalDetails?.[accountID]?.login;
const availableCompanyCards = CardUtils.removeExpensifyCardFromCompanyCards(cardFeeds);

const [list] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${selectedFeed}`);
const filteredCardList = CardUtils.getFilteredCardList(list);

const handleSubmit = () => {
if (!selectedFeed) {
setShouldShowError(true);
Expand All @@ -64,14 +69,26 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew
});
Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW.getRoute(policyID, ROUTES.WORKSPACE_MEMBER_DETAILS.getRoute(policyID, accountID)));
} else {
const data: Partial<AssignCardData> = {
email: memberLogin,
bankName: selectedFeed,
cardName: `${memberName}'s card`,
};
let currentStep: AssignCardStep = CONST.COMPANY_CARD.STEP.CARD;

if (CardUtils.hasOnlyOneCardToAssign(filteredCardList)) {
currentStep = CONST.COMPANY_CARD.STEP.TRANSACTION_START_DATE;
data.cardNumber = Object.keys(filteredCardList).at(0);
data.encryptedCardNumber = Object.values(filteredCardList).at(0);
}
CompanyCards.setAssignCardStepAndData({
currentStep: CONST.COMPANY_CARD.STEP.CARD,
data: {
email: memberLogin,
},
currentStep,
data,
isEditing: false,
});
Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute(policyID, selectedFeed, ROUTES.WORKSPACE_MEMBER_DETAILS.getRoute(policyID, accountID)));
Navigation.setNavigationActionToMicrotaskQueue(() =>
Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute(policyID, selectedFeed, ROUTES.WORKSPACE_MEMBER_DETAILS.getRoute(policyID, accountID))),
);
}
};

Expand Down
5 changes: 4 additions & 1 deletion src/types/onyx/Card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,5 +180,8 @@ type WorkspaceCardsList = Record<string, Card> & {
cardList?: Record<string, string>;
};

/** Card list with only available card */
type FilteredCardList = Record<string, string>;

export default Card;
export type {ExpensifyCardDetails, CardList, IssueNewCard, IssueNewCardStep, IssueNewCardData, WorkspaceCardsList, CardLimitType};
export type {ExpensifyCardDetails, CardList, IssueNewCard, IssueNewCardStep, IssueNewCardData, WorkspaceCardsList, CardLimitType, FilteredCardList};

0 comments on commit b3c8b56

Please sign in to comment.