improvement: FDI tooth chart overhauled.

This commit is contained in:
2026-08-19 00:06:21 +03:30
parent 245a238d1a
commit 6d90787c13
47 changed files with 1839 additions and 146 deletions

View File

@@ -45,8 +45,12 @@ import {
connectedTeethSet,
deriveTeethFromGroups,
groupsFromFlatTeeth,
linkAdjacentTeeth,
linkedEdgesFromGroups,
pruneToothProsthesisForGroups,
toothEdgeKey,
toggleToothInGroups,
unlinkAdjacentTeeth,
} from '@/components/treatment/toothSelectionGroups';
import type { LabDispatchAttentionItem } from '@/components/treatment/labDispatchAttention';
import { collectLabDispatchAttention } from '@/components/treatment/labDispatchAttention';
@@ -614,6 +618,10 @@ export function TreatmentWorkspace({
() => connectedTeethSet(activeDetail?.toothSelectionGroups ?? []),
[activeDetail?.toothSelectionGroups],
);
const linkedToothEdges = useMemo(
() => linkedEdgesFromGroups(activeDetail?.toothSelectionGroups ?? []),
[activeDetail?.toothSelectionGroups],
);
const rangeAnchorRef = useRef<FdiToothId | null>(null);
const wholePlanTeethSet = useMemo(() => {
@@ -2016,6 +2024,7 @@ export function TreatmentWorkspace({
{entryStep === 'teeth' ? (
<FdiToothChart
selected={chartSelectedTeeth}
linkedEdges={showWholeTreatmentPlan ? undefined : linkedToothEdges}
connectedTeeth={showWholeTreatmentPlan ? undefined : connectedSelectedTeeth}
toothColors={chartToothColors}
readOnly={showWholeTreatmentPlan}
@@ -2089,6 +2098,53 @@ export function TreatmentWorkspace({
}),
);
}}
onToggleLink={(a, b) => {
if (
!canEditTreatmentForDay ||
isDetailLocked(activeDetail) ||
showWholeTreatmentPlan ||
!activeDetailId
) {
return;
}
const detail = details.find((d) => d.clientId === activeDetailId);
if (!detail) return;
const currentGroups =
detail.toothSelectionGroups.length > 0
? detail.toothSelectionGroups
: groupsFromFlatTeeth(detail.teeth);
const edgeLinked = linkedEdgesFromGroups(currentGroups).has(toothEdgeKey(a, b));
const nextGroups = edgeLinked
? unlinkAdjacentTeeth(currentGroups, a, b)
: linkAdjacentTeeth(currentGroups, a, b);
if (!nextGroups) return;
setDetails((prev) =>
prev.map((d) =>
d.clientId !== activeDetailId
? d
: {
...d,
toothSelectionGroups: nextGroups,
teeth: deriveTeethFromGroups(nextGroups),
},
),
);
setLabCaseDrafts((prev) =>
prev.map((lc) => {
if (lc.detailClientId !== activeDetailId) return lc;
return {
...lc,
toothProsthesis: pruneToothProsthesisForGroups(
lc.toothProsthesis,
activeDetailId,
nextGroups,
),
};
}),
);
}}
disabled={!canEditTreatmentForDay || isDetailLocked(activeDetail)}
/>
) : null}