Repository navigation
feat: events — collection, enrollment, calendar, embeddable blocks - #98
Merged
Merged
Conversation
- events collection (drafts, uk/en locales, local/virtual location, cover, capacity) with published-only public reads; meeting link field-gated to authenticated reads and served to registered users via a server action - event-enrollments collection: unique user×event, rate-limited creates, user binding on REST, published/not-finished/capacity rules in hooks, owner-cancellable, cascaded from events and users deletes - frontend: /events with upcoming/past tabs, featured next-event card with live countdown; event page with enroll/unenroll, seats left, address/map for offline and a platform-aware join card for online events; add to calendar via Google link and public .ics endpoint (meeting link never leaks into static HTML or calendar files) - eventsBlock embeddable in posts (Lexical) and pages (layout), upcoming or hand-picked selection - profile shows upcoming registered events; events indexed in search - idempotent migration rehearsed on a ci-base fork (up, down, re-up) - 18 integration tests + e2e smoke specs
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
Playwright Report✅ E2E passed — job logs (run attempt 1) |
…on fails The comment list and like button fetched via server actions with no failure path, so any rejected action left the skeleton pulsing forever. Comments now show an error with a retry, the like button falls back to a usable state, and a toggle failure reverts the optimistic flip. Adds an e2e test that forces the actions to return 500.
…to claude/events-feature-df10f1
A script element has no rendered text, so toContainText always saw an empty string even though the page emits the correct Event schema.
The Google Meet accent was a teal that read as a different state next to the green registered badge. Meet now uses the exact success green and the join button uses the brand orange like other primary actions. The share row on event pages no longer sits in a bordered card.
- meetingLink is readable by admins only over REST; enrolled users still get it via getEventJoinInfo - serialize registrations per event with a transaction advisory lock so capacity cannot be exceeded - events block in selection mode no longer falls back to upcoming events - like button shows a neutral retry state instead of made-up 0 likes when the lookup fails - event changes also revalidate the homepage, CMS pages and posts that can embed the block - search results for events get a type label - event delete cascade pages through comments instead of capping at 10000 - cache the event lookup per render and drop the redundant router.refresh - remove unused i18n keys
Use the redesign's blue glow over the void gradient, the abstract hero lines and an accent line under the title. No rails are introduced.
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.
What
A full events feature that mirrors the courses/enrollments architecture.
Data model
eventscollection — drafts/versions with autosave + scheduled publish, localized title/description/address, cover (media), start/end datetime,locationType: local | virtualwith conditional fields (address + map link vs meeting link), optional capacity, auto-stampedpublishedAt. Public reads are published-only (authenticatedOrPublished); admin-only writes.event-enrollmentscollection — uniqueuser×event(hook check + DB unique index), rate-limited creates (event-enroll-create, 30/10 min), non-admin REST creates are bound to the requesting user before the duplicate check. Business rules enforced inbeforeValidatefor users and server actions alike (admins may bypass for manual sign-ups): event must be published, not finished, and have free seats. Owners may cancel their own registration (no forgeable progress here — mirrors Likes);enrolledAtserver-stamped.ON DELETE SET NULLFK shape requires it).Zoom integration decision
A real embedded Zoom client needs a Meeting SDK app, a backend JWT-signing endpoint and (since March 2026) ZAK/OBF app-authorization for external participants — heavy infrastructure and credential liability for marginal UX gain. Instead the meeting link is a first-class field rendered as a platform-aware join card (Zoom / Google Meet / YouTube accents), and it is treated as a perk of registration:
overrideAccess: false, so it never enters the shared ISR HTML,getEventJoinInfoserver action,.icsendpoint and Google Calendar links use the event page URL as the location for virtual events, so calendar files don't leak it either.Frontend (Iron Energy theme)
/events— ISR (300 s) page whose upcoming/past split happens client-side against the live clock (no stale boundary), with tabs, a featured next-event card with live countdown, date-tile cards echoing the homepage calendar motif, and enrolled badges fetched client-side so the page stays statically cacheable./events/[slug]— cover hero, format/seats badges, date-time summary, participants count, enroll/unenroll action bar (login redirect for guests, "no seats" state, confirm-on-cancel), location card with map link (offline) or join card (online), add-to-calendar (Google +.ics).eventsBlock— embeddable in posts (LexicalBlocksFeature) and pages (layout blocks), populated by upcoming events or a hand-picked selection; renders published-only via access-controlled re-fetch.Migration
20260815_120000_events— hand-written idempotent DDL mirroring drizzle push (incl. unprefixed compound index nameuser_event_idx,search_rels/pages_rels/_pages_v_relscolumns). Rehearsed on a temporary fork ofci-base: fresh up ✓, down ✓, re-up ✓; re-run on the pushed dev schema is a clean no-op.Tests
events,event-enrollments): publish validation (virtual needs link, local needs address, endDate > startDate), draft invisibility, meeting-link field access, non-admin write rejection, cascade delete, duplicate/draft/finished/capacity enrollment rejections, ongoing-event enrollment, user binding,adminOrOwnread matrix, owner-delete vs foreign-delete, owner-update rejection. Full suite: 141/141 passing..icsresponse.Verified in browser (dev branch)
Listing tabs/featured/countdown (uk + en), enroll → enrolled badge + Zoom join card with link + live seats decrement, unenroll → back to enroll state, profile section, admin list view,
.icsdownload.Notes
home-calendarglobal still powers the homepage "Найближчі зміни" section — it now overlaps with this collection and is worth migrating to events in a follow-up.🤖 Generated with Claude Code
Preview admin login fix
The preview branch URL is now included in Better Auth's trusted origins and Payload CORS. Preview auth uses the branch URL even if a production base URL override is configured, and the browser calls its own origin. This fixes
INVALID_ORIGINwhen signing in through the PR's stable Vercel preview link.Event page UX, time, interactions, and SEO
Europe/Kyivwith a visible Kyiv time label, while calendar files keep UTC instants.20260928_120000_event_interactionsenum migration.The long-lived Vercel preview database needs the two enum additions before event comments and likes can be exercised there. Previews deliberately skip migrations; production applies the migration during deployment.
Comments and likes stuck in skeleton (500s on event pages)
eventsto thetarget_collectionenums, but previews never run migrations, so the preview DB still hadposts,courses(,comments). Every event-page query failed withinvalid input value for enum enum_likes_target_collection: "events", the server action returned 500, and the client had no failure path, so the skeleton pulsed forever. The preview DB has been given the two enum values (same idempotent statements as20260928_120000_event_interactions); production gets them from the migration on deploy.CommentsSectionnow shows a load error with a retry button,LikeButtonfalls back to a usable button (and reverts the optimistic flip if a toggle throws), instead of pulsing forever when an action rejects. New i18n keyscommentsLoadError/commentsRetry(uk + en).tests/e2e/comments-resilience.e2e.spec.tsforces the server actions to return 500 and asserts the skeleton clears and retry works. Fixed the JSON-LD assertion inevents.e2e.spec.ts(a<script>has no rendered text, sotoContainTextalways saw an empty string).main(admin docs, PR feat: двотрекова документація в адмінці (/admin/docs) #99) into the branch. Full integration suite: 22/22 files passing.🤖 Generated with Claude Code
Code-review fixes and redesign alignment
meetingLinkis now readable by admins only over REST (it was readable by any signed-in user); enrolled users still get it throughgetEventJoinInfo.mainwith the homepage redesign (feat: platform-first homepage redesign, abstract lines, full partners grid #100). The event detail header now uses the redesign's glow, abstract lines and accent line, and/eventspicks up the accent line throughPageHead. No rails are used anywhere.🤖 Generated with Claude Code