Landing page polish, surety stats error, and tiered error banners - #1171
Merged
vjuliaife merged 3 commits intoAug 29, 2026
Conversation
… 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.
|
@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! 🚀 |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 aStepcomponent: 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
/signupform 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 didconsole.error, leaving the metric tiles blank with nothing telling the surety admin that anything failed. Added ametricsErrorflag: 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 existingconsole.errorcall is untouched.#1068 — Tiered error banners
Found an existing shared
ErrorBannercomponent 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 backingformatApiError():formatApiErrornow classifies each known error message intowarning(the user can plausibly fix the input and retry — insufficient balance, validation failures, temporary collateral locks, rate limiting) ordanger(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 todanger, so unclassified errors keep today's behavior.ErrorBannerreads that severity to choose warning (amber) vs danger (red) styling, and also accepts an optionalseverityoverride prop for banners that don't wrap an API error at all.--warningCSS custom property inglobals.cssalongside the existing--success/--dangertokens (same pattern, same place).ErrorBanner, tiering the component covers all of them without touching each page's markup individually.ErrorSeveritytype inlib/error-formatter.tsand mirrored onErrorBanneritself, so future error banners have a clear rule to follow instead of re-guessing.Test plan
lib/error-formatter.test.tscovering 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.tsc --noEmitandeslint— no new errors introduced by this change. (apps/webhas two pre-existing type errors inapp/surety/[id]/page.tsx,setConfirmClawbackundefined and aFormattedError-as-ReactNodemismatch, confirmed present on a cleanmainviagit stashbefore/after comparison — unrelated to this PR and not touched here.)next build; Turbopack compiles cleanly, the only build-time type-check failure is the same pre-existingsurety/[id]/page.tsxissue above.prettier --checkon all changed files./signupform fields (role select, email, password) so it doesn't drift from what signup really asks for.Closes
closes #1075
closes #1074
closes #1070
closes #1068