Files
dyolink/frontend/src/components/ui/lab/LabCaseProsthesisGroupsList.tsx

128 lines
3.8 KiB
TypeScript

'use client';
import { useTranslations } from 'next-intl';
import { ConnectedSelectionBadge } from '@/components/ui/treatment/ConnectedSelectionBadge';
import {
formatToothList,
prosthesisGroupLabelFromCatalog,
prosthesisTypeLabelStyleFromCatalog,
} from '@/components/treatment/prosthesisTypeDisplay';
import { ProsthesisStackedTypeLabel } from '@/components/ui/lab/ProsthesisStackedTypeLabel';
import type { ProsthesisCatalogEntry } from '@/types/treatment-catalog';
export type LabCaseProsthesisGroup = {
prosthesisTypeCode: string;
teeth: string[];
connected?: boolean;
selectionGroupId?: string;
};
interface LabCaseProsthesisGroupsListProps {
groups: LabCaseProsthesisGroup[];
prosthesisCatalog: readonly ProsthesisCatalogEntry[];
fallbackTeeth?: string[];
}
function prosthesisLabel(code: string, catalog: readonly ProsthesisCatalogEntry[]): string {
return catalog.find((entry) => entry.code === code)?.label ?? prosthesisGroupLabelFromCatalog(code, catalog);
}
export function LabCaseProsthesisGroupsList({
groups,
prosthesisCatalog,
fallbackTeeth = [],
}: LabCaseProsthesisGroupsListProps) {
const tTreatment = useTranslations('treatment');
const archLabels = {
UA: tTreatment('selectedArchUpper'),
LA: tTreatment('selectedArchLower'),
};
if (groups.length > 0) {
return (
<ul className="space-y-0.5">
{groups.map((group) => (
<li
key={`${group.selectionGroupId ?? ''}-${group.prosthesisTypeCode}-${group.teeth.join(',')}`}
className="text-[11px] leading-snug"
>
<ProsthesisStackedTypeLabel
className="font-medium"
code={group.prosthesisTypeCode}
catalog={prosthesisCatalog}
/>
{group.teeth.length > 0 ? (
<span className="text-text-muted">
{' · '}
{formatToothList(group.teeth, archLabels)}
</span>
) : null}
{group.connected ? (
<ConnectedSelectionBadge className="ms-1 align-middle text-[9px] px-1 py-px" />
) : null}
</li>
))}
</ul>
);
}
if (fallbackTeeth.length > 0) {
return <p className="text-[11px] text-text-muted">{formatToothList(fallbackTeeth, archLabels)}</p>;
}
return null;
}
export type LabCaseToothJobRow = {
teeth: string[];
codes: string[];
connected?: boolean;
};
interface LabCaseToothJobsListProps {
rows: LabCaseToothJobRow[];
prosthesisCatalog: readonly ProsthesisCatalogEntry[];
}
export function LabCaseToothJobsList({
rows,
prosthesisCatalog,
}: LabCaseToothJobsListProps) {
const tTreatment = useTranslations('treatment');
const archLabels = {
UA: tTreatment('selectedArchUpper'),
LA: tTreatment('selectedArchLower'),
};
if (rows.length === 0) return null;
return (
<ul className="space-y-1">
{rows.map((row) => (
<li
key={`${row.teeth.join(',')}-${row.codes.join('+')}`}
className="flex flex-wrap items-baseline gap-x-1.5 gap-y-0.5 text-[11px] leading-snug"
>
<span className="font-semibold text-text-primary">
{formatToothList(row.teeth, archLabels)}
</span>
{row.codes.map((code) => (
<span key={code} className="inline-flex items-baseline gap-x-1.5">
<span className="text-text-muted" aria-hidden>
·
</span>
<span
className="font-medium"
style={prosthesisTypeLabelStyleFromCatalog(code, prosthesisCatalog)}
>
{prosthesisLabel(code, prosthesisCatalog)}
</span>
</span>
))}
{row.connected ? (
<ConnectedSelectionBadge className="align-middle text-[9px] px-1 py-px" />
) : null}
</li>
))}
</ul>
);
}