Skip to content

feat: add registration closed functionality to events - #70

Merged
Bhav-ikkk merged 8 commits into
NexEvent:mainfrom
kanchan0505:main
Apr 3, 2026
Merged

feat: add registration closed functionality to events#70
Bhav-ikkk merged 8 commits into
NexEvent:mainfrom
kanchan0505:main

Conversation

@kanchan0505

@kanchan0505 kanchan0505 commented Apr 2, 2026

Copy link
Copy Markdown
Collaborator

added registration closed functionality to events

Summary by CodeRabbit

  • New Features

    • Added a “Registration Closed” event status with visual badges and disabled CTAs across event pages and listing cards.
    • Times and dates now render in the Asia/Kolkata timezone.
  • Behavior Changes

    • Cart now retains items for closed/unavailable events (marked unavailable) and preserves stored quantities.
    • Persist/hydrate logic strips runtime-only registration flags from stored cart data.
  • Bug Fixes

    • Checkout is blocked when unavailable/registration-closed items remain in cart.

Copilot AI review requested due to automatic review settings April 2, 2026 19:39
@coderabbitai

coderabbitai Bot commented Apr 2, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a non-null registration_closed boolean to events (with index) and surfaces it through event queries; cart persistence/validation now tracks unavailable items via a registrationClosed runtime flag rather than removing them; UI replaces seat-based sold-out logic with registration-closed gating across listing, detail, and cart views.

Changes

Cohort / File(s) Summary
Database Schema
src/services/database/migrations/009_add_registration_closed.sql, src/services/database/schema.sql, schema.sql
Add registration_closed BOOLEAN NOT NULL DEFAULT FALSE to events and create index idx_events_registration_closed.
Event Service
src/services/event-service.js
Extend event query selects to include registration_closed for published/featured/by-id/by-ids queries.
Cart State Management
src/store/slices/cartSlice.js
Strip runtime registrationClosed on persist/hydrate; validateCart.fulfilled now retains unavailable or removed DB events (marks with maxAvailable/registrationClosed) and preserves stored quantities instead of removing or capping them.
Cart View
src/views/cart/CartView.js
Render isUnavailable from registrationClosed, show "Registration Closed" badge, hide quantity control for unavailable items, update toasts/captions, and disable checkout when unavailable items exist or validation is pending.
Event Detail View
src/views/events/EventDetailView.js
Use isRegClosed (from registration_closed) to disable/suppress purchase/register CTAs; enforce Asia/Kolkata timezone for date/time formatting; remove sold-out seat gating.
Events Page View
src/views/events/EventsPageView.js
Format dates/times with Asia/Kolkata; use isRegClosed for primary CTA (disabled "Registration Closed"); remove sold-out/almost-full indicators and sold-out label behavior.

Sequence Diagram(s)

sequenceDiagram
    participant Client as UI (Listing/Detail/Cart)
    participant Server as EventService
    participant DB as Database
    Client->>Server: fetch events / event by id (include registration_closed)
    Server->>DB: SELECT ... , registration_closed FROM events
    DB-->>Server: rows (with registration_closed)
    Server-->>Client: event payloads include registration_closed
    Client->>Client: render CTAs (if registration_closed -> disable/suppress)
    Client->>Server: validateCart (cart items list)
    Server->>DB: fetch current events by ids (include registration_closed, availability)
    DB-->>Server: current event states
    Server-->>Client: validation result (items retained, marked registrationClosed/maxAvailable)
    Client->>Client: persistCart (strip runtime registrationClosed before localStorage)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

size/s

Poem

🐰 Registration gates close, a gentle thump,
Cart items kept, not tossed to the dump.
Flags flutter “closed” where seats once shone,
UI dims and badges quietly bemoan.
Hopping on code, I cheer this small bump 🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 77.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'feat: add registration closed functionality to events' accurately summarizes the main change: adding a new registration_closed feature to the event system across database schema, service layer, cart management, and UI components.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new events.registration_closed flag and threads it through admin editing, event fetching, cart validation, and checkout/payment enforcement to prevent new purchases when registration is closed.

Changes:

  • Added registration_closed column via DB migration and surfaced it in event/service queries.
  • Enforced “registration closed” during checkout validation and payment order creation.
  • Updated admin edit flow + UI and updated cart UI/behavior to mark closed items and block checkout.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/views/events/EventsPageView.js Shows “Registration Closed” state and disables some CTAs based on event.registration_closed.
src/views/events/EventDetailView.js Shows “Registration Closed” state and disables some CTAs based on event.registration_closed.
src/views/cart/CartView.js Visually marks closed items and blocks checkout when closed items exist.
src/store/slices/cartSlice.js Strips/persists registrationClosed flag and sets it during cart validation from DB.
src/services/payment-service.js Blocks order creation when registration_closed is true.
src/services/event-service.js Includes registration_closed in event payloads returned to the frontend.
src/services/database/migrations/009_add_registration_closed.sql Adds registration_closed column to events.
src/services/checkout-service.js Treats registration_closed as a validation error for checkout items.
src/services/admin-service.js Allows updating registration_closed via registrationClosed input.
src/pages/api/admin/events/[id].js Accepts registrationClosed in PUT and passes to admin service.
src/pages/admin/events/[id]/edit.js Adds a “Close Registration” switch in the admin event edit UI.
Comments suppressed due to low confidence (1)

src/store/slices/cartSlice.js:181

  • validateCart no longer removes sold-out events (dbAvailable <= 0) and also stops capping quantity to availability. This breaks the invariant described in addToCart/updateQuantity (cap at maxAvailable) and can allow checkout to proceed with impossible quantities until the backend rejects it. Consider either (a) returning null for sold-out items (consistent with validationRemovedCount messaging), or (b) keeping them with an explicit soldOut flag + maxAvailable: 0, and always capping quantity to dbAvailable.
            const dbAvailable = Math.max(0, (dbEvent.seats || 0) - (dbEvent.registered || 0))

            return {
              ...item,
              title: dbEvent.title,

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/views/events/EventsPageView.js Outdated
const spotsLeft = event.seats > 0 ? event.seats - (event.registered || 0) : null
const almostFull = spotsLeft !== null && spotsLeft <= Math.ceil(event.seats * 0.2)
const isRegClosed = !!event.registration_closed
const isDisabled = isRegClosed

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isDisabled only reflects registration_closed, so sold-out events (seats - registered <= 0) will still show enabled “Add to Cart” / “Buy Ticket” actions. Because addToCart is capped by maxAvailable, clicks can become no-ops (or route to checkout and fail server-side). Consider folding availability into isDisabled (and restoring the Sold Out label) so the UI matches backend behavior.

Suggested change
const isDisabled = isRegClosed
const seatLimit = Number(event.seats)
const registeredCount = Number(event.registered ?? 0)
const hasSeatLimit = Number.isFinite(seatLimit)
const isSoldOut = hasSeatLimit && seatLimit - registeredCount <= 0
const isDisabled = isRegClosed || isSoldOut

Copilot uses AI. Check for mistakes.
Comment thread src/views/events/EventDetailView.js Outdated
Comment on lines +233 to +237
const currentImage = allImages[activeImageIndex] || null
const hasGallery = allImages.length > 1
const isOver = event.start_time ? new Date(event.start_time).getTime() <= Date.now() : false
const isRegClosed = !!event.registration_closed
const isDisabled = isRegClosed

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isDisabled is set only from registration_closed, so the purchase CTAs can remain enabled when spotsLeft <= 0. This leads to “Buy Now” routing users into checkout where validation fails, and “Add to Cart” can silently do nothing due to maxAvailable: 0. Consider disabling CTAs (and showing a Sold Out state) when spotsLeft <= 0 as well.

Copilot uses AI. Check for mistakes.
Comment thread src/store/slices/cartSlice.js Outdated
const dbAvailable = Math.max(0, (dbEvent.seats || 0) - (dbEvent.registered || 0))
// Registration closed — mark it but keep in cart so user sees reason
if (dbEvent.registration_closed) {
return { ...item, title: dbEvent.title, registrationClosed: true }

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For registration-closed events, validateCart keeps the item but only updates title. That leaves other DB-sourced fields (ticketPrice/startTime/venue/image/maxAvailable) potentially stale in the cart UI. Consider refreshing the same fields you refresh in the non-closed branch, and setting maxAvailable to 0 (or similar) to clearly mark it as unavailable.

Suggested change
return { ...item, title: dbEvent.title, registrationClosed: true }
return {
...item,
title: dbEvent.title,
ticketPrice: parseFloat(dbEvent.ticket_price) || 0,
startTime: dbEvent.start_time,
venue: dbEvent.venue || item.venue,
image: dbEvent.images?.[0] || item.image,
maxAvailable: 0,
registrationClosed: true,
quantity: item.quantity
}

Copilot uses AI. Check for mistakes.
maxAvailable: dbAvailable,
// Cap stored quantity to actual availability — no fallback to 1
quantity: Math.min(item.quantity, dbAvailable)
maxAvailable: dbAvailable || null,

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maxAvailable: dbAvailable || null turns a real availability of 0 into null (treated as “unknown/unlimited” elsewhere, e.g. updateQuantity uses Infinity when maxAvailable is null). Keeping maxAvailable as a number (including 0) preserves correct quantity caps and UI disablement logic.

Suggested change
maxAvailable: dbAvailable || null,
maxAvailable: dbAvailable,

Copilot uses AI. Check for mistakes.
Comment on lines 559 to 564
<Button
variant='contained'
size='large'
fullWidth
disabled={validating}
disabled={validating || hasClosedItems}
endIcon={<Icon icon='tabler:arrow-right' />}

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checkout is disabled only for hasClosedItems, but with the current validateCart behavior sold-out/unavailable items can remain in the cart and still allow “Proceed to Checkout”, leading to a failure later in checkout validation. Consider also preventing checkout when any cart item is unavailable (e.g. maxAvailable === 0 / soldOut flag) and surfacing a clear message.

Copilot uses AI. Check for mistakes.
Comment on lines +4 to +8
ALTER TABLE events
ADD COLUMN IF NOT EXISTS registration_closed BOOLEAN NOT NULL DEFAULT FALSE;

-- Index for quick filtering
CREATE INDEX IF NOT EXISTS idx_events_registration_closed ON events(registration_closed);

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This migration adds events.registration_closed, but the repo keeps full schema snapshots in both schema.sql (root) and src/services/database/schema.sql. Those schema files currently don’t include registration_closed, so fresh DB bootstraps from schema.sql will diverge from migrated DBs. Please update both schema files to include the new column.

Copilot uses AI. Check for mistakes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
src/store/slices/cartSlice.js (2)

186-188: Potential UX issue: maxAvailable: dbAvailable || null loses sold-out cap.

When dbAvailable is 0 (sold out), 0 || null evaluates to null, removing the quantity cap. Users can increase quantity for sold-out events and only discover the issue at checkout.

Consider preserving 0 to maintain the cap:

♻️ Preserve zero availability for proper UI capping
-              maxAvailable: dbAvailable || null,
+              maxAvailable: dbAvailable >= 0 ? dbAvailable : null,

Or more explicitly:

-              maxAvailable: dbAvailable || null,
+              maxAvailable: typeof dbAvailable === 'number' ? dbAvailable : null,

This ensures QuantityControl properly disables the increment button when events are sold out.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/store/slices/cartSlice.js` around lines 186 - 188, The assignment
"maxAvailable: dbAvailable || null" discards a legitimate 0 value (sold out) and
should preserve zero so UI can disable increments; in the cart slice where
maxAvailable is set (look for the mapping that assigns maxAvailable and the
dbAvailable variable, e.g., in cartSlice reducer/selector), replace the falsy-or
pattern with a nullish check so 0 is kept (for example use the nullish
coalescing approach or an explicit undefined/null check) so maxAvailable becomes
0 when dbAvailable is 0 and only falls back to null when dbAvailable is actually
null/undefined.

172-175: Consider updating all item fields even for closed registrations.

When registration_closed is true, only title and registrationClosed are updated. Other fields like ticketPrice, venue, and startTime retain potentially stale values. While checkout is blocked, users may see outdated prices in the cart summary which could be confusing.

♻️ Suggested improvement to keep all fields fresh
             // Registration closed — mark it but keep in cart so user sees reason
             if (dbEvent.registration_closed) {
-              return { ...item, title: dbEvent.title, registrationClosed: true }
+              const dbAvailable = Math.max(0, (dbEvent.seats || 0) - (dbEvent.registered || 0))
+              return {
+                ...item,
+                title: dbEvent.title,
+                ticketPrice: parseFloat(dbEvent.ticket_price) || 0,
+                startTime: dbEvent.start_time,
+                venue: dbEvent.venue || item.venue,
+                image: dbEvent.images?.[0] || item.image,
+                maxAvailable: dbAvailable || null,
+                registrationClosed: true
+              }
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/store/slices/cartSlice.js` around lines 172 - 175, The branch in
cartSlice that handles dbEvent.registration_closed only updates title and
registrationClosed, leaving ticketPrice, venue, startTime, etc. stale; change
the return to copy the current item’s immutable fields (e.g., id, quantity) but
overwrite all event-related fields from dbEvent and still set
registrationClosed: true (e.g., merge/pick fields like ticketPrice, venue,
startTime, capacity, title from dbEvent into the item while preserving
user-specific keys), ensuring the cart summary shows fresh event data even when
checkout is blocked.
src/views/events/EventsPageView.js (1)

106-107: Consider removing the redundant isDisabled alias or documenting its purpose.

Currently isDisabled = isRegClosed is a simple alias. If this is intentional scaffolding for future conditions (e.g., isRegClosed || isSoldOut), consider adding a brief comment. Otherwise, using isRegClosed directly would be clearer.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/events/EventsPageView.js` around lines 106 - 107, The alias
isDisabled duplicates isRegClosed; either remove the redundant constant and
replace usages of isDisabled with isRegClosed, or keep isDisabled as intended
scaffolding by adding a short comment above the declaration (e.g., "scaffold for
future combined disabled logic like isRegClosed || isSoldOut") and update the
code to make clear it's deliberate; locate the constants isRegClosed and
isDisabled in EventsPageView.js and perform the change plus adjust any
downstream references (e.g., button disabled props or conditional rendering)
accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/services/checkout-service.js`:
- Around line 49-50: The new guard that checks event.registration_closed must
also be enforced inside createBookings(): when you reload the event under lock
in createBookings() (the block that re-queries the event and acquires the lock
around lines where the event is reloaded under lock), include
registration_closed in the SELECT or explicitly reject when
event.registration_closed is true so a direct caller or a post-validation race
cannot create bookings for closed events; update the query or add an if-check
immediately after reload in createBookings() to throw/return the same error used
by the preflight path to keep behavior consistent.

In `@src/services/database/migrations/009_add_registration_closed.sql`:
- Around line 4-8: The base schema file is out of sync with the migration: add
the new column and index to schema.sql so fresh DBs match migrated DBs;
specifically, update the CREATE TABLE events definition to include
"registration_closed BOOLEAN NOT NULL DEFAULT FALSE" and add a matching CREATE
INDEX IF NOT EXISTS idx_events_registration_closed ON
events(registration_closed) (or place the index after the table DDL) to mirror
the migration that adds registration_closed and its index.

In `@src/views/events/EventDetailView.js`:
- Around line 236-237: The change replaced the sold-out CTA state by making
isDisabled mirror isRegClosed; restore explicit handling by preserving ctaState
and using it in the CTA button branches instead of relying on isDisabled alone.
Update the render logic that builds the CTA (the branches that reference
isDisabled and event.registration_link) to check ctaState (e.g., "sold_out",
"closed", "open") and only render clickable registration_link actions when
ctaState indicates the event is open; keep isRegClosed for the closed-flag but
do not remove or overwrite the sold-out state so sold-out buttons render
non-clickable as intended.

---

Nitpick comments:
In `@src/store/slices/cartSlice.js`:
- Around line 186-188: The assignment "maxAvailable: dbAvailable || null"
discards a legitimate 0 value (sold out) and should preserve zero so UI can
disable increments; in the cart slice where maxAvailable is set (look for the
mapping that assigns maxAvailable and the dbAvailable variable, e.g., in
cartSlice reducer/selector), replace the falsy-or pattern with a nullish check
so 0 is kept (for example use the nullish coalescing approach or an explicit
undefined/null check) so maxAvailable becomes 0 when dbAvailable is 0 and only
falls back to null when dbAvailable is actually null/undefined.
- Around line 172-175: The branch in cartSlice that handles
dbEvent.registration_closed only updates title and registrationClosed, leaving
ticketPrice, venue, startTime, etc. stale; change the return to copy the current
item’s immutable fields (e.g., id, quantity) but overwrite all event-related
fields from dbEvent and still set registrationClosed: true (e.g., merge/pick
fields like ticketPrice, venue, startTime, capacity, title from dbEvent into the
item while preserving user-specific keys), ensuring the cart summary shows fresh
event data even when checkout is blocked.

In `@src/views/events/EventsPageView.js`:
- Around line 106-107: The alias isDisabled duplicates isRegClosed; either
remove the redundant constant and replace usages of isDisabled with isRegClosed,
or keep isDisabled as intended scaffolding by adding a short comment above the
declaration (e.g., "scaffold for future combined disabled logic like isRegClosed
|| isSoldOut") and update the code to make clear it's deliberate; locate the
constants isRegClosed and isDisabled in EventsPageView.js and perform the change
plus adjust any downstream references (e.g., button disabled props or
conditional rendering) accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0c359b03-73fb-4928-a43a-3042c9653a76

📥 Commits

Reviewing files that changed from the base of the PR and between b4f0031 and 79f88a3.

📒 Files selected for processing (11)
  • src/pages/admin/events/[id]/edit.js
  • src/pages/api/admin/events/[id].js
  • src/services/admin-service.js
  • src/services/checkout-service.js
  • src/services/database/migrations/009_add_registration_closed.sql
  • src/services/event-service.js
  • src/services/payment-service.js
  • src/store/slices/cartSlice.js
  • src/views/cart/CartView.js
  • src/views/events/EventDetailView.js
  • src/views/events/EventsPageView.js

Comment thread src/services/checkout-service.js Outdated
Comment on lines 49 to 50
e.registration_closed,
d.name AS "departmentName"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Enforce the same guard inside createBookings().

This new check only protects the preflight path. Later in the same file, createBookings() still reloads the event under lock but does not select or reject registration_closed (Lines 227-245), so a direct caller—or a close-registration race after validation—can still create a confirmed booking for a closed event.

Suggested follow-up in createBookings()
       const event = await t.oneOrNone(`
         SELECT
           id,
           name AS title,
           ticket_price,
           max_tickets AS seats,
           registered,
           status,
-          visibility
+          visibility,
+          registration_closed
         FROM events
         WHERE id = $1
         FOR UPDATE
       `, [eventId])

       if (!event) throw new Error(`Event ${eventId} not found`)
       if (event.status !== 'published') throw new Error(`Event "${event.title}" is not available`)
       if (event.visibility !== 'public') throw new Error(`Event "${event.title}" is not public`)
+      if (event.registration_closed) throw new Error(`Registration is closed for "${event.title}"`)

Also applies to: 71-74

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/services/checkout-service.js` around lines 49 - 50, The new guard that
checks event.registration_closed must also be enforced inside createBookings():
when you reload the event under lock in createBookings() (the block that
re-queries the event and acquires the lock around lines where the event is
reloaded under lock), include registration_closed in the SELECT or explicitly
reject when event.registration_closed is true so a direct caller or a
post-validation race cannot create bookings for closed events; update the query
or add an if-check immediately after reload in createBookings() to throw/return
the same error used by the preflight path to keep behavior consistent.

Comment thread src/services/database/migrations/009_add_registration_closed.sql
Comment thread src/views/events/EventDetailView.js Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/store/slices/cartSlice.js (1)

180-191: ⚠️ Potential issue | 🟠 Major

Reconcile quantity when availability shrinks.

Keeping item.quantity here leaves the cart subtotal overstated and keeps checkout open for partially oversubscribed items. src/services/checkout-service.js:68-92 caps the quantity later, so the user only sees the correction after leaving the cart. If you need the original request for messaging, store it separately instead of using quantity.

Suggested fix
             const dbAvailable = Math.max(0, (dbEvent.seats || 0) - (dbEvent.registered || 0))
+            const reconciledQuantity =
+              dbAvailable > 0 ? Math.min(item.quantity, dbAvailable) : item.quantity

             return {
               ...item,
               title: dbEvent.title,
               ticketPrice: parseFloat(dbEvent.ticket_price) || 0,
               startTime: dbEvent.start_time,
               venue: dbEvent.venue || item.venue,
               image: dbEvent.images?.[0] || item.image,
               maxAvailable: dbAvailable || null,
               registrationClosed: false,
-              quantity: item.quantity
+              quantity: reconciledQuantity
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/store/slices/cartSlice.js` around lines 180 - 191, The cart item is left
with the original item.quantity even when dbAvailable has shrunk; update the
mapping in cartSlice (the code that returns the merged item object) to set
quantity to Math.min(item.quantity, dbAvailable || 0) and store the original
request as a separate field (e.g., requestedQuantity or originalQuantity) so
messaging can still reference the user's requested amount; also set
registrationClosed to true when dbAvailable === 0 and ensure maxAvailable uses
null only when there is no numeric availability, preserving numeric zero.
src/views/cart/CartView.js (1)

584-613: ⚠️ Potential issue | 🟠 Major

Re-check availability after validateCart() before routing to checkout.

The disabled state is computed before the click. Because hydrateCart() strips runtime availability flags, the first validateCart() in this handler can mark items closed or sold out after the click has already started, and this code still forwards every current cart item to checkout.

Suggested fix
               const currentItems = store.getState().cart.items
+              const hasUnavailableItems = currentItems.some(
+                item => item.registrationClosed || item.maxAvailable === 0 || item.maxAvailable === null
+              )
+              if (hasUnavailableItems) {
+                toast.error('Remove unavailable events from your cart to proceed.')
+                return
+              }
               dispatch(setCheckoutItems({
                 items: currentItems.map(i => ({ eventId: i.eventId, quantity: i.quantity })),
                 source: 'cart'
               }))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/cart/CartView.js` around lines 584 - 613, The click handler
forwards the pre-hydrated cart items to checkout even if validateCart() removed
closed/sold-out events; instead, after calling validateCart() and confirming
result.meta.requestStatus === 'fulfilled', use the returned validEvents
(result.payload) to build the checkout items (map each validEvent to { eventId,
quantity }) when calling setCheckoutItems, so only validated items are sent to
checkout; keep the existing empty-check and routing logic (including
setExistingUser and router.push) but replace store.getState().cart.items with
the validated validEvents list to prevent closed/sold-out items from reaching
checkout (refer to validateCart, setCheckoutItems, result.payload / validEvents,
and setExistingUser).
src/views/events/EventDetailView.js (1)

1022-1103: ⚠️ Potential issue | 🟠 Major

The second mobile CTA block lost the external-registration path.

The hero CTA still branches on event.registration_link, but the Rounds-tab CTA now sends every open event through internal checkout. For externally registered events, mobile users see Buy Now / Add to Cart instead of Register Now.

Suggested fix
-                ) : (
+                ) : event.registration_link ? (
+                  <Button
+                    variant='contained'
+                    disableElevation
+                    fullWidth
+                    href={event.registration_link}
+                    target='_blank'
+                    rel='noopener noreferrer'
+                    startIcon={<Icon icon='tabler:external-link' fontSize={18} />}
+                  >
+                    Register Now
+                  </Button>
+                ) : (
                   <>
                     <Button
                       variant='contained'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/events/EventDetailView.js` around lines 1022 - 1103, The mobile CTA
always renders internal checkout buttons when ctaState === 'open', causing
externally-registered events to show "Buy Now"/"Add to Cart" instead of the
external flow; update the open-CTA branch in EventDetailView (the JSX that
currently renders the two Buttons with onClick handlers that call
setCheckoutItems and addToCart) to first check event.registration_link and, if
present, render the external "Register Now" button that navigates to
event.registration_link (preserving login/returnUrl behavior if needed) instead
of the internal Buy Now/Add to Cart buttons; keep the existing internal checkout
logic (dispatch(setCheckoutItems(...)), dispatch(setExistingUser(...)),
router.push('/checkout')) only for events without event.registration_link.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/views/events/EventDetailView.js`:
- Around line 236-240: The ctaState logic in EventDetailView.js relies on
event.registration_closed but getEventById in src/services/event-service.js
doesn’t fetch that field; update getEventById to include e.registration_closed
in its query/selection and ensure the returned event object maps
registration_closed (boolean) so EventDetailView's ctaState and isDisabled logic
see the correct value; verify any GraphQL/SQL projection or DTO mapping in
getEventById includes registration_closed.

---

Outside diff comments:
In `@src/store/slices/cartSlice.js`:
- Around line 180-191: The cart item is left with the original item.quantity
even when dbAvailable has shrunk; update the mapping in cartSlice (the code that
returns the merged item object) to set quantity to Math.min(item.quantity,
dbAvailable || 0) and store the original request as a separate field (e.g.,
requestedQuantity or originalQuantity) so messaging can still reference the
user's requested amount; also set registrationClosed to true when dbAvailable
=== 0 and ensure maxAvailable uses null only when there is no numeric
availability, preserving numeric zero.

In `@src/views/cart/CartView.js`:
- Around line 584-613: The click handler forwards the pre-hydrated cart items to
checkout even if validateCart() removed closed/sold-out events; instead, after
calling validateCart() and confirming result.meta.requestStatus === 'fulfilled',
use the returned validEvents (result.payload) to build the checkout items (map
each validEvent to { eventId, quantity }) when calling setCheckoutItems, so only
validated items are sent to checkout; keep the existing empty-check and routing
logic (including setExistingUser and router.push) but replace
store.getState().cart.items with the validated validEvents list to prevent
closed/sold-out items from reaching checkout (refer to validateCart,
setCheckoutItems, result.payload / validEvents, and setExistingUser).

In `@src/views/events/EventDetailView.js`:
- Around line 1022-1103: The mobile CTA always renders internal checkout buttons
when ctaState === 'open', causing externally-registered events to show "Buy
Now"/"Add to Cart" instead of the external flow; update the open-CTA branch in
EventDetailView (the JSX that currently renders the two Buttons with onClick
handlers that call setCheckoutItems and addToCart) to first check
event.registration_link and, if present, render the external "Register Now"
button that navigates to event.registration_link (preserving login/returnUrl
behavior if needed) instead of the internal Buy Now/Add to Cart buttons; keep
the existing internal checkout logic (dispatch(setCheckoutItems(...)),
dispatch(setExistingUser(...)), router.push('/checkout')) only for events
without event.registration_link.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 89b73815-227a-4911-8b9b-5a03452e5a7a

📥 Commits

Reviewing files that changed from the base of the PR and between 79f88a3 and 78cdd1c.

📒 Files selected for processing (6)
  • schema.sql
  • src/services/database/schema.sql
  • src/store/slices/cartSlice.js
  • src/views/cart/CartView.js
  • src/views/events/EventDetailView.js
  • src/views/events/EventsPageView.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/views/events/EventsPageView.js

Comment thread src/views/events/EventDetailView.js Outdated
Comment on lines +236 to +240
// ctaState: 'closed' | 'soldout' | 'open'
const ctaState = !!event.registration_closed ? 'closed'
: (spotsLeft <= 0) ? 'soldout'
: 'open'
const isDisabled = ctaState !== 'open'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

ctaState depends on a field the detail query still doesn't fetch.

src/services/event-service.js:240-280 still omits e.registration_closed from getEventById(). On this page, !!event.registration_closed will therefore stay false, so closed events with seats remaining render the open CTA until a later server-side rejection.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/events/EventDetailView.js` around lines 236 - 240, The ctaState
logic in EventDetailView.js relies on event.registration_closed but getEventById
in src/services/event-service.js doesn’t fetch that field; update getEventById
to include e.registration_closed in its query/selection and ensure the returned
event object maps registration_closed (boolean) so EventDetailView's ctaState
and isDisabled logic see the correct value; verify any GraphQL/SQL projection or
DTO mapping in getEventById includes registration_closed.

@Bhav-ikkk Bhav-ikkk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You did unnessary code changes, when we can just create one one condition and handle things from UI, this changes will intoduce another handling for registration closed, and also change the hash code of restricted files

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modification in this file is prohibited, just handle closing from UI

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/views/cart/CartView.js (1)

565-589: ⚠️ Potential issue | 🟠 Major

Missing registration-closed check after validation allows checkout with unavailable items.

The onClick handler re-validates the cart but doesn't check if any items were marked with registrationClosed: true before proceeding. This allows users to bypass the intended gate: if a user clicks checkout while hasUnavailableItems is false (before initial validation completes or during a brief window), and validation then marks items as unavailable, the handler will still proceed to checkout.

The validEvents.length === 0 check only catches the case where ALL events are removed, not where some have closed registration.

🔧 Proposed fix to check registration-closed status after validation
              const validEvents = result.payload ?? []
              if (validEvents.length === 0) {
                // All events removed (sold out / unpublished) — EmptyCart will render
                return
              }
              // Set checkout items (eventId + quantity only) and navigate
              const currentItems = store.getState().cart.items
+             // Block checkout if any items have registration closed
+             if (currentItems.some(item => item.registrationClosed)) {
+               toast.error('Some events have closed registration. Please remove them to proceed.')
+               return
+             }
              dispatch(setCheckoutItems({
                items: currentItems.map(i => ({ eventId: i.eventId, quantity: i.quantity })),
                source: 'cart'
              }))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/cart/CartView.js` around lines 565 - 589, After calling
validateCart() in the onClick handler, inspect the validation result
(result.payload returned by validateCart) for items marked registrationClosed
(or otherwise flagged as unavailable); if any item has registrationClosed ===
true (or is missing from validEvents), show a toast error like "Some items are
no longer available" and return instead of proceeding to setCheckoutItems or
navigating; keep the existing dispatch(setCheckoutItems(...)),
dispatch(setExistingUser(...)) and router.push(...) logic only after confirming
no registrationClosed items are present. Ensure you reference validateCart,
result.payload (validEvents), setCheckoutItems, setExistingUser, toast.error and
router.push when implementing the check.
🧹 Nitpick comments (1)
src/views/cart/CartView.js (1)

345-345: Note timing of hasUnavailableItems evaluation.

Per the cart slice logic, registrationClosed is stripped during hydration (line 88 of cartSlice.js) and only restored when validateCart.fulfilled runs. This means hasUnavailableItems will be false on initial render until validation completes. The validating flag in the disabled condition (line 563) mitigates the window, but see the related comment on the checkout handler for a remaining gap.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/cart/CartView.js` at line 345, The current computation of
hasUnavailableItems (const hasUnavailableItems = items.some(item =>
item.registrationClosed)) happens before validateCart.fulfilled restores
registrationClosed, so it can be false on initial render; update the logic in
CartView to consider validation state: compute hasUnavailableItems to be true if
validating is true and there is any indication of potential unavailability
(e.g., an item has a pre-hydration flag or missing registration info) or,
preferably, derive availability from the validated cart payload when
validateCart.fulfilled exists; in practice, change the check to use the
validated cart state (or guard with the validating flag) so that the checkout
handler and disabled condition reliably reflect availability until validation
completes (referencing hasUnavailableItems, items, registrationClosed,
validateCart.fulfilled, and validating).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/views/cart/CartView.js`:
- Around line 565-589: After calling validateCart() in the onClick handler,
inspect the validation result (result.payload returned by validateCart) for
items marked registrationClosed (or otherwise flagged as unavailable); if any
item has registrationClosed === true (or is missing from validEvents), show a
toast error like "Some items are no longer available" and return instead of
proceeding to setCheckoutItems or navigating; keep the existing
dispatch(setCheckoutItems(...)), dispatch(setExistingUser(...)) and
router.push(...) logic only after confirming no registrationClosed items are
present. Ensure you reference validateCart, result.payload (validEvents),
setCheckoutItems, setExistingUser, toast.error and router.push when implementing
the check.

---

Nitpick comments:
In `@src/views/cart/CartView.js`:
- Line 345: The current computation of hasUnavailableItems (const
hasUnavailableItems = items.some(item => item.registrationClosed)) happens
before validateCart.fulfilled restores registrationClosed, so it can be false on
initial render; update the logic in CartView to consider validation state:
compute hasUnavailableItems to be true if validating is true and there is any
indication of potential unavailability (e.g., an item has a pre-hydration flag
or missing registration info) or, preferably, derive availability from the
validated cart payload when validateCart.fulfilled exists; in practice, change
the check to use the validated cart state (or guard with the validating flag) so
that the checkout handler and disabled condition reliably reflect availability
until validation completes (referencing hasUnavailableItems, items,
registrationClosed, validateCart.fulfilled, and validating).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 699089ce-bb20-42e9-9ac3-3873d5a7aa0f

📥 Commits

Reviewing files that changed from the base of the PR and between 78cdd1c and c90f7b6.

📒 Files selected for processing (1)
  • src/views/cart/CartView.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/views/events/EventsPageView.js (1)

186-301: ⚠️ Potential issue | 🟠 Major

Keep sold-out separate from registration-closed.

Lines 186-301 and 367-482 now gate every CTA only on isRegClosed, even though spotsLeft is still used to compute availability. A full event with registration_closed = false will show live Register Now / Buy Ticket / Add to Cart actions again, and the cart dispatch can be sent maxAvailable: 0. Please restore a shared CTA state (open / soldOut / closed) and use it in both blocks.

Possible fix
   const spotsLeft = event.seats > 0 ? event.seats - (event.registered || 0) : null
   const isRegClosed = !!event.registration_closed
+  const isSoldOut = event.seats > 0 && spotsLeft <= 0
+  const ctaState = isRegClosed ? 'closed' : isSoldOut ? 'soldOut' : 'open'

Use ctaState instead of isRegClosed alone when branching the mobile and desktop CTA buttons.

Also applies to: 367-482

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/events/EventsPageView.js` around lines 186 - 301, The CTA logic
currently only checks isRegClosed which causes sold-out events to show live
actions; compute a shared ctaState (e.g., 'open' | 'soldOut' | 'closed') based
on registration_closed and spotsLeft/event.seats (spotsLeft <= 0 => 'soldOut',
registration_closed => 'closed', else 'open') and replace all isRegClosed
branches in both the mobile and desktop CTA blocks with checks against ctaState
(disable or change labels when ctaState === 'soldOut' and disable when
'closed'), and ensure the addToCart dispatch (addToCart) and
setCheckoutItems/buy flow (setCheckoutItems, setExistingUser, router.push)
respect maxAvailable (use null for unlimited, 0 when sold out) so you never
dispatch a purchase with maxAvailable: 0 for an actionable "Add to Cart" or "Buy
Ticket".
src/views/events/EventDetailView.js (1)

1018-1099: ⚠️ Potential issue | 🟠 Major

This mobile CTA block drops the external registration path.

Unlike Lines 519-540, Lines 1018-1099 never check event.registration_link. For link-based events, opening the Rounds tab on mobile swaps Register Now for internal Buy Now / Add to Cart, which sends the user down the wrong flow. Mirror the first mobile CTA block here, or reuse a shared CTA renderer so both sections stay in sync.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/events/EventDetailView.js` around lines 1018 - 1099, Mobile CTA
block always renders internal Buy Now / Add to Cart and ignores
event.registration_link; update the conditional in the block that renders the
Buy Now/Add to Cart (the JSX around the Button handlers that call
dispatch(setCheckoutItems), dispatch(addToCart), setExistingUser, getEventImage,
spotsLeft, etc.) to first check if event.registration_link exists and, if so,
render the external “Register Now” link button (opening registration_link)
instead of the internal flow, mirroring the logic used in the earlier mobile CTA
(the block at lines ~519-540), or refactor both spots into a shared renderer
(e.g., EventCTA or renderCTA) and use it here so both CTA locations stay in
sync.
♻️ Duplicate comments (1)
src/views/events/EventDetailView.js (1)

236-236: ⚠️ Potential issue | 🟠 Major

Keep sold out distinct from registration closed.

Line 236 now drives all CTAs from registration_closed, but spotsLeft is still computed and forwarded into the purchase paths. Sold-out events with registration_closed = false will render live Register Now / Buy Now / Add to Cart actions again, and the add-to-cart path now hands maxAvailable: 0 to src/store/slices/cartSlice.js:94-125, which silently drops the add. Please reintroduce a shared ctaState and use it in all three CTA sections.

Possible fix
   const isOver = event.start_time ? new Date(event.start_time).getTime() <= Date.now() : false
   const isRegClosed = !!event.registration_closed
+  const isSoldOut = event.seats > 0 && spotsLeft <= 0
+  const ctaState = isRegClosed ? 'closed' : isSoldOut ? 'soldOut' : 'open'

Then branch the mobile and desktop CTAs on ctaState instead of isRegClosed alone.

Also applies to: 501-604, 1018-1099, 1197-1327

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/events/EventDetailView.js` at line 236, The CTA logic is
incorrectly driven only by isRegClosed (const isRegClosed =
!!event.registration_closed) while spotsLeft is still passed into purchase
flows; reintroduce a shared ctaState (e.g., 'closed' | 'sold_out' | 'open')
computed from isRegClosed and spotsLeft (spotsLeft <= 0 => 'sold_out',
isRegClosed => 'closed', else 'open') and replace uses of isRegClosed in all CTA
render branches (desktop and mobile CTAs referenced around the blocks you noted)
so that Register/Buy/Add-to-Cart rendering and handlers branch on ctaState; also
ensure the add-to-cart / purchase payloads (where maxAvailable is passed into
src/store/slices/cartSlice.js) derive maxAvailable from spotsLeft only when
ctaState === 'open' (or set maxAvailable = 0 for 'sold_out'/'closed') so no
attempt is made to add when sold out.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/views/events/EventDetailView.js`:
- Around line 1018-1099: Mobile CTA block always renders internal Buy Now / Add
to Cart and ignores event.registration_link; update the conditional in the block
that renders the Buy Now/Add to Cart (the JSX around the Button handlers that
call dispatch(setCheckoutItems), dispatch(addToCart), setExistingUser,
getEventImage, spotsLeft, etc.) to first check if event.registration_link exists
and, if so, render the external “Register Now” link button (opening
registration_link) instead of the internal flow, mirroring the logic used in the
earlier mobile CTA (the block at lines ~519-540), or refactor both spots into a
shared renderer (e.g., EventCTA or renderCTA) and use it here so both CTA
locations stay in sync.

In `@src/views/events/EventsPageView.js`:
- Around line 186-301: The CTA logic currently only checks isRegClosed which
causes sold-out events to show live actions; compute a shared ctaState (e.g.,
'open' | 'soldOut' | 'closed') based on registration_closed and
spotsLeft/event.seats (spotsLeft <= 0 => 'soldOut', registration_closed =>
'closed', else 'open') and replace all isRegClosed branches in both the mobile
and desktop CTA blocks with checks against ctaState (disable or change labels
when ctaState === 'soldOut' and disable when 'closed'), and ensure the addToCart
dispatch (addToCart) and setCheckoutItems/buy flow (setCheckoutItems,
setExistingUser, router.push) respect maxAvailable (use null for unlimited, 0
when sold out) so you never dispatch a purchase with maxAvailable: 0 for an
actionable "Add to Cart" or "Buy Ticket".

---

Duplicate comments:
In `@src/views/events/EventDetailView.js`:
- Line 236: The CTA logic is incorrectly driven only by isRegClosed (const
isRegClosed = !!event.registration_closed) while spotsLeft is still passed into
purchase flows; reintroduce a shared ctaState (e.g., 'closed' | 'sold_out' |
'open') computed from isRegClosed and spotsLeft (spotsLeft <= 0 => 'sold_out',
isRegClosed => 'closed', else 'open') and replace uses of isRegClosed in all CTA
render branches (desktop and mobile CTAs referenced around the blocks you noted)
so that Register/Buy/Add-to-Cart rendering and handlers branch on ctaState; also
ensure the add-to-cart / purchase payloads (where maxAvailable is passed into
src/store/slices/cartSlice.js) derive maxAvailable from spotsLeft only when
ctaState === 'open' (or set maxAvailable = 0 for 'sold_out'/'closed') so no
attempt is made to add when sold out.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8b801651-d11c-4248-bafc-796880a55971

📥 Commits

Reviewing files that changed from the base of the PR and between c90f7b6 and 97c5f6c.

📒 Files selected for processing (2)
  • src/views/events/EventDetailView.js
  • src/views/events/EventsPageView.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/views/events/EventDetailView.js (1)

1020-1101: ⚠️ Potential issue | 🟠 Major

This mobile CTA branch dropped the event.registration_link path.

On the Rounds tab, open events with an external registration link now render Buy Now / Add to Cart instead of Register Now. That sends users into checkout for flows that should stay off-site. Line 503 already has the correct branch order; this block should mirror it.

🔁 Mirror the top mobile CTA branch here
-                ) : (
+                ) : event.registration_link ? (
+                  <Button
+                    variant='contained'
+                    disableElevation
+                    fullWidth
+                    href={event.registration_link}
+                    target='_blank'
+                    rel='noopener noreferrer'
+                    startIcon={<Icon icon='tabler:external-link' fontSize={18} />}
+                    sx={{
+                      bgcolor: color,
+                      color: c.white,
+                      borderRadius: '12px',
+                      fontWeight: 700,
+                      fontSize: '0.95rem',
+                      textTransform: 'none',
+                      py: 1.5,
+                      '&:hover': { bgcolor: alpha(color, 0.88) }
+                    }}
+                  >
+                    Register Now
+                  </Button>
+                ) : (
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/events/EventDetailView.js` around lines 1020 - 1101, The mobile CTA
branch currently renders the internal checkout buttons (Buy Now / Add to Cart)
even when event.registration_link exists; update the conditional around the
Buttons inside the isRegClosed false branch to mirror the top mobile CTA logic
by first checking for event.registration_link and rendering a single "Register
Now" button that opens the external URL (instead of calling
dispatch(setCheckoutItems) / router.push('/checkout') or dispatch(addToCart));
otherwise keep the existing Buy Now and Add to Cart flows (references:
isRegClosed, event.registration_link, setCheckoutItems, setExistingUser,
addToCart, getEventImage, router.push).
♻️ Duplicate comments (2)
src/views/events/EventDetailView.js (2)

238-239: ⚠️ Potential issue | 🟠 Major

registration_closed still isn't loaded for this view.

The relevant snippet from src/services/event-service.js:240-298 still omits e.registration_closed from getEventById(). That makes !!event.registration_closed false here, so the new closed-state CTAs never activate on the detail page.

🛠️ Include the field in the detail query
--- a/src/services/event-service.js
+++ b/src/services/event-service.js
@@
         e.ticket_price,
         e.registration_link,
+        e.registration_closed,
         e.created_at,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/events/EventDetailView.js` around lines 238 - 239, EventDetailView
uses event.registration_closed (seen as isRegClosed =
!!event.registration_closed) but getEventById in src/services/event-service.js
still omits e.registration_closed; update the getEventById query to include
e.registration_closed (ensure the select/projection that builds the event object
returns registration_closed) so EventDetailView receives that field and the
closed-state CTAs activate; locate the getEventById function and add
e.registration_closed to the returned fields.

503-543: ⚠️ Potential issue | 🟠 Major

Don't reopen external registration for sold-out events.

Both CTA blocks still go straight from isRegClosed to event.registration_link, so sold-out linked events render a live Register Now action instead of a disabled sold-out state. Line 573 and Line 1327 already model sold-out for the internal CTA path; the external-link path should keep that state too.

Also applies to: 1199-1245

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/events/EventDetailView.js` around lines 503 - 543, External
registration links still render an active "Register Now" button for sold-out
events because the external-link branch doesn't respect the isRegClosed state;
update the conditional logic in EventDetailView so the external registration
Button is only rendered when event.registration_link is present AND isRegClosed
is false (e.g., change the branch to require event.registration_link &&
!isRegClosed), otherwise render the same disabled "Registration Closed" Button
used for the internal CTA; apply the same fix to the other external-CTA
occurrence referenced (the block around lines 1199-1245).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/views/events/EventsPageView.js`:
- Around line 108-110: Define an explicit sold-out boolean (e.g., const
isSoldOut = spotsLeft === 0) and use that as a first-class gate for
rendering/behaviour of the CTA instead of relying solely on isRegClosed; update
places that currently check isRegClosed before using event.registration_link or
rendering the "Register Now" button (references: isRegClosed, spotsLeft,
almostFull, event.registration_link, the Register Now CTA rendering logic) so
that if isSoldOut is true you render the sold-out state/disabled CTA
consistently, otherwise fall back to registration_closed logic and the external
registration_link branch.

---

Outside diff comments:
In `@src/views/events/EventDetailView.js`:
- Around line 1020-1101: The mobile CTA branch currently renders the internal
checkout buttons (Buy Now / Add to Cart) even when event.registration_link
exists; update the conditional around the Buttons inside the isRegClosed false
branch to mirror the top mobile CTA logic by first checking for
event.registration_link and rendering a single "Register Now" button that opens
the external URL (instead of calling dispatch(setCheckoutItems) /
router.push('/checkout') or dispatch(addToCart)); otherwise keep the existing
Buy Now and Add to Cart flows (references: isRegClosed, event.registration_link,
setCheckoutItems, setExistingUser, addToCart, getEventImage, router.push).

---

Duplicate comments:
In `@src/views/events/EventDetailView.js`:
- Around line 238-239: EventDetailView uses event.registration_closed (seen as
isRegClosed = !!event.registration_closed) but getEventById in
src/services/event-service.js still omits e.registration_closed; update the
getEventById query to include e.registration_closed (ensure the
select/projection that builds the event object returns registration_closed) so
EventDetailView receives that field and the closed-state CTAs activate; locate
the getEventById function and add e.registration_closed to the returned fields.
- Around line 503-543: External registration links still render an active
"Register Now" button for sold-out events because the external-link branch
doesn't respect the isRegClosed state; update the conditional logic in
EventDetailView so the external registration Button is only rendered when
event.registration_link is present AND isRegClosed is false (e.g., change the
branch to require event.registration_link && !isRegClosed), otherwise render the
same disabled "Registration Closed" Button used for the internal CTA; apply the
same fix to the other external-CTA occurrence referenced (the block around lines
1199-1245).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2f863657-df54-4679-8751-5864d3e5dc65

📥 Commits

Reviewing files that changed from the base of the PR and between 97c5f6c and 79d26c6.

📒 Files selected for processing (2)
  • src/views/events/EventDetailView.js
  • src/views/events/EventsPageView.js

Comment thread src/views/events/EventsPageView.js

@Bhav-ikkk Bhav-ikkk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks nice and correct

@Bhav-ikkk
Bhav-ikkk merged commit 63daedf into NexEvent:main Apr 3, 2026
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

Development

Successfully merging this pull request may close these issues.

3 participants