Skip to content

feat(events): confirm on-chain publish in a ConfirmDialog that names the locked resale cap and royalty - #343

Merged
presidojay1 merged 2 commits into
StellarTickets:mainfrom
laurastephaniee:fix/278-publish-confirm-dialog
Oct 16, 2026
Merged

presidojay1 merged 2 commits into
StellarTickets:mainfrom
laurastephaniee:fix/278-publish-confirm-dialog

Conversation

@laurastephaniee

@laurastephaniee laurastephaniee commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Where things stood on main: handlePublish() no longer publishes on click. It sets showPublishReview, which swaps the button for an inline review panel, and confirmPublish() only runs from that panel's "Confirm and publish" button. So "publishing starts on click" is no longer literally true. The fix the issue asks for still wasn't there:

  1. The royalty was never shown. buildPublishSummary() listed name, date, venue, ticket types and resale cap, but not royaltyBps, one of the two values the issue says must be called out.
  2. Nothing said these values are permanent. The copy said publishing "locks the event parameters". It never said the resale cap and organizer royalty can't be changed afterwards.
  3. It wasn't a dialog. The panel was inline page content: no role/aria-modal, no focus move, no Escape. For a keyboard or screen-reader user the "confirmation" just appeared in the page flow, easy to Tab past or not notice, right before an irreversible wallet signature. No ConfirmDialog component existed in src/components/.

What changed

Commit 1: feat(events)

New src/components/confirm-dialog.tsx: a reusable ConfirmDialog

  • role="alertdialog" + aria-modal="true", with aria-labelledby (title) and aria-describedby (body), so screen readers announce it as an urgent confirmation and read the warning.
  • Focus starts on Cancel, the safe default for an irreversible action, so a stray Enter doesn't publish.
  • Tab / Shift+Tab stay inside the dialog.
  • Escape or a backdrop click cancels. Both are ignored while loading, and Cancel is disabled then too, so an in-flight publish can't be dismissed half-way.
  • Focus returns to the element that opened it (the Publish button) on close.
  • Uses the existing Button (variants, loading → aria-busy) and theme tokens (bg-surface, border-border, text-muted), so it matches the site.

src/app/dashboard/events/[id]/page.tsx

  • The Publish button always renders; it opens ConfirmDialog instead of swapping in the inline panel. Behaviour is unchanged otherwise: the wallet check in handlePublish() still runs first, and nothing is sent until "Confirm and publish" calls the existing confirmPublish().
  • The copy now states it explicitly: "Publishing writes this event to the ticketing contract and opens ticket sales. The resale cap and organizer royalty below can't be changed after publishing. Review the details before your wallet asks you to sign.", followed by the summary.

src/lib/event-details.ts: buildPublishSummary() takes an optional royaltyBps and adds Organizer royalty: 5% (via the existing formatRoyalty), defaulting to 0%. The page passes the full event, which already has royaltyBps.

Commit 2: test(events)

New src/components/confirm-dialog.test.tsx (React createRoot + act in jsdom; the repo has no Testing Library):

  • Renders nothing while closed.
  • aria-modal, and the title and body are wired through aria-labelledby / aria-describedby (the body contains the "can't be changed after publishing" warning).
  • Cancel has focus on open.
  • Only the confirm button calls onConfirm; Cancel and Escape call onCancel and never confirm.
  • While loading: Escape is ignored, Cancel is disabled, and the confirm button shows Publishing… with aria-busy.
  • Tab from the last button wraps to the first (focus trap).

src/lib/event-details.test.ts: the publish summary includes Organizer royalty: 5% next to the resale cap, and 0% when unset.

Testing

npx vitest run src/components/confirm-dialog.test.tsx src/lib/event-details.test.ts
 Test Files  2 passed (2)
      Tests  19 passed (19)
  • npx eslint on the three changed source files: clean.
  • npx tsc --noEmit: no errors in the changed files. The only 4 errors are TS18046 'err' is of type 'unknown' in src/lib/api-timeout.test.ts, which this PR doesn't touch.

Closes #278
Closes #279
Closes #280
Closes #281

…locked

Publishing writes the event to the ticketing contract and can't be
undone, but the review step was an inline panel that didn't mention the
organizer royalty or say that the resale cap and royalty are permanent.

- add ConfirmDialog: role=alertdialog + aria-modal, labelled/described,
  focus starts on Cancel, Tab trapped, Escape/backdrop cancel (not while
  loading), focus restored to the opener on close
- event page: Publish opens the dialog; nothing is sent until
  'Confirm and publish'; copy states that the resale cap and organizer
  royalty can't be changed after publishing
- buildPublishSummary now includes the organizer royalty

Closes StellarTickets#278
@netlify

netlify Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

❌ Deploy Preview for stellartickets failed.

Name Link
🔨 Latest commit 196a5e1
🔍 Latest deploy log https://app.netlify.com/projects/stellartickets/deploys/6ad1e0b0df489de6594f4e92

@drips-wave

drips-wave Bot commented Oct 16, 2026

Copy link
Copy Markdown

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

@presidojay1
presidojay1 merged commit 3896efd into StellarTickets:main Oct 16, 2026
0 of 5 checks passed
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