2026-05-19 22:29:29 +03:30
|
|
|
const FDI_TOOTH_IDS = new Set([
|
|
|
|
|
'11', '12', '13', '14', '15', '16', '17', '18',
|
|
|
|
|
'21', '22', '23', '24', '25', '26', '27', '28',
|
|
|
|
|
'31', '32', '33', '34', '35', '36', '37', '38',
|
|
|
|
|
'41', '42', '43', '44', '45', '46', '47', '48',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
export function normalizeTeeth(teeth: unknown): string[] {
|
|
|
|
|
if (!Array.isArray(teeth)) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
const unique = new Set<string>();
|
|
|
|
|
for (const tooth of teeth) {
|
|
|
|
|
if (typeof tooth !== 'string') continue;
|
|
|
|
|
const trimmed = tooth.trim();
|
|
|
|
|
if (FDI_TOOTH_IDS.has(trimmed)) {
|
|
|
|
|
unique.add(trimmed);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return [...unique].sort();
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-17 00:39:32 +03:30
|
|
|
export type ToothSelectionGroupNormalized = {
|
|
|
|
|
groupId: string;
|
|
|
|
|
kind: 'connected' | 'single';
|
|
|
|
|
teeth: string[];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export function normalizeToothSelectionGroups(
|
|
|
|
|
value: unknown,
|
|
|
|
|
fallbackTeeth: string[] = [],
|
|
|
|
|
): ToothSelectionGroupNormalized[] {
|
|
|
|
|
if (Array.isArray(value) && value.length > 0) {
|
|
|
|
|
const out: ToothSelectionGroupNormalized[] = [];
|
|
|
|
|
for (const row of value) {
|
|
|
|
|
if (!row || typeof row !== 'object') continue;
|
|
|
|
|
const rec = row as Record<string, unknown>;
|
|
|
|
|
const groupId = typeof rec.groupId === 'string' && rec.groupId.trim() ? rec.groupId.trim() : '';
|
|
|
|
|
if (!groupId) continue;
|
|
|
|
|
const kind = rec.kind === 'connected' ? 'connected' : 'single';
|
|
|
|
|
const teeth = normalizeTeeth(rec.teeth);
|
|
|
|
|
if (teeth.length === 0) continue;
|
|
|
|
|
out.push({
|
|
|
|
|
groupId,
|
|
|
|
|
kind: kind === 'connected' && teeth.length < 2 ? 'single' : kind,
|
|
|
|
|
teeth,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (out.length > 0) return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return fallbackTeeth.map((tooth, index) => ({
|
|
|
|
|
groupId: `legacy-${tooth}-${index}`,
|
|
|
|
|
kind: 'single' as const,
|
|
|
|
|
teeth: [tooth],
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-19 22:29:29 +03:30
|
|
|
export function generateTreatmentTitle(
|
|
|
|
|
cases: { treatmentType: string; teeth: string[] }[],
|
|
|
|
|
): string {
|
|
|
|
|
if (cases.length === 0) {
|
|
|
|
|
return 'Treatment';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const parts = cases.map((c) => {
|
|
|
|
|
const label = c.treatmentType.charAt(0).toUpperCase() + c.treatmentType.slice(1);
|
|
|
|
|
if (c.teeth.length > 0) {
|
|
|
|
|
return `${label} ${c.teeth.join(', ')}`;
|
|
|
|
|
}
|
|
|
|
|
return label;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return parts.join(' · ');
|
|
|
|
|
}
|