Commit Graph
31 Commits
Author SHA1 Message Date
MichilisandClaude Opus 5 ed1d3a8c12 Paginate the bookings page and the event attendee/ticket tabs.
Extract the pagination controls shared by the events and users lists into
components/admin/Pagination, along with a usePaginatedList helper that slices a
list and keeps the page in range when the list shrinks under it.

Bookings, the Attendees tab and the Tickets tab paginate client-side rather than
on the server: each already loads its full set for figures a slice would break —
the bookings stat cards, the group-booking totals and the sibling payment-method
lookup, and the per-status counts the two tabs share with the event's other tabs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 06:01:27 +00:00
MichilisandClaude Opus 5 5970d707af Paginate the admin events list.
The admin events page fetched every event on each load and rendered
them all, which grows without bound as the archive fills up. GET
/api/events now takes optional page and pageSize parameters and returns
total alongside the rows; pagination is opt-in, so the public pages and
the admin filter dropdowns that pass neither still get the full list
and the untouched response shape.

Page size is selectable (10/25/50/100) and deleting the last event on a
page falls back to the new last page instead of showing an empty table.
The ?edit=<id> deep link no longer depends on the target being in the
current page: when it is missing from the loaded rows the event is
fetched directly, guarded by a ref so the modal opens once.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 05:16:28 +00:00
MichilisandClaude Opus 5 e296e80e48 Rebuild the Scanner page into a unified door check-in screen.
Most attendees arrive without their QR open, and taking money at the
door meant leaving the scanner for the event dashboard, where the Add
Ticket modal demanded an email and recorded no payment method. The
screen now leads with manual name search, keeps the camera one tap away
behind a fullscreen overlay, and creates and charges walk-ins inline.

Check-in and payment are one action: anything done here is born
confirmed, paid (or comp) and checked in through a single endpoint,
POST /api/events/:eventId/door-checkin. There are no confirm dialogs
anywhere, because they stall the queue; a ten-second Undo replaces
them, reversing exactly what the action changed via the undo state
recorded alongside its idempotency key. Writes fire in the background
with retries, so venue wifi never blocks the person at the door, and a
capacity limit only warns, since staff at the door are the authority.

Every write carries a client-generated idempotency key, inserted in the
same transaction as the writes it guards, so a double tap or a retry
after a timeout cannot produce a second ticket, payment or check-in.
Search runs entirely in memory over one preloaded list: names are
matched accent- and case-insensitively in both directions, per word,
prefix before substring, with a mostly-numeric query searching phone
digits so two people with the same name can be told apart.

Door money is recorded as payments.source 'door' plus payments.method
(cash, bitcoin, transfer or guest) while provider keeps its existing
value, so capacity counting, the stale-booking sweeps and the admin
payment lists are unaffected and revenue can still be split pre-sale
versus door. Bitcoin records the payment as made, on the same trust
model as cash, with no invoice generated; lib/doorPayments.ts is where
a real Lightning flow slots in later.

Also fixes the SQLite tickets DDL, which still created the pre-split
attendee_name column with NOT NULL email and phone. Only fresh
databases were affected -- existing ones were relaxed by later ALTERs
-- but on those, door walk-ins (and any other ticket) could not be
inserted at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 06:09:55 +00:00
MichilisandClaude Opus 5 a0161a67d2 Add "paid at the door" ticket type to the Add Ticket modal.
Door walk-ins were only expressible as an unpaid ticket, which left the
cash out of revenue. The new type records the cash payment as paid and
makes every field optional, since a walk-in often gives no details:
a blank name is logged as "Walk-in", and a confirmation email only goes
out when an email is entered.

Door tickets reuse paymentStatus 'paid' (the column enum is capped at
paid/unpaid/comp) so badges and revenue totals pick them up with no
migration; the cash payment row is referenced "Paid at door" to keep
them distinguishable from emailed manual tickets.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 04:39:54 +00:00
MichilisandClaude Fable 5 9b2668f498 Unify admin ticket creation into one modal with first-class payment status.
- Replace the three Attendees-tab modals (Manual Ticket / Add at Door /
  Invite Guest) with a single Add Ticket modal: Paid/Unpaid/Guest segmented
  control, shared fields, "Check in now" for all types, and a live
  "what happens" preview, backed by one POST /api/tickets/admin/add.
- Add tickets.payment_status (paid | unpaid | comp) with a backfill
  migration; keep it in sync on every payment-settlement path (mark-paid,
  admin approval, Lightning, free bookings, hold recovery).
- Show Paid/Unpaid/Comp badges in the attendee list, count only paid
  tickets toward revenue, let unpaid tickets be resolved via Mark Paid,
  and flag unpaid tickets with their balance due in the door scanner.
- Replace the per-page useStatsPrivacy hook with an admin-wide
  PrivacyContext + SensitiveValue mask, toggled from the admin layout.
- Add server-side pagination with page-size options to the users page.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 05:25:00 +00:00
MichilisandClaude Fable 5 71c277045b Centralize seat capacity accounting and the payment provider registry.
Replace manualProviders.ts with a paymentProviders.ts registry
(automatic vs manual settlement) and move all seat counting into
capacity.ts as the single source of truth: only paid/checked-in tickets
and pending_approval payments hold a seat, so abandoned checkouts never
block sales. Admins can now knowingly approve a payment over capacity
(allowOverCapacity), with the booking and admin UIs updated to match.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 04:58:40 +00:00
MichilisandCursor 75e317d73e Replace spinners with skeleton loading states for faster perceived UX.
Add reusable Skeleton components and route-level loading files across public pages and admin lists so layouts stay stable while data loads.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 23:14:11 +00:00
MichilisandCursor cb422332c8 Fix booking defaults, dashboard alerts, admin edit flow, and ended-event payments.
Require an explicit payment method on booking, hide stale release banners for past or inactive events, open event editing in place on the detail page, and auto-reject unconfirmed payments after events end without sending email.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 22:04:22 +00:00
Michilis cacc52ec24 Security recovery: hold sweep, dashboard updates, and admin fixes. 2026-07-01 05:51:38 +00:00
MichilisandCursor 613bd7be1d Refactor monolithic modules and harden booking, email, and auth infrastructure.
Split oversized frontend API client, email service, and admin/booking pages into focused modules while preserving import surfaces, and add Redis-backed queues, stale booking cleanup, stronger auth, and scale deployment configs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-25 07:12:59 +00:00
MichilisandCursor a6840ea953 Harden auth, payments, and frontend against review findings.
Close exploitable gaps in booking/payment flows, enforce token versioning and account checks, gate sensitive payment data, and add middleware plus input validation across admin routes.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-24 19:59:02 +00:00
Michilis fc4af38e8a Add per-quantity TPago payment links for multi-ticket checkout.
Each ticket quantity (1-5) can have its own TPago link so checkout, emails, and admin config use the correct fixed-amount URL.
2026-06-18 23:59:17 +00:00
MichilisandCursor 1b2463f4bc Add human-readable event URL slugs with legacy redirect support.
Store unique slugs on events, backfill existing records, redirect old UUID and alias URLs to canonical slug pages, and expose slug editing plus alias management in the admin event modal.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-05 04:09:05 +00:00
Michilis 2cabd8c92f fix(admin): avoid rendering stray 0 for non-guest tickets on isGuest
Made-with: Love
2026-04-27 18:21:10 +00:00
Michilis 3dfb1689ad Booking flow: required terms and privacy checkbox with i18n
Also includes admin, dashboard, and API updates; PWA icon assets; and
assorted layout and utility changes on dev.
2026-04-27 03:21:15 +00:00
Michilis e09ff4ed60 Admin: stats privacy toggle, clickable event rows, fix payment method display
- Add useStatsPrivacy hook with localStorage persistence for stats visibility
- Single event page: desktop privacy button, hide capacity chip when stats hidden
- Events list: row/card click navigates to event detail; stopPropagation on actions
- Backend GET /tickets: include payment for each ticket (removes N+1)
- Bookings page: use list payment data, show — when payment method unknown

Made-with: Cursor
2026-03-10 01:10:42 +00:00
MichilisandCursor bbfaa1172a Add unlisted event status: hidden from listings but accessible by URL
- Backend: add 'unlisted' to schema enum and Zod validation; allow booking for unlisted events
- Frontend: Event type and guards updated; unlisted events bookable, excluded from public listing/sitemap
- Admin: badge, status dropdown, Make Unlisted / Make Public / Unpublish actions; scanner/emails/tickets include unlisted

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-19 02:21:41 +00:00
MichilisandCursor 958181e049 Mobile-friendly admin pages, redesigned homepage Next Event card
- Extract shared mobile components (BottomSheet, MoreMenu, Dropdown, etc.) into MobileComponents.tsx
- Make admin pages mobile-friendly: bookings, emails, events, faq, payments, tickets, users
- Redesign homepage Next Event card with banner image, responsive layout, and updated styling
- Fix past events showing on homepage/linktree: use proper Date comparison, auto-unfeature expired events
- Add "Over" tag to admin events list for past events
- Fix backend FRONTEND_URL for cache revalidation

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-18 03:27:49 +00:00
MichilisandCursor 6bc7e13e78 Admin event page: redesign UI, export endpoints, mobile fixes
- Backend: Add /events/:eventId/attendees/export and /events/:eventId/tickets/export with q/status; legacy redirect for old export path
- API: exportAttendees q param, new exportTicketsCSV for tickets CSV
- Admin event page: unified tabs+content container, portal dropdowns to fix clipping, separate mobile export/add-ticket sheets (fix double menu), responsive tab bar and card layout

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-14 18:27:27 +00:00
MichilisandCursor c3897efd02 feat(admin): add event attendees export (CSV) with status filters
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-14 05:27:17 +00:00
MichilisandCursor 62bf048680 Mobile scanner redesign + backend live search
- Scanner page: fullscreen mobile-first layout, Scan/Search/Recent tabs
- Scan tab: auto-start camera, switch camera, vibration/sound feedback
- Valid/invalid fullscreen states, confirm check-in, auto-return to camera
- Search tab: live backend search (300ms debounce), tap card for detail + check-in
- Recent tab: last 20 check-ins, session counter
- Backend: GET /api/tickets/search (live search), GET /api/tickets/stats/checkin
- Admin layout: hide sidebar on scanner page; fix hooks order (no early return before useEffect)
- Back button to dashboard/events (staff → events, others → admin)
- API: searchLive, getCheckinStats, LiveSearchResult; PostgreSQL LOWER cast for UUID

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-14 04:26:44 +00:00
MichilisandCursor b9f46b02cc Email queue + async sending; legal settings and placeholders
- Add in-memory email queue with rate limiting (MAX_EMAILS_PER_HOUR)
- Bulk send to event attendees now queues and returns immediately
- Frontend shows 'Emails are being sent in the background'
- Legal pages, settings, and placeholders updates

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 21:03:49 +00:00
MichilisandCursor 18254c566e SEO: robots.txt, sitemap, Organization & Event schema; dashboard fmtTime fix; frontend updates
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 07:55:43 +00:00
MichilisandCursor fe75912f23 Fix event capacity: no negative availability, sold-out enforcement, admin override
- Backend: use calculateAvailableSeats so availableSeats is never negative
- Backend: reject public booking when confirmed >= capacity; admin create/manual bypass capacity
- Frontend: spotsLeft = max(0, capacity - bookedCount), isSoldOut when bookedCount >= capacity
- Frontend: sold-out redirect on booking page, cap quantity by spotsLeft, never show negative

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 03:01:58 +00:00
MichilisandCursor 2b2f2cc4ed Admin event: Manual ticket + Tickets tab
- Backend: POST /api/tickets/admin/manual - creates ticket and sends confirmation + ticket email
- Frontend: Manual Ticket button and modal (email required, sends confirmation + ticket)
- New Tickets tab between Attendees and Send Email: confirmed tickets table with search (name/ticket ID), status filter, check-in actions

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-08 02:44:26 +00:00
Michilis 0c142884c7 feat: add featured event with automatic fallback
- Add featured_event_id to site_settings (schema + migration)
- Backend: featured event logic in /events/next/upcoming with auto-unset when event ends
- Site settings: PUT supports featuredEventId, add PUT /featured-event for admin
- Admin events: Set as featured checkbox in editor, star toggle in list, featured badge
- Admin settings: Featured Event section with current event and remove/change links
- API: siteSettingsApi.setFeaturedEvent(), Event.isFeatured, SiteSettings.featuredEventId
- Homepage/linktree unchanged: still use getNextUpcoming (now returns featured or fallback)
2026-02-03 19:24:00 +00:00
Michilis 4a84ad22c7 Backend and frontend updates: auth, email, payments, events, tickets; carrousel images; mobile event detail layout; i18n 2026-02-02 20:58:21 +00:00
Michilis 9410e83b89 Add ticket system with QR scanner and PDF generation
- Add ticket validation and check-in API endpoints
- Add PDF ticket generation with QR codes (pdfkit)
- Add admin QR scanner page with camera support
- Add admin site settings page
- Update email templates with PDF ticket download link
- Add checked_in_by_admin_id field for audit tracking
- Update booking success page with ticket download
- Various UI improvements to events and booking pages
2026-02-02 00:45:12 +00:00
Michilis 6df3baf0be Update site changes 2026-01-31 22:32:54 +00:00
root 47ba754f05 Add full SEO optimization for Spanglish social and language events
- Add comprehensive metadata to root layout with Open Graph, Twitter cards
- Create dynamic sitemap.ts for all pages and events
- Create robots.ts with proper allow/disallow rules
- Add JSON-LD Event structured data to event detail pages
- Add page-specific metadata to events, community, contact, FAQ pages
- Add FAQ structured data schema
- Update footer with local SEO text for Asunción, Paraguay
- Add web manifest for mobile SEO
- Create 404 page with proper noindex
- Optimize image alt text and add lazy loading
- Add NEXT_PUBLIC_SITE_URL env variable
- Add about/ folder to gitignore
2026-01-30 21:05:25 +00:00
Michaël 2302748c87 first commit 2026-01-29 14:13:11 -03:00