Skip to content

lightinig-bounties#163

Open
rosemary21 wants to merge 2 commits intoboundlessfi:mainfrom
rosemary21:lightinig-bounties
Open

lightinig-bounties#163
rosemary21 wants to merge 2 commits intoboundlessfi:mainfrom
rosemary21:lightinig-bounties

Conversation

@rosemary21
Copy link
Copy Markdown

@rosemary21 rosemary21 commented Mar 27, 2026

closes #145

Endpoint Purpose
GET /health/live Liveness probe (no dependency checks)
GET /health/detailed Full JSON metrics snapshot
GET /health/metrics Prometheus text exposition for scraping

Summary by CodeRabbit

  • New Features
    • Introduced Lightning Rounds — dedicated round pages with live countdowns, status badges, stats, progress bars, and organized category sections linking to bounties
    • Added Lightning Round banner and schedule components to the bounties page and sidebar for quick discovery
    • Added Lightning badge to bounty cards to highlight round-affiliated bounties

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 27, 2026

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

A member of the Team first needs to authorize it.

@drips-wave
Copy link
Copy Markdown

drips-wave bot commented Mar 27, 2026

@rosemary21 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 27, 2026

📝 Walkthrough

Walkthrough

Adds a Lightning Rounds feature: new landing page, banner, schedule widget, bounty-card badge, and hooks for round aggregation and countdown; integrates banner/schedule into existing bounty pages and provides client-side countdowns and per-round bounty grouping.

Changes

Cohort / File(s) Summary
Lightning Round Page
app/bounty/lightning-round/page.tsx
New client-side page rendering a selected or active lightning round: groups bounties by category, shows stats, progress, countdown, and round selector via windowId query param.
Hooks / Countdown
hooks/use-lightning-rounds.ts
New hooks exporting useLightningRounds(), useLightningRoundBounties(windowId), and useCountdown(targetDate); transform active bounties into rounds, fetch/organize bounties by category, compute totals/completed counts, and provide live countdown state.
Banner & Schedule UI
components/bounty/lightning-round-banner.tsx, components/bounty/lightning-round-schedule.tsx
New client components: banner with live/upcoming badges, stats, countdown and CTA; schedule card listing next rounds with status, links, and skeleton/loading/empty states.
Bounty Grid Integration
app/bounty/page.tsx
Inserts LightningRoundBanner into the main bounties page (adds spacing).
Bounty Card Enhancement
components/bounty/bounty-card.tsx
Adds conditional "Lightning" Badge with Zap icon when a bounty is associated with a bountyWindow.
Filters Sidebar
components/bounty/filters-sidebar.tsx
Renders LightningRoundSchedule alongside existing sidebar items and adds vertical spacing (space-y-4) in large view.

Sequence Diagrams

sequenceDiagram
    participant User
    participant Page as Lightning Round Page
    participant Hooks as useLightningRounds / useLightningRoundBounties
    participant Query as GraphQL Query
    participant Countdown as useCountdown

    User->>Page: Navigate to /bounty/lightning-round (optional windowId)
    Page->>Hooks: useLightningRounds()
    Hooks->>Query: useActiveBountiesQuery()
    Query-->>Hooks: Active bounties with bountyWindow info
    Hooks->>Hooks: Group by window, compute rounds & activeRound
    Hooks-->>Page: rounds[], activeRound

    Page->>Hooks: useLightningRoundBounties(windowId)
    Hooks->>Query: useBountiesQuery(windowId, limit=50)
    Query-->>Hooks: Bounties for window
    Hooks->>Hooks: Bucket by category, calc totals, completedCount
    Hooks-->>Page: bounties, byCategory, totals

    Page->>Countdown: useCountdown(targetDate)
    Countdown->>Countdown: tick every second
    Countdown-->>Page: days,hours,minutes,seconds,expired

    Page-->>User: Render categories, stats, progress bar, and countdown
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • Benjtalkshow

Poem

🐰⚡ I hopped in with a twitch and cheer,
Rounds lined up, the bounties near,
Tickers ticking, categories bloom,
Zap-badges flash — quick, make room!
Hooray for rounds that sparkle and steer ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.79% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title 'lightinig-bounties' is a typo-ridden and vague reference to the Lightning Bounties feature. While it's tangentially related to the main changeset, it lacks clarity and contains a spelling error ('lightinig' vs 'lightning'). Consider revising the title to be more descriptive and accurate, such as 'Add Lightning Rounds feature with banner, schedule, and dedicated page' to better represent the actual changes.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed The PR implements most core requirements from issue #145: lightning round banner [#145], dedicated round page [#145], bounty badge [#145], schedule widget [#145], and custom hooks [#145]. However, the PR description mentions health endpoints unrelated to Lightning Rounds, suggesting incomplete scope documentation.
Out of Scope Changes check ✅ Passed All code changes align directly with issue #145 requirements: banner component, dedicated page, bounty badge, schedule widget, and data hooks. The PR description's mention of health endpoints appears to be documentation error rather than actual code changes.

✏️ 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.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

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 (3)
components/bounty/bounty-card.tsx (1)

112-117: Consider displaying round name or checking round status.

The implementation correctly shows the Lightning badge when bountyWindow exists. Two optional enhancements to consider:

  1. The bountyWindow.name field is available (per the GraphQL fragment) and could replace the static "Lightning" text to show the actual round name.
  2. The badge displays regardless of bountyWindow.status — a completed or cancelled round will still show the badge. If the intent is to highlight only active lightning rounds, consider checking bountyWindow.status === 'ACTIVE'.

These are optional improvements; the current implementation meets the basic acceptance criteria.

💡 Optional: Show round name and check status
-              {bounty.bountyWindow && (
+              {bounty.bountyWindow && bounty.bountyWindow.status === 'ACTIVE' && (
                 <Badge className="text-[10px] px-1.5 py-0.5 gap-1 bg-yellow-500/15 text-yellow-400 border-yellow-500/30">
                   <Zap className="h-2.5 w-2.5 fill-yellow-400" />
-                  Lightning
+                  {bounty.bountyWindow.name}
                 </Badge>
               )}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/bounty/bounty-card.tsx` around lines 112 - 117, The badge
currently renders whenever bounty.bountyWindow is truthy and shows static
"Lightning"; update the JSX in the component that renders Badge (the block using
Badge and Zap) to instead display bounty.bountyWindow.name when available and to
only render the Badge for active rounds by checking bounty.bountyWindow.status
=== 'ACTIVE' (fall back to the static label if name is missing). Keep the
existing Badge and Zap usage but add the conditional on
bounty.bountyWindow.status and replace the hardcoded text with
bounty.bountyWindow.name || 'Lightning'.
app/bounty/lightning-round/page.tsx (2)

255-258: Label mismatch for ended rounds.

The label shows "Starts in" when isActive is false, but this includes ended rounds. Consider adjusting the label logic to avoid showing a countdown label at all for ended rounds.

Proposed fix
-                  <CountdownBlock
-                    targetDate={countdownTarget ?? null}
-                    label={isActive ? "Ends in" : "Starts in"}
-                  />
+                  {!isEnded && (
+                    <CountdownBlock
+                      targetDate={countdownTarget ?? null}
+                      label={isActive ? "Ends in" : "Starts in"}
+                    />
+                  )}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/bounty/lightning-round/page.tsx` around lines 255 - 258, The current
CountdownBlock is given label based only on isActive which causes ended rounds
(isActive false but past targetDate) to show "Starts in"; update the logic
around CountdownBlock/props (CountdownBlock, targetDate, label, countdownTarget,
isActive) to detect an ended round and either omit the label or skip rendering
the component: e.g., compute an isEnded check (targetDate in the past) and then
pass label={isActive ? "Ends in" : (isEnded ? undefined : "Starts in")} or
conditionally render CountdownBlock only when not ended so ended rounds do not
display a countdown label.

179-186: Countdown may show "Starts in" for already-ended rounds.

When a round has ended (isEnded is true) but its status is not "active", the code sets countdownTarget to startDate and displays "Starts in". This will show a confusing countdown for a past date (which useCountdown will mark as expired, returning null). While CountdownBlock handles expired by returning null, the label "Starts in" is still set—this is potentially misleading if there's any race condition or the countdown hasn't yet recalculated.

Consider explicitly handling the ended state:

Proposed fix
   const isActive = currentRound?.status.toLowerCase() === "active";
   const isEnded =
     currentRound?.endDate && new Date(currentRound.endDate) < new Date();
-  const countdownTarget = isActive ? currentRound?.endDate : currentRound?.startDate;
+  const countdownTarget = isActive
+    ? currentRound?.endDate
+    : isEnded
+      ? null
+      : currentRound?.startDate;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/bounty/lightning-round/page.tsx` around lines 179 - 186, The countdown
can show a "Starts in" label for already-ended rounds because countdownTarget is
set to startDate when isActive is false; update the logic that computes
countdownTarget (and any label selection) in the component using currentRound,
isActive and isEnded so that when isEnded is true you set countdownTarget to
null/undefined (or otherwise skip rendering the CountdownBlock) instead of using
currentRound.startDate; change the assignment of countdownTarget and any related
label computation (references: isActive, isEnded, countdownTarget, currentRound,
useCountdown, CountdownBlock) so ended rounds do not render a "Starts in"
countdown.
🤖 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/use-lightning-rounds.ts`:
- Around line 83-86: The hook useLightningRoundBounties currently calls
useBountiesQuery with query: { bountyWindowId: windowId, limit: 50 } even when
windowId is empty, causing unnecessary requests; update the call to pass an
enabled flag (e.g., enabled: Boolean(windowId)) so the query is skipped when
windowId is falsy, and keep the query object intact (bountyWindowId and limit)
to preserve behavior when enabled.

---

Nitpick comments:
In `@app/bounty/lightning-round/page.tsx`:
- Around line 255-258: The current CountdownBlock is given label based only on
isActive which causes ended rounds (isActive false but past targetDate) to show
"Starts in"; update the logic around CountdownBlock/props (CountdownBlock,
targetDate, label, countdownTarget, isActive) to detect an ended round and
either omit the label or skip rendering the component: e.g., compute an isEnded
check (targetDate in the past) and then pass label={isActive ? "Ends in" :
(isEnded ? undefined : "Starts in")} or conditionally render CountdownBlock only
when not ended so ended rounds do not display a countdown label.
- Around line 179-186: The countdown can show a "Starts in" label for
already-ended rounds because countdownTarget is set to startDate when isActive
is false; update the logic that computes countdownTarget (and any label
selection) in the component using currentRound, isActive and isEnded so that
when isEnded is true you set countdownTarget to null/undefined (or otherwise
skip rendering the CountdownBlock) instead of using currentRound.startDate;
change the assignment of countdownTarget and any related label computation
(references: isActive, isEnded, countdownTarget, currentRound, useCountdown,
CountdownBlock) so ended rounds do not render a "Starts in" countdown.

In `@components/bounty/bounty-card.tsx`:
- Around line 112-117: The badge currently renders whenever bounty.bountyWindow
is truthy and shows static "Lightning"; update the JSX in the component that
renders Badge (the block using Badge and Zap) to instead display
bounty.bountyWindow.name when available and to only render the Badge for active
rounds by checking bounty.bountyWindow.status === 'ACTIVE' (fall back to the
static label if name is missing). Keep the existing Badge and Zap usage but add
the conditional on bounty.bountyWindow.status and replace the hardcoded text
with bounty.bountyWindow.name || 'Lightning'.
🪄 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: CHILL

Plan: Pro

Run ID: 15eb379c-b598-49ee-8995-7de9514ad08b

📥 Commits

Reviewing files that changed from the base of the PR and between 3aad250 and 35e49e2.

📒 Files selected for processing (6)
  • app/bounty/lightning-round/page.tsx
  • app/bounty/page.tsx
  • components/bounty/bounty-card.tsx
  • components/bounty/lightning-round-banner.tsx
  • components/bounty/lightning-round-schedule.tsx
  • hooks/use-lightning-rounds.ts

Comment on lines +83 to +86
export function useLightningRoundBounties(windowId: string) {
const { data, isLoading, isError, error } = useBountiesQuery({
query: { bountyWindowId: windowId, limit: 50 },
});
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 | 🟡 Minor

Query executes with empty windowId when no round is selected.

Based on the context snippet showing useBountiesQuery does not skip when bountyWindowId is empty, this hook will fire a query with bountyWindowId: "" when called with an empty string (e.g., from the page when currentRound?.id ?? ""). This results in an unnecessary network request that likely returns zero or unexpected results.

Consider adding an enabled option to skip the query when windowId is falsy:

Proposed fix
 export function useLightningRoundBounties(windowId: string) {
   const { data, isLoading, isError, error } = useBountiesQuery({
     query: { bountyWindowId: windowId, limit: 50 },
+  }, {
+    enabled: !!windowId,
   });
📝 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
export function useLightningRoundBounties(windowId: string) {
const { data, isLoading, isError, error } = useBountiesQuery({
query: { bountyWindowId: windowId, limit: 50 },
});
export function useLightningRoundBounties(windowId: string) {
const { data, isLoading, isError, error } = useBountiesQuery({
query: { bountyWindowId: windowId, limit: 50 },
}, {
enabled: !!windowId,
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hooks/use-lightning-rounds.ts` around lines 83 - 86, The hook
useLightningRoundBounties currently calls useBountiesQuery with query: {
bountyWindowId: windowId, limit: 50 } even when windowId is empty, causing
unnecessary requests; update the call to pass an enabled flag (e.g., enabled:
Boolean(windowId)) so the query is skipped when windowId is falsy, and keep the
query object intact (bountyWindowId and limit) to preserve behavior when
enabled.

Copy link
Copy Markdown
Contributor

@Benjtalkshow Benjtalkshow left a comment

Choose a reason for hiding this comment

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

Hi @rosemary21 , great work on the Lightning Rounds feature,

There are a few things to address before merging:

The useLightningRoundBounties hook fires a query even when windowId is empty, so it should include enabled: !!windowId as suggested.

Both useLightningRounds and useLightningRoundBounties redundantly fetch bounties for the same window on the lightning round page, which leads to unnecessary data duplication.

The b.status === "completed" check in the hook is case-sensitive, while the rest of the codebase uses .toLowerCase(), so the completed count will likely always be 0.

Additionally, please address the following:

The light mode compatibility needs attention, as the text-yellow-300 countdown text and from-yellow-950/40 gradients will have poor contrast or appear muddy on light backgrounds.

Fix the PR title typo (lightiniglightning).

Add a Suspense boundary around useSearchParams() as required by Next.js 13+.

Apply the suggested correction for handling the empty windowId query and fix conflict.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

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 `@components/bounty/filters-sidebar.tsx`:
- Around line 212-214: The LightningRoundSchedule component currently renders an
empty-state when the rounds query fails; update
components/bounty/lightning-round-schedule.tsx to explicitly handle the query
error by checking the query's isError (or error) state from whatever data hook
(e.g., useRounds or useQuery) you use, and add an error UI branch that shows a
clear error message plus a retry action that calls the query's refetch (or
retry) method; ensure the component still handles loading and success branches
but returns the new error view when isError is true so the sidebar (where
LightningRoundSchedule is mounted) won’t display the misleading empty state.
🪄 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: CHILL

Plan: Pro

Run ID: 2756d2da-4797-41b3-a64f-0447ac935a0b

📥 Commits

Reviewing files that changed from the base of the PR and between 35e49e2 and 5c8071d.

📒 Files selected for processing (3)
  • app/bounty/page.tsx
  • components/bounty/bounty-card.tsx
  • components/bounty/filters-sidebar.tsx
✅ Files skipped from review due to trivial changes (2)
  • app/bounty/page.tsx
  • components/bounty/bounty-card.tsx

Comment on lines +212 to +214
<div className="hidden lg:block space-y-4">
<MiniLeaderboard className="w-full" />
<LightningRoundSchedule className="w-full" />
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

Handle Lightning schedule query failures explicitly.

By rendering LightningRoundSchedule in the sidebar (Line 214), users can now see a misleading empty-state message when the rounds query fails, since the component currently ignores isError. Please add an error branch (with retry) in components/bounty/lightning-round-schedule.tsx.

💡 Proposed fix (in components/bounty/lightning-round-schedule.tsx)
-export function LightningRoundSchedule({
+export function LightningRoundSchedule({
   className,
   maxItems = 3,
 }: LightningRoundScheduleProps) {
-  const { rounds, isLoading } = useLightningRounds();
+  const { rounds, isLoading, isError, refetch } = useLightningRounds();

   const displayed = rounds.slice(0, maxItems);

   return (
@@
       <CardContent className="p-0">
         {isLoading ? (
           <ScheduleSkeleton />
+        ) : isError ? (
+          <div className="px-4 py-6 text-center text-sm text-muted-foreground">
+            Failed to load round schedule.
+            <button
+              type="button"
+              onClick={() => refetch()}
+              className="ml-2 underline hover:text-foreground"
+            >
+              Retry
+            </button>
+          </div>
         ) : displayed.length === 0 ? (
           <div className="px-4 py-6 text-center text-sm text-muted-foreground">
             No upcoming rounds scheduled.
           </div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/bounty/filters-sidebar.tsx` around lines 212 - 214, The
LightningRoundSchedule component currently renders an empty-state when the
rounds query fails; update components/bounty/lightning-round-schedule.tsx to
explicitly handle the query error by checking the query's isError (or error)
state from whatever data hook (e.g., useRounds or useQuery) you use, and add an
error UI branch that shows a clear error message plus a retry action that calls
the query's refetch (or retry) method; ensure the component still handles
loading and success branches but returns the new error view when isError is true
so the sidebar (where LightningRoundSchedule is mounted) won’t display the
misleading empty state.

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.

Implement Lightning Rounds (Surge Events) Feature

2 participants