Skip to content

Landing page polish, surety stats error, and tiered error banners - #1171

Merged
vjuliaife merged 3 commits into
vjuliaife:mainfrom
davedumto:fix/landing-walkthrough-cta-copy-surety-stats-error-tiers
Aug 29, 2026
Merged

Landing page polish, surety stats error, and tiered error banners#1171
vjuliaife merged 3 commits into
vjuliaife:mainfrom
davedumto:fix/landing-walkthrough-cta-copy-surety-stats-error-tiers

Conversation

@davedumto

Copy link
Copy Markdown
Contributor

Summary

Four frontend UI/UX fixes on the web app:

#1075 — Walkthrough visual step treatment

The "How a tariff spike plays out" section on the landing page used a plain numbered <ol> with default browser list markers. Replaced it with a Step component: each of the 7 steps gets a numbered badge (circular, accent-colored) connected by a vertical line, giving the walkthrough an actual step-by-step visual rhythm instead of default list styling. Step copy is unchanged. Layout is a flex row per step (badge + text) that stacks naturally on narrow viewports — no fixed widths that would break on mobile.

#1074 — CTA preview copy

The "Try the demo" CTA gave no indication of what signup actually asks for. Added a short line of supporting copy under the CTA buttons: role choice (importer vs. surety admin), email, password — verified against the actual /signup form fields so the copy is accurate. No change to where the CTA links or how signup works, and no extra scrolling is needed to see it (it sits directly under the buttons, in the same viewport).

#1070 — Surety dashboard stats failure

api.getStats() failures on the surety dashboard only did console.error, leaving the metric tiles blank with nothing telling the surety admin that anything failed. Added a metricsError flag: on failure it now renders a small warning-tier inline message ("Metrics unavailable...") in place of the tiles, visually distinct (amber/warning) from the danger-tier banner used for the importer-list error below it. The importer list fetch is a separate try/catch and always renders independently, so this can't block it. The existing console.error call is untouched.

#1068 — Tiered error banners

Found an existing shared ErrorBanner component already used across the deposit/withdraw/tariff-update flow, the registration form, and the surety importer-detail page — all danger-styled regardless of what actually went wrong. Rather than duplicating tiered markup per page, extended that one component and its backing formatApiError():

  • formatApiError now classifies each known error message into warning (the user can plausibly fix the input and retry — insufficient balance, validation failures, temporary collateral locks, rate limiting) or danger (compliance/business-rule rejections like OFAC/AML/KYC, state conflicts like "already registered", not-found, and any technical/network failure). Anything not in the known-error table defaults to danger, so unclassified errors keep today's behavior.
  • ErrorBanner reads that severity to choose warning (amber) vs danger (red) styling, and also accepts an optional severity override prop for banners that don't wrap an API error at all.
  • Added a --warning CSS custom property in globals.css alongside the existing --success/--danger tokens (same pattern, same place).
  • Because deposit, withdraw, tariff-update, and registration errors all already flow through the shared ErrorBanner, tiering the component covers all of them without touching each page's markup individually.
  • The convention (what counts as warning vs danger, and why unclassified defaults to danger) is documented as a comment on the ErrorSeverity type in lib/error-formatter.ts and mirrored on ErrorBanner itself, so future error banners have a clear rule to follow instead of re-guessing.

Test plan

  • Added unit tests in lib/error-formatter.test.ts covering severity classification: recoverable/validation errors → warning, business-rule/compliance rejections → danger, technical/network errors → danger, and the unclassified-defaults-to-danger fallback. All existing tests in that file, plus the repo's other two test files (event-helpers.test.ts, api.test.ts), still pass.
  • Ran tsc --noEmit and eslint — no new errors introduced by this change. (apps/web has two pre-existing type errors in app/surety/[id]/page.tsx, setConfirmClawback undefined and a FormattedError-as-ReactNode mismatch, confirmed present on a clean main via git stash before/after comparison — unrelated to this PR and not touched here.)
  • Ran next build; Turbopack compiles cleanly, the only build-time type-check failure is the same pre-existing surety/[id]/page.tsx issue above.
  • Ran prettier --check on all changed files.
  • Verified the CTA copy against the actual /signup form fields (role select, email, password) so it doesn't drift from what signup really asks for.
  • Visual verification was done by reading the rendered JSX structure and Tailwind classes rather than in a live browser — I don't have a way to screenshot this environment, so I can't attach before/after images. Flagging that as an honest caveat; happy to adjust spacing/sizing on the step badges or warning banner if they look off once rendered.

Closes

closes #1075
closes #1074
closes #1070
closes #1068

… in CTA (vjuliaife#1075, vjuliaife#1074)

Restyles the "how a tariff spike plays out" walkthrough with numbered
badge step indicators and a connecting line instead of a plain ordered
list, and adds brief copy near the "Try the demo" CTA describing what
signup involves (role choice, email, password) so visitors know what
they're getting into before clicking through.
…juliaife#1070)

api.getStats() failures previously only logged to console, leaving the
metric tiles silently blank with no indication anything went wrong.
Now shows a small warning-tier message in place of the tiles when the
stats call fails, visually distinct from the danger-tier importer-list
error banner below it. The importer list still renders independently
either way since the two fetches are already isolated in separate
try/catch blocks.

Adds a --warning color token to globals.css alongside the existing
success/danger tokens, used here and by the tiered ErrorBanner change.
All error banners rendered through the shared ErrorBanner component
(deposit, withdraw, registration, and surety importer detail) used the
same danger/red styling regardless of whether the error was a
recoverable validation problem or a hard rejection/system failure.

formatApiError() now classifies each known error into a 'warning' tier
(the user can plausibly fix the input and retry — insufficient
balance, validation failures, temporary locks, rate limiting) or a
'danger' tier (compliance/business-rule rejections, state conflicts,
not-found, and technical/network failures). Unclassified errors
default to 'danger' as the safer fallback, so existing behavior is
preserved for anything not in the known-error table.

ErrorBanner reads formatted.severity to pick its styling and also
accepts an optional severity override prop for banners that aren't
wrapping an API error (not needed by any current caller, but available
for e.g. a hardcoded frozen-account notice). Error text content is
unchanged — only the visual treatment adapts. The tiering convention
is documented on the ErrorSeverity type and on ErrorBanner itself so
future error banners follow the same pattern instead of duplicating
danger-only styling.

Extends error-formatter.test.ts with coverage for the new severity
field across warning, danger, technical, and unclassified cases.
@drips-wave

drips-wave Bot commented Aug 28, 2026

Copy link
Copy Markdown

@davedumto 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

@vercel

vercel Bot commented Aug 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tariff-shield-web Error Error Aug 28, 2026 9:42am

@vjuliaife
vjuliaife merged commit 4466dec into vjuliaife:main Aug 29, 2026
9 of 26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment