Skip to content

feat(events): high priority fixes — approval lock, sort, slug collision, computed status - #21

Open
julianromli wants to merge 1 commit into
mainfrom
feat/events-high-priority-fixes
Open

feat(events): high priority fixes — approval lock, sort, slug collision, computed status#21
julianromli wants to merge 1 commit into
mainfrom
feat/events-high-priority-fixes

Conversation

@julianromli

@julianromli julianromli commented May 1, 2026

Copy link
Copy Markdown
Owner

Changes

🔒 Detail page locked to approved content

  • getEventBySlug now filters .eq('approved', true) so pending events are inaccessible via direct URL.

🔃 Sort control connected to data

  • AIEvent type extended with createdAt.
  • applyFilters accepts sort: 'nearest' | 'latest'.
  • event-list-client passes selectedSort into filter pipeline.
  • latest sorts by createdAt desc; nearest keeps existing date-based logic.

🏷️ Slug collision handling

  • submitEvent retries insert up to 5× on unique-violation (23505).
  • Auto-generates suffix -2, -3, etc. until success.

📅 Event status computed at read time

  • computeEventStatus(date) compares event date to today.
  • Overrides DB status\' in mapEventFromDB` for both public cache and detail actions.
  • No cron/trigger needed; badges and CTAs stay consistent automatically.

Closes high-priority items 1–4 from docs/events-feature/next-steps.md.


Note

Medium Risk
Changes Supabase read/write behavior (locking detail reads to approved, retrying inserts on unique violations, and computing status at read time), which can affect event visibility and ordering. Risk is moderate due to date/time edge cases and potential differences from stored DB status/created_at semantics.

Overview
Tightens public event visibility by updating getEventBySlug to only return events where approved = true, preventing direct access to pending events by URL.

Wires the list “Sort” control to real data by passing selectedSort through applyFilters, adding sort support (nearest vs latest), and extending AIEvent with createdAt to enable client-side “latest” sorting.

Improves data consistency and submission robustness by computing status from the event date when mapping DB rows (instead of trusting the stored DB status) and retrying submitEvent inserts with -2/-3/... slug suffixes on unique-constraint collisions.

Adds docs/events-feature/next-steps.md documenting prioritized follow-ups and noting these high-priority fixes.

Reviewed by Cursor Bugbot for commit f872277. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

Release Notes

  • New Features

    • Event sorting now responds to selected sort options (by latest or nearest date).
    • Event submissions automatically handle naming conflicts by generating unique variants.
  • Bug Fixes

    • Event status (past/upcoming) now accurately reflects actual event timing.
    • Unapproved events are excluded from public listings.
  • Documentation

    • Added roadmap for upcoming Events feature improvements.

- Lock detail page to approved events only (getEventBySlug filters approved=true)
- Connect sort control (nearest/latest) to client-side filtering via createdAt
- Handle slug collisions with auto-suffix unique strategy (retry on 23505)
- Compute event status (past/ongoing/upcoming) at read time based on date
@vercel

vercel Bot commented May 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
vibedevid Ready Ready Preview, Comment, Open in v0 May 1, 2026 9:41am

@coderabbitai

coderabbitai Bot commented May 1, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Event status is now computed server-side from event dates (past/ongoing/upcoming) rather than fetched from the database. Event submission now handles slug collisions by appending an incrementing suffix and retrying insertion. Sorting functionality is integrated into the filter pipeline, mapping "latest" to creation-date ordering. Direct URL access to events is gated by an approval check. Changes span client components, server actions, utilities, and type definitions.

Changes

Cohort / File(s) Summary
Event Status & Data Model
lib/server/events-public.ts, lib/actions/events.ts, types/events.ts
Status is now computed from event date (past/ongoing/upcoming) instead of read from DB. EventRow no longer expects a status column; created_at is mapped to optional createdAt property on AIEvent type.
Event Submission & Approval
lib/actions/events.ts
Submission now handles slug collisions with up to 5 retries, appending numeric suffixes on unique-constraint violations. Event retrieval by slug enforces approved = true filter to block pending events from direct access.
Sorting & Filtering
lib/events-utils.ts, app/event/list/event-list-client.tsx
EventFilters interface adds optional sort property. applyFilters conditionally sorts by creation date (desc) when sort: 'latest' or falls back to nearest-date ordering. Client component now passes selectedSort to the filter pipeline.
Documentation
docs/events-feature/next-steps.md
New file enumerating prioritized follow-up tasks for the Events feature, covering approval enforcement, sort wiring, slug strategies, status alignment, hook refactors, UX improvements, and test coverage.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Server as Server Action<br/>(submitEvent)
    participant Database

    Client->>Server: Submit event with slug
    Server->>Database: Insert event (attempt 1)
    alt Unique constraint violation (slug exists)
        Database-->>Server: Error 23505
        Server->>Server: Append suffix to slug<br/>(retry count++)
        Note over Server: Retry up to 5 times
        Server->>Database: Insert event (next attempt)
        Database-->>Server: Success or next error
    else Other database error
        Database-->>Server: Error (non-unique)
        Server-->>Client: Return failure
    else Success
        Database-->>Server: Event created
        Server-->>Client: Return success
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Events sort by latest light,
Status glows from date's midnight!
When slugs collide, we suffix fast,
Approval gates each query passed—
Hoppy code, a feature cast! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the four main changes (approval lock, sort, slug collision, computed status) and aligns with the changeset modifications across all files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/events-high-priority-fixes

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
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

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

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f8722773d9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lib/actions/events.ts
coverImage: data.cover_image,
category: data.category as EventCategory,
status: data.status,
status: computeEventStatus(data.date),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve ongoing status across multi-day event ranges

mapEventFromDB now overwrites DB status with computeEventStatus(data.date), which only compares the start date to today. For events that span multiple days (end_date is populated), day 2+ will be marked past even while the event is still running, so detail-page state that depends on event.status (e.g. past-event CTA behavior) becomes incorrect.

Useful? React with 👍 / 👎.

Comment thread lib/actions/events.ts
Comment on lines +202 to +203
currentSlug = `${dbData.slug}-${i + 2}`
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Attempt the last generated slug before exhausting retries

On a 23505 collision, the loop assigns a new suffix and continues, but on the final iteration that new value is never inserted because the loop exits immediately afterward. With maxRetries = 5, collisions on base, -2, -3, -4, and -5 return failure even if -6 is available, causing avoidable submission failures for common names.

Useful? React with 👍 / 👎.

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/server/events-public.ts`:
- Around line 6-15: The computeEventStatus function is duplicated; extract it
into a single shared utility module (e.g., events-utils) as an exported function
with the same signature and EventStatus return type, then remove the local
implementations and replace them with imports of computeEventStatus in both
places that currently define it (the server-side and actions code) so both files
call the shared computeEventStatus implementation.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6aeb93a3-93a6-40a0-a584-16c602070566

📥 Commits

Reviewing files that changed from the base of the PR and between 896ed68 and f872277.

📒 Files selected for processing (6)
  • app/event/list/event-list-client.tsx
  • docs/events-feature/next-steps.md
  • lib/actions/events.ts
  • lib/events-utils.ts
  • lib/server/events-public.ts
  • types/events.ts

Comment on lines +6 to +15
function computeEventStatus(date: string): EventStatus {
const eventDate = new Date(date)
eventDate.setHours(0, 0, 0, 0)
const today = new Date()
today.setHours(0, 0, 0, 0)

if (eventDate < today) return 'past'
if (eventDate.getTime() === today.getTime()) return 'ongoing'
return 'upcoming'
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Extract duplicated computeEventStatus to a shared utility.

This function is duplicated verbatim in lib/actions/events.ts (lines 10-19). Consider extracting it to a shared location (e.g., lib/events-utils.ts) to maintain a single source of truth for event status computation.

♻️ Suggested approach

Add to lib/events-utils.ts:

import type { EventStatus } from '@/types/events'

export function computeEventStatus(date: string): EventStatus {
  const eventDate = new Date(date)
  eventDate.setHours(0, 0, 0, 0)
  const today = new Date()
  today.setHours(0, 0, 0, 0)

  if (eventDate < today) return 'past'
  if (eventDate.getTime() === today.getTime()) return 'ongoing'
  return 'upcoming'
}

Then import from both lib/server/events-public.ts and lib/actions/events.ts.

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

In `@lib/server/events-public.ts` around lines 6 - 15, The computeEventStatus
function is duplicated; extract it into a single shared utility module (e.g.,
events-utils) as an exported function with the same signature and EventStatus
return type, then remove the local implementations and replace them with imports
of computeEventStatus in both places that currently define it (the server-side
and actions code) so both files call the shared computeEventStatus
implementation.

@cursor cursor 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.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f872277. Configure here.

Comment thread lib/actions/events.ts

if (eventDate < today) return 'past'
if (eventDate.getTime() === today.getTime()) return 'ongoing'
return 'upcoming'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Status computation ignores end date for multi-day events

Medium Severity

computeEventStatus only considers the start date, ignoring endDate. Multi-day events (e.g., a 3-day conference) are marked 'past' the day after they start, even though they're still running. The end_date column exists in the DB, is mapped to endDate in mapEventFromDB, and the codebase already handles multi-day display via formatEventDateRange. The status override now replaces whatever the DB had, so there's no fallback for these events.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f872277. Configure here.


if (eventDate < today) return 'past'
if (eventDate.getTime() === today.getTime()) return 'ongoing'
return 'upcoming'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Duplicated computeEventStatus across two files

Low Severity

computeEventStatus is identically defined in both lib/actions/events.ts and lib/server/events-public.ts. This duplication means any future fix (e.g., adding endDate support) needs to be applied in both places, risking inconsistency. A shared utility module could house this function.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f872277. Configure here.

@cubic-dev-ai cubic-dev-ai 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.

3 issues found across 6 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="docs/events-feature/next-steps.md">

<violation number="1" location="docs/events-feature/next-steps.md:1">
P3: New docs file was added without updating `docs/README.md`, which breaks the documented docs-index convention.</violation>
</file>

<file name="lib/actions/events.ts">

<violation number="1" location="lib/actions/events.ts:11">
P1: Parsing a date-only string with `new Date(date)` can shift the event day by timezone, producing incorrect computed statuses. Construct the date in local time from components instead.</violation>
</file>

<file name="lib/server/events-public.ts">

<violation number="1" location="lib/server/events-public.ts:7">
P2: Parsing a date-only string with `new Date(date)` can misclassify event status by one day in non-UTC timezones. Parse as local date explicitly before comparing.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread lib/actions/events.ts
import type { AIEvent, EventCategory, EventFormData, EventLocationType, EventStatus } from '@/types/events'

function computeEventStatus(date: string): EventStatus {
const eventDate = new Date(date)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: Parsing a date-only string with new Date(date) can shift the event day by timezone, producing incorrect computed statuses. Construct the date in local time from components instead.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/actions/events.ts, line 11:

<comment>Parsing a date-only string with `new Date(date)` can shift the event day by timezone, producing incorrect computed statuses. Construct the date in local time from components instead.</comment>

<file context>
@@ -5,7 +5,18 @@ import { ROLES } from '@/lib/actions/admin/schemas'
+import type { AIEvent, EventCategory, EventFormData, EventLocationType, EventStatus } from '@/types/events'
+
+function computeEventStatus(date: string): EventStatus {
+  const eventDate = new Date(date)
+  eventDate.setHours(0, 0, 0, 0)
+  const today = new Date()
</file context>
Suggested change
const eventDate = new Date(date)
const [year, month, day] = date.split('-').map(Number)
const eventDate = new Date(year, month - 1, day)

import type { AIEvent, EventCategory, EventLocationType, EventStatus } from '@/types/events'

function computeEventStatus(date: string): EventStatus {
const eventDate = new Date(date)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Parsing a date-only string with new Date(date) can misclassify event status by one day in non-UTC timezones. Parse as local date explicitly before comparing.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/server/events-public.ts, line 7:

<comment>Parsing a date-only string with `new Date(date)` can misclassify event status by one day in non-UTC timezones. Parse as local date explicitly before comparing.</comment>

<file context>
@@ -1,7 +1,18 @@
+import type { AIEvent, EventCategory, EventLocationType, EventStatus } from '@/types/events'
+
+function computeEventStatus(date: string): EventStatus {
+  const eventDate = new Date(date)
+  eventDate.setHours(0, 0, 0, 0)
+  const today = new Date()
</file context>
Suggested change
const eventDate = new Date(date)
const eventDate = new Date(`${date}T00:00:00`)

@@ -0,0 +1,41 @@
# Events feature — next steps

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: New docs file was added without updating docs/README.md, which breaks the documented docs-index convention.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/events-feature/next-steps.md, line 1:

<comment>New docs file was added without updating `docs/README.md`, which breaks the documented docs-index convention.</comment>

<file context>
@@ -0,0 +1,41 @@
+# Events feature — next steps
+
+Prioritas: **High**, **Medium**, **Low**. Ringkasan dari review fitur AI Events (listing, detail, submit, approval admin).
</file context>

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.

1 participant