"use client" import { useState, useRef, useEffect } from "react" import { Star } from "lucide-react" import { cn } from "@/lib/utils" interface RatingInputProps { value: number onChange?: (value: number) => void readonly?: boolean className?: string size?: "sm" | "md" | "lg" showSlider?: boolean } export function RatingInput({ value, onChange, readonly = false, className, size = "md", showSlider = true }: RatingInputProps) { const [localValue, setLocalValue] = useState(value) const [isEditing, setIsEditing] = useState(false) const inputRef = useRef(null) useEffect(() => { setLocalValue(value) }, [value]) const handleSliderChange = (e: React.ChangeEvent) => { const newValue = parseFloat(e.target.value) setLocalValue(newValue) onChange?.(newValue) } const handleInputChange = (e: React.ChangeEvent) => { const raw = e.target.value if (raw === "") { setLocalValue(0) return } const newValue = parseFloat(raw) if (!isNaN(newValue)) { const clamped = Math.min(10, Math.max(1, newValue)) const rounded = Math.round(clamped * 10) / 10 setLocalValue(rounded) } } const handleInputBlur = () => { setIsEditing(false) if (localValue > 0) { onChange?.(localValue) } } const handleInputKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Enter") { inputRef.current?.blur() } } // Visual star representation (readonly display) const renderStars = () => { const stars = [] const fullStars = Math.floor(localValue) const partialFill = (localValue - fullStars) * 100 for (let i = 0; i < 10; i++) { const isFull = i < fullStars const isPartial = i === fullStars && partialFill > 0 stars.push(
{(isFull || isPartial) && (
)}
) } return stars } if (readonly) { return (
{renderStars()}
{localValue > 0 ? localValue.toFixed(1) : "—"}
) } return (
{/* Stars Display + Numeric Input */}
{renderStars()}
{/* Numeric Input */}
setIsEditing(true)} onBlur={handleInputBlur} onKeyDown={handleInputKeyDown} className={cn( "w-14 h-8 px-2 text-center font-mono font-bold rounded-md", "border bg-background text-foreground", "focus:outline-none focus:ring-2 focus:ring-primary", size === "lg" ? "text-lg" : "text-sm" )} /> /10
{/* Slider */} {showSlider && (
1 10
)}
) } // Compact version for inline use export function RatingBadge({ value, className }: { value: number; className?: string }) { const getColor = () => { if (value >= 8) return "bg-green-500/10 text-green-600 border-green-500/20" if (value >= 6) return "bg-yellow-500/10 text-yellow-600 border-yellow-500/20" return "bg-red-500/10 text-red-600 border-red-500/20" } return ( {value.toFixed(1)} ) }