import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { pricingSchema, slotConfigSchema, type Pricing, type SlotConfig } from '@barev/core'; import { Badge, Button, Card, Field } from '@barev/ui'; import { errorText, useI18n, useToast, useTRPC } from '@barev/web'; import { useState } from 'react'; /** ADM-5: направления и услуги — цены, доступность, окно записи, удержание; скидки Barev+ */ export function Services({ isOwner }: { isOwner: boolean }) { const { t, amd } = useI18n(); const trpc = useTRPC(); const qc = useQueryClient(); const toast = useToast(); const list = useQuery(trpc.admin.services.list.queryOptions()); const [open, setOpen] = useState(null); const updateCategory = useMutation( trpc.admin.services.updateCategory.mutationOptions({ onSuccess: () => { toast(t('admin.saved')); void qc.invalidateQueries(); }, onError: (e) => toast(errorText(t, e), 'error'), }), ); return ( <>

{t('admin.nav.services')}

{list.data?.map((c) => (

{t(`categories.${c.code}.name`)}

{c.code !== 'PERKS' && ( )}
{c.services.map((s) => { const pricing = pricingSchema.parse(s.pricing); const from = pricing.kind === 'fixed' ? pricing.price : Math.min(...Object.values(pricing.tiers).map((x) => x.base)); return (
{t(`services.${s.code}.name`)} {t(s.active ? 'admin.services.active' : 'admin.services.inactive')} {t('common.from', { price: amd(from) })}
{open === s.id && setOpen(null)} />}
); })}
))} ); } function ServiceEditor({ service, onSaved, }: { service: { id: string; code: string; active: boolean; lateCancelFeePct: number; pricing: Pricing; slotConfig: SlotConfig; checklist: { code: string; requiresPhoto: boolean }[] }; onSaved: () => void; }) { const { t } = useI18n(); const trpc = useTRPC(); const qc = useQueryClient(); const toast = useToast(); const [active, setActive] = useState(service.active); const [fee, setFee] = useState(service.lateCancelFeePct); const [pricing, setPricing] = useState(service.pricing); const [slot, setSlot] = useState(service.slotConfig); const save = useMutation( trpc.admin.services.update.mutationOptions({ onSuccess: () => { toast(t('admin.saved')); void qc.invalidateQueries(); onSaved(); }, onError: (e) => toast(errorText(t, e), 'error'), }), ); const num = (v: string) => Math.max(0, Math.round(Number(v) || 0)); return (

{t('admin.services.prices')}

{pricing.kind === 'rooms' ? ( {Object.entries(pricing.tiers).map(([variant, tier]) => ( {(['base', 'perRoom', 'baseMinutes', 'perRoomMinutes', 'perBathroom', 'perBathroomMinutes'] as const).map((k) => ( ))} ))}
{t('admin.services.variant')} {t('admin.services.base')} {t('admin.services.perRoom')} {t('admin.services.baseMinutes')} {t('admin.services.perRoomMinutes')} {t('admin.services.perBathroom')} {t('admin.services.perBathroomMinutes')}
{t(`params.${service.code}.${pricing.variantParam}.${variant}`)} setPricing({ ...pricing, tiers: { ...pricing.tiers, [variant]: { ...tier, [k]: num(e.target.value) } } })} />
) : null} {pricing.kind === 'rooms' && Object.keys(pricing.extras).length > 0 && ( <>

{t('admin.services.extras')}

{Object.entries(pricing.extras).map(([code, e]) => ( {(['price', 'minutes'] as const).map((k) => ( ))} ))}
{t('admin.services.extra')} {t('admin.services.extraPrice')} {t('admin.services.extraMinutes')}
{t(`extras.${code}.name`)} {e.unit !== 'service' && {t(`extras.${code}.per`)}} setPricing({ ...pricing, extras: { ...pricing.extras, [code]: { ...e, [k]: num(ev.target.value) } } })} />
)} {pricing.kind === 'fixed' && (
setPricing({ ...pricing, price: num(e.target.value) })} /> setPricing({ ...pricing, minutes: Math.max(1, num(e.target.value)) })} />
)}
setFee(Math.min(100, num(e.target.value)))} /> setSlot({ ...slot, stepMinutes: Math.max(15, num(e.target.value)) })} /> setSlot({ ...slot, dayStartMinute: Math.min(24, num(e.target.value)) * 60 })} /> setSlot({ ...slot, dayEndMinute: Math.min(24, num(e.target.value)) * 60 })} /> setSlot({ ...slot, leadHours: num(e.target.value) })} /> setSlot({ ...slot, horizonDays: Math.max(1, num(e.target.value)) })} />
{service.checklist.length > 0 && (
{t('admin.order.checklist')}: {service.checklist.map((c) => `${t(`checklist.${c.code}`)}${c.requiresPhoto ? ' 📷' : ''}`).join(' · ')}
)}
); }