From 5b2caa96be7006c6e726601bc0a03cfdf7a093b8 Mon Sep 17 00:00:00 2001
From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com>
Date: Mon, 9 Feb 2026 21:34:44 +0530
Subject: [PATCH 01/10] Implement freeze card option
---
assets/images/freeze-card.svg | 9 +++++++++
src/CONST/index.ts | 1 +
src/components/Icon/Expensicons.ts | 2 ++
.../Icon/chunks/expensify-icons.chunk.ts | 2 ++
src/languages/en.ts | 1 +
src/libs/CardUtils.ts | 5 +++++
src/libs/Permissions.ts | 2 +-
.../Wallet/ExpensifyCardPage/index.tsx | 20 +++++++++++++++++--
8 files changed, 39 insertions(+), 3 deletions(-)
create mode 100644 assets/images/freeze-card.svg
diff --git a/assets/images/freeze-card.svg b/assets/images/freeze-card.svg
new file mode 100644
index 000000000000..f01ae0cf1e13
--- /dev/null
+++ b/assets/images/freeze-card.svg
@@ -0,0 +1,9 @@
+
diff --git a/src/CONST/index.ts b/src/CONST/index.ts
index 628246862f6a..a2be7e85c855 100755
--- a/src/CONST/index.ts
+++ b/src/CONST/index.ts
@@ -754,6 +754,7 @@ const CONST = {
SINGLE_USE_AND_EXPIRE_BY_CARDS: 'singleUseAndExpireByCards',
PERSONAL_CARD_IMPORT: 'personalCardImport',
SUGGESTED_FOLLOWUPS: 'suggestedFollowups',
+ FREEZE_CARD: 'freezeCard',
},
BUTTON_STATES: {
DEFAULT: 'default',
diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts
index 60746b906dfc..d1d23b1bd084 100644
--- a/src/components/Icon/Expensicons.ts
+++ b/src/components/Icon/Expensicons.ts
@@ -162,6 +162,7 @@ import Wallet from '@assets/images/wallet.svg';
import Workflows from '@assets/images/workflows.svg';
import Workspace from '@assets/images/workspace-default-avatar.svg';
import Clear from '@assets/images/x-circle.svg';
+import FreezeCard from '@assets/images/freeze-card.svg';
export {
Android,
@@ -328,4 +329,5 @@ export {
CertiniaSquare,
ZenefitsSquare,
GalleryPlus,
+ FreezeCard,
};
diff --git a/src/components/Icon/chunks/expensify-icons.chunk.ts b/src/components/Icon/chunks/expensify-icons.chunk.ts
index 8255f17c6f24..0912448c8291 100644
--- a/src/components/Icon/chunks/expensify-icons.chunk.ts
+++ b/src/components/Icon/chunks/expensify-icons.chunk.ts
@@ -240,6 +240,7 @@ import Workflows from '@assets/images/workflows.svg';
import Workspace from '@assets/images/workspace-default-avatar.svg';
import Clear from '@assets/images/x-circle.svg';
import Zoom from '@assets/images/zoom.svg';
+import FreezeCard from '@assets/images/freeze-card.svg';
const Expensicons = {
ReceiptBody,
@@ -324,6 +325,7 @@ const Expensicons = {
FlagLevelTwo,
FlagLevelThree,
Fingerprint,
+ FreezeCard,
Fullscreen,
Folder,
Tag,
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 7607efb39599..e3d5de7ae026 100644
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -2293,6 +2293,7 @@ const translations = {
alertMessage: ({cardLastFour, amount, merchant, date}: {cardLastFour: string; amount: string; merchant: string; date: string}) =>
`identified suspicious activity on card ending in ${cardLastFour}. Do you recognize this charge?\n\n${amount} for ${merchant} - ${date}`,
},
+ freezeCard: 'Freeze card',
},
workflowsPage: {
workflowTitle: 'Spend',
diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts
index 9f09c100c581..bee4bed3a7ee 100644
--- a/src/libs/CardUtils.ts
+++ b/src/libs/CardUtils.ts
@@ -997,6 +997,10 @@ function hasDisplayableAssignedCards(cardList: CardList | undefined): boolean {
);
}
+function isCardFrozen(card?: OnyxEntry): boolean {
+ return card?.state === CONST.EXPENSIFY_CARD.STATE.STATE_SUSPENDED && card?.nameValuePairs?.frozen != null;
+}
+
export {
getAssignedCardSortKey,
getDefaultExpensifyCardLimitType,
@@ -1070,6 +1074,7 @@ export {
isCardAlreadyAssigned,
generateCardID,
hasDisplayableAssignedCards,
+ isCardFrozen,
};
export type {CompanyCardFeedIcons, CompanyCardBankIcons};
diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts
index 7d0e831f3100..8a699bcede29 100644
--- a/src/libs/Permissions.ts
+++ b/src/libs/Permissions.ts
@@ -5,7 +5,7 @@ import type BetaConfiguration from '@src/types/onyx/BetaConfiguration';
// eslint-disable-next-line rulesdir/no-beta-handler
function canUseAllBetas(betas: OnyxEntry): boolean {
- return !!betas?.includes(CONST.BETAS.ALL);
+ return true;
}
/**
diff --git a/src/pages/settings/Wallet/ExpensifyCardPage/index.tsx b/src/pages/settings/Wallet/ExpensifyCardPage/index.tsx
index 864cc595a500..2f227ef37cba 100644
--- a/src/pages/settings/Wallet/ExpensifyCardPage/index.tsx
+++ b/src/pages/settings/Wallet/ExpensifyCardPage/index.tsx
@@ -24,7 +24,7 @@ import useNetwork from '@hooks/useNetwork';
import useOnyx from '@hooks/useOnyx';
import useThemeStyles from '@hooks/useThemeStyles';
import {resetValidateActionCodeSent} from '@libs/actions/User';
-import {formatCardExpiration, getDomainCards, maskCard, maskPin} from '@libs/CardUtils';
+import {formatCardExpiration, getDomainCards, isCardFrozen, maskCard, maskPin} from '@libs/CardUtils';
import {convertToDisplayString, getCurrencyKeyByCountryCode} from '@libs/CurrencyUtils';
import Navigation from '@libs/Navigation/Navigation';
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
@@ -43,6 +43,8 @@ import ROUTES from '@src/ROUTES';
import SCREENS from '@src/SCREENS';
import type {Card, PrivatePersonalDetails} from '@src/types/onyx';
import {useExpensifyCardActions, useExpensifyCardState} from './ExpensifyCardContextProvider';
+import { useSession } from '@components/OnyxListItemProvider';
+import usePermissions from '@hooks/usePermissions';
type ExpensifyCardPageProps =
| PlatformStackScreenProps
@@ -96,7 +98,7 @@ function ExpensifyCardPage({route}: ExpensifyCardPageProps) {
const expensifyCardTitle = isTravelCard ? translate('cardPage.expensifyTravelCard') : translate('cardPage.expensifyCard');
const pageTitle = shouldDisplayCardDomain ? expensifyCardTitle : (cardList?.[cardID]?.nameValuePairs?.cardTitle ?? expensifyCardTitle);
const {displayName} = useCurrentUserPersonalDetails();
- const expensifyIcons = useMemoizedLazyExpensifyIcons(['Flag', 'MoneySearch']);
+ const expensifyIcons = useMemoizedLazyExpensifyIcons(['Flag', 'MoneySearch', 'FreezeCard']);
const [isNotFound, setIsNotFound] = useState(false);
const cardsToShow = useMemo(() => {
@@ -155,6 +157,11 @@ function ExpensifyCardPage({route}: ExpensifyCardPageProps) {
const isSignedInAsDelegate = !!account?.delegatedAccess?.delegate || false;
+ const session = useSession();
+ const isCardHolder = currentCard?.accountID === session?.accountID;
+
+ const {isBetaEnabled} = usePermissions();
+
if (isNotFound) {
return Navigation.goBack(ROUTES.SETTINGS_WALLET)} />;
}
@@ -386,6 +393,15 @@ function ExpensifyCardPage({route}: ExpensifyCardPageProps) {
);
}}
/>
+ {isBetaEnabled(CONST.BETAS.FREEZE_CARD) && isCardHolder && !isCardFrozen(currentPhysicalCard) && (
+