feat(dashboard): payment links list and create form (#144) - #206
Merged
Conversation
A merchant could sign up, get a settlement wallet provisioned, and then had no
way to create a payment link except curl. This closes that.
The `/links` route was rendering the *detail* page: it called
`useParams<{ id }>()` on a route with no `[id]` segment, so `id` was always
undefined and the page requested `/payment-links/undefined`. There was no list
view and no create form anywhere. The detail page moves to `/links/[id]`, where
its param actually exists, and `/links` becomes the list it always claimed to
be.
Nearly every piece already existed — LinkCard, CreateLinkModal,
LinkCreatedModal, QRCodeModal, usePaymentLinks — but nothing rendered them.
This wires them up and fixes what did not survive contact with the API:
- `PaymentLinksResponse` declared `total`, `page` and `limit` as siblings of
`data`, a shape the API has never sent. It answers `{ data, meta }`, and the
client deliberately skips unwrapping when `meta` is present, so both halves
arrive intact. The one consumer only ever read `.data`, so pagination had
nothing to render from and nothing caught the mismatch.
- LinkCard had no route to the detail page. The ID is now the link, rather than
the whole card: the footer holds copy, QR and deactivate buttons, and nesting
those inside an anchor is invalid markup that would navigate on every click.
Search filters the loaded page client-side, because the API has no search
parameter for links yet — filtering server-side would silently return nothing.
Marked in the code for a move once the endpoint supports it.
Verified in the browser against the running API: list renders 5 links, filter
chips drive `?status=` (All 5 / Deactivated 1), create posts and the count goes
4 to 5 with the URL auto-copied, deactivate flips the badge in place, and
`/links/[id]` loads the link and its stats with real ids. No console errors.
Dashboard: typecheck clean, 0 lint errors, 38/38 tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
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.
Closes #144.
A merchant could sign up, get a settlement wallet auto-provisioned, and then had no way to create a payment link except curl. This closes that.
The routing bug underneath it
/linkswas rendering the detail page — it calleduseParams<{ id }>()on a route with no[id]segment, soidwas alwaysundefinedand the page requested/payment-links/undefined. There was no list view and no create form anywhere in the app.The detail page moves to
/links/[id], where its param exists./linksbecomes the list it always claimed to be.Mostly wiring, plus two real fixes
LinkCard,CreateLinkModal,LinkCreatedModal,QRCodeModalandusePaymentLinksall already existed — nothing rendered them. Two things didn't survive contact with the API:PaymentLinksResponsedescribed a shape the API has never sent. It declaredtotal,page,limitas siblings ofdata. The API answers{ data, meta }, and the client deliberately skips unwrapping whenmetais present, so both halves arrive intact. The single consumer only read.data, so pagination had nothing to render from and nothing caught it.LinkCardhad no route to the detail page. The ID is now the link rather than the whole card — the footer holds copy, QR and deactivate buttons, and nesting those inside an anchor is invalid markup that would navigate on every click.Acceptance criteria
/linksrenders without crashingEmptyState variant="links"+ create CTA)?status=DELETE, badge updatesmeta.totalPages, hidden at 1 pagePOSTCombinedAuthGuardaccepts the dashboard's JWT, no API key needed, no follow-up requiredSearch filters the loaded page client-side — the API has no search parameter for links yet, so filtering server-side would silently return nothing. Flagged in the code to move once the endpoint supports it.
Verified in the browser against the running API
List renders, filters drive the query param, create → count increments, deactivate → badge flips,
/links/[id]loads the link and its stats with real ids. No console errors.Dashboard: typecheck clean, 0 lint errors, 38/38 tests.
Note, not in scope: created links point at
https://pay.useroutr.com/<code>becausePAYMENT_LINK_BASE_URLis unset locally — so they aren't clickable in dev. That's the #113 config item.