improvement: treatment plan turned into a wizard. shift+click control added to FDI tooth chart for cunnected prosthesises.

This commit is contained in:
2026-07-17 00:39:32 +03:30
parent cf07b4d8a8
commit 68fc9d5d6d
28 changed files with 1461 additions and 477 deletions

View File

@@ -18,7 +18,7 @@ export async function generateLabCaseTasks(
const toothProsthesisRows = await tx.labCaseToothProsthesis.findMany({
where: { labCaseId },
include: {
detail: { select: { id: true, treatmentType: true } },
detail: { select: { id: true, treatmentType: true, toothSelectionGroups: true } },
},
});
@@ -58,19 +58,29 @@ export async function generateLabCaseTasks(
const stepLabels = await resolveStepLabels(tx, allStepCodes, locale);
// Group teeth that share the same (treatment detail + prosthesis type): one task set
// per group, with each step covering every tooth in that group.
// Group by selection group + prosthesis type so connected spans stay one task set,
// and separate singles stay separate even with the same prosthesis type.
const groups = new Map<
string,
{ treatmentDetailId: string; treatmentType: string; prosthesisTypeCode: string; teeth: string[] }
{
treatmentDetailId: string;
treatmentType: string;
prosthesisTypeCode: string;
selectionGroupId: string;
teeth: string[];
}
>();
for (const row of toothProsthesisRows) {
const key = `${row.treatmentDetailId}::${row.prosthesisTypeCode}`;
const selectionGroupId =
row.selectionGroupId?.trim() ||
fallbackGroupIdForTooth(row.detail.toothSelectionGroups, row.tooth, row.prosthesisTypeCode);
const key = `${row.treatmentDetailId}::${selectionGroupId}::${row.prosthesisTypeCode}`;
const group = groups.get(key) ?? {
treatmentDetailId: row.treatmentDetailId,
treatmentType: row.detail.treatmentType,
prosthesisTypeCode: row.prosthesisTypeCode,
selectionGroupId,
teeth: [],
};
group.teeth.push(row.tooth);
@@ -94,6 +104,7 @@ export async function generateLabCaseTasks(
teeth,
treatmentType: group.treatmentType,
prosthesisTypeCode: group.prosthesisTypeCode,
selectionGroupId: group.selectionGroupId,
workflowStepCode: step.workflowStepCode,
stepOrder: step.stepOrder,
stepLabel: stepLabels.get(step.workflowStepCode) ?? step.workflowStepCode,
@@ -110,6 +121,24 @@ export async function generateLabCaseTasks(
return taskRows.length;
}
function fallbackGroupIdForTooth(
toothSelectionGroups: unknown,
tooth: string,
prosthesisTypeCode: string,
): string {
if (Array.isArray(toothSelectionGroups)) {
for (const row of toothSelectionGroups) {
if (!row || typeof row !== 'object') continue;
const rec = row as { groupId?: unknown; teeth?: unknown };
if (typeof rec.groupId !== 'string') continue;
if (Array.isArray(rec.teeth) && rec.teeth.includes(tooth)) {
return rec.groupId;
}
}
}
return `legacy-${prosthesisTypeCode}`;
}
function sortTeeth(teeth: string[]): string[] {
return [...new Set(teeth)].sort((a, b) => {
const na = Number(a);