Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
99 lines
3.0 KiB
TypeScript
99 lines
3.0 KiB
TypeScript
'use client'
|
|
|
|
import { Button } from '@heroui/react'
|
|
|
|
import type { EventAgeRestriction, EventGenderRestriction } from '@/lib/eventAudience'
|
|
import { AGE_RESTRICTION_OPTIONS, GENDER_RESTRICTION_OPTIONS } from '@/lib/eventAudience'
|
|
import { cn } from '@/lib/cn'
|
|
import { texts } from '@/texts'
|
|
|
|
interface AudienceRestrictionSelectorProps {
|
|
ageRestriction: EventAgeRestriction
|
|
genderRestriction: EventGenderRestriction
|
|
disabled?: boolean
|
|
onAgeRestrictionChange: (value: EventAgeRestriction) => void
|
|
onGenderRestrictionChange: (value: EventGenderRestriction) => void
|
|
}
|
|
|
|
/** Content-width button groups for age and gender limits shown to event guests. */
|
|
export default function AudienceRestrictionSelector({
|
|
ageRestriction,
|
|
genderRestriction,
|
|
disabled = false,
|
|
onAgeRestrictionChange,
|
|
onGenderRestrictionChange,
|
|
}: AudienceRestrictionSelectorProps) {
|
|
return (
|
|
<div className={cn('grid gap-4', disabled && 'pointer-events-none opacity-70')}>
|
|
<RestrictionGroup
|
|
ariaLabel={texts.events.genderRestrictionLabel}
|
|
disabled={disabled}
|
|
options={GENDER_RESTRICTION_OPTIONS}
|
|
value={genderRestriction}
|
|
onChange={(value) => {
|
|
onGenderRestrictionChange(value as EventGenderRestriction)
|
|
}}
|
|
/>
|
|
<RestrictionGroup
|
|
ariaLabel={texts.events.ageRestrictionLabel}
|
|
disabled={disabled}
|
|
options={AGE_RESTRICTION_OPTIONS}
|
|
value={ageRestriction}
|
|
onChange={(value) => {
|
|
onAgeRestrictionChange(value as EventAgeRestriction)
|
|
}}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function RestrictionGroup({
|
|
ariaLabel,
|
|
disabled = false,
|
|
options,
|
|
value,
|
|
onChange,
|
|
}: {
|
|
ariaLabel: string
|
|
disabled?: boolean
|
|
options: readonly { name: string; selectKey: string }[]
|
|
value: string
|
|
onChange: (value: string) => void
|
|
}) {
|
|
return (
|
|
<div>
|
|
<div
|
|
aria-label={ariaLabel}
|
|
className="flex w-full gap-1 rounded-xl bg-secondary-50 p-1"
|
|
role="group"
|
|
>
|
|
{options.map((option) => {
|
|
const active = option.selectKey === value
|
|
|
|
return (
|
|
<Button
|
|
key={option.selectKey}
|
|
aria-pressed={active}
|
|
className={cn(
|
|
'flex-1 min-h-10 px-3 text-xs font-medium rounded-xl',
|
|
active
|
|
? '[--button-bg:var(--color-secondary-30)] [--button-bg-hover:var(--color-secondary-30)] [--button-bg-pressed:var(--color-secondary-30)] [--button-fg:#ffffff]'
|
|
: '[--button-bg:var(--color-secondary-40)] [--button-bg-hover:var(--color-secondary-40)] [--button-bg-pressed:var(--color-secondary-40)] [--button-fg:var(--color-secondary-10)]'
|
|
)}
|
|
isDisabled={disabled}
|
|
size="sm"
|
|
variant="secondary"
|
|
onPress={() => {
|
|
if (disabled) return
|
|
onChange(option.selectKey)
|
|
}}
|
|
>
|
|
{option.name}
|
|
</Button>
|
|
)
|
|
})}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|