Skip to content

feat(events): medium priority fixes — form cleanup, end date, CTA, Event Saya tab#22

Open
julianromli wants to merge 2 commits into
mainfrom
feat/events-medium-priority-fixes
Open

feat(events): medium priority fixes — form cleanup, end date, CTA, Event Saya tab#22
julianromli wants to merge 2 commits into
mainfrom
feat/events-medium-priority-fixes

Conversation

@julianromli

@julianromli julianromli commented May 1, 2026

Copy link
Copy Markdown
Owner

Changes

🧹 useEventForm cleanup (#5)

  • Removed dead "Phase 1 mock" code from handleSubmit.
  • Hook now imports and calls submitEvent server action directly.
  • SubmitEventModal delegates submission to useEventForm.handleSubmit() via onSuccess callback.

📅 Optional end date / end time (#7)

  • EventFormData type restored endDate and endTime fields.
  • Submit form now shows optional "Tanggal Selesai" and "Waktu Selesai" inputs.
  • submitEvent server action maps endDate / endTime to DB end_date / end_time.

🎯 Event card CTA (#6)

  • Grid variant primary button now links directly to registrationUrl (target=_blank, rel=noopener noreferrer).
  • Secondary "Detail Event" link added below for users who want to read more first.

👤 Event Saya tab on profile (#8)

  • New getMyEvents server action fetches events by submitted_by.
  • New EventTab component displays submitted events with status + approval badges.
  • Tab only visible to profile owner (isOwner) on /[username] page.
  • Empty state prompts owner to submit their first event.

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 the submitEvent server action), and extends the submit modal + types to support optional endDate/endTime persisted to end_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 honor selectedSort via applyFilters (latest uses createdAt), getEventBySlug is restricted to approved = true, event status is computed from date on read, and submitEvent retries 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.

- 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
@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 10:29am

@coderabbitai

coderabbitai Bot commented May 1, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@julianromli has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 11 minutes and 52 seconds before requesting another review.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 37ebeedf-1c98-4445-a4a5-19753e8f8947

📥 Commits

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

📒 Files selected for processing (11)
  • app/[username]/page.tsx
  • app/event/list/event-list-client.tsx
  • components/event/event-card.tsx
  • components/event/submit-event-modal.tsx
  • components/profile/event-tab.tsx
  • docs/events-feature/next-steps.md
  • hooks/useEventForm.ts
  • lib/actions/events.ts
  • lib/events-utils.ts
  • lib/server/events-public.ts
  • types/events.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/events-medium-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 11 minutes and 52 seconds.

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: 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".

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 Badge 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 👍 / 👎.

Comment thread app/[username]/page.tsx
registrationUrl: e.registration_url,
coverImage: e.cover_image,
category: e.category,
status: e.status,

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 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 👍 / 👎.

@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 4 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 d14341d. Configure here.

Comment thread app/[username]/page.tsx
registrationUrl: e.registration_url,
coverImage: e.cover_image,
category: e.category,
status: e.status,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d14341d. Configure here.

Comment thread lib/actions/events.ts
console.error('Unexpected error fetching my events:', error)
return { events: [], error: 'An unexpected error occurred' }
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d14341d. 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.

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)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d14341d. Configure here.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d14341d. 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.

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) => {

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: 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>

Comment thread app/[username]/page.tsx
registrationUrl: e.registration_url,
coverImage: e.cover_image,
category: e.category,
status: e.status,

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: 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)

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 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>
Suggested change
const eventDate = new Date(date)
const [year, month, day] = date.split('-').map(Number)
const eventDate = new Date(year, month - 1, day)

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.

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>
Suggested change
const eventDate = new Date(date)
const eventDate = new Date(`${date}T00:00:00`)

Comment thread lib/actions/events.ts
}

// Get events submitted by the current user
export async function getMyEvents() {

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: 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>

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

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: 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>
Suggested change
createdAt: data.created_at,
createdAt: data.created_at,
approved: data.approved,

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