Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
70 lines
1.8 KiB
TypeScript
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
|