Skip to content

Commit

Permalink
Merge pull request #44741 from koko57/feature/44309-issue-new-card-fl…
Browse files Browse the repository at this point in the history
…ow-steps

[No QA] 1-6 Card Flow UI
  • Loading branch information
mountiny authored Jul 9, 2024
2 parents bb9b486 + 12b2fa7 commit 87fdc79
Show file tree
Hide file tree
Showing 24 changed files with 602 additions and 127 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2130,6 +2130,10 @@ const CONST = {
CARD_NAME: 'CardName',
CONFIRMATION: 'Confirmation',
},
CARD_TYPE: {
PHYSICAL: 'physical',
VIRTUAL: 'virtual',
},
},
AVATAR_ROW_SIZE: {
DEFAULT: 4,
Expand Down
4 changes: 2 additions & 2 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -556,8 +556,8 @@ const ONYXKEYS = {
NEW_CHAT_NAME_FORM_DRAFT: 'newChatNameFormDraft',
SUBSCRIPTION_SIZE_FORM: 'subscriptionSizeForm',
SUBSCRIPTION_SIZE_FORM_DRAFT: 'subscriptionSizeFormDraft',
ISSUE_NEW_EXPENSIFY_CARD_FORM: 'issueNewExpensifyCardForm',
ISSUE_NEW_EXPENSIFY_CARD_FORM_DRAFT: 'issueNewExpensifyCardFormDraft',
ISSUE_NEW_EXPENSIFY_CARD_FORM: 'issueNewExpensifyCard',
ISSUE_NEW_EXPENSIFY_CARD_FORM_DRAFT: 'issueNewExpensifyCardDraft',
SAGE_INTACCT_CREDENTIALS_FORM: 'sageIntacctCredentialsForm',
SAGE_INTACCT_CREDENTIALS_FORM_DRAFT: 'sageIntacctCredentialsFormDraft',
NETSUITE_TOKEN_INPUT_FORM: 'netsuiteTokenInputForm',
Expand Down
11 changes: 4 additions & 7 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -815,13 +815,10 @@ const ROUTES = {
route: 'settings/workspaces/:policyID/expensify-card',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/expensify-card` as const,
},
// TODO: uncomment after development is done
// WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW: {
// route: 'settings/workspaces/:policyID/expensify-card/issues-new',
// getRoute: (policyID: string) => `settings/workspaces/${policyID}/expensify-card/issue-new` as const,
// },
// TODO: remove after development is done - this one is for testing purposes
WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW: 'settings/workspaces/expensify-card/issue-new',
WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW: {
route: 'settings/workspaces/:policyID/expensify-card/issue-new',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/expensify-card/issue-new` as const,
},
WORKSPACE_DISTANCE_RATES: {
route: 'settings/workspaces/:policyID/distance-rates',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/distance-rates` as const,
Expand Down
2 changes: 2 additions & 0 deletions src/components/Icon/Illustrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ import ThumbsUpStars from '@assets/images/simple-illustrations/simple-illustrati
import TrackShoe from '@assets/images/simple-illustrations/simple-illustration__track-shoe.svg';
import TrashCan from '@assets/images/simple-illustrations/simple-illustration__trashcan.svg';
import TreasureChest from '@assets/images/simple-illustrations/simple-illustration__treasurechest.svg';
import VirtualCard from '@assets/images/simple-illustrations/simple-illustration__virtualcard.svg';
import WalletAlt from '@assets/images/simple-illustrations/simple-illustration__wallet-alt.svg';
import Workflows from '@assets/images/simple-illustrations/simple-illustration__workflows.svg';
import ExpensifyApprovedLogoLight from '@assets/images/subscription-details__approvedlogo--light.svg';
Expand Down Expand Up @@ -196,4 +197,5 @@ export {
CheckmarkCircle,
CreditCardEyes,
LockClosedOrange,
VirtualCard,
};
4 changes: 2 additions & 2 deletions src/libs/CurrencyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,11 @@ function convertToFrontendAmountAsInteger(amountAsInt: number, currency: string
*
* @note we do not support any currencies with more than two decimal places.
*/
function convertToFrontendAmountAsString(amountAsInt: number | null | undefined, currency: string = CONST.CURRENCY.USD): string {
function convertToFrontendAmountAsString(amountAsInt: number | null | undefined, currency: string = CONST.CURRENCY.USD, withDecimals = true): string {
if (amountAsInt === null || amountAsInt === undefined) {
return '';
}
const decimals = getCurrencyDecimals(currency);
const decimals = withDecimals ? getCurrencyDecimals(currency) : 0;
return convertToFrontendAmountAsInteger(amountAsInt, currency).toFixed(decimals);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial<Record<FullScreenName, string[]>> = {
SCREENS.WORKSPACE.REPORT_FIELDS_VALUE_SETTINGS,
SCREENS.WORKSPACE.REPORT_FIELDS_EDIT_VALUE,
],
[SCREENS.WORKSPACE.EXPENSIFY_CARD]: [],
[SCREENS.WORKSPACE.EXPENSIFY_CARD]: [SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW],
};

export default FULL_SCREEN_TO_RHP_MAPPING;
2 changes: 1 addition & 1 deletion src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
path: ROUTES.WORKSPACE_PROFILE_SHARE.route,
},
[SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW]: {
path: ROUTES.WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW,
path: ROUTES.WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW.route,
},
[SCREENS.WORKSPACE.RATE_AND_UNIT]: {
path: ROUTES.WORKSPACE_RATE_AND_UNIT.route,
Expand Down
7 changes: 6 additions & 1 deletion src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,9 @@ type SettingsNavigatorParamList = {
policyID: string;
taxID: string;
};
[SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW]: {
policyID: string;
};
} & ReimbursementAccountNavigatorParamList;

type NewChatNavigatorParamList = {
Expand Down Expand Up @@ -993,7 +996,6 @@ type FullScreenNavigatorParamList = {
[SCREENS.WORKSPACE.DISTANCE_RATES]: {
policyID: string;
};

[SCREENS.WORKSPACE.ACCOUNTING.ROOT]: {
policyID: string;
};
Expand All @@ -1006,6 +1008,9 @@ type FullScreenNavigatorParamList = {
[SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECTOR]: {
policyID: string;
};
[SCREENS.WORKSPACE.EXPENSIFY_CARD]: {
policyID: string;
};
};

type OnboardingModalNavigatorParamList = {
Expand Down
9 changes: 9 additions & 0 deletions src/libs/OptionsListUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,10 @@ type FilterOptionsConfig = Pick<
'sortByReportTypeInSearch' | 'canInviteUser' | 'betas' | 'selectedOptions' | 'excludeUnknownUsers' | 'excludeLogins' | 'maxRecentReportsToShow'
> & {preferChatroomsOverThreads?: boolean};

type HasText = {
text?: string;
};

/**
* OptionsListUtils is used to build a list options passed to the OptionsList component. Several different UI views can
* be configured to display different results based on the options passed to the private getOptions() method. Public
Expand Down Expand Up @@ -2559,6 +2563,10 @@ function filterOptions(options: Options, searchInputValue: string, config?: Filt
};
}

function sortItemsAlphabetically<T extends HasText>(membersList: T[]): T[] {
return membersList.sort((a, b) => (a.text ?? '').toLowerCase().localeCompare((b.text ?? '').toLowerCase()));
}

export {
getAvatarsForAccountIDs,
isCurrentUser,
Expand All @@ -2584,6 +2592,7 @@ export {
getEnabledCategoriesCount,
hasEnabledOptions,
sortCategories,
sortItemsAlphabetically,
sortTags,
getCategoryOptionTree,
hasEnabledTags,
Expand Down
9 changes: 9 additions & 0 deletions src/libs/PolicyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ function getTagListName(policyTagList: OnyxEntry<PolicyTagList>, orderWeight: nu

return Object.values(policyTagList).find((tag) => tag.orderWeight === orderWeight)?.name ?? '';
}

/**
* Gets all tag lists of a policy
*/
Expand Down Expand Up @@ -657,6 +658,13 @@ function getCurrentConnectionName(policy: Policy | undefined): string | undefine
return connectionKey ? CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionKey] : undefined;
}

/**
* Check if the policy member is deleted from the workspace
*/
function isDeletedPolicyEmployee(policyEmployee: PolicyEmployee, isOffline: boolean) {
return !isOffline && policyEmployee.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && isEmptyObject(policyEmployee.errors);
}

export {
canEditTaxRate,
extractPolicyIDFromPath,
Expand Down Expand Up @@ -690,6 +698,7 @@ export {
hasPolicyErrorFields,
hasTaxRateError,
isExpensifyTeam,
isDeletedPolicyEmployee,
isFreeGroupPolicy,
isInstantSubmitEnabled,
isPaidGroupPolicy,
Expand Down
34 changes: 30 additions & 4 deletions src/libs/actions/Card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,21 @@ import type {ActivatePhysicalExpensifyCardParams, ReportVirtualExpensifyCardFrau
import {SIDE_EFFECT_REQUEST_COMMANDS, WRITE_COMMANDS} from '@libs/API/types';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {ExpensifyCardDetails, IssueNewCardStep} from '@src/types/onyx/Card';
import type {ExpensifyCardDetails, IssueNewCardData, IssueNewCardStep} from '@src/types/onyx/Card';

type ReplacementReason = 'damaged' | 'stolen';

type IssueNewCardFlowData = {
/** Step to be set in Onyx */
step?: IssueNewCardStep;

/** Whether the user is editing step */
isEditing?: boolean;

/** Data required to be sent to issue a new card */
data?: Partial<IssueNewCardData>;
};

function reportVirtualExpensifyCardFraud(cardID: number) {
const optimisticData: OnyxUpdate[] = [
{
Expand Down Expand Up @@ -185,9 +196,24 @@ function revealVirtualCardDetails(cardID: number): Promise<ExpensifyCardDetails>
});
}

function setIssueNewCardStep(step: IssueNewCardStep | null) {
Onyx.merge(ONYXKEYS.ISSUE_NEW_EXPENSIFY_CARD, {currentStep: step});
function setIssueNewCardStepAndData({data, isEditing, step}: IssueNewCardFlowData) {
Onyx.merge(ONYXKEYS.ISSUE_NEW_EXPENSIFY_CARD, {data, isEditing, currentStep: step});
}

function clearIssueNewCardFlow() {
Onyx.set(ONYXKEYS.ISSUE_NEW_EXPENSIFY_CARD, {
currentStep: null,
data: {},
});
}

export {requestReplacementExpensifyCard, activatePhysicalExpensifyCard, clearCardListErrors, reportVirtualExpensifyCardFraud, revealVirtualCardDetails, setIssueNewCardStep};
export {
requestReplacementExpensifyCard,
activatePhysicalExpensifyCard,
clearCardListErrors,
reportVirtualExpensifyCardFraud,
revealVirtualCardDetails,
setIssueNewCardStepAndData,
clearIssueNewCardFlow,
};
export type {ReplacementReason};
16 changes: 4 additions & 12 deletions src/pages/workspace/WorkspaceMembersPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import type {InvitedEmailsToAccountIDs, PersonalDetailsList, PolicyEmployee, PolicyEmployeeList, Session} from '@src/types/onyx';
import type {InvitedEmailsToAccountIDs, PersonalDetailsList, PolicyEmployeeList, Session} from '@src/types/onyx';
import type {Errors, PendingAction} from '@src/types/onyx/OnyxCommon';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import type {WithPolicyAndFullscreenLoadingProps} from './withPolicyAndFullscreenLoading';
Expand Down Expand Up @@ -304,14 +304,6 @@ function WorkspaceMembersPage({personalDetails, invitedEmailsToAccountIDsDraft,
[route.params.policyID],
);

/**
* Check if the policy member is deleted from the workspace
*/
const isDeletedPolicyEmployee = useCallback(
(policyEmployee: PolicyEmployee): boolean => !isOffline && policyEmployee.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && isEmptyObject(policyEmployee.errors),
[isOffline],
);

const policyOwner = policy?.owner;
const currentUserLogin = currentUserPersonalDetails.login;
const invitedPrimaryToSecondaryLogins = invertObject(policy?.primaryLoginsInvited ?? {});
Expand All @@ -320,7 +312,7 @@ function WorkspaceMembersPage({personalDetails, invitedEmailsToAccountIDsDraft,

Object.entries(policy?.employeeList ?? {}).forEach(([email, policyEmployee]) => {
const accountID = Number(policyMemberEmailsToAccountIDs[email] ?? '');
if (isDeletedPolicyEmployee(policyEmployee)) {
if (PolicyUtils.isDeletedPolicyEmployee(policyEmployee, isOffline)) {
return;
}

Expand Down Expand Up @@ -375,13 +367,13 @@ function WorkspaceMembersPage({personalDetails, invitedEmailsToAccountIDsDraft,
invitedSecondaryLogin: details?.login ? invitedPrimaryToSecondaryLogins[details.login] ?? '' : '',
});
});
result = result.sort((a, b) => (a.text ?? '').toLowerCase().localeCompare((b.text ?? '').toLowerCase()));
result = OptionsListUtils.sortItemsAlphabetically(result);
return result;
}, [
isOffline,
currentUserLogin,
formatPhoneNumber,
invitedPrimaryToSecondaryLogins,
isDeletedPolicyEmployee,
isPolicyAdmin,
personalDetails,
policy?.owner,
Expand Down
Loading

0 comments on commit 87fdc79

Please sign in to comment.