Skip to content

Resolve bounty dashboard and accessibility issues - #230

Closed
Ishant5436 wants to merge 2 commits into
boundlessfi:mainfrom
Ishant5436:feature/all-bounty-issues
Closed

Ishant5436 wants to merge 2 commits into
boundlessfi:mainfrom
Ishant5436:feature/all-bounty-issues

Conversation

@Ishant5436

@Ishant5436 Ishant5436 commented May 27, 2026

Copy link
Copy Markdown

Resolves Issue 216: Add missing aria-labels to icon-only buttons for accessibility compliance.\nResolves Issue 212: Standardize loading skeleton implementation and consolidate into components/ui/loading.\nResolves Issue 205: Implement discrete maintainer actions and UI loading states in model4-maintainer-dashboard.

Summary by CodeRabbit

  • New Features

    • Enhanced loading skeletons for bounty cards, leaderboard, and wallet pages.
    • Optimized mobile experience with dedicated call-to-action interface for bounty interactions.
  • Refactor

    • Reorganized and centralized component state management for bounty actions.
    • Consolidated skeleton components for improved maintainability.
  • Style

    • Improved accessibility with enhanced button labels and aria attributes.

Review Change Stack

- Extracted MobileCTA into its own file
- Extracted shared state into useBountyCTAState hook
- Updated bounty-detail-client.tsx to import MobileCTA from new file
Resolves Issue 216: Add missing aria-labels to icon-only buttons for accessibility compliance.

Resolves Issue 212: Standardize loading skeleton implementation and consolidate into components/ui/loading.

Resolves Issue 205: Implement discrete maintainer actions and UI loading states in model4-maintainer-dashboard.
@vercel

vercel Bot commented May 27, 2026

Copy link
Copy Markdown

@Ishant5436 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 May 27, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR refactors loading UI and bounty detail call-to-action logic: skeleton components are consolidated into components/ui/loading, imports updated across the codebase, bounty CTA state extracted into a reusable hook, and MobileCTA split into a dedicated file. The maintainer dashboard gains dedicated action handlers replacing "Coming soon" placeholders.

Changes

Skeleton UI Centralization and Bounty CTA Refactoring

Layer / File(s) Summary
Skeleton components infrastructure
components/ui/loading/bounty-card-skeleton.tsx, components/ui/loading/leaderboard-row-skeleton.tsx, components/ui/loading/wallet-page-skeleton.tsx
New skeleton loading components added: BountyCardSkeleton and BountyListSkeleton for bounty cards/lists, LeaderboardRowSkeleton and LeaderboardTableSkeleton for leaderboard loading, and WalletPageSkeleton for wallet pages.
Import migration for skeleton components
app/bounty/page.tsx, app/wallet/page.tsx, components/bounty/bounty-grid.tsx, components/bounty/bounty-list.tsx, components/leaderboard/leaderboard-table.tsx
Updated imports to reference new centralized skeleton paths; LeaderboardTable now uses LeaderboardTableSkeleton component instead of inline skeleton rendering; old components/bounty/bounty-card-skeleton.tsx removed.
Bounty CTA state hook extraction
components/bounty-detail/use-bounty-cta-state.ts
New useBountyCTAState hook centralizes call-to-action logic: derives permission flags (canAct, canCancel, canRaiseDispute), wires cancellation/dispute/join state, provides handlers for apply/copy/label computation, and returns consolidated state and handlers.
Bounty detail CTA component refactoring
components/bounty-detail/bounty-detail-mobile-cta.tsx, components/bounty-detail/bounty-detail-sidebar-cta.tsx, components/bounty-detail/bounty-detail-client.tsx
MobileCTA extracted to dedicated file with fixed-bottom mobile-only layout; SidebarCTA refactored to use useBountyCTAState hook, removing duplicate logic; MobileCTA removed from sidebar module; client imports updated to reference new file locations.
Maintainer dashboard action handlers
components/bounty-detail/model4-maintainer-dashboard.tsx
Dedicated async handlers (handleMessage, handleViewSubmissions, handleReleasePayment, handleAdvance, handleRemove) replace "Coming soon" placeholders; handlers manage loadingAction state, simulate latency, and display toast feedback; buttons now show active action text.
Formatting and accessibility improvements
components/mode-toggle.tsx
Code normalized with consistent semicolons and import formatting; aria-label="Toggle theme" added to theme toggle button for improved accessibility.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • boundlessfi/bounties#209: Directly addresses the refactoring request to extract MobileCTA into its own file, add useBountyCTAState, and update imports in bounty detail components.
  • boundlessfi/bounties#212: Implements the requested centralization of skeleton loaders into components/ui/loading with standardized imports across all pages.
  • boundlessfi/bounties#205: Directly implements dedicated per-contributor action handlers in maintainer dashboard, replacing "Coming soon" labels.

Possibly related PRs

  • boundlessfi/bounties#34: Introduces skeleton component infrastructure that overlaps with the main PR's relocation of BountyCardSkeleton and BountyListSkeleton from the bounty domain to the shared UI loading directory.
  • boundlessfi/bounties#88: Touches bounty detail client imports and CTA component structure that the main PR refactors via hook extraction and file reorganization.

Suggested reviewers

  • Benjtalkshow

🐰 A refactor hops along so fine,
New hooks and skeletons align,
CTA logic shared with care,
Mobile views split off with flair,
Loading states now centralize!

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately reflects the main changes: it resolves accessibility issues (aria-labels), standardizes loading skeletons, and implements maintainer dashboard improvements.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate 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.

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

🧹 Nitpick comments (1)
components/bounty-detail/model4-maintainer-dashboard.tsx (1)

41-46: ⚡ Quick win

Guard loadingAction reset with finally in async handlers.

If these handlers later call real APIs and one throws, loadingAction can remain stuck and keep controls disabled.

Suggested pattern
  const handleMessage = async (userName: string) => {
-   setLoadingAction(`Message-${userName}`);
-   await new Promise((r) => setTimeout(r, 800));
-   toast.success(`Message sent to ${userName}`);
-   setLoadingAction(null);
+   setLoadingAction(`Message-${userName}`);
+   try {
+     await new Promise((r) => setTimeout(r, 800));
+     toast.success(`Message sent to ${userName}`);
+   } finally {
+     setLoadingAction(null);
+   }
  };

Also applies to: 48-53, 55-60, 62-67, 69-74

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/bounty-detail/model4-maintainer-dashboard.tsx` around lines 41 -
46, The async handlers that set loadingAction (e.g., handleMessage) must ensure
loadingAction is always cleared even if an awaited call throws; wrap the async
work in try/finally and move setLoadingAction(null) into the finally block so
the spinner/disabled state cannot get stuck, keeping the toast and awaits inside
the try and preserving the initial setLoadingAction(...) before the try; apply
the same change to all other async handlers that set loadingAction in this file.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@components/bounty-detail/bounty-detail-mobile-cta.tsx`:
- Around line 176-178: The mobile cancel dialog currently preserves previous
input because closing via AlertDialogCancel doesn't clear the cancelReason
state; update the component so dismissing the dialog resets the reason—either
add an onClick handler to AlertDialogCancel that calls setCancelReason('') (and
keep disabled tied to isCancelling) or handle the dialog's open state change
(e.g., onOpenChange) to detect when it closes and call setCancelReason('');
reference AlertDialogCancel, isCancelling, cancelReason, and setCancelReason
when making the change.

In `@components/bounty-detail/model4-maintainer-dashboard.tsx`:
- Around line 42-43: The loadingAction key currently uses userName which can
collide for contributors with identical display names; update all calls that
setLoadingAction (e.g., setLoadingAction(`Message-${userName}`) and similar
occurrences) to use the unique contributor.userId (e.g.,
`Message-${contributor.userId}`) while leaving userName only for toast/display
text; apply the same change to every other instance mentioned (lines
corresponding to the other setLoadingAction calls) so all loading keys are built
from contributor.userId to avoid cross-row collisions.

---

Nitpick comments:
In `@components/bounty-detail/model4-maintainer-dashboard.tsx`:
- Around line 41-46: The async handlers that set loadingAction (e.g.,
handleMessage) must ensure loadingAction is always cleared even if an awaited
call throws; wrap the async work in try/finally and move setLoadingAction(null)
into the finally block so the spinner/disabled state cannot get stuck, keeping
the toast and awaits inside the try and preserving the initial
setLoadingAction(...) before the try; apply the same change to all other async
handlers that set loadingAction in this file.
🪄 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: 11a3f33b-acb8-451f-a8c0-2b9f47bd2b45

📥 Commits

Reviewing files that changed from the base of the PR and between ede3b20 and 7ae8707.

📒 Files selected for processing (16)
  • app/bounty/page.tsx
  • app/wallet/page.tsx
  • components/bounty-detail/bounty-detail-client.tsx
  • components/bounty-detail/bounty-detail-mobile-cta.tsx
  • components/bounty-detail/bounty-detail-sidebar-cta.tsx
  • components/bounty-detail/model4-maintainer-dashboard.tsx
  • components/bounty-detail/use-bounty-cta-state.ts
  • components/bounty/bounty-card-skeleton.tsx
  • components/bounty/bounty-grid.tsx
  • components/bounty/bounty-list.tsx
  • components/leaderboard/leaderboard-table.tsx
  • components/mode-toggle.tsx
  • components/ui/loading/bounty-card-skeleton.tsx
  • components/ui/loading/bounty-detail-skeleton.tsx
  • components/ui/loading/leaderboard-row-skeleton.tsx
  • components/ui/loading/wallet-page-skeleton.tsx
💤 Files with no reviewable changes (1)
  • components/bounty/bounty-card-skeleton.tsx

Comment on lines +176 to +178
<AlertDialogCancel disabled={isCancelling}>
Keep Bounty
</AlertDialogCancel>

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 | ⚡ Quick win

Reset cancel reason when dismissing the mobile cancel dialog.

Closing with “Keep Bounty” currently preserves the previous reason text, so reopening can submit stale input unintentionally.

Suggested fix
-            <AlertDialogCancel disabled={isCancelling}>
+            <AlertDialogCancel
+              disabled={isCancelling}
+              onClick={() => setCancelReason("")}
+            >
               Keep Bounty
             </AlertDialogCancel>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/bounty-detail/bounty-detail-mobile-cta.tsx` around lines 176 -
178, The mobile cancel dialog currently preserves previous input because closing
via AlertDialogCancel doesn't clear the cancelReason state; update the component
so dismissing the dialog resets the reason—either add an onClick handler to
AlertDialogCancel that calls setCancelReason('') (and keep disabled tied to
isCancelling) or handle the dialog's open state change (e.g., onOpenChange) to
detect when it closes and call setCancelReason(''); reference AlertDialogCancel,
isCancelling, cancelReason, and setCancelReason when making the change.

Comment on lines +42 to +43
setLoadingAction(`Message-${userName}`);
await new Promise((r) => setTimeout(r, 800));

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 | ⚡ Quick win

Use userId (not userName) in loadingAction keys to avoid collisions.

Two contributors can share the same display name, which can bind loading indicators to the wrong row/action. Build the key from contributor.userId and keep userName only for toast text.

Suggested fix
- const handleReleasePayment = async (userName: string) => {
-   setLoadingAction(`Release Payment-${userName}`);
+ const handleReleasePayment = async (userId: string, userName: string) => {
+   setLoadingAction(`release-payment:${userId}`);
    await new Promise((r) => setTimeout(r, 1200));
    toast.success(`Payment released for ${userName}`);
    setLoadingAction(null);
  };
- onClick={() => handleReleasePayment(contributor.userName)}
+ onClick={() => handleReleasePayment(contributor.userId, contributor.userName)}

- {loadingAction === `Release Payment-${contributor.userName}` ? (
+ {loadingAction === `release-payment:${contributor.userId}` ? (

Also applies to: 49-50, 56-57, 63-64, 70-71, 205-206, 226-227

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/bounty-detail/model4-maintainer-dashboard.tsx` around lines 42 -
43, The loadingAction key currently uses userName which can collide for
contributors with identical display names; update all calls that
setLoadingAction (e.g., setLoadingAction(`Message-${userName}`) and similar
occurrences) to use the unique contributor.userId (e.g.,
`Message-${contributor.userId}`) while leaving userName only for toast/display
text; apply the same change to every other instance mentioned (lines
corresponding to the other setLoadingAction calls) so all loading keys are built
from contributor.userId to avoid cross-row collisions.

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.

2 participants