From 2579a6a26dfbb4c7ba17bfd54ad0c6e383f5554e Mon Sep 17 00:00:00 2001
From: Kevin Brian Bader <kevin.bader96@gmail.com>
Date: Fri, 19 Dec 2025 14:17:14 -0800
Subject: [PATCH 1/5] fix: expand comment explaining
 FAILED_COMPANY_CARDS_ASSIGNMENTS collection

---
 src/ONYXKEYS.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts
index 1f487f0b417d..904c174edb14 100755
--- a/src/ONYXKEYS.ts
+++ b/src/ONYXKEYS.ts
@@ -709,7 +709,7 @@ const ONYXKEYS = {
          */
         WORKSPACE_CARDS_LIST: 'cards_',
 
-        /** Collection of objects where each object represents the card assignment that failed */
+        /** Collection of objects where each object represents the card assignment that failed because we can't store errors in cardList or card feed due to server-provided IDs that aren't optimistic. */
         FAILED_COMPANY_CARDS_ASSIGNMENTS: 'failedCompanyCardsAssignments_',
 
         /** Expensify cards settings */

From 0746ad2b7f8701381dfe13ac3a3cf069a19e8fe7 Mon Sep 17 00:00:00 2001
From: Kevin Brian Bader <kevin.bader96@gmail.com>
Date: Fri, 19 Dec 2025 14:33:17 -0800
Subject: [PATCH 2/5] fix: move inline margin styles to style files in
 TableHeader

---
 Mobile-Expensify                     | 1 -
 src/components/Table/TableHeader.tsx | 6 +-----
 src/styles/index.ts                  | 6 ++++++
 3 files changed, 7 insertions(+), 6 deletions(-)
 delete mode 160000 Mobile-Expensify

diff --git a/Mobile-Expensify b/Mobile-Expensify
deleted file mode 160000
index 660879c70c61..000000000000
--- a/Mobile-Expensify
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 660879c70c61843b93fafbff8abb3c731ac3772d
diff --git a/src/components/Table/TableHeader.tsx b/src/components/Table/TableHeader.tsx
index c4154cbe9466..f105898a3379 100644
--- a/src/components/Table/TableHeader.tsx
+++ b/src/components/Table/TableHeader.tsx
@@ -126,11 +126,7 @@ function TableHeaderColumn<T, ColumnKey extends string = string>({column}: {colu
             <Text
                 numberOfLines={1}
                 color={theme.textSupporting}
-                style={[
-                    styles.lh16,
-                    isSortingByColumn ? styles.textMicroBoldSupporting : [styles.textMicroSupporting, styles.pr1, {marginRight: variables.iconSizeExtraSmall, marginBottom: 1, marginTop: 1}],
-                    column.styling?.labelStyles,
-                ]}
+                style={[styles.lh16, isSortingByColumn ? styles.textMicroBoldSupporting : [styles.textMicroSupporting, styles.pr1, styles.tableHeaderIconSpacing]]}
             >
                 {column.label}
             </Text>
diff --git a/src/styles/index.ts b/src/styles/index.ts
index fbd3a48b5512..d6ebcf9d89fe 100644
--- a/src/styles/index.ts
+++ b/src/styles/index.ts
@@ -4601,6 +4601,12 @@ const staticStyles = (theme: ThemeColors) =>
             paddingHorizontal: 32,
         },
 
+        tableHeaderIconSpacing: {
+            marginRight: variables.iconSizeExtraSmall,
+            marginBottom: 1,
+            marginTop: 1,
+        },
+
         cardItemSecondaryIconStyle: {
             position: 'absolute',
             bottom: -4,

From def9ce69bc04aebfbb2202c6d96334d8034c894c Mon Sep 17 00:00:00 2001
From: Kevin Brian Bader <kevin.bader96@gmail.com>
Date: Fri, 19 Dec 2025 14:36:48 -0800
Subject: [PATCH 3/5] fix: add JSDoc comments and improve code clarity in
 useAssignCard

---
 src/hooks/useAssignCard.ts | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/hooks/useAssignCard.ts b/src/hooks/useAssignCard.ts
index 4336731b2119..0fc2edb1f31e 100644
--- a/src/hooks/useAssignCard.ts
+++ b/src/hooks/useAssignCard.ts
@@ -30,8 +30,11 @@ import useOnyx from './useOnyx';
 import usePolicy from './usePolicy';
 
 type UseAssignCardProps = {
+    /** The currently selected card feed (includes domain ID) */
     selectedFeed: CompanyCardFeedWithDomainID | undefined;
+    /** The ID of the workspace/policy */
     policyID: string;
+    /** Callback to show/hide the offline modal */
     setShouldShowOfflineModal: (shouldShow: boolean) => void;
 };
 

From 0fa4ecc51fd88965006cb90890c8a260c21552aa Mon Sep 17 00:00:00 2001
From: Kevin Brian Bader <kevin.bader96@gmail.com>
Date: Fri, 19 Dec 2025 14:45:06 -0800
Subject: [PATCH 4/5] fix: add unit tests for new CardUtils utility methods

---
 tests/unit/CardUtilsTest.ts | 74 +++++++++++++++++++++++++++++++++++++
 1 file changed, 74 insertions(+)

diff --git a/tests/unit/CardUtilsTest.ts b/tests/unit/CardUtilsTest.ts
index 2a42ca3f1226..8569bf609877 100644
--- a/tests/unit/CardUtilsTest.ts
+++ b/tests/unit/CardUtilsTest.ts
@@ -35,9 +35,11 @@ import {
     isCustomFeed as isCustomFeedCardUtils,
     isExpensifyCard,
     isExpensifyCardFullySetUp,
+    isMaskedCardNumberEqual,
     lastFourNumbersFromCardName,
     maskCardNumber,
     sortCardsByCardholderName,
+    splitMaskedCardNumber,
 } from '@src/libs/CardUtils';
 import type {Card, CardFeeds, CardList, CompanyCardFeed, CompanyCardFeedWithDomainID, ExpensifyCardSettings, PersonalDetailsList, Policy, WorkspaceCardsList} from '@src/types/onyx';
 import type {CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
@@ -1376,4 +1378,76 @@ describe('CardUtils', () => {
             expect(iconUrl).toBe(`${CONST.COMPANY_CARD_PLAID}ins_129663.png`);
         });
     });
+
+    describe('splitMaskedCardNumber', () => {
+        it('should split a masked card number correctly', () => {
+            const result = splitMaskedCardNumber('1234XXXX5678');
+            expect(result.firstDigits).toBe('1234');
+            expect(result.lastDigits).toBe('5678');
+        });
+
+        it('should handle card numbers with custom mask character', () => {
+            const result = splitMaskedCardNumber('1234****5678', '*');
+            expect(result.firstDigits).toBe('1234');
+            expect(result.lastDigits).toBe('5678');
+        });
+
+        it('should handle undefined card number', () => {
+            const result = splitMaskedCardNumber(undefined);
+            expect(result.firstDigits).toBeUndefined();
+            expect(result.lastDigits).toBeUndefined();
+        });
+
+        it('should handle card number with only first digits', () => {
+            const result = splitMaskedCardNumber('1234XXXX');
+            expect(result.firstDigits).toBe('1234');
+            expect(result.lastDigits).toBe('');
+        });
+
+        it('should handle card number with only last digits', () => {
+            const result = splitMaskedCardNumber('XXXX5678');
+            expect(result.firstDigits).toBe('');
+            expect(result.lastDigits).toBe('5678');
+        });
+    });
+    
+    describe('isMaskedCardNumberEqual', () => {
+        it('should return true for identical masked card numbers', () => {
+            expect(isMaskedCardNumberEqual('1234XXXX5678', '1234XXXX5678')).toBe(true);
+        });
+
+        it('should return true for card numbers with matching first and last digits', () => {
+            expect(isMaskedCardNumberEqual('1234XXXX5678', '1234XXXXXX5678')).toBe(true);
+        });
+
+        it('should return false for card numbers with different first digits', () => {
+            expect(isMaskedCardNumberEqual('1234XXXX5678', '5678XXXX5678')).toBe(false);
+        });
+
+        it('should return false for card numbers with different last digits', () => {
+            expect(isMaskedCardNumberEqual('1234XXXX5678', '1234XXXX1234')).toBe(false);
+        });
+
+        it('should handle undefined card numbers', () => {
+            expect(isMaskedCardNumberEqual(undefined, '1234XXXX5678')).toBe(false);
+            expect(isMaskedCardNumberEqual('1234XXXX5678', undefined)).toBe(false);
+            expect(isMaskedCardNumberEqual(undefined, undefined)).toBe(false);
+        });
+
+        it('should handle custom mask character', () => {
+            expect(isMaskedCardNumberEqual('1234****5678', '1234****5678', '*')).toBe(true);
+        });
+
+        it('should return false when compareIfPatternDoesNotMatch is false and patterns differ', () => {
+            // '1234XXXX5678' has 4 first digits and 4 last digits
+            // '123XXXX5678' has 3 first digits and 4 last digits
+            // When compareIfPatternDoesNotMatch is false, it should return false because the patterns differ
+            expect(isMaskedCardNumberEqual('1234XXXX5678', '123XXXX5678', CONST.COMPANY_CARD.CARD_NUMBER_MASK_CHAR, false)).toBe(false);
+        });
+
+        it('should return true when compareIfPatternDoesNotMatch is false and patterns match', () => {
+            // Both have 4 first digits and 4 last digits, so patterns match
+            expect(isMaskedCardNumberEqual('1234XXXX5678', '1234XXXXXX5678', CONST.COMPANY_CARD.CARD_NUMBER_MASK_CHAR, false)).toBe(true);
+        });
+    });
 });

From 8d6fa9cd384d2785f84daf9f19695c0e90ac9216 Mon Sep 17 00:00:00 2001
From: Kevin Brian Bader <kevin.bader96@gmail.com>
Date: Fri, 19 Dec 2025 14:56:00 -0800
Subject: [PATCH 5/5] fix: use translated string for card assignment failure
 error

---
 Mobile-Expensify                 | 1 +
 src/languages/de.ts              | 1 +
 src/languages/en.ts              | 1 +
 src/languages/es.ts              | 1 +
 src/languages/fr.ts              | 1 +
 src/languages/it.ts              | 1 +
 src/languages/ja.ts              | 1 +
 src/languages/nl.ts              | 1 +
 src/languages/pl.ts              | 1 +
 src/languages/pt-BR.ts           | 1 +
 src/languages/zh-hans.ts         | 1 +
 src/libs/actions/CompanyCards.ts | 2 +-
 tests/unit/CardUtilsTest.ts      | 2 +-
 13 files changed, 13 insertions(+), 2 deletions(-)
 create mode 160000 Mobile-Expensify

diff --git a/Mobile-Expensify b/Mobile-Expensify
new file mode 160000
index 000000000000..660879c70c61
--- /dev/null
+++ b/Mobile-Expensify
@@ -0,0 +1 @@
+Subproject commit 660879c70c61843b93fafbff8abb3c731ac3772d
diff --git a/src/languages/de.ts b/src/languages/de.ts
index 7b720228ba5e..66372546f3d1 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -4863,6 +4863,7 @@ _Für ausführlichere Anweisungen [besuchen Sie unsere Hilfeseite](${CONST.NETSU
             ukRegulation:
                 'Expensify Limited ist als Vertreter von Plaid Financial Ltd. tätig, einem zugelassenen Zahlungsinstitut, das von der Financial Conduct Authority gemäß den Payment Services Regulations 2017 reguliert wird (Firm Reference Number: 804718). Plaid stellt Ihnen über Expensify Limited als dessen Vertreter regulierte Kontoinformationsdienste zur Verfügung.',
             assign: 'Zuweisen',
+            assignCardFailedError: 'Kartenzuweisung fehlgeschlagen.',
         },
         expensifyCard: {
             issueAndManageCards: 'Expensify Cards ausstellen und verwalten',
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 3b10cf34ea67..4af324b29c4f 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -4747,6 +4747,7 @@ const translations = {
             chooseCardFeed: 'Choose card feed',
             ukRegulation:
                 'Expensify Limited is an agent of Plaid Financial Ltd., an authorised payment institution regulated by the Financial Conduct Authority under the Payment Services Regulations 2017 (Firm Reference Number: 804718). Plaid provides you with regulated account information services through Expensify Limited as its agent.',
+            assignCardFailedError: 'Card assignment failed.',
         },
         expensifyCard: {
             issueAndManageCards: 'Issue and manage your Expensify Cards',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 78b1cb1a5551..83eaaf037acb 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -4451,6 +4451,7 @@ ${amount} para ${merchant} - ${date}`,
             chooseCardFeed: 'Elige feed de tarjetas',
             ukRegulation:
                 'Expensify Limited es un agente de Plaid Financial Ltd., una institución de pago autorizada y regulada por la Financial Conduct Authority conforme al Reglamento de Servicios de Pago de 2017 (Número de Referencia de la Firma: 804718). Plaid te proporciona servicios regulados de información de cuentas a través de Expensify Limited como su agente.',
+            assignCardFailedError: 'Error al asignar la tarjeta.',
         },
         expensifyCard: {
             issueAndManageCards: 'Emitir y gestionar Tarjetas Expensify',
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index 932ca7f7308c..46ae6bb64814 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -4867,6 +4867,7 @@ _Pour des instructions plus détaillées, [visitez notre site d’aide](${CONST.
             ukRegulation:
                 'Expensify Limited est un agent de Plaid Financial Ltd., un établissement de paiement agréé réglementé par la Financial Conduct Authority en vertu des Payment Services Regulations 2017 (numéro de référence de l’entreprise : 804718). Plaid vous fournit des services réglementés d’information sur les comptes par l’intermédiaire de Expensify Limited en tant que son agent.',
             assign: 'Assigner',
+            assignCardFailedError: 'L’attribution de la carte a échoué.',
         },
         expensifyCard: {
             issueAndManageCards: 'Émettre et gérer vos cartes Expensify',
diff --git a/src/languages/it.ts b/src/languages/it.ts
index d49a38821005..d24cb822ecc1 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -4845,6 +4845,7 @@ _Per istruzioni più dettagliate, [visita il nostro sito di assistenza](${CONST.
             ukRegulation:
                 'Expensify Limited è un agente di Plaid Financial Ltd., un istituto di pagamento autorizzato e regolamentato dalla Financial Conduct Authority ai sensi delle Payment Services Regulations 2017 (Numero di riferimento dell’impresa: 804718). Plaid ti fornisce servizi regolamentati di informazione sui conti tramite Expensify Limited in qualità di suo agente.',
             assign: 'Assegna',
+            assignCardFailedError: 'Assegnazione della carta non riuscita.',
         },
         expensifyCard: {
             issueAndManageCards: 'Emetti e gestisci le tue Expensify Card',
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index ebb82c454dba..f6b2a4ccdaff 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -4819,6 +4819,7 @@ _より詳しい手順については、[ヘルプサイトをご覧ください
             ukRegulation:
                 'Expensify Limited は、Plaid Financial Ltd. の代理人であり、Payment Services Regulations 2017 に基づき Financial Conduct Authority によって規制されている認可支払機関です（企業登録番号：804718）。Plaid は、その代理人である Expensify Limited を通じて、規制対象の口座情報サービスをお客様に提供します。',
             assign: '割り当て',
+            assignCardFailedError: 'カードの割り当てに失敗しました。',
         },
         expensifyCard: {
             issueAndManageCards: 'Expensify カードの発行と管理',
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index 1920b4d659f7..0fa3e9c1da3b 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -4840,6 +4840,7 @@ _Voor gedetailleerdere instructies, [bezoek onze helpsite](${CONST.NETSUITE_IMPO
             ukRegulation:
                 'Expensify Limited is een agent van Plaid Financial Ltd., een erkende betalingsinstelling die wordt gereguleerd door de Financial Conduct Authority onder de Payment Services Regulations 2017 (Firm Reference Number: 804718). Plaid biedt u gereguleerde rekeninginformatiediensten via Expensify Limited als haar agent.',
             assign: 'Toewijzen',
+            assignCardFailedError: 'Toewijzing van kaart mislukt.',
         },
         expensifyCard: {
             issueAndManageCards: 'Uw Expensify Cards uitgeven en beheren',
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index c715cf1633be..d0a13d16226f 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -4831,6 +4831,7 @@ _Aby uzyskać bardziej szczegółowe instrukcje, [odwiedź naszą stronę pomocy
             ukRegulation:
                 'Expensify Limited jest agentem Plaid Financial Ltd., autoryzowanej instytucji płatniczej regulowanej przez Financial Conduct Authority na mocy Payment Services Regulations 2017 (Numer Referencyjny Firmy: 804718). Plaid świadczy Ci regulowane usługi informacji o rachunku za pośrednictwem Expensify Limited jako swojego agenta.',
             assign: 'Przypisz',
+            assignCardFailedError: 'Przypisanie karty nie powiodło się.',
         },
         expensifyCard: {
             issueAndManageCards: 'Wydawaj i zarządzaj swoimi kartami Expensify',
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index 7a2bfcc95e4c..22820a0a746f 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -4832,6 +4832,7 @@ _Para instruções mais detalhadas, [visite nosso site de ajuda](${CONST.NETSUIT
             ukRegulation:
                 'Expensify Limited é um agente da Plaid Financial Ltd., uma instituição de pagamento autorizada e regulada pela Financial Conduct Authority sob as Payment Services Regulations 2017 (Número de Referência da Empresa: 804718). A Plaid fornece a você serviços regulados de informações de conta por meio da Expensify Limited como seu agente.',
             assign: 'Atribuir',
+            assignCardFailedError: 'Falha na atribuição do cartão.',
         },
         expensifyCard: {
             issueAndManageCards: 'Emitir e gerenciar seus Cartões Expensify',
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index 0842cf34003e..155979c24733 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -4748,6 +4748,7 @@ _如需更详细的说明，请[访问我们的帮助网站](${CONST.NETSUITE_IM
             ukRegulation:
                 'Expensify Limited 是 Plaid Financial Ltd. 的代理机构，Plaid Financial Ltd. 是一家在《2017 年支付服务条例》下受金融行为监管局（Financial Conduct Authority）监管并获授权的支付机构（公司参考编号：804718）。Plaid 通过其代理 Expensify Limited 向您提供受监管的账户信息服务。',
             assign: '分配',
+            assignCardFailedError: '卡片分配失败。',
         },
         expensifyCard: {
             issueAndManageCards: '发放和管理您的 Expensify 卡',
diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts
index 73cde3c85807..25b0019923fc 100644
--- a/src/libs/actions/CompanyCards.ts
+++ b/src/libs/actions/CompanyCards.ts
@@ -323,7 +323,7 @@ function assignWorkspaceCompanyCard(policy: OnyxEntry<Policy>, domainOrWorkspace
         cardNumber: encryptedCardNumber,
         pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD,
         errors: {
-            failed: 'Card assignment failed.',
+            failed: Localize.translateLocal('workspace.companyCards.assignCardFailedError'),
         },
     };
 
diff --git a/tests/unit/CardUtilsTest.ts b/tests/unit/CardUtilsTest.ts
index 8569bf609877..120d123ad42f 100644
--- a/tests/unit/CardUtilsTest.ts
+++ b/tests/unit/CardUtilsTest.ts
@@ -1410,7 +1410,7 @@ describe('CardUtils', () => {
             expect(result.lastDigits).toBe('5678');
         });
     });
-    
+
     describe('isMaskedCardNumberEqual', () => {
         it('should return true for identical masked card numbers', () => {
             expect(isMaskedCardNumberEqual('1234XXXX5678', '1234XXXX5678')).toBe(true);
