diff --git a/components/bounty-detail/bounty-detail-submissions-card.tsx b/components/bounty-detail/bounty-detail-submissions-card.tsx index c9270a90..a2069f52 100644 --- a/components/bounty-detail/bounty-detail-submissions-card.tsx +++ b/components/bounty-detail/bounty-detail-submissions-card.tsx @@ -1,411 +1,4 @@ -"use client"; - -import { Button } from "@/components/ui/button"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { TransactionLink } from "@/components/ui/stellar-link"; -import { Textarea } from "@/components/ui/textarea"; -import { useSubmissionDraft } from "@/hooks/use-submission-draft"; -import { - useMarkSubmissionPaid, - useReviewSubmission, - useSubmitToBounty, -} from "@/hooks/use-submission-mutations"; -import { authClient } from "@/lib/auth-client"; -import { BountySubmissionType } from "@/lib/graphql/generated"; -import { DollarSign, Loader2 } from "lucide-react"; -import { useEffect, useState } from "react"; - -interface ExtendedUser { - id: string; - name?: string | null; - email?: string | null; - image?: string | null; - organizations?: string[]; -} - -interface BountyDetailSubmissionsCardProps { - bounty: { - id: string; - status: string; - organizationId: string; - submissions?: Array | null; - }; -} - -export function BountyDetailSubmissionsCard({ - bounty, -}: BountyDetailSubmissionsCardProps) { - const { data: session } = authClient.useSession(); - const submissions = bounty.submissions || []; - const { draft, clearDraft, autoSave } = useSubmissionDraft(bounty.id); - - const [submitDialogOpen, setSubmitDialogOpen] = useState(false); - const [reviewDialogOpen, setReviewDialogOpen] = useState(false); - const [markPaidDialogOpen, setMarkPaidDialogOpen] = useState(false); - - const [selectedSubmission, setSelectedSubmission] = - useState(null); - const [selectedPaidSubmission, setSelectedPaidSubmission] = - useState(null); - - const [prUrl, setPrUrl] = useState(""); - const [submitComments, setSubmitComments] = useState(""); - const [reviewComments, setReviewComments] = useState(""); - const reviewStatus = "APPROVED"; - const [transactionHash, setTransactionHash] = useState(""); - - const submitToBounty = useSubmitToBounty(); - const reviewSubmission = useReviewSubmission(); - const markSubmissionPaid = useMarkSubmissionPaid(); - - const [isHydrated, setIsHydrated] = useState(false); - - // Load draft on mount - useEffect(() => { - if (draft?.formData) { - setPrUrl(draft.formData.githubPullRequestUrl); - setSubmitComments(draft.formData.comments); - } - setIsHydrated(true); - }, [draft]); - - // Auto-save on form changes - useEffect(() => { - if (!isHydrated) return; - const cleanup = autoSave({ - githubPullRequestUrl: prUrl, - comments: submitComments, - }); - return cleanup; - }, [prUrl, submitComments, autoSave, isHydrated]); - - const isOrgMember = - (session?.user as ExtendedUser)?.organizations?.includes( - bounty.organizationId, - ) ?? false; - - const handleSubmitPR = async () => { - if (!prUrl.trim()) return; - try { - await submitToBounty.mutateAsync({ - bountyId: bounty.id, - githubPullRequestUrl: prUrl, - comments: submitComments.trim() || undefined, - }); - clearDraft(); - } catch (err) { - // Replace with toast or error UI as needed - console.error("Submit PR failed:", err); - } finally { - setPrUrl(""); - setSubmitComments(""); - setSubmitDialogOpen(false); - } - }; - - const handleReviewSubmission = async () => { - if (!selectedSubmission) return; - try { - await reviewSubmission.mutateAsync({ - submissionId: selectedSubmission.id, - status: reviewStatus, - reviewComments: reviewComments.trim() || undefined, - }); - } catch (err) { - // Replace with toast or error UI as needed - console.error("Review submission failed:", err); - } finally { - setReviewDialogOpen(false); - setSelectedSubmission(null); - setReviewComments(""); - } - }; - - const handleMarkPaid = async (submission: BountySubmissionType) => { - if (!transactionHash.trim()) return; - try { - await markSubmissionPaid.mutateAsync({ - submissionId: submission.id, - transactionHash: transactionHash.trim(), - }); - } catch (err) { - // Replace with toast or error UI as needed - console.error("Mark paid failed:", err); - } finally { - setTransactionHash(""); - setSelectedPaidSubmission(null); - setMarkPaidDialogOpen(false); - } - }; - - const getStatusBadgeColor = (status: string) => { - switch (status) { - case "APPROVED": - return "bg-emerald-100 text-emerald-900"; - case "REJECTED": - return "bg-red-100 text-red-900"; - case "PENDING": - default: - return "bg-gray-100 text-gray-900"; - } - }; - - const isSafeHttpUrl = (url: string) => { - try { - const parsed = new URL(url); - return parsed.protocol === "http:" || parsed.protocol === "https:"; - } catch { - return false; - } - }; - - return ( -
- {/* Submit PR Section */} - {bounty.status === "OPEN" && ( -
-

- Submit Your PR -

- - - - - - - - Submit Pull Request - - Submit your GitHub pull request URL. - {draft && ( - - Draft restored from{" "} - {new Date(draft.updatedAt).toLocaleString()} - - )} - - - -
-
- - setPrUrl(e.target.value)} - /> -
- -
- -