feat(events): medium priority fixes — form cleanup, end date, CTA, Event Saya tab#22
feat(events): medium priority fixes — form cleanup, end date, CTA, Event Saya tab#22julianromli wants to merge 2 commits into
Conversation
- 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
- Clean up useEventForm: remove mock code, hook now calls submitEvent server action directly - Add optional end_date / end_time fields to submit form, type, and server action - Event card CTA: primary button goes to registrationUrl (new tab), secondary link to detail - Add 'Event Saya' tab on profile page (owner-only) with approval status badge - Add getMyEvents server action for fetching user's submitted events
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Rate limit exceeded
To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (11)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Review rate limit: 0/1 reviews remaining, refill in 11 minutes and 52 seconds.Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d14341daa6
ℹ️ 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".
| import type { AIEvent, EventCategory, EventFormData, EventLocationType, EventStatus } from '@/types/events' | ||
|
|
||
| function computeEventStatus(date: string): EventStatus { | ||
| const eventDate = new Date(date) |
There was a problem hiding this comment.
Parse event dates without UTC shift before status checks
computeEventStatus uses new Date(date) on YYYY-MM-DD strings, which JavaScript interprets as UTC; in negative-offset timezones (e.g. US), calling setHours(0,0,0,0) then shifts the event one day earlier and marks same-day events as past. This directly affects user flows like the detail page CTA (isPastEvent) and status badges. The same logic is duplicated in lib/server/events-public.ts, so list/detail/profile status can all be wrong for users outside UTC+ timezones.
Useful? React with 👍 / 👎.
| registrationUrl: e.registration_url, | ||
| coverImage: e.cover_image, | ||
| category: e.category, | ||
| status: e.status, |
There was a problem hiding this comment.
Derive profile event status from date instead of raw column
The new profile “Events” tab maps status directly from e.status, but event submissions are inserted with status: 'upcoming' and this commit’s dynamic status logic is only applied via mapEventFromDB in server actions/public fetchers. As a result, submitted events on /[username] can remain labeled upcoming even after their date has passed, which makes the status badge inaccurate specifically in the new owner tab.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 4 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d14341d. Configure here.
| registrationUrl: e.registration_url, | ||
| coverImage: e.cover_image, | ||
| category: e.category, | ||
| status: e.status, |
There was a problem hiding this comment.
Profile page uses stale DB status instead of computed
High Severity
The inline event mapping in the profile page sets status: e.status, which reads the raw DB value. Since submitEvent always inserts events with status: 'upcoming' and nothing ever updates this column, all events in the "Event Saya" tab will permanently display as "upcoming" — even past or ongoing ones. Every other mapEventFromDB in the codebase uses computeEventStatus(data.date) to derive status dynamically from the event date.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit d14341d. Configure here.
| console.error('Unexpected error fetching my events:', error) | ||
| return { events: [], error: 'An unexpected error occurred' } | ||
| } | ||
| } |
There was a problem hiding this comment.
Exported getMyEvents server action is never used
Medium Severity
The getMyEvents server action is exported but never imported or called anywhere in the codebase. The profile page duplicates its logic with an inline Supabase client query instead of using this server action. This introduces dead code and causes the profile page to miss the computeEventStatus computation that mapEventFromDB (used by getMyEvents) provides.
Reviewed by Cursor Bugbot for commit d14341d. Configure here.
|
|
||
| if (eventDate < today) return 'past' | ||
| if (eventDate.getTime() === today.getTime()) return 'ongoing' | ||
| return 'upcoming' |
There was a problem hiding this comment.
computeEventStatus duplicated across two files
Low Severity
The computeEventStatus function is identically implemented in both lib/actions/events.ts and lib/server/events-public.ts. This duplication increases the risk of the two copies diverging during future changes (e.g., adding endDate awareness for multi-day events). Extracting it to a shared utility like lib/event-form-utils.ts or lib/events-utils.ts would keep the logic in one place.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit d14341d. Configure here.
| category: data.category as EventCategory, | ||
| status: data.status, | ||
| status: computeEventStatus(data.date), | ||
| createdAt: data.created_at, |
There was a problem hiding this comment.
mapEventFromDB omits newly-added approved field
Medium Severity
The mapEventFromDB function in lib/actions/events.ts doesn't map data.approved to the returned AIEvent object, even though approved?: boolean was added to AIEvent in this PR. The getMyEvents function uses this mapper, so any events it returns will have approved as undefined. The EventTab component checks event.approved to render approval badges — with undefined, the ternary always evaluates to falsy, showing "Menunggu Review" even for approved events.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit d14341d. Configure here.
There was a problem hiding this comment.
6 issues found across 11 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="components/profile/event-tab.tsx">
<violation number="1" location="components/profile/event-tab.tsx:54">
P1: `next/image` `onError` callback is used in a Server Component. Event callbacks on `Image` require a Client Component boundary.</violation>
</file>
<file name="lib/server/events-public.ts">
<violation number="1" location="lib/server/events-public.ts:7">
P2: Parsing `YYYY-MM-DD` with `new Date(date)` can misclassify event status by one day in non-UTC timezones. Parse the date parts into a local date object instead.</violation>
</file>
<file name="lib/actions/events.ts">
<violation number="1" location="lib/actions/events.ts:11">
P2: Parsing date-only strings with `new Date(date)` can misclassify event status across timezones. Build the date in local time to avoid off-by-one-day status errors.</violation>
<violation number="2" location="lib/actions/events.ts:39">
P2: `mapEventFromDB` does not map the `approved` field from the DB row, even though `approved?: boolean` was added to `AIEvent` in this PR. `getMyEvents` uses this mapper, so all returned events will have `approved` as `undefined`. The `EventTab` component's `event.approved ? 'Disetujui' : 'Menunggu Review'` ternary will then always evaluate to falsy, showing "Menunggu Review" for every event — including approved ones. Add `approved: data.approved,` to the returned object.</violation>
<violation number="3" location="lib/actions/events.ts:331">
P2: `getMyEvents` is exported but never imported anywhere — the profile page duplicates its logic with an inline Supabase query instead. This introduces dead code and causes the profile page to miss the `computeEventStatus` call that `mapEventFromDB` (used inside `getMyEvents`) provides. Consider using `getMyEvents` directly on the profile page instead of the inline query.</violation>
</file>
<file name="app/[username]/page.tsx">
<violation number="1" location="app/[username]/page.tsx:409">
P1: This reads the raw DB `status` column, which is always `'upcoming'` at insert time and never updated. Every other event mapper in the codebase uses `computeEventStatus(data.date)` to derive the status dynamically. Import and call `computeEventStatus(e.date)` here (or reuse `getMyEvents` which already does this via `mapEventFromDB`) so that past/ongoing events display the correct badge in the Event Saya tab.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| sizes="192px" | ||
| loading="lazy" | ||
| className="object-cover transition-transform duration-500 group-hover:scale-105" | ||
| onError={(e) => { |
There was a problem hiding this comment.
P1: next/image onError callback is used in a Server Component. Event callbacks on Image require a Client Component boundary.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At components/profile/event-tab.tsx, line 54:
<comment>`next/image` `onError` callback is used in a Server Component. Event callbacks on `Image` require a Client Component boundary.</comment>
<file context>
@@ -0,0 +1,112 @@
+ sizes="192px"
+ loading="lazy"
+ className="object-cover transition-transform duration-500 group-hover:scale-105"
+ onError={(e) => {
+ e.currentTarget.src = '/placeholder.jpg'
+ }}
</file context>
| registrationUrl: e.registration_url, | ||
| coverImage: e.cover_image, | ||
| category: e.category, | ||
| status: e.status, |
There was a problem hiding this comment.
P1: This reads the raw DB status column, which is always 'upcoming' at insert time and never updated. Every other event mapper in the codebase uses computeEventStatus(data.date) to derive the status dynamically. Import and call computeEventStatus(e.date) here (or reuse getMyEvents which already does this via mapEventFromDB) so that past/ongoing events display the correct badge in the Event Saya tab.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/[username]/page.tsx, line 409:
<comment>This reads the raw DB `status` column, which is always `'upcoming'` at insert time and never updated. Every other event mapper in the codebase uses `computeEventStatus(data.date)` to derive the status dynamically. Import and call `computeEventStatus(e.date)` here (or reuse `getMyEvents` which already does this via `mapEventFromDB`) so that past/ongoing events display the correct badge in the Event Saya tab.</comment>
<file context>
@@ -366,18 +370,47 @@ export default function ProfilePage() {
+ registrationUrl: e.registration_url,
+ coverImage: e.cover_image,
+ category: e.category,
+ status: e.status,
+ approved: e.approved,
+ })),
</file context>
| import type { AIEvent, EventCategory, EventLocationType, EventStatus } from '@/types/events' | ||
|
|
||
| function computeEventStatus(date: string): EventStatus { | ||
| const eventDate = new Date(date) |
There was a problem hiding this comment.
P2: Parsing YYYY-MM-DD with new Date(date) can misclassify event status by one day in non-UTC timezones. Parse the date parts into a local date object instead.
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 `YYYY-MM-DD` with `new Date(date)` can misclassify event status by one day in non-UTC timezones. Parse the date parts into a local date object instead.</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>
| 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, EventFormData, EventLocationType, EventStatus } from '@/types/events' | ||
|
|
||
| function computeEventStatus(date: string): EventStatus { | ||
| const eventDate = new Date(date) |
There was a problem hiding this comment.
P2: Parsing date-only strings with new Date(date) can misclassify event status across timezones. Build the date in local time to avoid off-by-one-day status errors.
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 date-only strings with `new Date(date)` can misclassify event status across timezones. Build the date in local time to avoid off-by-one-day status errors.</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>
| const eventDate = new Date(date) | |
| const eventDate = new Date(`${date}T00:00:00`) |
| } | ||
|
|
||
| // Get events submitted by the current user | ||
| export async function getMyEvents() { |
There was a problem hiding this comment.
P2: getMyEvents is exported but never imported anywhere — the profile page duplicates its logic with an inline Supabase query instead. This introduces dead code and causes the profile page to miss the computeEventStatus call that mapEventFromDB (used inside getMyEvents) provides. Consider using getMyEvents directly on the profile page instead of the inline query.
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 331:
<comment>`getMyEvents` is exported but never imported anywhere — the profile page duplicates its logic with an inline Supabase query instead. This introduces dead code and causes the profile page to miss the `computeEventStatus` call that `mapEventFromDB` (used inside `getMyEvents`) provides. Consider using `getMyEvents` directly on the profile page instead of the inline query.</comment>
<file context>
@@ -290,3 +326,33 @@ export async function rejectEvent(eventId: string) {
}
+
+// Get events submitted by the current user
+export async function getMyEvents() {
+ try {
+ const supabase = await createClient()
</file context>
| category: data.category as EventCategory, | ||
| status: data.status, | ||
| status: computeEventStatus(data.date), | ||
| createdAt: data.created_at, |
There was a problem hiding this comment.
P2: mapEventFromDB does not map the approved field from the DB row, even though approved?: boolean was added to AIEvent in this PR. getMyEvents uses this mapper, so all returned events will have approved as undefined. The EventTab component's event.approved ? 'Disetujui' : 'Menunggu Review' ternary will then always evaluate to falsy, showing "Menunggu Review" for every event — including approved ones. Add approved: data.approved, to the returned object.
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 39:
<comment>`mapEventFromDB` does not map the `approved` field from the DB row, even though `approved?: boolean` was added to `AIEvent` in this PR. `getMyEvents` uses this mapper, so all returned events will have `approved` as `undefined`. The `EventTab` component's `event.approved ? 'Disetujui' : 'Menunggu Review'` ternary will then always evaluate to falsy, showing "Menunggu Review" for every event — including approved ones. Add `approved: data.approved,` to the returned object.</comment>
<file context>
@@ -24,7 +35,8 @@ function mapEventFromDB(data: any): AIEvent {
category: data.category as EventCategory,
- status: data.status,
+ status: computeEventStatus(data.date),
+ createdAt: data.created_at,
}
}
</file context>
| createdAt: data.created_at, | |
| createdAt: data.created_at, | |
| approved: data.approved, |


Changes
🧹 useEventForm cleanup (#5)
handleSubmit.submitEventserver action directly.SubmitEventModaldelegates submission touseEventForm.handleSubmit()viaonSuccesscallback.📅 Optional end date / end time (#7)
EventFormDatatype restoredendDateandendTimefields.submitEventserver action mapsendDate/endTimeto DBend_date/end_time.🎯 Event card CTA (#6)
registrationUrl(target=_blank,rel=noopener noreferrer).👤 Event Saya tab on profile (#8)
getMyEventsserver action fetches events bysubmitted_by.EventTabcomponent displays submitted events with status + approval badges.isOwner) on/[username]page.Closes medium-priority items 5–8 from
docs/events-feature/next-steps.md.Note
Medium Risk
Touches event submission and retrieval paths (server actions + Supabase queries), including slug-collision handling and approval gating, which could affect what users can see/submit if incorrect. UI changes are straightforward but depend on new/updated event fields (
createdAt,approved, end date/time).Overview
Adds an owner-only “Events” tab on user profiles that lists the user’s submitted events (with approval/status badges) and an empty-state CTA to submit new ones.
Cleans up the event submit flow by moving submission into
useEventForm.handleSubmit()(calling thesubmitEventserver action), and extends the submit modal + types to support optionalendDate/endTimepersisted toend_date/end_time.Improves event listing/detail behavior: event cards now primary-link to
registrationUrl(new tab) with a secondary “Detail Event” link, client-side filters now honorselectedSortviaapplyFilters(latestusescreatedAt),getEventBySlugis restricted toapproved = true, eventstatusis computed from date on read, andsubmitEventretries with slug suffixes on unique-collision.Reviewed by Cursor Bugbot for commit d14341d. Bugbot is set up for automated code reviews on this repo. Configure here.