'use client'; import type { ReactNode } from 'react'; import { useTranslations } from 'next-intl'; import { X } from 'lucide-react'; import { Button } from '@/components/ui/shared/Button'; import { ResponsiveDialogOverlay, ResponsiveDialogPanel, } from '@/components/ui/shared/ResponsiveDialog'; import type { ProsthesisCatalogEntry } from '@/types/treatment-catalog'; import { prosthesisTypeColorFromCatalog } from '@/components/treatment/prosthesisTypeDisplay'; import { addonLeaves, categoryCodesInCatalog, categoryTileColor, leavesFor, subcategoriesFor, type ArchTarget, type PickerScope, } from '@/components/treatment/prosthesisTree'; const CHIP_INK = '#14253d'; const TILE_CLASS = 'flex h-full min-h-0 w-[3.6rem] shrink-0 flex-row items-center justify-center gap-0.5 rounded-[var(--radius-md)] border px-0.5 py-2 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/40'; const TILE_TEXT_CLASS = 'max-h-full overflow-hidden [writing-mode:vertical-rl] [text-orientation:mixed] whitespace-normal break-words text-center text-xs font-medium leading-tight sm:text-[13px]'; const HEADER_BTN_CLASS = 'h-auto min-h-8 px-2.5 py-1 text-xs leading-tight'; export type PickerStep = | { kind: 'category' } | { kind: 'subcategory'; category: string; arch?: ArchTarget } | { kind: 'leaves'; category: string; subcategory?: string; arch?: ArchTarget } | { kind: 'addon'; slot: 'implant' | 'post_core' }; interface ProsthesisJobPopoverProps { open: boolean; mobile: boolean; catalog: readonly ProsthesisCatalogEntry[]; step: PickerStep; scope: PickerScope; lockedArch?: ArchTarget; jobCodes: string[]; tooth: string | null; rangeLabel?: string | null; allowImplantAddon: boolean; allowPostCoreAddon: boolean; onStep: (step: PickerStep) => void; onLockedArchChange?: (arch: ArchTarget) => void; onPickLeaf: (code: string, arch?: ArchTarget) => void; onPickAddon: (code: string | null, kind: 'implant' | 'post_core') => void; onRemoveJob: (code: string) => void; onClear: () => void; onClose: () => void; } function Tile({ label, caption, color, active, onClick, }: { label: string; caption?: string; color?: string; active?: boolean; onClick: () => void; }) { return ( ); } function VRule() { return
; } function parentStep(step: PickerStep): PickerStep | null { if (step.kind === 'category') return null; if (step.kind === 'addon') return { kind: 'category' }; if (step.kind === 'leaves') { if (step.subcategory) { return { kind: 'subcategory', category: step.category, arch: step.arch }; } return { kind: 'category' }; } if (step.kind === 'subcategory') return { kind: 'category' }; return { kind: 'category' }; } export function ProsthesisJobPopover({ open, mobile, catalog, step, scope, lockedArch, jobCodes, tooth, rangeLabel, allowImplantAddon, allowPostCoreAddon, onStep, onLockedArchChange, onPickLeaf, onPickAddon, onRemoveJob, onClear, onClose, }: ProsthesisJobPopoverProps) { const t = useTranslations('prosthesis'); if (!open) return null; const categories = categoryCodesInCatalog(catalog, scope); const implantAddons = addonLeaves(catalog, 'implant'); const postCoreAddons = addonLeaves(catalog, 'post_core'); const currentImplant = jobCodes.find((c) => catalog.find((e) => e.code === c)?.addonKind === 'implant') ?? ''; const currentPost = jobCodes.find((c) => catalog.find((e) => e.code === c)?.addonKind === 'post_core') ?? ''; const hasLeaf = jobCodes.length > 0; const byCode = new Map(catalog.map((e) => [e.code, e])); const archForLeaves = step.kind === 'leaves' || step.kind === 'subcategory' ? step.arch : lockedArch; function categoryLabel(code: string) { return t(`category_${code}` as never); } function subLabel(code: string) { return t(`sub_${code}` as never); } const back = parentStep(step); function openCategory(code: string) { const subs = subcategoriesFor(catalog, code); const arch = scope === 'arch' ? lockedArch : undefined; if (subs.length > 0) { onStep({ kind: 'subcategory', category: code, arch }); return; } onStep({ kind: 'leaves', category: code, arch }); } function leafTile(leaf: ProsthesisCatalogEntry) { return ( onPickLeaf(leaf.code, archForLeaves)} /> ); } const addonTiles: ReactNode[] = []; if (scope === 'tooth' && hasLeaf) { if (allowImplantAddon && implantAddons.length > 0) { addonTiles.push( onStep( step.kind === 'addon' && step.slot === 'implant' ? { kind: 'category' } : { kind: 'addon', slot: 'implant' }, ) } />, ); } if (allowPostCoreAddon && postCoreAddons.length > 0) { addonTiles.push( onStep( step.kind === 'addon' && step.slot === 'post_core' ? { kind: 'category' } : { kind: 'addon', slot: 'post_core' }, ) } />, ); } } let selectedNode: { label: string; color?: string } | null = null; let childTiles: ReactNode[] = []; let childrenKey = 'root'; if (step.kind === 'category') { childTiles = categories.map((code) => ( openCategory(code)} /> )); } else if (step.kind === 'addon') { selectedNode = { label: step.slot === 'implant' ? t('slotImplant') : t('slotPostCore'), }; childrenKey = `addon-${step.slot}`; const options = step.slot === 'implant' ? implantAddons : postCoreAddons; const current = step.slot === 'implant' ? currentImplant : currentPost; childTiles = [ onPickAddon(null, step.slot)} />, ...options.map((opt) => ( onPickAddon(opt.code, step.slot)} /> )), ]; } else if (step.kind === 'leaves' && step.subcategory) { selectedNode = { label: subLabel(step.subcategory) }; childrenKey = `leaves-${step.category}-${step.subcategory}`; childTiles = leavesFor(catalog, step.category, step.subcategory).map(leafTile); } else { selectedNode = { label: categoryLabel(step.category), color: categoryTileColor(step.category), }; childrenKey = `${step.kind}-${step.category}`; const arch = step.arch; const subs = step.kind === 'subcategory' ? subcategoriesFor(catalog, step.category).map((sub) => ( onStep({ kind: 'leaves', category: step.category, subcategory: sub, arch, }) } /> )) : []; const leaves = leavesFor( catalog, step.category, step.kind === 'leaves' ? step.subcategory : undefined, ).map(leafTile); childTiles = [...subs, ...leaves]; } const tileRow = (
{selectedNode ? (
back && onStep(back)} />
{childTiles}
) : ( childTiles )}
); const title = rangeLabel ? t('pickerTeeth', { teeth: rangeLabel }) : scope === 'arch' ? lockedArch === 'both' ? t('pickerArchBoth') : lockedArch === 'lower' ? t('pickerArchLower') : t('pickerArchUpper') : tooth ? t('pickerTooth', { tooth }) : t('pickerTitle'); const header = (

{title}

{scope === 'arch' && onLockedArchChange ? (
{(['upper', 'lower', 'both'] as const).map((arch) => ( ))}
) : null} {jobCodes.length === 0 ? (

{t('noneYet')}

) : (
{jobCodes.map((code) => { const entry = byCode.get(code); const color = prosthesisTypeColorFromCatalog(code, catalog); return ( {entry?.category ? t(`category_${entry.category}` as never) : t('regionCrown')} {entry?.label ?? code} ); })}
)}

{scope === 'arch' ? t('clearHintArch') : t('clearHint')}

); const addonRail = addonTiles.length > 0 ? (
{addonTiles}
) : null; const body = (
{header}
{tileRow} {addonRail}
); if (mobile) { return (

{t('pickerTitle')}

{body}
); } return (
); }