Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
cc41517
Implement frozen-by-admin info and ask-to-unfreeze functionality
ShridharGoel Feb 28, 2026
c550cf9
Implement frozen-by-admin info and ask-to-unfreeze functionality
ShridharGoel Feb 28, 2026
2b78bdb
Address comments
ShridharGoel Feb 28, 2026
5259ca6
Merge main
ShridharGoel Mar 4, 2026
2cef018
Merge main
ShridharGoel Mar 4, 2026
1a3c1c8
Prettier fix
ShridharGoel Mar 4, 2026
04d0574
Pass missing props from WorkspaceExpensifyCardDetailsPage
ShridharGoel Mar 5, 2026
4df6e50
Fix naming
ShridharGoel Mar 6, 2026
4503498
Update status text and add link
ShridharGoel Mar 11, 2026
e7e0ccb
Merge branch 'main' of https://github.com/Expensify/App into freezeCard9
ShridharGoel Mar 11, 2026
d3f44ef
Update
ShridharGoel Mar 11, 2026
5830ba3
Merge branch 'main' of https://github.com/Expensify/App into freezeCard9
ShridharGoel Mar 11, 2026
d513564
Add link in the text shown to member also
ShridharGoel Mar 11, 2026
449f645
Merge branch 'main' into freezeCard9
ShridharGoel Mar 18, 2026
e848cec
Show frozen card details in Expensify card list
ShridharGoel Mar 22, 2026
8b7dedd
Upgrade React Native and Expo dependencies
ShridharGoel Mar 22, 2026
d3fea6c
Fix lint
ShridharGoel Mar 22, 2026
a521cae
Enable for narrow layouts as well
ShridharGoel Mar 22, 2026
8ef5660
Update import path for resolveDetachReceiptConflicts
ShridharGoel Mar 23, 2026
86a7ee0
Update import path for clearCachedAttachments
ShridharGoel Mar 23, 2026
35775d3
Merge branch 'main' of https://github.com/Expensify/App into freezeCard9
ShridharGoel Mar 23, 2026
7950bea
Update text shown in card rows
ShridharGoel Mar 23, 2026
8a03271
Fix lint
ShridharGoel Mar 23, 2026
c224e07
Merge branch 'main' of https://github.com/Expensify/App into freezeCard9
ShridharGoel Mar 23, 2026
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
67 changes: 51 additions & 16 deletions src/components/FrozenCardHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {cardByIdSelector} from '@selectors/Card';
import React, {useMemo} from 'react';
import React from 'react';
import {View} from 'react-native';
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
Expand All @@ -9,41 +8,77 @@ import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import DateUtils from '@libs/DateUtils';
import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils';
import Navigation from '@navigation/Navigation';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import Button from './Button';
import Icon from './Icon';
import Text from './Text';
import TextLink from './TextLink';

type FrozenCardHeaderProps = {
cardID: string;
cardPreview: React.ReactNode;
onUnfreezePress: () => void;
onAskToUnfreezePress: () => void;
canUnfreezeCard: boolean;
isWorkspaceAdmin: boolean;
frozenByAccountID?: number;
frozenDate?: string;
};

function FrozenCardHeader({cardID, cardPreview, onUnfreezePress}: FrozenCardHeaderProps) {
function FrozenCardHeader({cardPreview, onUnfreezePress, onAskToUnfreezePress, canUnfreezeCard, isWorkspaceAdmin, frozenByAccountID, frozenDate}: FrozenCardHeaderProps) {
const styles = useThemeStyles();
const theme = useTheme();
const {translate} = useLocalize();
const {isOffline} = useNetwork();
const icons = useMemoizedLazyExpensifyIcons(['FreezeCard'] as const);
const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST);
const [session] = useOnyx(ONYXKEYS.SESSION);
const [card] = useOnyx(ONYXKEYS.CARD_LIST, {selector: cardByIdSelector(cardID)});

const frozenByAccountID = card?.nameValuePairs?.frozen?.byAccountID;
const frozenDate = card?.nameValuePairs?.frozen?.date;
const isCurrentUser = frozenByAccountID === session?.accountID;

const frozenByName = frozenByAccountID ? getDisplayNameOrDefault(personalDetails?.[frozenByAccountID]) : '';
const formattedDate = frozenDate ? DateUtils.formatWithUTCTimeZone(frozenDate, CONST.DATE.MONTH_DAY_YEAR_ABBR_FORMAT) : '';
const adminFrozenTextPrefix = translate('cardPage.frozenByAdminPrefix', {date: formattedDate});
const frozenNeedsUnfreezePrefix = translate('cardPage.frozenByAdminNeedsUnfreezePrefix');
const frozenNeedsUnfreezeSuffix = translate('cardPage.frozenByAdminNeedsUnfreezeSuffix');

let statusText: React.ReactNode;

const statusText = useMemo(() => {
if (isCurrentUser) {
return translate('cardPage.youFroze', {date: formattedDate});
if (isCurrentUser) {
statusText = translate('cardPage.youFroze', {date: formattedDate});
} else if (isWorkspaceAdmin) {
if (!frozenByAccountID || !frozenByName) {
statusText = `${adminFrozenTextPrefix}${translate('common.someone')}`;
} else {
statusText = (
<>
{adminFrozenTextPrefix}
<TextLink
onPress={() => Navigation.navigate(ROUTES.PROFILE.getRoute(Number(frozenByAccountID), Navigation.getActiveRoute()))}
style={styles.link}
>
{frozenByName}
</TextLink>
</>
);
}
return translate('cardPage.frozenBy', {date: formattedDate, person: frozenByName});
}, [formattedDate, frozenByName, isCurrentUser, translate]);
} else if (!frozenByAccountID || !frozenByName) {
statusText = translate('cardPage.frozenByAdminNeedsUnfreeze', {person: frozenByName || translate('common.someone')});
} else {
statusText = (
<>
{frozenNeedsUnfreezePrefix}
<TextLink
onPress={() => Navigation.navigate(ROUTES.PROFILE.getRoute(Number(frozenByAccountID), Navigation.getActiveRoute()))}
style={styles.link}
>
{frozenByName}
</TextLink>
{frozenNeedsUnfreezeSuffix}
</>
);
}

return (
<View style={[styles.ph5, styles.pb5]}>
Expand All @@ -58,9 +93,9 @@ function FrozenCardHeader({cardID, cardPreview, onUnfreezePress}: FrozenCardHead
</View>
<Button
medium
text={translate('cardPage.unfreeze')}
onPress={onUnfreezePress}
isDisabled={isOffline}
text={translate(canUnfreezeCard ? 'cardPage.unfreeze' : 'cardPage.askToUnfreeze')}
onPress={canUnfreezeCard ? onUnfreezePress : onAskToUnfreezePress}
isDisabled={canUnfreezeCard && isOffline}
style={[styles.mt4]}
/>
</View>
Expand Down
5 changes: 5 additions & 0 deletions src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2367,12 +2367,17 @@ ${amount} für ${merchant} – ${date}`,
freezeCard: 'Karte sperren',
unfreeze: 'Entsperren',
unfreezeCard: 'Karte entsperren',
askToUnfreeze: 'Entsperrung anfragen',
freezeDescription: 'Eine gesperrte Karte kann nicht für Käufe und Transaktionen verwendet werden. Du kannst sie jederzeit entsperren.',
unfreezeDescription:
'Durch das Entsperren dieser Karte werden Käufe und Transaktionen wieder zugelassen. Fahre nur fort, wenn du sicher bist, dass die Karte sicher verwendet werden kann.',
frozen: 'Gesperrt',
youFroze: ({date}: {date: string}) => `Du hast diese Karte am ${date} gesperrt.`,
frozenBy: ({person, date}: {person: string; date: string}) => `${person} hat diese Karte am ${date} gesperrt.`,
frozenByAdminPrefix: ({date}: {date: string}) => `Diese Karte wurde am ${date} gesperrt von `,
frozenByAdminNeedsUnfreezePrefix: 'Diese Karte wurde von ',
frozenByAdminNeedsUnfreezeSuffix: ' gesperrt. Bitte kontaktiere einen Admin, um sie zu entsperren.',
frozenByAdminNeedsUnfreeze: ({person}: {person: string}) => `Diese Karte wurde von ${person} gesperrt. Bitte kontaktiere einen Admin, um sie zu entsperren.`,
},
workflowsPage: {
workflowTitle: 'Ausgaben',
Expand Down
5 changes: 5 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2419,11 +2419,16 @@ const translations = {
freezeCard: 'Freeze card',
unfreeze: 'Unfreeze',
unfreezeCard: 'Unfreeze card',
askToUnfreeze: 'Ask to unfreeze',
freezeDescription: 'A frozen card cannot be used for purchases and transactions. You can unfreeze it at any time.',
unfreezeDescription: "Unfreezing this card will start allowing purchases and transactions again. Only proceed if you're sure the card is safe to use.",
frozen: 'Frozen',
youFroze: ({date}: {date: string}) => `You froze this card on ${date}.`,
frozenBy: ({person, date}: {person: string; date: string}) => `${person} froze this card on ${date}.`,
frozenByAdminPrefix: ({date}: {date: string}) => `This card was frozen on ${date} by `,
frozenByAdminNeedsUnfreezePrefix: 'This card was frozen by ',
frozenByAdminNeedsUnfreezeSuffix: '. Please contact an admin to unfreeze it.',
frozenByAdminNeedsUnfreeze: ({person}: {person: string}) => `This card was frozen by ${person}. Please contact an admin to unfreeze it.`,
},
workflowsPage: {
workflowTitle: 'Spend',
Expand Down
5 changes: 5 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2277,11 +2277,16 @@ ${amount} para ${merchant} - ${date}`,
freezeCard: 'Congelar tarjeta',
unfreeze: 'Descongelar',
unfreezeCard: 'Descongelar tarjeta',
askToUnfreeze: 'Solicitar descongelación',
freezeDescription: 'Una tarjeta congelada no se puede usar para compras ni transacciones. Puedes descongelarla en cualquier momento.',
unfreezeDescription: 'Al descongelar esta tarjeta se volverán a permitir compras y transacciones. Continúa solo si estás seguro de que la tarjeta es segura para usar.',
frozen: 'Congelada',
youFroze: ({date}: {date: string}) => `Congelaste esta tarjeta el ${date}.`,
frozenBy: ({person, date}: {person: string; date: string}) => `${person} congeló esta tarjeta el ${date}.`,
frozenByAdminPrefix: ({date}: {date: string}) => `Esta tarjeta fue congelada el ${date} por `,
frozenByAdminNeedsUnfreezePrefix: 'Esta tarjeta fue congelada por ',
frozenByAdminNeedsUnfreezeSuffix: '. Ponte en contacto con un administrador para descongelarla.',
frozenByAdminNeedsUnfreeze: ({person}: {person: string}) => `Esta tarjeta fue congelada por ${person}. Ponte en contacto con un administrador para descongelarla.`,
},
workflowsPage: {
workflowTitle: 'Gasto',
Expand Down
5 changes: 5 additions & 0 deletions src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2374,12 +2374,17 @@ ${amount} pour ${merchant} - ${date}`,
freezeCard: 'Geler la carte',
unfreeze: 'Dégeler',
unfreezeCard: 'Dégeler la carte',
askToUnfreeze: 'Demander le dégel',
freezeDescription: 'Une carte gelée ne peut pas être utilisée pour des achats ni des transactions. Vous pouvez la dégeler à tout moment.',
unfreezeDescription:
'Dégeler cette carte permettra à nouveau les achats et les transactions. Continuez uniquement si vous êtes sûr(e) que la carte peut être utilisée en toute sécurité.',
frozen: 'Gelée',
youFroze: ({date}: {date: string}) => `Vous avez gelé cette carte le ${date}.`,
frozenBy: ({person, date}: {person: string; date: string}) => `${person} a gelé cette carte le ${date}.`,
frozenByAdminPrefix: ({date}: {date: string}) => `Cette carte a été gelée le ${date} par `,
frozenByAdminNeedsUnfreezePrefix: 'Cette carte a été gelée par ',
frozenByAdminNeedsUnfreezeSuffix: '. Veuillez contacter un administrateur pour la dégeler.',
frozenByAdminNeedsUnfreeze: ({person}: {person: string}) => `Cette carte a été gelée par ${person}. Veuillez contacter un administrateur pour la dégeler.`,
},
workflowsPage: {
workflowTitle: 'Dépense',
Expand Down
5 changes: 5 additions & 0 deletions src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2364,11 +2364,16 @@ ${amount} per ${merchant} - ${date}`,
freezeCard: 'Blocca carta',
unfreeze: 'Sblocca',
unfreezeCard: 'Sblocca carta',
askToUnfreeze: 'Richiedi sblocco',
freezeDescription: 'Una carta bloccata non può essere usata per acquisti e transazioni. Puoi sbloccarla in qualsiasi momento.',
unfreezeDescription: 'Sbloccando questa carta torneranno ad essere consentiti acquisti e transazioni. Procedi solo se sei sicuro che la carta sia sicura da usare.',
frozen: 'Bloccata',
youFroze: ({date}: {date: string}) => `Hai bloccato questa carta il ${date}.`,
frozenBy: ({person, date}: {person: string; date: string}) => `${person} ha bloccato questa carta il ${date}.`,
frozenByAdminPrefix: ({date}: {date: string}) => `Questa carta è stata bloccata il ${date} da `,
frozenByAdminNeedsUnfreezePrefix: 'Questa carta è stata bloccata da ',
frozenByAdminNeedsUnfreezeSuffix: '. Contatta un amministratore per sbloccarla.',
frozenByAdminNeedsUnfreeze: ({person}: {person: string}) => `Questa carta è stata bloccata da ${person}. Contatta un amministratore per sbloccarla.`,
},
workflowsPage: {
workflowTitle: 'Spesa',
Expand Down
5 changes: 5 additions & 0 deletions src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2339,11 +2339,16 @@ ${date} の ${merchant} への ${amount}`,
freezeCard: 'カードを一時停止',
unfreeze: '再開',
unfreezeCard: 'カードの一時停止を解除',
askToUnfreeze: '一時停止解除を依頼',
freezeDescription: '一時停止したカードは購入や取引に使用できません。いつでも再開できます。',
unfreezeDescription: 'このカードの一時停止を解除すると、購入と取引が再び可能になります。カードが安全に使用できると確信できる場合にのみ続行してください。',
frozen: '凍結中',
youFroze: ({date}: {date: string}) => `${date}にこのカードを一時停止しました。`,
frozenBy: ({person, date}: {person: string; date: string}) => `${person}が${date}にこのカードを一時停止しました。`,
frozenByAdminPrefix: ({date}: {date: string}) => `${date}にこのカードを一時停止したのは`,
frozenByAdminNeedsUnfreezePrefix: 'このカードは',
frozenByAdminNeedsUnfreezeSuffix: 'によって一時停止されました。解除するには管理者に連絡してください。',
frozenByAdminNeedsUnfreeze: ({person}: {person: string}) => `このカードは${person}によって一時停止されました。解除するには管理者に連絡してください。`,
},
workflowsPage: {
workflowTitle: '支出',
Expand Down
5 changes: 5 additions & 0 deletions src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2362,11 +2362,16 @@ ${amount} voor ${merchant} - ${date}`,
freezeCard: 'Kaart blokkeren',
unfreeze: 'Deblokkeren',
unfreezeCard: 'Kaart deblokkeren',
askToUnfreeze: 'Vraag om deblokkering',
freezeDescription: 'Een geblokkeerde kaart kan niet worden gebruikt voor aankopen en transacties. Je kunt deze op elk moment deblokkeren.',
unfreezeDescription: 'Door deze kaart te deblokkeren worden aankopen en transacties weer toegestaan. Ga alleen verder als je zeker weet dat de kaart veilig is om te gebruiken.',
frozen: 'Geblokkeerd',
youFroze: ({date}: {date: string}) => `Je hebt deze kaart op ${date} geblokkeerd.`,
frozenBy: ({person, date}: {person: string; date: string}) => `${person} heeft deze kaart op ${date} geblokkeerd.`,
frozenByAdminPrefix: ({date}: {date: string}) => `Deze kaart is op ${date} bevroren door `,
frozenByAdminNeedsUnfreezePrefix: 'Deze kaart is bevroren door ',
frozenByAdminNeedsUnfreezeSuffix: '. Neem contact op met een beheerder om deze te deblokkeren.',
frozenByAdminNeedsUnfreeze: ({person}: {person: string}) => `Deze kaart is bevroren door ${person}. Neem contact op met een beheerder om deze te deblokkeren.`,
},
workflowsPage: {
workflowTitle: 'Uitgaven',
Expand Down
5 changes: 5 additions & 0 deletions src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2358,11 +2358,16 @@ ${amount} dla ${merchant} - ${date}`,
freezeCard: 'Zamroź kartę',
unfreeze: 'Odmroź',
unfreezeCard: 'Odmroź kartę',
askToUnfreeze: 'Poproś o odmrożenie',
freezeDescription: 'Zamrożonej karty nie można używać do zakupów i transakcji. Możesz ją odmrozić w dowolnym momencie.',
unfreezeDescription: 'Odmrożenie tej karty ponownie umożliwi zakupy i transakcje. Kontynuuj tylko wtedy, gdy masz pewność, że korzystanie z karty jest bezpieczne.',
frozen: 'Zamrożona',
youFroze: ({date}: {date: string}) => `Zamroziłeś tę kartę ${date}.`,
frozenBy: ({person, date}: {person: string; date: string}) => `${person} zamroził(a) tę kartę ${date}.`,
frozenByAdminPrefix: ({date}: {date: string}) => `Ta karta została zamrożona ${date} przez `,
frozenByAdminNeedsUnfreezePrefix: 'Ta karta została zamrożona przez ',
frozenByAdminNeedsUnfreezeSuffix: '. Skontaktuj się z administratorem, aby ją odmrozić.',
frozenByAdminNeedsUnfreeze: ({person}: {person: string}) => `Ta karta została zamrożona przez ${person}. Skontaktuj się z administratorem, aby ją odmrozić.`,
},
workflowsPage: {
workflowTitle: 'Wydatki',
Expand Down
5 changes: 5 additions & 0 deletions src/languages/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2355,11 +2355,16 @@ ${amount} para ${merchant} - ${date}`,
freezeCard: 'Bloquear cartão',
unfreeze: 'Desbloquear',
unfreezeCard: 'Desbloquear cartão',
askToUnfreeze: 'Pedir desbloqueio',
freezeDescription: 'Um cartão bloqueado não pode ser usado para compras e transações. Você pode desbloqueá-lo a qualquer momento.',
unfreezeDescription: 'Ao desbloquear este cartão, compras e transações voltarão a ser permitidas. Continue apenas se tiver certeza de que o cartão é seguro para uso.',
frozen: 'Congelado',
youFroze: ({date}: {date: string}) => `Você bloqueou este cartão em ${date}.`,
frozenBy: ({person, date}: {person: string; date: string}) => `${person} bloqueou este cartão em ${date}.`,
frozenByAdminPrefix: ({date}: {date: string}) => `Este cartão foi bloqueado em ${date} por `,
frozenByAdminNeedsUnfreezePrefix: 'Este cartão foi bloqueado por ',
frozenByAdminNeedsUnfreezeSuffix: '. Entre em contato com um administrador para desbloqueá-lo.',
frozenByAdminNeedsUnfreeze: ({person}: {person: string}) => `Este cartão foi bloqueado por ${person}. Entre em contato com um administrador para desbloqueá-lo.`,
},
workflowsPage: {
workflowTitle: 'Gastos',
Expand Down
5 changes: 5 additions & 0 deletions src/languages/zh-hans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2299,11 +2299,16 @@ ${amount},商户:${merchant} - 日期:${date}`,
freezeCard: '冻结卡片',
unfreeze: '解冻',
unfreezeCard: '解冻卡片',
askToUnfreeze: '申请解冻',
freezeDescription: '冻结的卡片无法用于购买和交易。你可以随时解冻。',
unfreezeDescription: '解冻此卡后,将重新允许购买和交易。仅当你确定该卡可以安全使用时再继续。',
frozen: '已冻结',
youFroze: ({date}: {date: string}) => `你于${date}冻结了此卡。`,
frozenBy: ({person, date}: {person: string; date: string}) => `${person}于${date}冻结了此卡。`,
frozenByAdminPrefix: ({date}: {date: string}) => `此卡于${date}被冻结,操作人是`,
frozenByAdminNeedsUnfreezePrefix: '此卡已被',
frozenByAdminNeedsUnfreezeSuffix: '冻结。请联系管理员解冻。',
frozenByAdminNeedsUnfreeze: ({person}: {person: string}) => `此卡已被${person}冻结。请联系管理员解冻。`,
},
workflowsPage: {
workflowTitle: '支出',
Expand Down
Loading
Loading