admin/components/reviews/StarRating.tsx
alisaza e1eaf5eff5 feat: initial ghabilee-admin backoffice app
Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js
app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
2026-09-05 13:12:59 +03:30

70 lines
1.8 KiB
TypeScript

'use client'
import Button from '@/components/formElements/Button'
import HeatIcon from '@/components/icons/HeatIcon'
import HeatIconFill from '@/components/icons/HeatIconFill'
import { cn } from '@/lib/cn'
import { texts, format } from '@/texts'
interface StarRatingProps {
value: number
onChange?: (value: number) => void
size?: 'sm' | 'md'
className?: string
/** Read-only display when onChange is omitted. */
label?: string
}
const ICON_SIZE = {
sm: 'size-3.5',
md: 'size-5',
} as const
const StarRating = ({ value, onChange, size = 'md', className, label }: StarRatingProps) => {
const interactive = typeof onChange === 'function'
const iconClass = ICON_SIZE[size]
return (
<div
aria-label={label ?? format(texts.reviews.ratingValueAria, { value })}
className={cn('flex items-center gap-1', className)}
role={interactive ? 'radiogroup' : 'img'}
>
{[1, 2, 3, 4, 5].map((star) => {
const filled = star <= value
const Icon = filled ? HeatIconFill : HeatIcon
const colorClass = filled ? 'text-primary' : 'text-tertiary-900/35'
if (!interactive) {
return (
<Icon
key={star}
className={cn(iconClass, colorClass)}
/>
)
}
return (
<Button
key={star}
aria-checked={star === value}
aria-label={format(texts.reviews.heartAria, { star })}
className={cn('min-h-11 min-w-11 px-1 shadow-none transition-transform hover:scale-110', colorClass)}
color="default"
radius="none"
role="radio"
variant="light"
onClick={() => {
onChange(star)
}}
>
<Icon className={iconClass} />
</Button>
)
})}
</div>
)
}
export default StarRating