-
Notifications
You must be signed in to change notification settings - Fork 98
Feat/bounty selection flow model 2 #200
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,6 +29,12 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; | |
| import { MilestoneSubmissionCard } from "./milestone-submission-card"; | ||
| import { Model4MaintainerDashboard } from "./model4-maintainer-dashboard"; | ||
| import type { Milestone, ContributorProgress } from "@/types/bounty"; | ||
| import { | ||
| ApplicationReviewDashboard, | ||
| type Application, | ||
| } from "@/components/bounty/application-review-dashboard"; | ||
| import { SubmissionApprovalPanel } from "@/components/bounty/submission-approval-panel"; | ||
| import { ApplicationSubmitWorkPanel } from "@/components/bounty/application-submit-work-panel"; | ||
|
|
||
| type BountyData = ReturnType<typeof useBountyDetail>["data"]; | ||
|
|
||
|
|
@@ -62,6 +68,15 @@ function getFullMilestoneData(bounty: BountyData): { | |
| }; | ||
| } | ||
|
|
||
| // Backend does not currently provide applications in the response. | ||
| // Fall back to empty array until the schema supports it. | ||
| const getApplications = (bounty: BountyData): Application[] => { | ||
| return ( | ||
| (bounty as BountyData & { applications?: Application[] })?.applications ?? | ||
| [] | ||
| ); | ||
| }; | ||
|
|
||
| export function BountyDetailClient({ bountyId }: { bountyId: string }) { | ||
| const router = useRouter(); | ||
| const { data: bounty, isPending, isError, error } = useBountyDetail(bountyId); | ||
|
|
@@ -133,6 +148,18 @@ export function BountyDetailClient({ bountyId }: { bountyId: string }) { | |
| const isCreator = | ||
| (session?.user as { id?: string } | undefined)?.id === bounty.createdBy; | ||
| const isFinalized = bounty.status === "COMPLETED"; | ||
| // walletAddress is required for contract actions. Do NOT fallback to user.id. | ||
| const walletAddress = | ||
| (session?.user as { walletAddress?: string })?.walletAddress || ""; | ||
|
|
||
| // Identify if the current user is the assigned contributor | ||
| // using a fallback check on submissions or assumed backend field. | ||
| 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"); | ||
|
|
||
| // submissions is present on BountyQuery (single-bounty query) but not on | ||
| // BountyFieldsFragment (list query). The cast is safe here because | ||
| // useBountyDetail returns BountyFieldsFragment & Partial<BountyQuery["bounty"]>. | ||
|
|
@@ -200,9 +227,43 @@ export function BountyDetailClient({ bountyId }: { bountyId: string }) { | |
|
|
||
| {!isCancelled && pool && <EscrowDetailPanel poolId={bountyId} />} | ||
| <RefundStatusTracker bountyId={bountyId} isCancelled={isCancelled} /> | ||
| {bounty.type !== "FIXED_PRICE" && !isCompetition && ( | ||
| <BountyDetailSubmissionsCard bounty={bounty} /> | ||
| )} | ||
|
|
||
| {/* Model 2 Application Flow integration */} | ||
| {bounty.type === "MILESTONE_BASED" && | ||
| isCreator && | ||
| bounty.status === "OPEN" && ( | ||
| <ApplicationReviewDashboard | ||
| bountyId={bountyId} | ||
| creatorAddress={walletAddress} | ||
| applications={getApplications(bounty)} | ||
| /> | ||
| )} | ||
|
|
||
| {bounty.type === "MILESTONE_BASED" && | ||
| isAssignedApplicant && | ||
| walletAddress && | ||
| bounty.status === "IN_PROGRESS" && ( | ||
| <ApplicationSubmitWorkPanel | ||
| bountyId={bountyId} | ||
| contributorAddress={walletAddress} | ||
| /> | ||
| )} | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| {bounty.type === "MILESTONE_BASED" && | ||
| isCreator && | ||
| bounty.status === "UNDER_REVIEW" && ( | ||
| <SubmissionApprovalPanel | ||
| bounty={bounty} | ||
| creatorAddress={walletAddress} | ||
| submittedWorkCid={ | ||
| bounty.submissions?.[0]?.githubPullRequestUrl || undefined | ||
| } | ||
|
Comment on lines
+258
to
+260
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 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.tsxRepository: boundlessfi/bounties Length of output: 400 🏁 Script executed: # Search for BountySubmission type definition and related fields
rg -n 'type BountySubmission|interface BountySubmission' --type=tsRepository: 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 -50Repository: boundlessfi/bounties Length of output: 2507 🏁 Script executed: # Read the full BountySubmission interface
sed -n '51,65p' types/bounty.tsRepository: 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 -30Repository: boundlessfi/bounties Length of output: 2654 🏁 Script executed: # Search for SubmissionApprovalPanel component definition
rg -n 'SubmissionApprovalPanel' --type=ts -A 20 | head -50Repository: boundlessfi/bounties Length of output: 4534 🏁 Script executed: # Search for submittedWorkCid prop usage throughout codebase
rg -n 'submittedWorkCid' --type=ts -B 2 -A 2Repository: 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 -100Repository: boundlessfi/bounties Length of output: 5494
The Add a dedicated 🤖 Prompt for AI Agents |
||
| /> | ||
| )} | ||
|
|
||
| {bounty.type !== "FIXED_PRICE" && | ||
| bounty.type !== "MILESTONE_BASED" && | ||
| !isCompetition && <BountyDetailSubmissionsCard bounty={bounty} />} | ||
| {bounty.type === "FIXED_PRICE" && <FcfsApprovalPanel bounty={bounty} />} | ||
| {isCompetition && isCreator && (pastDeadline || isFinalized) && ( | ||
| <CompetitionJudging | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gate creator panels on non-empty
walletAddressto prevent invalid contract calls.ApplicationReviewDashboard(line 237) andSubmissionApprovalPanel(line 257) receivewalletAddressascreatorAddresswithout 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