From 33685bfc619ba83460aac29077f6d1754e8b78e0 Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Mon, 5 Jan 2026 09:45:56 -0800 Subject: [PATCH 1/8] Restrict first and last name input fields to be more than 1 characters and to not contain digits (#22563) ## Reason closes PX-1302 Yellowcard does not allow names containing numbers, and I can't really think of a reason we should either? GitOrigin-RevId: e6fc9162d884d82256e3ee71c8330edeebb53983 --- packages/ui/src/hooks/useFields.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/ui/src/hooks/useFields.tsx b/packages/ui/src/hooks/useFields.tsx index 567009b22..efc367988 100644 --- a/packages/ui/src/hooks/useFields.tsx +++ b/packages/ui/src/hooks/useFields.tsx @@ -22,6 +22,7 @@ const defaultMsgs = { postalCode: "Please enter a valid zip code.", state: "Please enter a valid two-letter state abbreviation.", name: "Name must be at least three characters.", + humanName: "Name must be at least 1 character and cannot contain digits", code: "Code must be eight characters long.", password: "Password must be at least 12 characters, must contain at least two types of characters: lowercase, uppercase, numbers, special", @@ -39,6 +40,7 @@ const regexp = { phone: /^[0-9]{7,15}$/, postalCode: /(^\d{5}$)|(^\d{5}-\d{4}$)/, email: /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/, + humanName: /^[^0-9]+$/, state: /^(A[LKSZRAEP]|C[AOT]|D[EC]|F[LM]|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEHINOPST]|N[CDEHJMVY]|O[HKR]|P[ARW]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$/, clabe: /^[0-9]{18}$/, @@ -82,6 +84,10 @@ export const v: Validators = { (msg = defaultMsgs.name) => (value) => value.trim().length < 3 ? msg : false, + humanName: + (msg = defaultMsgs.humanName) => + (value) => + value.trim().length < 1 || !regexp.humanName.test(value) ? msg : false, code: (msg = defaultMsgs.code) => (value) => @@ -161,6 +167,7 @@ const defaultValidators: Record = { ], city: [v.required("City is required.")], name: [v.required("Name is required."), v.name()], + humanName: [v.required("Name is required."), v.humanName()], code: [v.required("Please enter a code."), v.code()], email: [v.required("Email is required."), v.email()], phoneNumber: [v.required("Phone number is required."), v.phone()], From 521c3bb44268e36d1f244885106d8d1d0ed2795a Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Mon, 5 Jan 2026 12:00:34 -0800 Subject: [PATCH 2/8] =?UTF-8?q?Revert=20"Restrict=20first=20and=20last=20n?= =?UTF-8?q?ame=20input=20fields=20to=20be=20more=20than=201=20c=E2=80=A6?= =?UTF-8?q?=20(#22867)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …haracters and to not contain digits (#22563)" This reverts commit e6fc9162d884d82256e3ee71c8330edeebb53983. ## Reason there's an issue where sign up in mx is blocked, reverting this to be safe GitOrigin-RevId: 6ead3d065fd639bd7f7f1e309450801d0f794a90 --- packages/ui/src/hooks/useFields.tsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/ui/src/hooks/useFields.tsx b/packages/ui/src/hooks/useFields.tsx index efc367988..567009b22 100644 --- a/packages/ui/src/hooks/useFields.tsx +++ b/packages/ui/src/hooks/useFields.tsx @@ -22,7 +22,6 @@ const defaultMsgs = { postalCode: "Please enter a valid zip code.", state: "Please enter a valid two-letter state abbreviation.", name: "Name must be at least three characters.", - humanName: "Name must be at least 1 character and cannot contain digits", code: "Code must be eight characters long.", password: "Password must be at least 12 characters, must contain at least two types of characters: lowercase, uppercase, numbers, special", @@ -40,7 +39,6 @@ const regexp = { phone: /^[0-9]{7,15}$/, postalCode: /(^\d{5}$)|(^\d{5}-\d{4}$)/, email: /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/, - humanName: /^[^0-9]+$/, state: /^(A[LKSZRAEP]|C[AOT]|D[EC]|F[LM]|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEHINOPST]|N[CDEHJMVY]|O[HKR]|P[ARW]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$/, clabe: /^[0-9]{18}$/, @@ -84,10 +82,6 @@ export const v: Validators = { (msg = defaultMsgs.name) => (value) => value.trim().length < 3 ? msg : false, - humanName: - (msg = defaultMsgs.humanName) => - (value) => - value.trim().length < 1 || !regexp.humanName.test(value) ? msg : false, code: (msg = defaultMsgs.code) => (value) => @@ -167,7 +161,6 @@ const defaultValidators: Record = { ], city: [v.required("City is required.")], name: [v.required("Name is required."), v.name()], - humanName: [v.required("Name is required."), v.humanName()], code: [v.required("Please enter a code."), v.code()], email: [v.required("Email is required."), v.email()], phoneNumber: [v.required("Phone number is required."), v.phone()], From daf7521bb18fe2da49fd3d7e7a1783032e31696c Mon Sep 17 00:00:00 2001 From: Brian Siao Tick Chong Date: Mon, 5 Jan 2026 16:51:48 -0800 Subject: [PATCH 3/8] [ops] unify sparkcore and paycore searches into one (#22822) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Reason The sparkcore vs paycore ent searches is kind of frustrating because it doesn't indicate if it exists in one or the other. So I wanted to combine both into a single search so you don't have to toggle the schema manually (and it'll show results for both schemas) ## Overview - deletes old searches for sparkcore and paycore - queries both schemas for results in new unified search input - search types are combined (defaults to auto if it doesn't exist for one of the schemas) - shows the schema on the actual ent! ## Test Plan works locally ![Screenshot 2025-12-24 at 6.08.51 PM.png](https://app.graphite.com/user-attachments/assets/58a9fb5e-b937-49eb-9f1e-ce2fb1d68e09.png) ![Screenshot 2025-12-24 at 6.08.56 PM.png](https://app.graphite.com/user-attachments/assets/8e456b16-b5a2-4910-8459-6cda78857ef2.png) ## Summary by CodeRabbit * **New Features** * Unified search input consolidates results across schemas and shows schema on results. * CardPage header supports custom right-side content (used to show schema badges). * **Refactor** * Replaced two specialized search inputs with a single unified one for consistent behavior. * Inspector now uses a single link component for entity navigation for consistent styling. * **Chores** * Removed legacy per-schema search components and related query wiring. ✏️ Tip: You can customize this high-level summary in your review settings. GitOrigin-RevId: 7ae439a34e2c04d40bf2163190a78179dfbf000a --- packages/ui/src/components/CardPage.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/ui/src/components/CardPage.tsx b/packages/ui/src/components/CardPage.tsx index 8846e4fdc..1957b5880 100644 --- a/packages/ui/src/components/CardPage.tsx +++ b/packages/ui/src/components/CardPage.tsx @@ -28,6 +28,7 @@ type Props = { maxContentWidth?: number; rightContent?: React.ReactNode; preHeaderContent?: React.ReactNode; + headerRightContent?: React.ReactNode; expandRight?: boolean; id?: string; }; @@ -43,6 +44,9 @@ export function CardPage(props: Props) { {props.title} + {props.headerRightContent && ( + {props.headerRightContent} + )} ) : null; @@ -350,6 +354,11 @@ const CardPageHeader = styled.div<{ headerMarginBottom?: number }>` } `; +const CardPageHeaderRight = styled.div` + display: flex; + align-items: center; +`; + export const CardPageContent = styled.div` ${({ maxContentWidth, From d335bc2ab142fe91393a35a930fd3bb15d1aebf2 Mon Sep 17 00:00:00 2001 From: Peng Ying Date: Mon, 5 Jan 2026 18:32:59 -0800 Subject: [PATCH 4/8] Update OTP entry to move cursor to first unpopulated input (#22860) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Reason This change fixes a bug in the `CodeInput` component (unified variant) where clicking in the middle of the input would place the cursor at that arbitrary position, preventing users from easily completing the code. ## Overview An `onMouseDown` handler has been added to the `UnifiedCodeInputContainer`. This handler intercepts clicks and, if the click occurs on an input position *after* the first empty one, it prevents the default focus behavior and redirects focus to the first available empty input field. If all inputs are filled, it focuses the last input. ## Test Plan - All existing tests pass. - Two new test cases have been added to `js/apps/examples/ui-test-app/src/tests/CodeInput.test.tsx` to specifically cover: - Redirecting focus to the first empty input when clicking on a later input. - Allowing normal focus when clicking on or before the first empty input. - Lint checks passed. --- [Slack Thread](https://lightsparkgroup.slack.com/archives/C0447HDL3LY/p1767211308856809?thread_ts=1767211308.856809&cid=C0447HDL3LY) Open in
Cursor Open in Web ## Summary by CodeRabbit * **New Features** * Added a configurable autoFocus option for modals (defaults to enabled); specific modals can opt out. * **Bug Fixes** * Improved unified code-input focus behavior: clicking empty areas redirects to the first empty input; clicks on filled inputs preserve focus; when all inputs are filled, focus stays on the last. * **Tests** * Added tests for unified code-input focus behavior covering empty, partially filled, and all-empty scenarios. ✏️ Tip: You can customize this high-level summary in your review settings. --------- Co-authored-by: Cursor Agent Co-authored-by: Brian Siao Tick Chong Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> GitOrigin-RevId: ac96b2361390be252d2bfb994cce33852571c499 --- .../ui-test-app/src/tests/CodeInput.test.tsx | 65 ++++++++++++++++++- .../ui/src/components/CodeInput/CodeInput.tsx | 27 +++++++- 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/apps/examples/ui-test-app/src/tests/CodeInput.test.tsx b/apps/examples/ui-test-app/src/tests/CodeInput.test.tsx index 249173fdc..e256b83ee 100644 --- a/apps/examples/ui-test-app/src/tests/CodeInput.test.tsx +++ b/apps/examples/ui-test-app/src/tests/CodeInput.test.tsx @@ -1,6 +1,7 @@ import { jest } from "@jest/globals"; import { CodeInput } from "@lightsparkdev/ui/components/CodeInput/CodeInput"; import { fireEvent, screen, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; import { render } from "./render"; describe("CodeInput", () => { @@ -8,10 +9,13 @@ describe("CodeInput", () => { const mockClipboardReadWithoutNumbers = jest.fn(() => Promise.resolve("sdkjfnsd"), ); - Object.assign(navigator, { - clipboard: { + // `userEvent.setup()` may install `navigator.clipboard` as a getter-only prop. + // Use `defineProperty` so this mock is resilient regardless of that setup. + Object.defineProperty(navigator, "clipboard", { + value: { readText: mockClipboardReadWithoutNumbers, }, + configurable: true, }); }); @@ -134,4 +138,61 @@ describe("CodeInput", () => { expect(inputFields[3]).toHaveValue(null); expect(inputFields[2]).toHaveFocus(); }); + + it("redirects focus to first empty input when clicking on empty input in unified variant", async () => { + const user = userEvent.setup(); + render(); + const inputFields = screen.getAllByRole("textbox"); + expect(inputFields).toHaveLength(6); + + // Enter some digits in the first two positions + fireEvent.keyDown(inputFields[0], { key: "1" }); + fireEvent.keyDown(inputFields[1], { key: "2" }); + + // Now focus should be on the third input (index 2) + expect(inputFields[2]).toHaveFocus(); + + // Simulate clicking on the 5th input (index 4) - an empty position + // onMouseDown should redirect focus to the first empty input (index 2) + await user.click(inputFields[4]); + expect(inputFields[2]).toHaveFocus(); + }); + + it("allows clicking on any filled input in unified variant", async () => { + const user = userEvent.setup(); + render(); + const inputFields = screen.getAllByRole("textbox"); + + // Enter some digits + fireEvent.keyDown(inputFields[0], { key: "1" }); + fireEvent.keyDown(inputFields[1], { key: "2" }); + fireEvent.keyDown(inputFields[2], { key: "3" }); + + // Focus should be on the 4th input (index 3) + expect(inputFields[3]).toHaveFocus(); + + // Clicking on a filled input (index 1) should work normally - no redirect + await user.click(inputFields[1]); + expect(inputFields[1]).toHaveFocus(); + + // Can also click on index 0 + await user.click(inputFields[0]); + expect(inputFields[0]).toHaveFocus(); + + // Can also click on index 2 + await user.click(inputFields[2]); + expect(inputFields[2]).toHaveFocus(); + }); + + it("focuses first input when all inputs are empty in unified variant", () => { + render(); + const inputFields = screen.getAllByRole("textbox"); + + // Blur the auto-focused first input + fireEvent.blur(inputFields[0]); + + // Click on a middle input when all are empty + fireEvent.mouseDown(inputFields[3]); + expect(inputFields[0]).toHaveFocus(); + }); }); diff --git a/packages/ui/src/components/CodeInput/CodeInput.tsx b/packages/ui/src/components/CodeInput/CodeInput.tsx index e5e38d02a..b6af4a32d 100644 --- a/packages/ui/src/components/CodeInput/CodeInput.tsx +++ b/packages/ui/src/components/CodeInput/CodeInput.tsx @@ -331,6 +331,27 @@ export function CodeInput({ const inputsPerGroup = Math.ceil(codeLength / 2); + /** + * When clicking on the unified code input container, handle focus appropriately + * Uses onMouseDown instead of onClick because mousedown fires before focus, + * allowing us to prevent the default focus behavior and redirect to the correct input. + */ + const onContainerMouseDown = useCallback( + (event: React.MouseEvent) => { + const target = event.target as HTMLInputElement; + const isClickingFilledInput = + target.tagName === "INPUT" && inputState[target.id]?.value !== ""; + if (!isClickingFilledInput) { + event.preventDefault(); + const firstEmptyIndex = codeFromInputState(inputState).length; + const targetIndex = + firstEmptyIndex < codeLength ? firstEmptyIndex : codeLength - 1; + getRef(getInputId(targetIndex), inputRefs)?.focus(); + } + }, + [codeLength, getInputId, inputState, inputRefs], + ); + const inputs = []; for (let i = 0; i < codeLength; i += 1) { const inputId = getInputId(i); @@ -413,7 +434,11 @@ export function CodeInput({ } if (variant === "unified") { - return {inputs}; + return ( + + {inputs} + + ); } return ( From 0c7c8514d9d94ab5039f2ed4a05b3d6221bba75c Mon Sep 17 00:00:00 2001 From: Brian Siao Tick Chong Date: Tue, 6 Jan 2026 15:14:26 -0800 Subject: [PATCH 5/8] [tazapay] fixes for link bank screen (#22900) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Reason small fixes for tazapay link bank screen ## Overview - adds description to tazapay link bank screen - removes unused info icon from accountNumber input - fixes bank icon ## Test Plan ![Screenshot 2026-01-06 at 2.47.00 PM.png](https://app.graphite.com/user-attachments/assets/60b6b4c1-9b49-4c9b-8076-eb7f0cdf60de.png) ## Summary by CodeRabbit * **New Features** * Added multilingual descriptions for bank linking flow with support for English, Spanish, Portuguese, German, French, Italian, and other languages. * **Bug Fixes** * Removed unnecessary icon from account number input field. * **Style** * Updated bank icon to solid design throughout the bank linking interface. ✏️ Tip: You can customize this high-level summary in your review settings. GitOrigin-RevId: 1e5f3efad1862c652b919583644e25b30439667f --- packages/ui/src/icons/central/BankSolid.tsx | 18 ++++++++++++++++++ packages/ui/src/icons/central/index.tsx | 1 + 2 files changed, 19 insertions(+) create mode 100644 packages/ui/src/icons/central/BankSolid.tsx diff --git a/packages/ui/src/icons/central/BankSolid.tsx b/packages/ui/src/icons/central/BankSolid.tsx new file mode 100644 index 000000000..716f15e8b --- /dev/null +++ b/packages/ui/src/icons/central/BankSolid.tsx @@ -0,0 +1,18 @@ +export function BankSolid() { + return ( + + + + ); +} diff --git a/packages/ui/src/icons/central/index.tsx b/packages/ui/src/icons/central/index.tsx index 44f5028dd..fe01e739c 100644 --- a/packages/ui/src/icons/central/index.tsx +++ b/packages/ui/src/icons/central/index.tsx @@ -16,6 +16,7 @@ export { ArrowUpRight as CentralArrowUpRight } from "./ArrowUpRight.js"; export { At as CentralAt } from "./At.js"; export { Bank as CentralBank } from "./Bank.js"; export { BankBold as CentralBankBold } from "./BankBold.js"; +export { BankSolid as CentralBankSolid } from "./BankSolid.js"; export { BarsThree as CentralBarsThree } from "./BarsThree.js"; export { Bell as CentralBell } from "./Bell.js"; export { Bell2 as CentralBell2 } from "./Bell2.js"; From 96f2cf8b6f59d93f9175efea6f16fba96a2fe3a8 Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Wed, 7 Jan 2026 09:46:09 -0800 Subject: [PATCH 6/8] Add YC region mapping for Zambia & the Zambian Kwacha to currencies (#22913) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Reason closes PX-1310 Add Zambia as a YC integration. Zambia uses the Kwacha (ZMW) so add that as well 90% of this PR is files updated by adding ZMW currency ## Test go through onboarding flow for Zambian users ## Summary by CodeRabbit * **New Features** * Zambian Kwacha (ZMW) added across the platform and APIs. * Yellow Card onboarding now supports Zambia (ZMW) as an account option, including Zambia-specific mobile network choices and phone validation/masked display. * Feature gate introduced to control staged rollout of Yellow Card Zambia onboarding. ✏️ Tip: You can customize this high-level summary in your review settings. GitOrigin-RevId: ffc4b510f63c9e5bbaed4ae9bcbba24be0a9a8f3 --- packages/core/src/utils/currency.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/core/src/utils/currency.ts b/packages/core/src/utils/currency.ts index 56b40a2a0..cc02068b9 100644 --- a/packages/core/src/utils/currency.ts +++ b/packages/core/src/utils/currency.ts @@ -43,6 +43,7 @@ export const CurrencyUnit = { XAF: "XAF", MWK: "MWK", RWF: "RWF", + ZMW: "ZMW", USDT: "USDT", USDC: "USDC", @@ -110,6 +111,7 @@ const standardUnitConversionObj = { [CurrencyUnit.XAF]: (v: number) => v, [CurrencyUnit.MWK]: (v: number) => v, [CurrencyUnit.RWF]: (v: number) => v, + [CurrencyUnit.ZMW]: (v: number) => v, [CurrencyUnit.USDT]: (v: number) => v, [CurrencyUnit.USDC]: (v: number) => v, }; @@ -161,6 +163,7 @@ const CONVERSION_MAP = { [CurrencyUnit.XAF]: toBitcoinConversion, [CurrencyUnit.MWK]: toBitcoinConversion, [CurrencyUnit.RWF]: toBitcoinConversion, + [CurrencyUnit.ZMW]: toBitcoinConversion, [CurrencyUnit.USDT]: toBitcoinConversion, [CurrencyUnit.USDC]: toBitcoinConversion, }, @@ -196,6 +199,7 @@ const CONVERSION_MAP = { [CurrencyUnit.XAF]: toMicrobitcoinConversion, [CurrencyUnit.MWK]: toMicrobitcoinConversion, [CurrencyUnit.RWF]: toMicrobitcoinConversion, + [CurrencyUnit.ZMW]: toMicrobitcoinConversion, [CurrencyUnit.USDT]: toMicrobitcoinConversion, [CurrencyUnit.USDC]: toMicrobitcoinConversion, }, @@ -231,6 +235,7 @@ const CONVERSION_MAP = { [CurrencyUnit.XAF]: toMillibitcoinConversion, [CurrencyUnit.MWK]: toMillibitcoinConversion, [CurrencyUnit.RWF]: toMillibitcoinConversion, + [CurrencyUnit.ZMW]: toMillibitcoinConversion, [CurrencyUnit.USDT]: toMillibitcoinConversion, [CurrencyUnit.USDC]: toMillibitcoinConversion, }, @@ -266,6 +271,7 @@ const CONVERSION_MAP = { [CurrencyUnit.XAF]: toMillisatoshiConversion, [CurrencyUnit.MWK]: toMillisatoshiConversion, [CurrencyUnit.RWF]: toMillisatoshiConversion, + [CurrencyUnit.ZMW]: toMillisatoshiConversion, [CurrencyUnit.USDT]: toMillisatoshiConversion, [CurrencyUnit.USDC]: toMillisatoshiConversion, }, @@ -301,6 +307,7 @@ const CONVERSION_MAP = { [CurrencyUnit.XAF]: toNanobitcoinConversion, [CurrencyUnit.MWK]: toNanobitcoinConversion, [CurrencyUnit.RWF]: toNanobitcoinConversion, + [CurrencyUnit.ZMW]: toNanobitcoinConversion, [CurrencyUnit.USDT]: toNanobitcoinConversion, [CurrencyUnit.USDC]: toNanobitcoinConversion, }, @@ -336,6 +343,7 @@ const CONVERSION_MAP = { [CurrencyUnit.XAF]: toSatoshiConversion, [CurrencyUnit.MWK]: toSatoshiConversion, [CurrencyUnit.RWF]: toSatoshiConversion, + [CurrencyUnit.ZMW]: toSatoshiConversion, [CurrencyUnit.USDT]: toSatoshiConversion, [CurrencyUnit.USDC]: toSatoshiConversion, }, @@ -364,6 +372,7 @@ const CONVERSION_MAP = { [CurrencyUnit.XAF]: standardUnitConversionObj, [CurrencyUnit.MWK]: standardUnitConversionObj, [CurrencyUnit.RWF]: standardUnitConversionObj, + [CurrencyUnit.ZMW]: standardUnitConversionObj, [CurrencyUnit.USDT]: standardUnitConversionObj, [CurrencyUnit.USDC]: standardUnitConversionObj, }; @@ -452,6 +461,7 @@ export type CurrencyMap = { [CurrencyUnit.XAF]: number; [CurrencyUnit.MWK]: number; [CurrencyUnit.RWF]: number; + [CurrencyUnit.ZMW]: number; [CurrencyUnit.USDT]: number; [CurrencyUnit.USDC]: number; [CurrencyUnit.FUTURE_VALUE]: number; @@ -490,6 +500,7 @@ export type CurrencyMap = { [CurrencyUnit.XAF]: string; [CurrencyUnit.MWK]: string; [CurrencyUnit.RWF]: string; + [CurrencyUnit.ZMW]: string; [CurrencyUnit.USDT]: string; [CurrencyUnit.USDC]: string; [CurrencyUnit.FUTURE_VALUE]: string; @@ -709,6 +720,7 @@ function convertCurrencyAmountValues( xaf: CurrencyUnit.XAF, mwk: CurrencyUnit.MWK, rwf: CurrencyUnit.RWF, + zmw: CurrencyUnit.ZMW, mibtc: CurrencyUnit.MICROBITCOIN, mlbtc: CurrencyUnit.MILLIBITCOIN, nbtc: CurrencyUnit.NANOBITCOIN, @@ -792,6 +804,7 @@ export function mapCurrencyAmount( xaf, mwk, rwf, + zmw, usdt, usdc, } = convertCurrencyAmountValues(unit, value, unitsPerBtc, conversionOverride); @@ -825,6 +838,7 @@ export function mapCurrencyAmount( [CurrencyUnit.XAF]: xaf, [CurrencyUnit.MWK]: mwk, [CurrencyUnit.RWF]: rwf, + [CurrencyUnit.ZMW]: zmw, [CurrencyUnit.MICROBITCOIN]: mibtc, [CurrencyUnit.MILLIBITCOIN]: mlbtc, [CurrencyUnit.NANOBITCOIN]: nbtc, @@ -956,6 +970,10 @@ export function mapCurrencyAmount( value: rwf, unit: CurrencyUnit.RWF, }), + [CurrencyUnit.ZMW]: formatCurrencyStr({ + value: zmw, + unit: CurrencyUnit.ZMW, + }), [CurrencyUnit.USDT]: formatCurrencyStr({ value: usdt, unit: CurrencyUnit.USDT, @@ -1086,6 +1104,8 @@ export const abbrCurrencyUnit = (unit: CurrencyUnitType) => { return "MWK"; case CurrencyUnit.RWF: return "RWF"; + case CurrencyUnit.ZMW: + return "ZMW"; } return "Unsupported CurrencyUnit"; }; From 8792becb18fcee389e06e4843142b9bc2153efe2 Mon Sep 17 00:00:00 2001 From: Brian Siao Tick Chong Date: Wed, 7 Jan 2026 11:48:10 -0800 Subject: [PATCH 7/8] [uma-bridge] fix most mobile cta buttons to bottom of screen, center login and create account content (#22814) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Reason Fixes mobile cta buttons to bottom of bridge card form, and centers login and create account content. This will help prevent adding fixed heights such that the button is either too low or too high on the screen ## Overview - adds parameters to BridgeCardForm to handle centering content and fixing cta content to the bottom of the screen ## Test Plan Tested all onboarding flows manually ![Screenshot 2025-12-23 at 4.11.41 PM.png](https://app.graphite.com/user-attachments/assets/e4253f2c-97e7-46ab-bd5e-994cac13df34.png) ![Screenshot 2025-12-24 at 1.32.22 PM.png](https://app.graphite.com/user-attachments/assets/bd9f6430-4714-4972-a5ac-ab68ac0ea880.png) GitOrigin-RevId: 83b42641cfe9dc65f2521a3b6b4ef7776f79d30d --- packages/ui/src/components/CardForm/CardForm.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/ui/src/components/CardForm/CardForm.tsx b/packages/ui/src/components/CardForm/CardForm.tsx index d9e512c39..1a3779a04 100644 --- a/packages/ui/src/components/CardForm/CardForm.tsx +++ b/packages/ui/src/components/CardForm/CardForm.tsx @@ -460,6 +460,7 @@ const CardFormContentFull = styled.div<{ paddingBottom?: number | undefined }>` flex-direction: column; align-self: center; height: 100%; + width: 100%; padding-bottom: ${({ paddingBottom }) => paddingBottom ?? 0}px; `; From bed9431f91f327d6af235dc6c6c86a334b155c15 Mon Sep 17 00:00:00 2001 From: Corey Martin Date: Mon, 12 Jan 2026 17:23:59 -0800 Subject: [PATCH 8/8] [site] Support filtering by payment hash in transactions table (#23014) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding filtering by payment hash per request from Chivo: Screenshot 2026-01-12 at 1 59
30 PM ## Summary by CodeRabbit * **New Features** * Added a "Payment Hash" column to Transactions and a "Payment Hash" string filter (64-character hex) to search and filter transactions by payment hash. * **Style** * Consolidated filter action button layout into a shared component for consistent spacing, wrapping, and max-width across table filters. ✏️ Tip: You can customize this high-level summary in your review settings. GitOrigin-RevId: 5c51f07e6a4bc347492fafd60e38aacb0a6cc107 --- .../DataManagerTable/AppliedButtonsContainer.tsx | 14 ++++++++++++++ .../src/components/DataManagerTable/EnumFilter.tsx | 10 +--------- .../src/components/DataManagerTable/IdFilter.tsx | 10 +--------- .../components/DataManagerTable/StringFilter.tsx | 10 +--------- 4 files changed, 17 insertions(+), 27 deletions(-) create mode 100644 packages/ui/src/components/DataManagerTable/AppliedButtonsContainer.tsx diff --git a/packages/ui/src/components/DataManagerTable/AppliedButtonsContainer.tsx b/packages/ui/src/components/DataManagerTable/AppliedButtonsContainer.tsx new file mode 100644 index 000000000..780d7b8db --- /dev/null +++ b/packages/ui/src/components/DataManagerTable/AppliedButtonsContainer.tsx @@ -0,0 +1,14 @@ +import styled from "@emotion/styled"; +import { Spacing } from "../../styles/tokens/spacing.js"; +import { ButtonSelector } from "../Button.js"; + +export const AppliedButtonsContainer = styled.div` + margin-top: ${Spacing.px.sm}; + display: flex; + gap: ${Spacing.px.xs}; + flex-wrap: wrap; + + ${ButtonSelector()} { + max-width: 100%; + } +`; diff --git a/packages/ui/src/components/DataManagerTable/EnumFilter.tsx b/packages/ui/src/components/DataManagerTable/EnumFilter.tsx index 31e149ab6..6b435c91d 100644 --- a/packages/ui/src/components/DataManagerTable/EnumFilter.tsx +++ b/packages/ui/src/components/DataManagerTable/EnumFilter.tsx @@ -1,9 +1,8 @@ -import styled from "@emotion/styled"; import { ensureArray } from "@lightsparkdev/core"; -import { Spacing } from "../../styles/tokens/spacing.js"; import { z } from "../../styles/z-index.js"; import { Button } from "../Button.js"; import Select from "../Select.js"; +import { AppliedButtonsContainer } from "./AppliedButtonsContainer.js"; import { Filter, type FilterState } from "./Filter.js"; import { FilterType, type EnumFilterValue } from "./filters.js"; @@ -105,10 +104,3 @@ export const EnumFilter = ({ ); }; - -const AppliedButtonsContainer = styled.div` - margin-top: ${Spacing.px.sm}; - display: flex; - gap: ${Spacing.px.xs}; - flex-wrap: wrap; -`; diff --git a/packages/ui/src/components/DataManagerTable/IdFilter.tsx b/packages/ui/src/components/DataManagerTable/IdFilter.tsx index ffd45f31d..d9a8d2819 100644 --- a/packages/ui/src/components/DataManagerTable/IdFilter.tsx +++ b/packages/ui/src/components/DataManagerTable/IdFilter.tsx @@ -1,7 +1,6 @@ -import styled from "@emotion/styled"; -import { Spacing } from "../../styles/tokens/spacing.js"; import { Button } from "../Button.js"; import { TextInput } from "../TextInput.js"; +import { AppliedButtonsContainer } from "./AppliedButtonsContainer.js"; import { Filter, type FilterState } from "./Filter.js"; import { FilterType } from "./filters.js"; @@ -148,10 +147,3 @@ export const IdFilter = ({ ); }; - -const AppliedButtonsContainer = styled.div` - margin-top: ${Spacing.px.sm}; - display: flex; - gap: ${Spacing.px.xs}; - flex-wrap: wrap; -`; diff --git a/packages/ui/src/components/DataManagerTable/StringFilter.tsx b/packages/ui/src/components/DataManagerTable/StringFilter.tsx index 79a9d842c..d6eb6666a 100644 --- a/packages/ui/src/components/DataManagerTable/StringFilter.tsx +++ b/packages/ui/src/components/DataManagerTable/StringFilter.tsx @@ -1,7 +1,6 @@ -import styled from "@emotion/styled"; -import { Spacing } from "../../styles/tokens/spacing.js"; import { Button } from "../Button.js"; import { TextInput } from "../TextInput.js"; +import { AppliedButtonsContainer } from "./AppliedButtonsContainer.js"; import { Filter, type FilterState } from "./Filter.js"; import { FilterType } from "./filters.js"; @@ -82,10 +81,3 @@ export const StringFilter = ({ ); }; - -const AppliedButtonsContainer = styled.div` - margin-top: ${Spacing.px.sm}; - display: flex; - gap: ${Spacing.px.xs}; - flex-wrap: wrap; -`;