Skip to content

Fix Horizon URL validation bypass, tier price formatting, preview race condition, and drawer focus trap - #1037

Merged
temma02 merged 4 commits into
StellerCraft:mainfrom
Fidelis900:fix/craft-issue-batch-1000-1003
Jul 28, 2026
Merged

Fix Horizon URL validation bypass, tier price formatting, preview race condition, and drawer focus trap#1037
temma02 merged 4 commits into
StellerCraft:mainfrom
Fidelis900:fix/craft-issue-batch-1000-1003

Conversation

@Fidelis900

Copy link
Copy Markdown
Contributor

Summary

Four independent bug fixes bundled into one PR per request:

  • Repair Horizon URL Network-Mismatch Validation Bypass in Stellar Config Schema #1003stellarConfigSchema.superRefine in validate-stellar.ts compared horizonUrl against the testnet/mainnet Horizon constants with strict string equality, so a trailing slash, different scheme, or casing silently bypassed the mainnet/testnet mismatch check. Replaced the raw === comparisons with a getHorizonHost() helper that parses the URL and compares the lowercased hostname, so trailing slashes, path segments, and scheme/case differences can no longer evade the check. Error messages and the horizonUrl field path are unchanged.

  • Harden Currency Formatting in Subscription Tier Price Displays #1002SubscriptionComparison.tsx and UpgradeFlow.tsx (plus UpgradePrompt.tsx and the billing settings page) derived tier price strings via cents / 100 interpolation with no decimal normalization, so a non-round-dollar price (e.g. 1950 cents) would render as $19.5 instead of $19.50, and large prices had no thousands separator. Added a centralized formatTierPrice(cents) helper in pricing.ts built on Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }), and updated all four consumers to use it instead of ad-hoc math.

  • Eliminate Out-of-Order Preview Race Condition in Viewport Refresh Pipeline #1000PreviewWorkspace.tsx's refresh effect had no request-identity tracking, so rapidly switching viewports (or editing customization) could let an earlier, slower refreshPreview call resolve after a newer one and clobber the current preview state with stale data. Added an AbortController-based staleness guard in usePreviewService.ts (aborts the previous in-flight call when a new one starts, and throws an AbortError if a call turns out to be superseded) plus a request-sequence guard in PreviewWorkspace.tsx's handleRefresh/effect, mirroring the existing pattern in useTemplates.ts.

  • Engineer Accessible Focus Trap for Mobile Navigation Drawer #1001MobileDrawer.tsx never moved focus into the drawer on open, never trapped Tab/Shift+Tab within it, and never restored focus to the triggering element on close, and it lacked role="dialog"/aria-modal. Added focus-in-on-open (to the close button), a Tab/Shift+Tab focus trap cycling within the drawer's focusable elements, focus restoration to the previously-focused trigger element on close, and role="dialog" + aria-modal={open} on the drawer element.

Changes

  • apps/frontend/src/lib/customization/validate-stellar.ts
  • apps/frontend/src/lib/stripe/pricing.ts
  • apps/frontend/src/components/app/SubscriptionComparison.tsx
  • apps/frontend/src/components/app/UpgradeFlow.tsx
  • apps/frontend/src/components/app/UpgradePrompt.tsx
  • apps/frontend/src/app/app/settings/billing/page.tsx
  • apps/frontend/src/hooks/usePreviewService.ts
  • apps/frontend/src/components/app/preview/PreviewWorkspace.tsx
  • apps/frontend/src/components/app/MobileDrawer.tsx

Closes #1003
Closes #1002
Closes #1000
Closes #1001

Test plan

  • pnpm test --filter=frontend -- validate-stellar
  • pnpm test --filter=frontend -- pricing
  • pnpm test --filter=frontend -- SubscriptionComparison
  • pnpm test --filter=frontend -- PreviewWorkspace
  • pnpm test --filter=frontend -- usePreviewService
  • pnpm test --filter=frontend -- MobileDrawer
  • pnpm lint
  • pnpm typecheck

No automated tests were added in this PR per explicit request from the author; the checklist above reflects the tickets' original test requirements and is left for reviewer follow-up.

…ation

Compares URL hostnames instead of raw strings so trailing slashes,
path segments, scheme, or case differences no longer bypass the
mainnet/testnet horizon URL mismatch check.

Closes StellerCraft#1003

Co-authored-by: Fidelis <fidelisobed79@gmail.com>
Adds a shared formatTierPrice helper built on Intl.NumberFormat and
replaces the ad-hoc `cents / 100` string interpolation across
SubscriptionComparison, UpgradeFlow, UpgradePrompt, and the billing
settings page so fractional-dollar prices always render two decimal
places and large prices get thousands separators.

Closes StellerCraft#1002

Co-authored-by: Fidelis <fidelisobed79@gmail.com>
Adds an AbortController-based staleness check in usePreviewService and
a request-sequence guard in PreviewWorkspace's handleRefresh/effect so
an earlier, slower refreshPreview call can no longer clobber state set
by a more recent viewport/customization change, mirroring the pattern
in useTemplates.ts.

Closes StellerCraft#1000

Co-authored-by: Fidelis <fidelisobed79@gmail.com>
Moves focus into the drawer's close button when it opens, traps
Tab/Shift+Tab cycling within the drawer's focusable elements while
open, and restores focus to the triggering element on close. Adds
role="dialog" and aria-modal so assistive tech announces it as a
modal surface.

Closes StellerCraft#1001

Co-authored-by: Fidelis <fidelisobed79@gmail.com>
@drips-wave

drips-wave Bot commented Jul 27, 2026

Copy link
Copy Markdown

@Fidelis900 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@temma02
temma02 merged commit 90e7e36 into StellerCraft:main Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants