Skip to content

Feat/bounty selection flow model 2#200

Merged
Benjtalkshow merged 4 commits into
boundlessfi:mainfrom
legend4tech:feat/bounty-selection-flow-model-2
Apr 29, 2026
Merged

Feat/bounty selection flow model 2#200
Benjtalkshow merged 4 commits into
boundlessfi:mainfrom
legend4tech:feat/bounty-selection-flow-model-2

Conversation

@legend4tech
Copy link
Copy Markdown
Contributor

@legend4tech legend4tech commented Apr 29, 2026

Feature: Complete Application-Based Bounty Selection Flow (Model 2)

Closes #146

Overview

This PR completes the frontend implementation for the Application-Based Bounty Selection Flow (Model 2). It builds upon the partial implementation to introduce a robust, end-to-end lifecycle for contributors applying to a bounty and creators reviewing, selecting, and approving those applications.

Key Changes

  • Expanded Application Form (application-dialog.tsx)

    • Upgraded the application form to capture structured proposal fields: Approach, Estimated Timeline, Relevant Experience, and an optional Portfolio URL.
    • Added a preview step so applicants can verify their proposal before committing it on-chain.
  • Creator Review Dashboard (application-review-dashboard.tsx)

    • Built a new dashboard for creators to view all incoming applications.
    • Displays applicant reputation scores, tiers, and proposal summaries.
    • Implemented a side-by-side comparison mode for evaluating top candidates.
    • Added "Select" and "Decline" actions, where "Select" triggers the select_applicant contract call.
  • Work Submission (application-submit-work-panel.tsx)

    • Created a dedicated panel for the selected contributor to upload deliverables.
    • Requires providing an IPFS CID link to the work and handles the submit_work on-chain interaction.
  • Creator Approval Panel (submission-approval-panel.tsx)

    • Built an approval panel allowing the creator to view the submitted IPFS deliverable.
    • Creators can "Approve & Release Payment", which awards reputation points and triggers the approve_submission contract call to release the escrow.
    • Added UI placeholders for requesting revisions and providing feedback.
  • Contract Hooks (use-bounty-application.ts)

    • Extracted all smart contract interactions into a new custom hook file.
    • Provides robust useMutation wrappers for apply, select_applicant, submit_work, and approve_submission.
    • Configured optimistic UI updates through React Query to instantly reflect status changes in the frontend.
  • Bounty Detail Integration

    • Seamlessly wired the new panels into BountyDetailClient and the SidebarCTA/MobileCTA to ensure the correct views render based on the current user's role (Creator vs. Applicant) and the bounty's status.

Testing Instructions

  1. Open a Model 2 (MILESTONE_BASED) bounty in the OPEN state.
  2. As an Applicant: Click "Apply for Bounty", fill out the structured form, preview, and submit.
  3. As a Creator: View the Review Dashboard, compare applications, and click "Select".
  4. As the Selected Applicant: Observe the "Submit Work" panel and submit an IPFS CID.
  5. As a Creator: Review the submitted IPFS link, assign reputation points, and approve the deliverable.

Summary by CodeRabbit

  • New Features

    • Milestone-based application flow: three required fields (approach, timeline, experience), stricter portfolio URL validation, two-step preview submit
    • Creator review dashboard with compare/select applicants
    • Contributor submit-work panel (IPFS CID) and creator approval panel (award reputation)
    • Client-side application lifecycle actions (apply/select/submit/approve) with optimistic UI updates
  • Bug Fixes / UX

    • CTA flow updated for milestone bounties; apply disabled when wallet missing; mobile cancel preserved
    • Improved applicant/assignment detection and empty-applications feedback

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 29, 2026

@legend4tech 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 Apr 29, 2026

@legend4tech 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 Apr 29, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b7d8daa5-e99f-4d3c-8d9b-597c29b664b9

📥 Commits

Reviewing files that changed from the base of the PR and between d061724 and 69e6e82.

📒 Files selected for processing (1)
  • components/bounty/submission-approval-panel.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/bounty/submission-approval-panel.tsx

📝 Walkthrough

Walkthrough

Integrates a Model‑2 application flow for MILESTONE_BASED bounties: structured application/dialog with preview, creator review dashboard with comparison and selection, contributor submit-work panel, approval panel, and React Query hooks that call contract methods (apply/select/submit/approve).

Changes

Cohort / File(s) Summary
Bounty Detail Client & CTA
components/bounty-detail/bounty-detail-client.tsx, components/bounty-detail/bounty-detail-sidebar-cta.tsx
Adds milestone-specific rendering and role/status-based panels; derives walletAddress from session.user.walletAddress; adds isAssignedApplicant predicate; integrates ApplicationDialog and useApplyToBounty into CTA flows; mobile CTA tweak to retain cancel icon.
Application Form / Dialog
components/bounty/application-dialog.tsx
Replaces coverLetter with approach, estimatedTimeline, relevantExperience; stricter portfolioUrl (http/https); exports ApplicationFormValues; changes onApply signature; adds two‑step preview flow and scrollable preview UI.
Application Review UI
components/bounty/application-review-dashboard.tsx
New component exporting Application interface and ApplicationReviewDashboard; renders application list, reputation/stats, select action, two-up comparison mode, and empty-state card.
Work Submission Panel
components/bounty/application-submit-work-panel.tsx
New client component ApplicationSubmitWorkPanel for contributor IPFS CID submission; required input, mutation wiring, disabled/submit states.
Submission Approval Panel
components/bounty/submission-approval-panel.tsx
New client component SubmissionApprovalPanel for creators to review submissions, set/clamp reputation points, call approval mutation; optional CID link display and placeholder "Needs Changes" UI.
Application Hooks
hooks/use-bounty-application.ts
New client hooks: useApplyToBounty, useSelectApplicant, useSubmitApplicationWork, useApproveApplicationSubmission; adds ApplicationError type/class, ID parsing helper, contract client resolution, optimistic cache updates, and query invalidation.

Sequence Diagram(s)

sequenceDiagram
    participant Contributor
    participant Frontend
    participant Contract as BountyRegistry
    participant Creator

    rect rgba(100, 150, 200, 0.5)
    Note over Contributor,Frontend: Application Phase (OPEN)
    Contributor->>Frontend: Open ApplicationDialog & submit proposal
    Frontend->>Contract: apply(applicantAddress, bountyId, proposal)
    Contract-->>Frontend: tx result / event
    end

    rect rgba(100, 200, 150, 0.5)
    Note over Creator,Frontend: Review & Select Phase (OPEN)
    Creator->>Frontend: Open ApplicationReviewDashboard
    Creator->>Frontend: Select applicant (possibly compare two)
    Frontend->>Contract: select_applicant(creatorAddress, bountyId, applicantAddress)
    Contract-->>Frontend: tx result / event (assign + lock escrow)
    end

    rect rgba(200, 150, 100, 0.5)
    Note over Contributor,Frontend: Submit Work (IN_PROGRESS)
    Contributor->>Frontend: Open ApplicationSubmitWorkPanel, submit work CID
    Frontend->>Contract: submit_work(contributorAddress, bountyId, workCid)
    Contract-->>Frontend: tx result / event (status -> UNDER_REVIEW)
    end

    rect rgba(200, 100, 150, 0.5)
    Note over Creator,Frontend: Approval Phase (UNDER_REVIEW)
    Creator->>Frontend: Open SubmissionApprovalPanel, approve with points
    Frontend->>Contract: approve_submission(creatorAddress, bountyId, points)
    Contract-->>Frontend: tx result / event (release escrow / COMPLETED)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • Benjtalkshow

Poem

🐰 I hopped in with a quill and tidy paws,
Proposals take shape with approach and cause,
Creators compare, then pick one to trust,
Contributors send CIDs without a fuss,
Contracts hum, coins flow — my carrot jar applause.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% 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 PR title 'Feat/bounty selection flow model 2' is specific and directly refers to the main feature being implemented, though slightly generic with the convention prefix.
Linked Issues check ✅ Passed The PR implements all core requirements from issue #146: structured application form with proposal fields, creator review dashboard with comparison, select_applicant contract call, work submission panel, and approval panel with reputation points.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the Application-Based Bounty Selection Flow (Model 2) as defined in issue #146; no out-of-scope modifications detected.

✏️ 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
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/bounty/application-dialog.tsx (1)

33-40: ⚠️ Potential issue | 🟠 Major

Harden portfolio URL validation to http/https only.

Current validation accepts any URL scheme; for user-generated links, restrict protocols before rendering anchor tags.

Also applies to: 158-165

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

In `@components/bounty/application-dialog.tsx` around lines 33 - 40, The
portfolioUrl Zod schema currently allows any URL scheme; update its refine
predicate (the portfolioUrl field in the z.string() schema) to only accept empty
string or URLs whose protocol is either "http:" or "https:"—for example by
parsing with the URL constructor and checking url.protocol === "http:" ||
url.protocol === "https:" or by explicitly validating
startsWith("http://")/startsWith("https://"); apply the same change to the
duplicate schema instance referenced around the 158-165 area so both validations
consistently restrict to http/https before rendering anchor tags.
🧹 Nitpick comments (1)
components/bounty-detail/bounty-detail-sidebar-cta.tsx (1)

95-104: Consider deduplicating apply mutation wiring between desktop/mobile.

useApplyToBounty + handleApply are repeated almost verbatim in both components. Extracting shared logic would reduce drift.

Also applies to: 426-435

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

In `@components/bounty-detail/bounty-detail-sidebar-cta.tsx` around lines 95 -
104, Duplicate wiring of the apply mutation occurs in both components: the
useApplyToBounty hook + handleApply logic (using symbols useApplyToBounty,
handleApply, ApplicationFormValues, applyToBounty, walletAddress, bounty.id).
Extract this into a shared helper hook (e.g., useBountyApply or useApplyHandler)
that calls useApplyToBounty internally and returns a memoized apply function (or
mutateAsync) which accepts ApplicationFormValues and handles the
walletAddress/bounty.id checks and JSON.stringify(proposal); replace the inline
handleApply implementations in both desktop and mobile components with the
shared hook to remove duplication and keep behavior identical.
🤖 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-detail/bounty-detail-client.tsx`:
- Around line 182-185: The current fallback sets walletAddress using
session.user.id which can pass non-wallet IDs into contract actions; update the
logic so walletAddress is strictly derived from an actual wallet field (e.g.,
(session?.user as { walletAddress?: string })?.walletAddress) and remove the
fallback to session?.user?.id, and then gate UI/actions that call contract
mutations (places using creatorAddress/contributorAddress and functions that use
walletAddress in bounty actions) to require a non-empty walletAddress before
enabling or invoking contract calls.
- Around line 266-273: The submit-work panel is currently shown to any
non-creator while a bounty is IN_PROGRESS; restrict it to the actual selected
applicant by adding a check that the viewer's walletAddress matches the bounty's
selected applicant address before rendering ApplicationSubmitWorkPanel. Update
the condition that now uses bounty.type, !isCreator and bounty.status to also
require walletAddress === bounty.selectedApplicantAddress (or the actual
property name that holds the selected contributor, e.g.,
bounty.selectedApplicant or bounty.selectedContributor) so only the selected
contributor sees the submit-work UI for the given bountyId.
- Around line 71-109: The code is still using the MOCK_APPLICATIONS constant for
creator review/approval flows; replace uses of MOCK_APPLICATIONS in
bounty-detail-client.tsx (including the creator review/approval code paths
referenced around lines 71-109, 259-263, 281-283) with real
application/submission data from the app's data layer: call the appropriate
loader or hook (e.g., a function like loadApplicationsForBounty(bountyId) or
useBountyApplications(bountyId)) to fetch on-chain/off-chain state, map that
response into the Application shape used by the UI, and remove or gate the
MOCK_APPLICATIONS fallback so production uses live data only. Ensure components
that reference MOCK_APPLICATIONS (creator review UI, approval handlers) accept
the fetched data and handle loading/error states.

In `@components/bounty-detail/bounty-detail-sidebar-cta.tsx`:
- Around line 222-236: The "Apply for Bounty" milestone branch currently allows
creators to open the applicant flow; update the conditional around
ApplicationDialog (and the similar block later) to prevent bounty creators from
applying by adding an explicit creator check (e.g., && !isCreator or
walletAddress !== bounty.creatorAddress). Locate the branch using bounty.type
=== "MILESTONE_BASED" && canAct and modify it to require that the current
walletAddress is not the bounty owner (or use an existing isCreator boolean)
before rendering ApplicationDialog/trigger with handleApply and walletAddress.

In `@components/bounty/application-review-dashboard.tsx`:
- Around line 147-153: The portfolioUrl is used directly in the anchor href
(app.proposal.portfolioUrl) so validate its protocol before rendering to avoid
unsafe schemes; update the JSX around the anchor in
application-review-dashboard.tsx to parse/check the URL (e.g., using URL
constructor or a simple protocol regex) and only render the <a> tag when the
protocol is http: or https:, otherwise render a safe fallback (plain text or
nothing). Ensure you reference app.proposal.portfolioUrl in the check and keep
target="_blank" rel="noreferrer" only for validated links.
- Around line 117-123: The Decline button currently renders without any click
handler; wire it to the application-decline flow by adding an onClick that calls
the decline mutation (e.g., use the existing declineApplication or similar
mutation hook) with the application's id and handle UI state (disable while
loading, show toast/error). Update the Button in
components/bounty/application-review-dashboard.tsx (the element with <Button
...><XCircle ... /> Decline</Button>) to invoke the mutation, pass the correct
application identifier from the component props/state, and ensure you either
refetch or update the application list cache/optimistically remove the
application on success and surface errors to the user.

In `@components/bounty/application-submit-work-panel.tsx`:
- Around line 27-40: The form captures a description in the description state
but never sends it — causing silent loss; update the handleSubmit flow so that
the mutate returned from useSubmitApplicationWork (submitWork) is called with
the description included (e.g., add description to the payload alongside
bountyId, contributorAddress, workCid), and ensure any backend/contract
submission path used by submitWork (or its implementation in
useSubmitApplicationWork) is updated to accept and persist the description so
notes are actually stored and displayed.

In `@components/bounty/submission-approval-panel.tsx`:
- Around line 49-54: The approveSubmission call uses the raw points value from
the form which can be 0 or out-of-range; update the handlers (e.g.,
handleApprove and the other similar handlers around lines 117-124 and 130-137)
to parse the points as a number, clamp it to the allowed range (min > 0 and max
as defined by the UI or constants), and replace the direct points variable with
this validated/clamped value before calling approveSubmission (or the equivalent
mutation). Ensure you also enforce integer conversion (Math.floor or equivalent)
and use the same clamped value in the mutation payload so no invalid points are
submitted.
- Around line 57-63: The handleRequestRevisions function is currently a no-op;
replace the console-only behavior by calling a request revision mutation hook
(e.g., requestRevision or useRequestRevisionMutation) to persist the status
"REVISION_REQUESTED" and feedback, update local UI state
(setIsRequestingRevisions(false), clear feedback) on success, and handle errors
(show toast/error state) on failure; locate and wire the same mutation in the
other panel instance referenced around the block at lines ~187-195 so both
places call the mutation and update the submission status and UI consistently.

In `@hooks/use-bounty-application.ts`:
- Around line 178-188: The optimistic update in the onMutate handler of
useSubmitApplicationWork (in use-bounty-application.ts) sets bounty.status to
"IN_REVIEW", but the UI in components/bounty-detail/bounty-detail-client.tsx
checks for "UNDER_REVIEW"; change the optimistic status to "UNDER_REVIEW" so the
optimistic state matches downstream UI checks (update the status string in the
qc.setQueryData call for bounty.status and verify any other places in
useSubmitApplicationWork that reference the same status).

---

Outside diff comments:
In `@components/bounty/application-dialog.tsx`:
- Around line 33-40: The portfolioUrl Zod schema currently allows any URL
scheme; update its refine predicate (the portfolioUrl field in the z.string()
schema) to only accept empty string or URLs whose protocol is either "http:" or
"https:"—for example by parsing with the URL constructor and checking
url.protocol === "http:" || url.protocol === "https:" or by explicitly
validating startsWith("http://")/startsWith("https://"); apply the same change
to the duplicate schema instance referenced around the 158-165 area so both
validations consistently restrict to http/https before rendering anchor tags.

---

Nitpick comments:
In `@components/bounty-detail/bounty-detail-sidebar-cta.tsx`:
- Around line 95-104: Duplicate wiring of the apply mutation occurs in both
components: the useApplyToBounty hook + handleApply logic (using symbols
useApplyToBounty, handleApply, ApplicationFormValues, applyToBounty,
walletAddress, bounty.id). Extract this into a shared helper hook (e.g.,
useBountyApply or useApplyHandler) that calls useApplyToBounty internally and
returns a memoized apply function (or mutateAsync) which accepts
ApplicationFormValues and handles the walletAddress/bounty.id checks and
JSON.stringify(proposal); replace the inline handleApply implementations in both
desktop and mobile components with the shared hook to remove duplication and
keep behavior identical.
🪄 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: 3d6addd9-8622-477e-8968-918df3d66609

📥 Commits

Reviewing files that changed from the base of the PR and between 06bf865 and 64b9f80.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (7)
  • components/bounty-detail/bounty-detail-client.tsx
  • components/bounty-detail/bounty-detail-sidebar-cta.tsx
  • components/bounty/application-dialog.tsx
  • components/bounty/application-review-dashboard.tsx
  • components/bounty/application-submit-work-panel.tsx
  • components/bounty/submission-approval-panel.tsx
  • hooks/use-bounty-application.ts

Comment thread components/bounty-detail/bounty-detail-client.tsx Outdated
Comment thread components/bounty-detail/bounty-detail-client.tsx Outdated
Comment thread components/bounty-detail/bounty-detail-client.tsx
Comment thread components/bounty-detail/bounty-detail-sidebar-cta.tsx Outdated
Comment on lines +117 to +123
<Button
size="sm"
variant="outline"
className="border-red-500/30 text-red-400 hover:bg-red-500/10"
>
<XCircle className="size-4 mr-1" /> Decline
</Button>
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

Decline action is non-functional.

The Decline button is rendered but has no onClick/mutation wiring, so creators cannot reject applications from this dashboard.

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

In `@components/bounty/application-review-dashboard.tsx` around lines 117 - 123,
The Decline button currently renders without any click handler; wire it to the
application-decline flow by adding an onClick that calls the decline mutation
(e.g., use the existing declineApplication or similar mutation hook) with the
application's id and handle UI state (disable while loading, show toast/error).
Update the Button in components/bounty/application-review-dashboard.tsx (the
element with <Button ...><XCircle ... /> Decline</Button>) to invoke the
mutation, pass the correct application identifier from the component
props/state, and ensure you either refetch or update the application list
cache/optimistically remove the application on success and surface errors to the
user.

Comment thread components/bounty/application-review-dashboard.tsx Outdated
Comment on lines +27 to +40
const [workCid, setWorkCid] = useState("");
const [description, setDescription] = useState("");

const { mutate: submitWork, isPending } = useSubmitApplicationWork();

const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!workCid.trim()) return;

submitWork({
bountyId,
contributorAddress,
workCid,
});
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

“Notes for the Creator” is currently discarded.

The component captures description, but submitWork(...) only sends workCid. This creates silent data loss and misleading UI.

Suggested direction
-  const [description, setDescription] = useState("");
+  // Remove notes field until a persistence path exists

or extend the submission payload path (contract/off-chain) so notes are actually persisted/displayed.

Also applies to: 79-90

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

In `@components/bounty/application-submit-work-panel.tsx` around lines 27 - 40,
The form captures a description in the description state but never sends it —
causing silent loss; update the handleSubmit flow so that the mutate returned
from useSubmitApplicationWork (submitWork) is called with the description
included (e.g., add description to the payload alongside bountyId,
contributorAddress, workCid), and ensure any backend/contract submission path
used by submitWork (or its implementation in useSubmitApplicationWork) is
updated to accept and persist the description so notes are actually stored and
displayed.

Comment thread components/bounty/submission-approval-panel.tsx
Comment on lines +57 to +63
const handleRequestRevisions = () => {
// In a full implementation, this would trigger a different mutation
// that sets the status to "REVISION_REQUESTED" and records the feedback
console.log("Requesting revisions with feedback:", feedback);
setIsRequestingRevisions(false);
setFeedback("");
};
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

Revision request flow is still a no-op.

handleRequestRevisions only logs feedback and resets local state. This does not transition status or persist feedback, so the revision workflow is not actually implemented.

I can draft a request_revision mutation hook + panel wiring if you want me to open a follow-up issue template.

Also applies to: 187-195

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

In `@components/bounty/submission-approval-panel.tsx` around lines 57 - 63, The
handleRequestRevisions function is currently a no-op; replace the console-only
behavior by calling a request revision mutation hook (e.g., requestRevision or
useRequestRevisionMutation) to persist the status "REVISION_REQUESTED" and
feedback, update local UI state (setIsRequestingRevisions(false), clear
feedback) on success, and handle errors (show toast/error state) on failure;
locate and wire the same mutation in the other panel instance referenced around
the block at lines ~187-195 so both places call the mutation and update the
submission status and UI consistently.

Comment thread hooks/use-bounty-application.ts
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.

Solid architecture overall. The contract hooks mirror the use-competition-bounty.ts pattern, the application dialog uses FormFieldWrapper and the preview step is a nice touch, and the optimistic updates with rollback are clean. CI green and typecheck clean.

A few items before merge.

The review dashboard is fed by a hardcoded MOCK_APPLICATIONS array at bounty-detail-client.tsx:72-109. So a creator opening any MILESTONE_BASED bounty in OPEN will always see the same two fake Alice/Bob entries no matter what. Same anti-pattern PRs #178 and #186 had to remove. Either source the list from bounty.submissions (or whichever GraphQL field will hold applications once the contract events sync), or render an empty state until that's wired. Don't ship hardcoded fake data into the production UI.

Same goes for the approval panel: submittedWorkCid="QmHash123MockedWorkCid" and submissionDescription="I have completed the feature..." are passed as literal props at bounty-detail-client.tsx:281-282. Pull these from the real submission record or block the panel from rendering until they exist.

Decline button at application-review-dashboard.tsx:117-123 has no onClick. Either wire it to a real mutation or label it [Coming soon] and disable it.

Request Revisions at submission-approval-panel.tsx:57-63 only does console.log and resets local state. The // In a full implementation... comment confirms it's a stub. Same pattern, please disable with [Coming soon] or hide until the mutation exists.

The optimistic update on submit_work writes status "IN_REVIEW" at use-bounty-application.ts:186, but the repo's BountyStatus enum uses "UNDER_REVIEW". The optimistic state will diverge from what the backend returns. Use UNDER_REVIEW.

Minor: PR title formatting Feat/bounty selection flow model 2 — please clean it up. And the points field at submission-approval-panel.tsx defaults to 5 with no tier guidance, leaving the creator guessing what's typical. A small hint or a tier-based default would help.

Please address all CodeRabbit findings as well.

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

♻️ Duplicate comments (1)
components/bounty-detail/bounty-detail-client.tsx (1)

155-161: ⚠️ Potential issue | 🟠 Major

isAssignedApplicant logic is unreliable and overly permissive.

The predicate has multiple issues:

  1. assignedContributorId does not exist on the Bounty type (see types/bounty.ts:86-119), so the type assertion silently fails.
  2. The final fallback (!isCreator && bounty.status === "IN_PROGRESS") allows any non-creator to pass when the bounty is in progress, not just the selected contributor.

This means the ApplicationSubmitWorkPanel at lines 242-250 may render for users who aren't the assigned applicant.

🛠️ Suggested approach

Add assignedContributorId (or similar) to the Bounty type and ensure the backend populates it. Until then, consider a stricter fallback:

- const isAssignedApplicant =
-   (bounty as BountyData & { assignedContributorId?: string })
-     ?.assignedContributorId === session?.user?.id ||
-   bounty.submissions?.some((s) => s.submittedBy === session?.user?.id) ||
-   (!isCreator && bounty.status === "IN_PROGRESS");
+ // Require explicit assignment data; fallback to submissions check only
+ const assignedContributorId = (bounty as BountyData & { assignedContributorId?: string })
+   ?.assignedContributorId;
+ const isAssignedApplicant =
+   (assignedContributorId && assignedContributorId === session?.user?.id) ||
+   bounty.submissions?.some((s) => s.submittedBy === session?.user?.id);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/bounty-detail/bounty-detail-client.tsx` around lines 155 - 161,
The isAssignedApplicant predicate is too permissive and uses a silent type
assertion for a non-existent assignedContributorId on Bounty; update the Bounty
type to include assignedContributorId (or a backend-populated equivalent) and
ensure the backend returns it, then change the isAssignedApplicant logic (used
to gate ApplicationSubmitWorkPanel and related UI) to: 1) avoid casting (remove
the (bounty as BountyData & { assignedContributorId?: string }) assertion), 2)
check assignedContributorId strictly against session.user.id if present, and 3)
remove the broad fallback that treats any non-creator as assigned when
bounty.status === "IN_PROGRESS" (or replace it with a conservative check such as
membership in bounty.submissions or an explicit contributor list).
🧹 Nitpick comments (1)
components/bounty/application-review-dashboard.tsx (1)

51-57: Add user feedback for applicant selection outcome.

handleSelectApplicant fires the mutation but provides no success/error feedback to the user. For a critical action like selecting a contributor, users should see confirmation (toast/alert) on success or an error message on failure.

💡 Example with toast feedback
+ import { toast } from "@/components/ui/use-toast"; // or your toast library

  const handleSelectApplicant = (applicantAddress: string) => {
    selectApplicant(
      {
        bountyId,
        creatorAddress,
        applicantAddress,
      },
+     {
+       onSuccess: () => {
+         toast({ title: "Applicant selected", description: "The bounty is now in progress." });
+       },
+       onError: (error) => {
+         toast({ title: "Selection failed", description: error.message, variant: "destructive" });
+       },
+     }
    );
  };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/bounty/application-review-dashboard.tsx` around lines 51 - 57,
handleSelectApplicant currently fires the selectApplicant mutation without user
feedback; update it to await the mutation call (selectApplicant) and show a
success toast/alert on resolved promise (including context like applicantAddress
and bountyId) and show an error toast/alert when the mutation rejects
(displaying the error message). Use any existing UI toast/notification helper in
the component (or add one) and ensure you handle loading state (disable UI while
pending) so users get clear success/error feedback for the selectApplicant({
bountyId, creatorAddress, applicantAddress }) action.
🤖 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-detail/bounty-detail-client.tsx`:
- Around line 231-240: The creator-only panels render even when walletAddress is
empty; update the gating conditions for ApplicationReviewDashboard and
SubmissionApprovalPanel to require a non-empty walletAddress (e.g., add
walletAddress && to the existing conditional that checks bounty.type, isCreator,
and bounty.status) so they only render when a connected wallet exists, and
continue passing walletAddress as creatorAddress into those components
(ApplicationReviewDashboard, SubmissionApprovalPanel).
- Around line 258-260: The prop submittedWorkCid is receiving
githubPullRequestUrl (a URL) causing an IPFS CID/type mismatch; add a new field
workCid to the BountySubmission type in types/bounty.ts, populate it wherever
BountySubmission instances are created/returned, and update the component usage
in bounty-detail-client.tsx to pass bounty.submissions?.[0]?.workCid (not
githubPullRequestUrl) into SubmissionApprovalPanel's submittedWorkCid prop; also
audit any consumers (e.g., SubmissionApprovalPanel prop types/usage) and
submission creation code to ensure workCid is typed/filled and
githubPullRequestUrl remains unchanged.

---

Duplicate comments:
In `@components/bounty-detail/bounty-detail-client.tsx`:
- Around line 155-161: The isAssignedApplicant predicate is too permissive and
uses a silent type assertion for a non-existent assignedContributorId on Bounty;
update the Bounty type to include assignedContributorId (or a backend-populated
equivalent) and ensure the backend returns it, then change the
isAssignedApplicant logic (used to gate ApplicationSubmitWorkPanel and related
UI) to: 1) avoid casting (remove the (bounty as BountyData & {
assignedContributorId?: string }) assertion), 2) check assignedContributorId
strictly against session.user.id if present, and 3) remove the broad fallback
that treats any non-creator as assigned when bounty.status === "IN_PROGRESS" (or
replace it with a conservative check such as membership in bounty.submissions or
an explicit contributor list).

---

Nitpick comments:
In `@components/bounty/application-review-dashboard.tsx`:
- Around line 51-57: handleSelectApplicant currently fires the selectApplicant
mutation without user feedback; update it to await the mutation call
(selectApplicant) and show a success toast/alert on resolved promise (including
context like applicantAddress and bountyId) and show an error toast/alert when
the mutation rejects (displaying the error message). Use any existing UI
toast/notification helper in the component (or add one) and ensure you handle
loading state (disable UI while pending) so users get clear success/error
feedback for the selectApplicant({ bountyId, creatorAddress, applicantAddress })
action.
🪄 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: 2753e918-2a68-4706-9bbc-41346ebcfffc

📥 Commits

Reviewing files that changed from the base of the PR and between 64b9f80 and d061724.

📒 Files selected for processing (7)
  • components/bounty-detail/bounty-detail-client.tsx
  • components/bounty-detail/bounty-detail-sidebar-cta.tsx
  • components/bounty/application-dialog.tsx
  • components/bounty/application-review-dashboard.tsx
  • components/bounty/application-submit-work-panel.tsx
  • components/bounty/submission-approval-panel.tsx
  • hooks/use-bounty-application.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • components/bounty/application-submit-work-panel.tsx
  • components/bounty-detail/bounty-detail-sidebar-cta.tsx
  • components/bounty/submission-approval-panel.tsx
  • hooks/use-bounty-application.ts

Comment on lines +231 to +240
{/* Model 2 Application Flow integration */}
{bounty.type === "MILESTONE_BASED" &&
isCreator &&
bounty.status === "OPEN" && (
<ApplicationReviewDashboard
bountyId={bountyId}
creatorAddress={walletAddress}
applications={getApplications(bounty)}
/>
)}
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

Gate creator panels on non-empty walletAddress to prevent invalid contract calls.

ApplicationReviewDashboard (line 237) and SubmissionApprovalPanel (line 257) receive walletAddress as creatorAddress without verifying it's non-empty. If the user's wallet isn't connected, these panels will render but contract mutations will fail or behave unexpectedly.

🔧 Proposed fix
 {bounty.type === "MILESTONE_BASED" &&
   isCreator &&
+  walletAddress &&
   bounty.status === "OPEN" && (
     <ApplicationReviewDashboard
       ...
     />
   )}

 {bounty.type === "MILESTONE_BASED" &&
   isCreator &&
+  walletAddress &&
   bounty.status === "UNDER_REVIEW" && (
     <SubmissionApprovalPanel
       ...
     />
   )}

Also applies to: 252-262

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

In `@components/bounty-detail/bounty-detail-client.tsx` around lines 231 - 240,
The creator-only panels render even when walletAddress is empty; update the
gating conditions for ApplicationReviewDashboard and SubmissionApprovalPanel to
require a non-empty walletAddress (e.g., add walletAddress && to the existing
conditional that checks bounty.type, isCreator, and bounty.status) so they only
render when a connected wallet exists, and continue passing walletAddress as
creatorAddress into those components (ApplicationReviewDashboard,
SubmissionApprovalPanel).

Comment on lines +258 to +260
submittedWorkCid={
bounty.submissions?.[0]?.githubPullRequestUrl || undefined
}
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if BountySubmission type has a dedicated CID field
rg -n 'workCid|ipfsCid|deliverableCid' --type=ts types/

Repository: boundlessfi/bounties

Length of output: 46


🏁 Script executed:

# Find and examine the BountySubmission type definition
fd -t f 'bounty.ts' types/

Repository: boundlessfi/bounties

Length of output: 80


🏁 Script executed:

# Check the context around the submittedWorkCid prop in the component
rg -B5 -A5 'submittedWorkCid=' components/bounty-detail/bounty-detail-client.tsx

Repository: boundlessfi/bounties

Length of output: 400


🏁 Script executed:

# Search for BountySubmission type definition and related fields
rg -n 'type BountySubmission|interface BountySubmission' --type=ts

Repository: boundlessfi/bounties

Length of output: 310


🏁 Script executed:

# Search for githubPullRequestUrl usage to understand the field's purpose
rg -n 'githubPullRequestUrl' --type=ts -B2 -A2 | head -50

Repository: boundlessfi/bounties

Length of output: 2507


🏁 Script executed:

# Read the full BountySubmission interface
sed -n '51,65p' types/bounty.ts

Repository: boundlessfi/bounties

Length of output: 509


🏁 Script executed:

# Find and examine the SubmissionApprovalPanel component
fd -t f 'SubmissionApprovalPanel' components/

Repository: boundlessfi/bounties

Length of output: 46


🏁 Script executed:

# Search for IPFS or CID references in the codebase
rg -n 'ipfs|cid|CID' --type=ts components/ -i | head -30

Repository: boundlessfi/bounties

Length of output: 2654


🏁 Script executed:

# Search for SubmissionApprovalPanel component definition
rg -n 'SubmissionApprovalPanel' --type=ts -A 20 | head -50

Repository: boundlessfi/bounties

Length of output: 4534


🏁 Script executed:

# Search for submittedWorkCid prop usage throughout codebase
rg -n 'submittedWorkCid' --type=ts -B 2 -A 2

Repository: boundlessfi/bounties

Length of output: 1841


🏁 Script executed:

# Look for where githubPullRequestUrl is actually used/set to understand field purpose
rg -n 'githubPullRequestUrl' --type=ts -B 3 -A 3 | head -100

Repository: boundlessfi/bounties

Length of output: 5494


submittedWorkCid receives githubPullRequestUrl — incompatible types.

The SubmissionApprovalPanel expects submittedWorkCid to be an IPFS CID (it constructs https://ipfs.io/ipfs/${submittedWorkCid}), but the code passes bounty.submissions?.[0]?.githubPullRequestUrl, which is a GitHub PR URL. This mismatch breaks the IPFS link rendering.

Add a dedicated workCid field to BountySubmission in types/bounty.ts to store the actual IPFS CID separately from githubPullRequestUrl.

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

In `@components/bounty-detail/bounty-detail-client.tsx` around lines 258 - 260,
The prop submittedWorkCid is receiving githubPullRequestUrl (a URL) causing an
IPFS CID/type mismatch; add a new field workCid to the BountySubmission type in
types/bounty.ts, populate it wherever BountySubmission instances are
created/returned, and update the component usage in bounty-detail-client.tsx to
pass bounty.submissions?.[0]?.workCid (not githubPullRequestUrl) into
SubmissionApprovalPanel's submittedWorkCid prop; also audit any consumers (e.g.,
SubmissionApprovalPanel prop types/usage) and submission creation code to ensure
workCid is typed/filled and githubPullRequestUrl remains unchanged.

@legend4tech
Copy link
Copy Markdown
Contributor Author

@Benjtalkshow can u review again? made a new commit

@Benjtalkshow
Copy link
Copy Markdown
Contributor

@Benjtalkshow can u review again? made a new commit

On it

Leftover from before Request Revisions was replaced with the disabled
Coming Soon button.
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.

All five items from the previous round are clean. Mock data gone, hardcoded submission gone, Decline removed, Request Revisions disabled with Coming Soon, status enum fixed. The bonus fixes are nice too.

Pushed a small cleanup (69e6e82) on your branch removing the unused feedback / isRequestingRevisions state in submission-approval-panel.tsx left over from the rewrite. Lint is clean now.

Merging this in.

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 Application-Based Bounty Selection Flow (Model 2)

2 participants