From 09e49c6d30d3226acc57f671a3ff4ab894590e18 Mon Sep 17 00:00:00 2001 From: josephchimebuka Date: Fri, 30 Jan 2026 13:39:01 +0000 Subject: [PATCH 1/8] git rm pnpm-lock.yaml && git rebase --continue git rm pnpm-lock.yaml ls -la pnpm-lock.yaml || true git status --porcelain -b git rm pnpm-lock.yaml || true && git add -A && git rebase --continue || true git rm pnpm-lock.yaml git update-index --remove pnpm-lock.yaml && git add -A && git status --porcelain -b feat(rating): add RatingStars, RatingModal; integrate maintainer rating flow; remove debug banner --- components/bounty/bounty-sidebar.tsx | 75 +++++++++++++++++++++++++- components/rating/rating-modal.tsx | 81 ++++++++++++++++++++++++++++ components/rating/rating-stars.tsx | 49 +++++++++++++++++ lib/mock-bounty.ts | 4 +- 4 files changed, 206 insertions(+), 3 deletions(-) create mode 100644 components/rating/rating-modal.tsx create mode 100644 components/rating/rating-stars.tsx diff --git a/components/bounty/bounty-sidebar.tsx b/components/bounty/bounty-sidebar.tsx index 1808aeb..f13d0db 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,9 @@ 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 + // Mock user ID and maintainer check for now - in real app this comes from auth context const CURRENT_USER_ID = "mock-user-123" + const IS_MAINTAINER = true // TODO: Replace with real maintainer check // const isClaimable = bounty.status === "open" @@ -75,7 +77,61 @@ 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)) + 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 +211,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} +
+
+ +
+