diff --git a/components/bounty/bounty-sidebar.tsx b/components/bounty/bounty-sidebar.tsx index 1808aeb..0311cb8 100644 --- a/components/bounty/bounty-sidebar.tsx +++ b/components/bounty/bounty-sidebar.tsx @@ -1,6 +1,7 @@ "use client" import { useMemo, useState } from "react" +import { RatingModal } from "../rating/rating-modal" import { Button } from "@/components/ui/button" import { Separator } from "@/components/ui/separator" import type { Bounty } from "@/types/bounty" @@ -20,8 +21,22 @@ export function BountySidebar({ bounty }: BountySidebarProps) { const [loading, setLoading] = useState(false) // const router = useRouter() - // Mock user ID for now - in real app this comes from auth context - const CURRENT_USER_ID = "mock-user-123" + // Mock user ID and maintainer check for now - in real app this comes from auth context + // DEV-MOCK: Allow maintainer to test rating flow locally. + // WARNING: This is a client-side dev-only bypass and MUST NOT be enabled in production. + // TODO: Replace with real auth context and enforce authorization server-side. + + // Opt-in via environment (local/.env.local): + // NEXT_PUBLIC_MOCK_MAINTAINER=true + // NEXT_PUBLIC_MOCK_USER_ID=mock-user-123 + const CURRENT_USER_ID = process.env.NEXT_PUBLIC_MOCK_USER_ID ?? "mock-user-123" + const IS_MAINTAINER = process.env.NEXT_PUBLIC_MOCK_MAINTAINER === "true" + + if (typeof window !== "undefined" && process.env.NEXT_PUBLIC_MOCK_MAINTAINER === "true") { + console.warn( + "DEV: Mock maintainer enabled in components/bounty/bounty-sidebar.tsx — do NOT enable in production" + ) + } // const isClaimable = bounty.status === "open" @@ -75,7 +90,63 @@ export function BountySidebar({ bounty }: BountySidebarProps) { } } + // Rating modal state + const [showRating, setShowRating] = useState(false) + const [completed, setCompleted] = useState(false) + const [lastRating, setLastRating] = useState(null) + const [reputationGain, setReputationGain] = useState(null) + const [hasRated, setHasRated] = useState(false) + + const handleMarkCompleted = async () => { + if (!IS_MAINTAINER) { + alert('Only maintainers can mark as completed.'); + return; + } + setLoading(true) + // Simulate completion API call + setTimeout(() => { + setLoading(false) + setCompleted(true) + setShowRating(true) + }, 1000) + } + + const handleSubmitRating = async (rating: number, feedback: string) => { + if (hasRated) { + alert('You have already rated this contributor.'); + return; + } + if (!IS_MAINTAINER) { + alert('Only maintainers can rate contributors.'); + return; + } + if (!completed) { + alert('Bounty must be marked as completed before rating.'); + return; + } + // Simulate API call to reputation endpoint and calculate points + await new Promise((res) => setTimeout(res, 1000)) + // Use feedback variable to avoid unused variable lint warnings + void feedback + setLastRating(rating) + setReputationGain(rating * 10) + setHasRated(true) + setShowRating(false) + // Notify contributor (mock) + toast.success(`You have been rated ${rating} star${rating > 1 ? 's' : ''} and gained +${rating * 10} reputation!`, { + description: 'Congratulations on your contribution!' + }) + } + const renderActionButton = () => { + if (bounty.status === 'claimed' && IS_MAINTAINER && !completed) { + return ( + + ) + } if (bounty.status !== 'open') { const labels: Record = { claimed: 'Already Claimed', @@ -155,6 +226,23 @@ export function BountySidebar({ bounty }: BountySidebarProps) { return (
+ {/* Sidebar UI */} + {showRating && !hasRated && ( + setShowRating(false)} + /> + )} + + {/* Show rating and reputation gain after rating, visible to all users if available */} + {lastRating && reputationGain && ( +
+
{IS_MAINTAINER ? 'You rated the contributor:' : 'Contributor was rated:'} {lastRating} / 5 stars
+
Reputation gained: +{reputationGain}
+
+ )} +
+ ); + } + + return ( +
+

Rate Contributor

+
+ Bounty: {bounty.title} +
+
+ Contributor: {contributor.name} +
+
+ Current Reputation: {contributor.reputation} +
+
+ +
+