Skip to content

Atlas-Bounty: Fix Hackathon Draft Deletion Failure (#416) - #422

Closed
Fankouzu wants to merge 9 commits into
boundlessfi:mainfrom
Fankouzu:main
Closed

Atlas-Bounty: Fix Hackathon Draft Deletion Failure (#416)#422
Fankouzu wants to merge 9 commits into
boundlessfi:mainfrom
Fankouzu:main

Conversation

@Fankouzu

@Fankouzu Fankouzu commented Mar 1, 2026

Copy link
Copy Markdown
Contributor

Overview

Resolved the issue where hackathon drafts could not be deleted due to an API endpoint mismatch between drafts and published hackathons.

Key Changes

  • API: Added deleteDraft to lib/api/hackathons/draft.ts targeting the correct organization-scoped route.
  • Hooks: Refactored useDeleteHackathon to support both draft and published hackathon deletions via an isDraft parameter.
  • UI: Updated the organization hackathons list page to correctly pass the draft status to the deletion hook.

Impact

  • Users can now successfully delete stale or accidental drafts from the dashboard.
  • Zero regressions for published hackathon deletions.
  • 100% type-safe implementation.

Payout Information

Address: 0xa5F1e2596DC1e878a6a039f41330d9A97c771bE9

Summary by CodeRabbit

  • New Features

    • Earnings dashboard: view total earned, pending/completed withdrawals, source breakdown, and recent activity.
    • Sidebar link for quick access to Earnings.
    • Support for deleting both hackathon drafts and published hackathons.
  • Improvements

    • Deletion flows now use contextual messaging for drafts vs. published hackathons.
    • Success/error handling for deletions streamlined and centralized.

@Fankouzu
Fankouzu requested a review from 0xdevcollins as a code owner March 1, 2026 02:00
@vercel

vercel Bot commented Mar 1, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the Threadflow Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Mar 1, 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 draft-aware hackathon deletion flow (isDraft flag + deleteDraft API) and implements a new client-side Earnings dashboard with typed API endpoints and a sidebar navigation item.

Changes

Cohort / File(s) Summary
Hackathon Deletion
app/(landing)/organizations/[id]/hackathons/page.tsx, hooks/hackathon/use-delete-hackathon.ts, lib/api/hackathons/draft.ts
Introduced isDraft in delete state and hook options; useDeleteHackathon now accepts a discriminated UseDeleteHackathonOptions object and routes to new deleteDraft(organizationId, draftId) when isDraft:true; adjusted validation, messaging, and toast handling.
Earnings Dashboard
app/me/earnings/page.tsx, lib/api/user/earnings.ts, components/app-sidebar.tsx
Added EarningsPage (default export) with UI components, loading/error handling, and entry animation; added typed API getUserEarnings/claimEarning and data models; added "Earnings" nav item with IconCurrencyDollar.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant EarningsPage as "Earnings Page"
    participant API as "API Layer"
    participant Backend as "Backend Server"

    User->>EarningsPage: Navigate to /me/earnings
    EarningsPage->>EarningsPage: mount → show loading state
    EarningsPage->>API: getUserEarnings()
    API->>Backend: GET /user/earnings
    Backend->>API: 200 OK (EarningsData)
    API->>EarningsPage: GetEarningsResponse (data)
    EarningsPage->>EarningsPage: render cards, breakdown, activities
    EarningsPage->>User: display dashboard
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • 0xdevcollins

Poem

🐰 I nudged a draft with careful paw,
A dashboard blossomed near the straw,
Cards counted coins and activities hopped,
A new link guided where the meadow popped,
Hooray — the code makes the burrow clap!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 title 'Atlas-Bounty: Fix Hackathon Draft Deletion Failure' directly describes the main change: fixing the deletion of hackathon drafts by addressing an API endpoint mismatch, which is the core objective of this PR.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@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)
hooks/hackathon/use-delete-hackathon.ts (1)

29-37: ⚠️ Potential issue | 🟡 Minor

Make auth/validation copy draft-aware.

With draft deletion now supported, hardcoded “hackathons” / “Hackathon ID” messages are inaccurate for draft flows.

💡 Suggested fix
   const deleteHackathonAction = useCallback(async () => {
+    const targetPlural = isDraft ? 'drafts' : 'hackathons';
+    const targetSingular = isDraft ? 'Draft' : 'Hackathon';
+
     if (!isAuthenticated) {
-      toast.error('Please sign in to delete hackathons');
+      toast.error(`Please sign in to delete ${targetPlural}`);
       throw new Error('Authentication required');
     }

     if (!organizationId || !hackathonId) {
-      toast.error('Organization ID and Hackathon ID are required');
-      throw new Error('Organization ID and Hackathon ID are required');
+      const idError = `Organization ID and ${targetSingular} ID are required`;
+      toast.error(idError);
+      throw new Error(idError);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hooks/hackathon/use-delete-hackathon.ts` around lines 29 - 37, The current
auth/validation error messages use hardcoded "hackathons"/"Hackathon ID" and
aren't draft-aware; update the checks in the block that references
isAuthenticated, organizationId, and hackathonId to produce contextual messages
depending on the draft flow (e.g., use an isDraft boolean or existing draft flag
in scope). Replace static strings like 'Please sign in to delete hackathons' and
'Organization ID and Hackathon ID are required' with conditional messages such
as 'Please sign in to delete this hackathon draft' vs 'Please sign in to delete
this hackathon' and similarly for the ID validation (or create a small helper
like getEntityLabel(isDraft) and use it in the toast.error and thrown Error
messages). Ensure you update both toast.error and thrown Error text and
reference the existing variables isAuthenticated, organizationId, and
hackathonId (and the draft flag) so messages reflect draft vs published flows.
app/(landing)/organizations/[id]/hackathons/page.tsx (1)

100-114: ⚠️ Potential issue | 🟡 Minor

Draft deletes can show incorrect/duplicate toast copy.

onSuccess/onError still hardcode “Hackathon”, while the hook already emits draft-aware toasts. This can show wrong wording for drafts and double-toast users.

💡 Suggested fix
   const { isDeleting, deleteHackathon } = useDeleteHackathon({
     organizationId,
     hackathonId: hackathonToDelete?.id || '',
     isDraft: hackathonToDelete?.isDraft || false,
     onSuccess: () => {
-      // Refresh the hackathons list after successful deletion
       refetchAll();
-      toast.success('Hackathon deleted successfully', {
-        description: `"${hackathonToDelete?.title}" has been permanently deleted.`,
-      });
-    },
-    onError: error => {
-      toast.error('Failed to delete hackathon', {
-        description: error,
-      });
     },
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/`(landing)/organizations/[id]/hackathons/page.tsx around lines 100 - 114,
The component is duplicating toast messages that the useDeleteHackathon hook
already emits; remove the hardcoded toast calls in the onSuccess and onError
callbacks to avoid wrong/double toasts. In the useDeleteHackathon call (symbols:
useDeleteHackathon, onSuccess, onError, hackathonToDelete), keep the success
callback to perform non-UI side-effects like refetchAll() but delete the
toast.success call (including the description that references
hackathonToDelete.title), and remove the toast.error call from onError so the
hook’s draft-aware error toast is the only user-facing message.
🤖 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 `@app/`(landing)/organizations/[id]/hackathons/page.tsx:
- Around line 100-114: The component is duplicating toast messages that the
useDeleteHackathon hook already emits; remove the hardcoded toast calls in the
onSuccess and onError callbacks to avoid wrong/double toasts. In the
useDeleteHackathon call (symbols: useDeleteHackathon, onSuccess, onError,
hackathonToDelete), keep the success callback to perform non-UI side-effects
like refetchAll() but delete the toast.success call (including the description
that references hackathonToDelete.title), and remove the toast.error call from
onError so the hook’s draft-aware error toast is the only user-facing message.

In `@hooks/hackathon/use-delete-hackathon.ts`:
- Around line 29-37: The current auth/validation error messages use hardcoded
"hackathons"/"Hackathon ID" and aren't draft-aware; update the checks in the
block that references isAuthenticated, organizationId, and hackathonId to
produce contextual messages depending on the draft flow (e.g., use an isDraft
boolean or existing draft flag in scope). Replace static strings like 'Please
sign in to delete hackathons' and 'Organization ID and Hackathon ID are
required' with conditional messages such as 'Please sign in to delete this
hackathon draft' vs 'Please sign in to delete this hackathon' and similarly for
the ID validation (or create a small helper like getEntityLabel(isDraft) and use
it in the toast.error and thrown Error messages). Ensure you update both
toast.error and thrown Error text and reference the existing variables
isAuthenticated, organizationId, and hackathonId (and the draft flag) so
messages reflect draft vs published flows.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4ec4081 and 14343d5.

📒 Files selected for processing (6)
  • app/(landing)/organizations/[id]/hackathons/page.tsx
  • app/me/earnings/page.tsx
  • components/app-sidebar.tsx
  • hooks/hackathon/use-delete-hackathon.ts
  • lib/api/hackathons/draft.ts
  • lib/api/user/earnings.ts

@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 `@hooks/hackathon/use-delete-hackathon.ts`:
- Around line 29-30: Remove any trailing whitespace and fix formatting on the
line that declares the variable targetLabel (const targetLabel = isDraft ?
'draft' : 'hackathon';) so it matches project Prettier rules; run prettier
--write (or your editor's formatter) on use-delete-hackathon.ts to apply the
formatting changes and ensure no extra spaces or inconsistent quotes remain.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 14343d5 and 80712dc.

📒 Files selected for processing (2)
  • app/(landing)/organizations/[id]/hackathons/page.tsx
  • hooks/hackathon/use-delete-hackathon.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/(landing)/organizations/[id]/hackathons/page.tsx

Comment thread hooks/hackathon/use-delete-hackathon.ts 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.

🧹 Nitpick comments (2)
hooks/hackathon/use-delete-hackathon.ts (2)

17-26: Consider using const arrow function per coding guidelines.

The hook uses a function declaration, but coding guidelines prefer const arrow functions with explicit type annotations.

♻️ Proposed refactor
-export function useDeleteHackathon({
+export const useDeleteHackathon = ({
   organizationId,
   hackathonId,
   isDraft = false,
   onSuccess,
   onError,
-}: UseDeleteHackathonOptions) {
+}: UseDeleteHackathonOptions) => {

As per coding guidelines: "Prefer const arrow functions with explicit type annotations over function declarations".

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

In `@hooks/hackathon/use-delete-hackathon.ts` around lines 17 - 26, Change the
function declaration useDeleteHackathon(...) to a const arrow function with an
explicit return type annotation matching its current signature (keep
UseDeleteHackathonOptions for the params type), e.g. declare const
useDeleteHackathon: (opts: UseDeleteHackathonOptions) => ReturnType and
implement the body unchanged (preserve state hooks isDeleting/setIsDeleting and
error state). Ensure you export the const and keep any existing named
imports/usages intact.

36-40: Consider conditional validation for organizationId.

The validation requires organizationId even when isDraft = false, but deleteHackathon(hackathonId) doesn't use it. This may cause unnecessary errors for callers who don't have organizationId available when deleting published hackathons.

♻️ Proposed fix for conditional validation
-    if (!organizationId || !hackathonId) {
-      const idError = `Organization ID and ${isDraft ? 'Draft' : 'Hackathon'} ID are required`;
+    if ((isDraft && !organizationId) || !hackathonId) {
+      const idError = isDraft 
+        ? 'Organization ID and Draft ID are required'
+        : 'Hackathon ID is required';
       toast.error(idError);
       throw new Error(idError);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hooks/hackathon/use-delete-hackathon.ts` around lines 36 - 40, The current
validation block forces organizationId even for non-draft deletes; change it to
require organizationId only when isDraft is true and always require hackathonId:
check hackathonId first and if missing show toast.error/throw, and if isDraft &&
!organizationId show toast.error/throw; update the idError messages accordingly
so deleteHackathon(hackathonId) callers without organizationId can proceed when
isDraft is false. Ensure you modify the validation surrounding isDraft,
organizationId, hackathonId, toast.error and the thrown Error to reflect the
conditional checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@hooks/hackathon/use-delete-hackathon.ts`:
- Around line 17-26: Change the function declaration useDeleteHackathon(...) to
a const arrow function with an explicit return type annotation matching its
current signature (keep UseDeleteHackathonOptions for the params type), e.g.
declare const useDeleteHackathon: (opts: UseDeleteHackathonOptions) =>
ReturnType and implement the body unchanged (preserve state hooks
isDeleting/setIsDeleting and error state). Ensure you export the const and keep
any existing named imports/usages intact.
- Around line 36-40: The current validation block forces organizationId even for
non-draft deletes; change it to require organizationId only when isDraft is true
and always require hackathonId: check hackathonId first and if missing show
toast.error/throw, and if isDraft && !organizationId show toast.error/throw;
update the idError messages accordingly so deleteHackathon(hackathonId) callers
without organizationId can proceed when isDraft is false. Ensure you modify the
validation surrounding isDraft, organizationId, hackathonId, toast.error and the
thrown Error to reflect the conditional checks.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 80712dc and 129af63.

📒 Files selected for processing (1)
  • hooks/hackathon/use-delete-hackathon.ts

@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

🧹 Nitpick comments (1)
hooks/hackathon/use-delete-hackathon.ts (1)

7-13: Model UseDeleteHackathonOptions as a discriminated union.

Line 8 makes organizationId required even when isDraft is false, while draft-only requirements are enforced at runtime. This weakens type guarantees and forces non-draft callers to pass unused data.

♻️ Suggested type-safe shape
-interface UseDeleteHackathonOptions {
-  organizationId: string;
-  hackathonId: string;
-  isDraft?: boolean;
-  onSuccess?: () => void;
-  onError?: (error: string) => void;
-}
+type UseDeleteHackathonOptions =
+  | {
+      isDraft: true;
+      organizationId: string;
+      hackathonId: string;
+      onSuccess?: () => void;
+      onError?: (error: string) => void;
+    }
+  | {
+      isDraft?: false;
+      organizationId?: string;
+      hackathonId: string;
+      onSuccess?: () => void;
+      onError?: (error: string) => void;
+    };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hooks/hackathon/use-delete-hackathon.ts` around lines 7 - 13, Change the flat
UseDeleteHackathonOptions interface into a discriminated union so organizationId
is only required for draft deletions: define one variant { isDraft: true;
organizationId: string; hackathonId: string; onSuccess?: () => void; onError?:
(error: string) => void } and another for non-draft { isDraft?: false;
hackathonId: string; onSuccess?: () => void; onError?: (error: string) => void
}, then update usages of UseDeleteHackathonOptions/ isDraft checks in the delete
hook to rely on the discriminant instead of runtime assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@hooks/hackathon/use-delete-hackathon.ts`:
- Around line 32-45: Precondition checks (isAuthenticated, hackathonId, isDraft
&& !organizationId) currently throw before the try/catch so setError/onError are
never invoked and the draft error message is mislabeled; update the checks
inside the same try block or, instead of throwing directly, call setError(error)
and invoke onError(error) (if provided) and return early after showing toast so
hook error state/callbacks run; also change the hackathonId toast/text to use
"Draft ID" when isDraft is true (use the isDraft flag to pick the correct
label). Ensure you modify the code paths referencing isAuthenticated,
hackathonId, isDraft, organizationId, setError and onError accordingly.

---

Nitpick comments:
In `@hooks/hackathon/use-delete-hackathon.ts`:
- Around line 7-13: Change the flat UseDeleteHackathonOptions interface into a
discriminated union so organizationId is only required for draft deletions:
define one variant { isDraft: true; organizationId: string; hackathonId: string;
onSuccess?: () => void; onError?: (error: string) => void } and another for
non-draft { isDraft?: false; hackathonId: string; onSuccess?: () => void;
onError?: (error: string) => void }, then update usages of
UseDeleteHackathonOptions/ isDraft checks in the delete hook to rely on the
discriminant instead of runtime assertions.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 129af63 and f7b341f.

📒 Files selected for processing (1)
  • hooks/hackathon/use-delete-hackathon.ts

Comment thread hooks/hackathon/use-delete-hackathon.ts 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

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

Inline comments:
In `@hooks/hackathon/use-delete-hackathon.ts`:
- Around line 52-54: TypeScript loses the narrowed type for organizationId in
the ternary call; when isDraft is true the earlier runtime check guarantees
organizationId is present, so call deleteDraft with a non-null assertion on
organizationId (use organizationId!) in the branch that calls deleteDraft,
leaving the deleteHackathon(hackathonId) branch unchanged; reference the isDraft
flag and the deleteDraft/deleteHackathon calls to locate the ternary expression.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f7b341f and 5746069.

📒 Files selected for processing (1)
  • hooks/hackathon/use-delete-hackathon.ts

Comment on lines +52 to +54
const response = isDraft
? await deleteDraft(organizationId, hackathonId)
: await deleteHackathon(hackathonId);

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 | 🔴 Critical

Fix TypeScript error: organizationId may be undefined.

The pipeline fails because TypeScript cannot narrow organizationId after destructuring the discriminated union. Although the runtime check at lines 48-50 ensures organizationId exists when isDraft is true, TypeScript loses this narrowing context.

Use a non-null assertion since the preceding validation guarantees organizationId is defined in the draft branch:

🐛 Proposed fix
       const response = isDraft 
-        ? await deleteDraft(organizationId, hackathonId)
+        ? await deleteDraft(organizationId!, hackathonId)
         : await deleteHackathon(hackathonId);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const response = isDraft
? await deleteDraft(organizationId, hackathonId)
: await deleteHackathon(hackathonId);
const response = isDraft
? await deleteDraft(organizationId!, hackathonId)
: await deleteHackathon(hackathonId);
🧰 Tools
🪛 GitHub Actions: CI/CD Pipeline

[error] 53-53: TypeScript error TS2345: Argument of type 'string | undefined' is not assignable to parameter of type 'string'. (Occurred during 'npm run type-check' / 'tsc --noEmit')

🪛 GitHub Check: Code Quality & Linting

[failure] 53-53:
Argument of type 'string | undefined' is not assignable to parameter of type 'string'.

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

In `@hooks/hackathon/use-delete-hackathon.ts` around lines 52 - 54, TypeScript
loses the narrowed type for organizationId in the ternary call; when isDraft is
true the earlier runtime check guarantees organizationId is present, so call
deleteDraft with a non-null assertion on organizationId (use organizationId!) in
the branch that calls deleteDraft, leaving the deleteHackathon(hackathonId)
branch unchanged; reference the isDraft flag and the deleteDraft/deleteHackathon
calls to locate the ternary expression.

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

@Fankouzu
Issues in this repository are intended for humans, not bots. This PR will be closed.

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