2026-07-06 20:40:19 +03:30
|
|
|
|
import { CatalogEntityKind } from '@prisma/client';
|
|
|
|
|
|
|
|
|
|
|
|
export type CatalogTranslationSeed = {
|
|
|
|
|
|
entityKind: CatalogEntityKind;
|
|
|
|
|
|
entityCode: string;
|
|
|
|
|
|
locale: string;
|
|
|
|
|
|
label: string;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-07 13:13:04 +03:30
|
|
|
|
export type TreatmentTypeSeed = {
|
|
|
|
|
|
code: string;
|
|
|
|
|
|
labDependent: boolean;
|
|
|
|
|
|
sortOrder: number;
|
|
|
|
|
|
/** Selectable when booking an appointment. Defaults to true. */
|
|
|
|
|
|
availableInAppointments?: boolean;
|
|
|
|
|
|
/** Selectable as a treatment plan detail. Defaults to true. */
|
|
|
|
|
|
availableInTreatment?: boolean;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export const TREATMENT_TYPES: readonly TreatmentTypeSeed[] = [
|
2026-07-06 20:40:19 +03:30
|
|
|
|
{ code: 'restoration', labDependent: false, sortOrder: 1 },
|
|
|
|
|
|
{ code: 'specialized_restoration', labDependent: false, sortOrder: 2 },
|
|
|
|
|
|
{ code: 'radiography', labDependent: false, sortOrder: 3 },
|
|
|
|
|
|
{ code: 'endo', labDependent: false, sortOrder: 4 },
|
|
|
|
|
|
{ code: 'surgery', labDependent: false, sortOrder: 5 },
|
|
|
|
|
|
{ code: 'prosthesis', labDependent: true, sortOrder: 6 },
|
|
|
|
|
|
{ code: 'implant', labDependent: false, sortOrder: 7 },
|
|
|
|
|
|
{ code: 'orthodontics', labDependent: false, sortOrder: 8 },
|
|
|
|
|
|
{ code: 'perio', labDependent: false, sortOrder: 9 },
|
|
|
|
|
|
{ code: 'pediatrics', labDependent: false, sortOrder: 10 },
|
|
|
|
|
|
{ code: 'extraction', labDependent: false, sortOrder: 11 },
|
2026-07-07 13:13:04 +03:30
|
|
|
|
{
|
|
|
|
|
|
code: 'clinic_visit',
|
|
|
|
|
|
labDependent: false,
|
|
|
|
|
|
sortOrder: 12,
|
|
|
|
|
|
availableInTreatment: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
code: 'continue_treatment',
|
|
|
|
|
|
labDependent: false,
|
|
|
|
|
|
sortOrder: 13,
|
|
|
|
|
|
availableInTreatment: false,
|
|
|
|
|
|
},
|
2026-07-06 20:40:19 +03:30
|
|
|
|
] as const;
|
|
|
|
|
|
|
|
|
|
|
|
/** Legacy codes kept for historical rows; hidden from catalog. */
|
2026-07-07 13:13:04 +03:30
|
|
|
|
export const LEGACY_TREATMENT_TYPES: readonly TreatmentTypeSeed[] = [
|
2026-07-06 20:40:19 +03:30
|
|
|
|
{ code: 'consultation', labDependent: false, sortOrder: 99 },
|
|
|
|
|
|
{ code: 'filling', labDependent: false, sortOrder: 100 },
|
|
|
|
|
|
{ code: 'visit', labDependent: false, sortOrder: 101 },
|
|
|
|
|
|
{ code: 'hygiene', labDependent: false, sortOrder: 102 },
|
|
|
|
|
|
] as const;
|
|
|
|
|
|
|
|
|
|
|
|
export const LAB_WORKFLOW_STEPS = [
|
|
|
|
|
|
{ code: 'intraoral_scan', sortOrder: 1 },
|
2026-09-02 17:24:01 +03:30
|
|
|
|
{ code: 'choosing_abutment', sortOrder: 2 },
|
|
|
|
|
|
{ code: 'design', sortOrder: 3 },
|
|
|
|
|
|
{ code: 'model_create', sortOrder: 4 },
|
|
|
|
|
|
{ code: 'milling_dry', sortOrder: 5 },
|
|
|
|
|
|
{ code: 'milling_wet', sortOrder: 6 },
|
|
|
|
|
|
{ code: 'printer_resin', sortOrder: 7 },
|
|
|
|
|
|
{ code: 'printer_metal', sortOrder: 8 },
|
|
|
|
|
|
{ code: 'pressing', sortOrder: 9 },
|
|
|
|
|
|
{ code: 'sinter', sortOrder: 10 },
|
|
|
|
|
|
{ code: 'build_up', sortOrder: 11 },
|
|
|
|
|
|
{ code: 'stain', sortOrder: 12 },
|
|
|
|
|
|
{ code: 'glaze', sortOrder: 13 },
|
|
|
|
|
|
{ code: 'polish_prep', sortOrder: 14 },
|
|
|
|
|
|
{ code: 'packing', sortOrder: 15 },
|
|
|
|
|
|
{ code: 'shipping', sortOrder: 16 },
|
2026-07-06 20:40:19 +03:30
|
|
|
|
] as const;
|
|
|
|
|
|
|
2026-09-01 21:03:04 +03:30
|
|
|
|
export type ProsthesisChartRegion = 'crown' | 'root' | 'arch';
|
feat(voice): adapt voice entry to the stacked-jobs prosthesis model
Authored by the /orchestrate builder agent, committed unrepaired so the
fixes that follow are reviewable against it.
Backend: replaces the flat prosthesisDefaultType/prosthesisOverrides wire
shape with a prosthesis: ProsthesisAssignment[] list whose targets can be a
tooth or a jaw; adds resolveAssignmentTarget / classifyTypeCode /
resolveProsthesisAssignment for leaf-vs-category classification, region
validity with mixed-region deferral, and assignmentIndex on unresolved
items; adds PROSTHESIS_CATEGORY and PROSTHESIS_SUBCATEGORY to
CatalogEntityKind with a migration and seeded fa/en/nl translations; and
rewrites the extraction prompt to render the catalog as a tree.
Frontend: merged "teeth and prosthesis" row, stack preview through the
existing applyLeafToJobs, three chip-fold paths, rewritten applyVoiceResult
and voiceForEditor, and the two carried-forward recording fixes — the
container fallback that refused Safari and the render gate that never
checked isMediaRecorderSupported().
Adds Vitest for the frontend's pure helpers, and updates CLAUDE.md.
Gate was green: backend 16 suites / 209 tests, nest build, prisma validate;
frontend 37 Vitest tests, tsc --noEmit, next build.
KNOWN DEFECTS, fixed in the commits that follow:
- VoiceReviewSheet.tsx:169 — a picked tooth chip is dropped on Apply
- VoiceReviewSheet.tsx:213 / TreatmentWorkspace.tsx:2215 — decision 41's
type-row lock is missing, so unticking it saves prosthesis lab rows on a
non-prosthesis detail
Reviewed on the correctness lens only; regression-risk never ran. The
migration was validated but never applied.
Spec: docs/specs/voice-treatment-entry/spec.md
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 12:23:58 +08:00
|
|
|
|
export type ProsthesisStackGroup =
|
|
|
|
|
|
| 'restoration'
|
|
|
|
|
|
| 'implant'
|
|
|
|
|
|
| 'post_core'
|
|
|
|
|
|
| 'arch';
|
2026-09-01 21:03:04 +03:30
|
|
|
|
|
2026-07-06 20:40:19 +03:30
|
|
|
|
export type ProsthesisTypeSeed = {
|
|
|
|
|
|
code: string;
|
|
|
|
|
|
sortOrder: number;
|
|
|
|
|
|
skipPackingShipping?: boolean;
|
2026-09-01 21:03:04 +03:30
|
|
|
|
isActive?: boolean;
|
|
|
|
|
|
category: string;
|
|
|
|
|
|
subcategory?: string;
|
|
|
|
|
|
chartRegion: ProsthesisChartRegion;
|
|
|
|
|
|
stackGroup: ProsthesisStackGroup;
|
2026-09-02 17:24:01 +03:30
|
|
|
|
/** Manufacturing sequence. Packing and shipping are appended except for smile_design. */
|
2026-07-06 20:40:19 +03:30
|
|
|
|
manufacturingSteps: readonly string[];
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-09-02 17:24:01 +03:30
|
|
|
|
const SCAN_DESIGN_MODEL = ['intraoral_scan', 'design', 'model_create'] as const;
|
feat(voice): adapt voice entry to the stacked-jobs prosthesis model
Authored by the /orchestrate builder agent, committed unrepaired so the
fixes that follow are reviewable against it.
Backend: replaces the flat prosthesisDefaultType/prosthesisOverrides wire
shape with a prosthesis: ProsthesisAssignment[] list whose targets can be a
tooth or a jaw; adds resolveAssignmentTarget / classifyTypeCode /
resolveProsthesisAssignment for leaf-vs-category classification, region
validity with mixed-region deferral, and assignmentIndex on unresolved
items; adds PROSTHESIS_CATEGORY and PROSTHESIS_SUBCATEGORY to
CatalogEntityKind with a migration and seeded fa/en/nl translations; and
rewrites the extraction prompt to render the catalog as a tree.
Frontend: merged "teeth and prosthesis" row, stack preview through the
existing applyLeafToJobs, three chip-fold paths, rewritten applyVoiceResult
and voiceForEditor, and the two carried-forward recording fixes — the
container fallback that refused Safari and the render gate that never
checked isMediaRecorderSupported().
Adds Vitest for the frontend's pure helpers, and updates CLAUDE.md.
Gate was green: backend 16 suites / 209 tests, nest build, prisma validate;
frontend 37 Vitest tests, tsc --noEmit, next build.
KNOWN DEFECTS, fixed in the commits that follow:
- VoiceReviewSheet.tsx:169 — a picked tooth chip is dropped on Apply
- VoiceReviewSheet.tsx:213 / TreatmentWorkspace.tsx:2215 — decision 41's
type-row lock is missing, so unticking it saves prosthesis lab rows on a
non-prosthesis detail
Reviewed on the correctness lens only; regression-risk never ran. The
migration was validated but never applied.
Spec: docs/specs/voice-treatment-entry/spec.md
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 12:23:58 +08:00
|
|
|
|
const INDIRECT_FULL = [
|
|
|
|
|
|
...SCAN_DESIGN_MODEL,
|
|
|
|
|
|
'milling_wet',
|
|
|
|
|
|
'sinter',
|
|
|
|
|
|
'stain',
|
|
|
|
|
|
'glaze',
|
|
|
|
|
|
] as const;
|
2026-09-02 17:24:01 +03:30
|
|
|
|
const INDIRECT_LAYERED = [
|
|
|
|
|
|
...SCAN_DESIGN_MODEL,
|
|
|
|
|
|
'milling_wet',
|
|
|
|
|
|
'sinter',
|
|
|
|
|
|
'build_up',
|
|
|
|
|
|
'stain',
|
|
|
|
|
|
'glaze',
|
|
|
|
|
|
] as const;
|
|
|
|
|
|
/** Unlisted types keep scan+design plus today’s manufacturing (no model-create). */
|
|
|
|
|
|
const KEEP_FULL_CONTOUR = [
|
|
|
|
|
|
'intraoral_scan',
|
|
|
|
|
|
'design',
|
|
|
|
|
|
'milling_wet',
|
|
|
|
|
|
'stain',
|
|
|
|
|
|
'glaze',
|
|
|
|
|
|
'polish_prep',
|
|
|
|
|
|
] as const;
|
|
|
|
|
|
const KEEP_LAYERED = [
|
|
|
|
|
|
'intraoral_scan',
|
|
|
|
|
|
'design',
|
|
|
|
|
|
'milling_dry',
|
|
|
|
|
|
'sinter',
|
|
|
|
|
|
'build_up',
|
|
|
|
|
|
'stain',
|
|
|
|
|
|
'glaze',
|
|
|
|
|
|
'polish_prep',
|
|
|
|
|
|
] as const;
|
feat(voice): adapt voice entry to the stacked-jobs prosthesis model
Authored by the /orchestrate builder agent, committed unrepaired so the
fixes that follow are reviewable against it.
Backend: replaces the flat prosthesisDefaultType/prosthesisOverrides wire
shape with a prosthesis: ProsthesisAssignment[] list whose targets can be a
tooth or a jaw; adds resolveAssignmentTarget / classifyTypeCode /
resolveProsthesisAssignment for leaf-vs-category classification, region
validity with mixed-region deferral, and assignmentIndex on unresolved
items; adds PROSTHESIS_CATEGORY and PROSTHESIS_SUBCATEGORY to
CatalogEntityKind with a migration and seeded fa/en/nl translations; and
rewrites the extraction prompt to render the catalog as a tree.
Frontend: merged "teeth and prosthesis" row, stack preview through the
existing applyLeafToJobs, three chip-fold paths, rewritten applyVoiceResult
and voiceForEditor, and the two carried-forward recording fixes — the
container fallback that refused Safari and the render gate that never
checked isMediaRecorderSupported().
Adds Vitest for the frontend's pure helpers, and updates CLAUDE.md.
Gate was green: backend 16 suites / 209 tests, nest build, prisma validate;
frontend 37 Vitest tests, tsc --noEmit, next build.
KNOWN DEFECTS, fixed in the commits that follow:
- VoiceReviewSheet.tsx:169 — a picked tooth chip is dropped on Apply
- VoiceReviewSheet.tsx:213 / TreatmentWorkspace.tsx:2215 — decision 41's
type-row lock is missing, so unticking it saves prosthesis lab rows on a
non-prosthesis detail
Reviewed on the correctness lens only; regression-risk never ran. The
migration was validated but never applied.
Spec: docs/specs/voice-treatment-entry/spec.md
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 12:23:58 +08:00
|
|
|
|
const DENTURE = [
|
|
|
|
|
|
'intraoral_scan',
|
|
|
|
|
|
'design',
|
|
|
|
|
|
'printer_resin',
|
|
|
|
|
|
'polish_prep',
|
|
|
|
|
|
] as const;
|
|
|
|
|
|
const APPLIANCE = [
|
|
|
|
|
|
'intraoral_scan',
|
|
|
|
|
|
'design',
|
|
|
|
|
|
'printer_resin',
|
|
|
|
|
|
'polish_prep',
|
|
|
|
|
|
] as const;
|
|
|
|
|
|
const POST_CORE = [
|
|
|
|
|
|
'intraoral_scan',
|
|
|
|
|
|
'design',
|
|
|
|
|
|
'milling_wet',
|
|
|
|
|
|
'polish_prep',
|
|
|
|
|
|
] as const;
|
2026-09-01 21:03:04 +03:30
|
|
|
|
|
|
|
|
|
|
function crown(
|
|
|
|
|
|
code: string,
|
|
|
|
|
|
sortOrder: number,
|
|
|
|
|
|
manufacturingSteps: readonly string[],
|
|
|
|
|
|
extra?: Partial<ProsthesisTypeSeed>,
|
|
|
|
|
|
): ProsthesisTypeSeed {
|
|
|
|
|
|
return {
|
|
|
|
|
|
code,
|
|
|
|
|
|
sortOrder,
|
|
|
|
|
|
category: 'crown',
|
|
|
|
|
|
chartRegion: 'crown',
|
|
|
|
|
|
stackGroup: 'restoration',
|
|
|
|
|
|
manufacturingSteps,
|
|
|
|
|
|
...extra,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-04 17:04:48 +03:30
|
|
|
|
function implant(
|
2026-09-01 21:03:04 +03:30
|
|
|
|
code: string,
|
|
|
|
|
|
sortOrder: number,
|
|
|
|
|
|
manufacturingSteps: readonly string[],
|
2026-09-02 17:24:01 +03:30
|
|
|
|
extra?: Partial<ProsthesisTypeSeed>,
|
2026-09-01 21:03:04 +03:30
|
|
|
|
): ProsthesisTypeSeed {
|
|
|
|
|
|
return {
|
|
|
|
|
|
code,
|
|
|
|
|
|
sortOrder,
|
|
|
|
|
|
category: 'implant',
|
|
|
|
|
|
chartRegion: 'root',
|
|
|
|
|
|
stackGroup: 'implant',
|
|
|
|
|
|
manufacturingSteps,
|
2026-09-02 17:24:01 +03:30
|
|
|
|
...extra,
|
2026-09-01 21:03:04 +03:30
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function indirect(
|
|
|
|
|
|
indication: string,
|
|
|
|
|
|
technique: 'full_contour' | 'layered',
|
|
|
|
|
|
sortOrder: number,
|
|
|
|
|
|
): ProsthesisTypeSeed {
|
|
|
|
|
|
return {
|
|
|
|
|
|
code: `${indication}_${technique}`,
|
|
|
|
|
|
sortOrder,
|
|
|
|
|
|
category: 'indirect',
|
|
|
|
|
|
subcategory: indication,
|
|
|
|
|
|
chartRegion: 'crown',
|
|
|
|
|
|
stackGroup: 'restoration',
|
feat(voice): adapt voice entry to the stacked-jobs prosthesis model
Authored by the /orchestrate builder agent, committed unrepaired so the
fixes that follow are reviewable against it.
Backend: replaces the flat prosthesisDefaultType/prosthesisOverrides wire
shape with a prosthesis: ProsthesisAssignment[] list whose targets can be a
tooth or a jaw; adds resolveAssignmentTarget / classifyTypeCode /
resolveProsthesisAssignment for leaf-vs-category classification, region
validity with mixed-region deferral, and assignmentIndex on unresolved
items; adds PROSTHESIS_CATEGORY and PROSTHESIS_SUBCATEGORY to
CatalogEntityKind with a migration and seeded fa/en/nl translations; and
rewrites the extraction prompt to render the catalog as a tree.
Frontend: merged "teeth and prosthesis" row, stack preview through the
existing applyLeafToJobs, three chip-fold paths, rewritten applyVoiceResult
and voiceForEditor, and the two carried-forward recording fixes — the
container fallback that refused Safari and the render gate that never
checked isMediaRecorderSupported().
Adds Vitest for the frontend's pure helpers, and updates CLAUDE.md.
Gate was green: backend 16 suites / 209 tests, nest build, prisma validate;
frontend 37 Vitest tests, tsc --noEmit, next build.
KNOWN DEFECTS, fixed in the commits that follow:
- VoiceReviewSheet.tsx:169 — a picked tooth chip is dropped on Apply
- VoiceReviewSheet.tsx:213 / TreatmentWorkspace.tsx:2215 — decision 41's
type-row lock is missing, so unticking it saves prosthesis lab rows on a
non-prosthesis detail
Reviewed on the correctness lens only; regression-risk never ran. The
migration was validated but never applied.
Spec: docs/specs/voice-treatment-entry/spec.md
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 12:23:58 +08:00
|
|
|
|
manufacturingSteps:
|
|
|
|
|
|
technique === 'layered' ? INDIRECT_LAYERED : INDIRECT_FULL,
|
2026-09-01 21:03:04 +03:30
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-06 20:40:19 +03:30
|
|
|
|
export const PROSTHESIS_TYPES: ProsthesisTypeSeed[] = [
|
2026-09-02 17:24:01 +03:30
|
|
|
|
crown('pfm_crown', 1, [
|
|
|
|
|
|
...SCAN_DESIGN_MODEL,
|
|
|
|
|
|
'milling_wet',
|
|
|
|
|
|
'sinter',
|
|
|
|
|
|
'build_up',
|
|
|
|
|
|
'stain',
|
|
|
|
|
|
'glaze',
|
|
|
|
|
|
]),
|
|
|
|
|
|
crown('pfz_crown', 2, [
|
|
|
|
|
|
...SCAN_DESIGN_MODEL,
|
|
|
|
|
|
'milling_dry',
|
|
|
|
|
|
'sinter',
|
|
|
|
|
|
'build_up',
|
|
|
|
|
|
'stain',
|
|
|
|
|
|
'glaze',
|
|
|
|
|
|
]),
|
|
|
|
|
|
crown('monolithic_zirconia', 3, [
|
|
|
|
|
|
...SCAN_DESIGN_MODEL,
|
|
|
|
|
|
'milling_dry',
|
|
|
|
|
|
'sinter',
|
|
|
|
|
|
'stain',
|
|
|
|
|
|
'glaze',
|
|
|
|
|
|
]),
|
|
|
|
|
|
crown('glass_ceramic_crown', 4, [
|
|
|
|
|
|
...SCAN_DESIGN_MODEL,
|
|
|
|
|
|
'milling_dry',
|
|
|
|
|
|
'sinter',
|
|
|
|
|
|
'stain',
|
|
|
|
|
|
'glaze',
|
|
|
|
|
|
]),
|
feat(voice): adapt voice entry to the stacked-jobs prosthesis model
Authored by the /orchestrate builder agent, committed unrepaired so the
fixes that follow are reviewable against it.
Backend: replaces the flat prosthesisDefaultType/prosthesisOverrides wire
shape with a prosthesis: ProsthesisAssignment[] list whose targets can be a
tooth or a jaw; adds resolveAssignmentTarget / classifyTypeCode /
resolveProsthesisAssignment for leaf-vs-category classification, region
validity with mixed-region deferral, and assignmentIndex on unresolved
items; adds PROSTHESIS_CATEGORY and PROSTHESIS_SUBCATEGORY to
CatalogEntityKind with a migration and seeded fa/en/nl translations; and
rewrites the extraction prompt to render the catalog as a tree.
Frontend: merged "teeth and prosthesis" row, stack preview through the
existing applyLeafToJobs, three chip-fold paths, rewritten applyVoiceResult
and voiceForEditor, and the two carried-forward recording fixes — the
container fallback that refused Safari and the render gate that never
checked isMediaRecorderSupported().
Adds Vitest for the frontend's pure helpers, and updates CLAUDE.md.
Gate was green: backend 16 suites / 209 tests, nest build, prisma validate;
frontend 37 Vitest tests, tsc --noEmit, next build.
KNOWN DEFECTS, fixed in the commits that follow:
- VoiceReviewSheet.tsx:169 — a picked tooth chip is dropped on Apply
- VoiceReviewSheet.tsx:213 / TreatmentWorkspace.tsx:2215 — decision 41's
type-row lock is missing, so unticking it saves prosthesis lab rows on a
non-prosthesis detail
Reviewed on the correctness lens only; regression-risk never ran. The
migration was validated but never applied.
Spec: docs/specs/voice-treatment-entry/spec.md
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 12:23:58 +08:00
|
|
|
|
crown('full_metal_crown', 5, [
|
|
|
|
|
|
...SCAN_DESIGN_MODEL,
|
|
|
|
|
|
'milling_wet',
|
|
|
|
|
|
'polish_prep',
|
|
|
|
|
|
]),
|
|
|
|
|
|
crown('temporary_resin_crown', 6, [
|
|
|
|
|
|
'intraoral_scan',
|
|
|
|
|
|
'design',
|
|
|
|
|
|
'milling_wet',
|
|
|
|
|
|
'polish_prep',
|
|
|
|
|
|
]),
|
2026-09-02 17:24:01 +03:30
|
|
|
|
crown('pmma', 7, ['intraoral_scan', 'design', 'milling_dry', 'polish_prep']),
|
feat(voice): adapt voice entry to the stacked-jobs prosthesis model
Authored by the /orchestrate builder agent, committed unrepaired so the
fixes that follow are reviewable against it.
Backend: replaces the flat prosthesisDefaultType/prosthesisOverrides wire
shape with a prosthesis: ProsthesisAssignment[] list whose targets can be a
tooth or a jaw; adds resolveAssignmentTarget / classifyTypeCode /
resolveProsthesisAssignment for leaf-vs-category classification, region
validity with mixed-region deferral, and assignmentIndex on unresolved
items; adds PROSTHESIS_CATEGORY and PROSTHESIS_SUBCATEGORY to
CatalogEntityKind with a migration and seeded fa/en/nl translations; and
rewrites the extraction prompt to render the catalog as a tree.
Frontend: merged "teeth and prosthesis" row, stack preview through the
existing applyLeafToJobs, three chip-fold paths, rewritten applyVoiceResult
and voiceForEditor, and the two carried-forward recording fixes — the
container fallback that refused Safari and the render gate that never
checked isMediaRecorderSupported().
Adds Vitest for the frontend's pure helpers, and updates CLAUDE.md.
Gate was green: backend 16 suites / 209 tests, nest build, prisma validate;
frontend 37 Vitest tests, tsc --noEmit, next build.
KNOWN DEFECTS, fixed in the commits that follow:
- VoiceReviewSheet.tsx:169 — a picked tooth chip is dropped on Apply
- VoiceReviewSheet.tsx:213 / TreatmentWorkspace.tsx:2215 — decision 41's
type-row lock is missing, so unticking it saves prosthesis lab rows on a
non-prosthesis detail
Reviewed on the correctness lens only; regression-risk never ran. The
migration was validated but never applied.
Spec: docs/specs/voice-treatment-entry/spec.md
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 12:23:58 +08:00
|
|
|
|
crown('peek_crown', 8, [
|
|
|
|
|
|
'intraoral_scan',
|
|
|
|
|
|
'design',
|
|
|
|
|
|
'milling_dry',
|
|
|
|
|
|
'polish_prep',
|
|
|
|
|
|
]),
|
2026-09-02 17:24:01 +03:30
|
|
|
|
crown('press_ceramic', 9, [
|
|
|
|
|
|
'intraoral_scan',
|
|
|
|
|
|
'design',
|
|
|
|
|
|
'printer_resin',
|
|
|
|
|
|
'pressing',
|
|
|
|
|
|
'stain',
|
|
|
|
|
|
'glaze',
|
|
|
|
|
|
'polish_prep',
|
|
|
|
|
|
]),
|
2026-09-01 21:03:04 +03:30
|
|
|
|
indirect('veneer', 'full_contour', 20),
|
|
|
|
|
|
indirect('veneer', 'layered', 21),
|
|
|
|
|
|
indirect('inlay', 'full_contour', 22),
|
|
|
|
|
|
indirect('inlay', 'layered', 23),
|
|
|
|
|
|
indirect('onlay', 'full_contour', 24),
|
|
|
|
|
|
indirect('onlay', 'layered', 25),
|
|
|
|
|
|
indirect('overlay', 'full_contour', 26),
|
|
|
|
|
|
indirect('overlay', 'layered', 27),
|
2026-07-06 20:40:19 +03:30
|
|
|
|
{
|
2026-09-01 21:03:04 +03:30
|
|
|
|
code: 'cast_post_core',
|
|
|
|
|
|
sortOrder: 30,
|
|
|
|
|
|
category: 'post_core',
|
|
|
|
|
|
chartRegion: 'root',
|
|
|
|
|
|
stackGroup: 'post_core',
|
|
|
|
|
|
manufacturingSteps: POST_CORE,
|
2026-07-06 20:40:19 +03:30
|
|
|
|
},
|
|
|
|
|
|
{
|
2026-09-01 21:03:04 +03:30
|
|
|
|
code: 'fiber_post_core',
|
|
|
|
|
|
sortOrder: 31,
|
|
|
|
|
|
category: 'post_core',
|
|
|
|
|
|
chartRegion: 'root',
|
|
|
|
|
|
stackGroup: 'post_core',
|
|
|
|
|
|
manufacturingSteps: POST_CORE,
|
2026-07-06 20:40:19 +03:30
|
|
|
|
},
|
2026-09-04 17:04:48 +03:30
|
|
|
|
implant('prefabricated_abutment', 40, [
|
2026-09-02 17:24:01 +03:30
|
|
|
|
'intraoral_scan',
|
|
|
|
|
|
'choosing_abutment',
|
|
|
|
|
|
'model_create',
|
|
|
|
|
|
'polish_prep',
|
|
|
|
|
|
]),
|
feat(voice): adapt voice entry to the stacked-jobs prosthesis model
Authored by the /orchestrate builder agent, committed unrepaired so the
fixes that follow are reviewable against it.
Backend: replaces the flat prosthesisDefaultType/prosthesisOverrides wire
shape with a prosthesis: ProsthesisAssignment[] list whose targets can be a
tooth or a jaw; adds resolveAssignmentTarget / classifyTypeCode /
resolveProsthesisAssignment for leaf-vs-category classification, region
validity with mixed-region deferral, and assignmentIndex on unresolved
items; adds PROSTHESIS_CATEGORY and PROSTHESIS_SUBCATEGORY to
CatalogEntityKind with a migration and seeded fa/en/nl translations; and
rewrites the extraction prompt to render the catalog as a tree.
Frontend: merged "teeth and prosthesis" row, stack preview through the
existing applyLeafToJobs, three chip-fold paths, rewritten applyVoiceResult
and voiceForEditor, and the two carried-forward recording fixes — the
container fallback that refused Safari and the render gate that never
checked isMediaRecorderSupported().
Adds Vitest for the frontend's pure helpers, and updates CLAUDE.md.
Gate was green: backend 16 suites / 209 tests, nest build, prisma validate;
frontend 37 Vitest tests, tsc --noEmit, next build.
KNOWN DEFECTS, fixed in the commits that follow:
- VoiceReviewSheet.tsx:169 — a picked tooth chip is dropped on Apply
- VoiceReviewSheet.tsx:213 / TreatmentWorkspace.tsx:2215 — decision 41's
type-row lock is missing, so unticking it saves prosthesis lab rows on a
non-prosthesis detail
Reviewed on the correctness lens only; regression-risk never ran. The
migration was validated but never applied.
Spec: docs/specs/voice-treatment-entry/spec.md
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 12:23:58 +08:00
|
|
|
|
implant('ti_base_abutment', 41, [
|
|
|
|
|
|
'intraoral_scan',
|
|
|
|
|
|
'model_create',
|
|
|
|
|
|
'polish_prep',
|
|
|
|
|
|
]),
|
2026-09-04 17:04:48 +03:30
|
|
|
|
implant('multi_unit_abutment', 42, [
|
2026-09-02 17:24:01 +03:30
|
|
|
|
'intraoral_scan',
|
|
|
|
|
|
'choosing_abutment',
|
|
|
|
|
|
'model_create',
|
|
|
|
|
|
'polish_prep',
|
|
|
|
|
|
]),
|
2026-09-04 17:04:48 +03:30
|
|
|
|
implant('customized_abutment', 43, [
|
2026-09-02 17:24:01 +03:30
|
|
|
|
...SCAN_DESIGN_MODEL,
|
|
|
|
|
|
'milling_wet',
|
|
|
|
|
|
'polish_prep',
|
|
|
|
|
|
]),
|
feat(voice): adapt voice entry to the stacked-jobs prosthesis model
Authored by the /orchestrate builder agent, committed unrepaired so the
fixes that follow are reviewable against it.
Backend: replaces the flat prosthesisDefaultType/prosthesisOverrides wire
shape with a prosthesis: ProsthesisAssignment[] list whose targets can be a
tooth or a jaw; adds resolveAssignmentTarget / classifyTypeCode /
resolveProsthesisAssignment for leaf-vs-category classification, region
validity with mixed-region deferral, and assignmentIndex on unresolved
items; adds PROSTHESIS_CATEGORY and PROSTHESIS_SUBCATEGORY to
CatalogEntityKind with a migration and seeded fa/en/nl translations; and
rewrites the extraction prompt to render the catalog as a tree.
Frontend: merged "teeth and prosthesis" row, stack preview through the
existing applyLeafToJobs, three chip-fold paths, rewritten applyVoiceResult
and voiceForEditor, and the two carried-forward recording fixes — the
container fallback that refused Safari and the render gate that never
checked isMediaRecorderSupported().
Adds Vitest for the frontend's pure helpers, and updates CLAUDE.md.
Gate was green: backend 16 suites / 209 tests, nest build, prisma validate;
frontend 37 Vitest tests, tsc --noEmit, next build.
KNOWN DEFECTS, fixed in the commits that follow:
- VoiceReviewSheet.tsx:169 — a picked tooth chip is dropped on Apply
- VoiceReviewSheet.tsx:213 / TreatmentWorkspace.tsx:2215 — decision 41's
type-row lock is missing, so unticking it saves prosthesis lab rows on a
non-prosthesis detail
Reviewed on the correctness lens only; regression-risk never ran. The
migration was validated but never applied.
Spec: docs/specs/voice-treatment-entry/spec.md
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 12:23:58 +08:00
|
|
|
|
implant('zirconia_abutment', 44, [
|
|
|
|
|
|
...SCAN_DESIGN_MODEL,
|
|
|
|
|
|
'milling_dry',
|
|
|
|
|
|
'sinter',
|
|
|
|
|
|
]),
|
2026-09-04 17:04:48 +03:30
|
|
|
|
implant(
|
2026-09-02 17:24:01 +03:30
|
|
|
|
'screw_retained',
|
|
|
|
|
|
45,
|
|
|
|
|
|
[...SCAN_DESIGN_MODEL, 'milling_wet', 'polish_prep'],
|
|
|
|
|
|
{ chartRegion: 'crown' },
|
|
|
|
|
|
),
|
2026-07-06 20:40:19 +03:30
|
|
|
|
{
|
2026-09-01 21:03:04 +03:30
|
|
|
|
code: 'complete_denture',
|
|
|
|
|
|
sortOrder: 50,
|
|
|
|
|
|
category: 'removable',
|
|
|
|
|
|
chartRegion: 'arch',
|
|
|
|
|
|
stackGroup: 'arch',
|
|
|
|
|
|
manufacturingSteps: DENTURE,
|
2026-07-06 20:40:19 +03:30
|
|
|
|
},
|
|
|
|
|
|
{
|
2026-09-01 21:03:04 +03:30
|
|
|
|
code: 'partial_denture',
|
|
|
|
|
|
sortOrder: 51,
|
|
|
|
|
|
category: 'removable',
|
2026-09-02 17:24:01 +03:30
|
|
|
|
chartRegion: 'crown',
|
2026-09-01 21:03:04 +03:30
|
|
|
|
stackGroup: 'arch',
|
|
|
|
|
|
manufacturingSteps: DENTURE,
|
2026-07-06 20:40:19 +03:30
|
|
|
|
},
|
|
|
|
|
|
{
|
2026-09-01 21:03:04 +03:30
|
|
|
|
code: 'overdenture',
|
|
|
|
|
|
sortOrder: 52,
|
|
|
|
|
|
category: 'removable',
|
|
|
|
|
|
chartRegion: 'arch',
|
|
|
|
|
|
stackGroup: 'arch',
|
|
|
|
|
|
manufacturingSteps: DENTURE,
|
2026-07-06 20:40:19 +03:30
|
|
|
|
},
|
|
|
|
|
|
{
|
2026-09-01 21:03:04 +03:30
|
|
|
|
code: 'night_guard_soft',
|
|
|
|
|
|
sortOrder: 60,
|
|
|
|
|
|
category: 'appliance',
|
|
|
|
|
|
subcategory: 'night_guard',
|
|
|
|
|
|
chartRegion: 'arch',
|
|
|
|
|
|
stackGroup: 'arch',
|
|
|
|
|
|
manufacturingSteps: APPLIANCE,
|
2026-07-06 20:40:19 +03:30
|
|
|
|
},
|
|
|
|
|
|
{
|
2026-09-01 21:03:04 +03:30
|
|
|
|
code: 'night_guard_hard',
|
|
|
|
|
|
sortOrder: 61,
|
|
|
|
|
|
category: 'appliance',
|
|
|
|
|
|
subcategory: 'night_guard',
|
|
|
|
|
|
chartRegion: 'arch',
|
|
|
|
|
|
stackGroup: 'arch',
|
|
|
|
|
|
manufacturingSteps: APPLIANCE,
|
2026-07-06 20:40:19 +03:30
|
|
|
|
},
|
|
|
|
|
|
{
|
2026-09-01 21:03:04 +03:30
|
|
|
|
code: 'night_guard_dual',
|
|
|
|
|
|
sortOrder: 62,
|
|
|
|
|
|
category: 'appliance',
|
|
|
|
|
|
subcategory: 'night_guard',
|
|
|
|
|
|
chartRegion: 'arch',
|
|
|
|
|
|
stackGroup: 'arch',
|
|
|
|
|
|
manufacturingSteps: APPLIANCE,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
code: 'bleaching_tray',
|
|
|
|
|
|
sortOrder: 63,
|
|
|
|
|
|
category: 'appliance',
|
|
|
|
|
|
chartRegion: 'arch',
|
|
|
|
|
|
stackGroup: 'arch',
|
|
|
|
|
|
manufacturingSteps: APPLIANCE,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
code: 'clear_aligner',
|
|
|
|
|
|
sortOrder: 64,
|
|
|
|
|
|
category: 'appliance',
|
|
|
|
|
|
chartRegion: 'arch',
|
|
|
|
|
|
stackGroup: 'arch',
|
|
|
|
|
|
manufacturingSteps: APPLIANCE,
|
2026-07-06 20:40:19 +03:30
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
code: 'soft_structure',
|
2026-09-01 21:03:04 +03:30
|
|
|
|
sortOrder: 65,
|
|
|
|
|
|
category: 'appliance',
|
|
|
|
|
|
chartRegion: 'arch',
|
|
|
|
|
|
stackGroup: 'arch',
|
2026-09-02 17:24:01 +03:30
|
|
|
|
manufacturingSteps: ['intraoral_scan', 'design', 'milling_dry', 'sinter'],
|
2026-07-06 20:40:19 +03:30
|
|
|
|
},
|
|
|
|
|
|
{
|
2026-09-01 21:03:04 +03:30
|
|
|
|
code: 'surgical_guide',
|
|
|
|
|
|
sortOrder: 70,
|
|
|
|
|
|
category: 'digital',
|
|
|
|
|
|
chartRegion: 'arch',
|
|
|
|
|
|
stackGroup: 'arch',
|
|
|
|
|
|
manufacturingSteps: APPLIANCE,
|
2026-07-06 20:40:19 +03:30
|
|
|
|
},
|
|
|
|
|
|
{
|
2026-09-01 21:03:04 +03:30
|
|
|
|
code: 'smile_design',
|
|
|
|
|
|
sortOrder: 71,
|
|
|
|
|
|
category: 'digital',
|
|
|
|
|
|
chartRegion: 'arch',
|
|
|
|
|
|
stackGroup: 'arch',
|
|
|
|
|
|
skipPackingShipping: true,
|
2026-09-02 17:24:01 +03:30
|
|
|
|
manufacturingSteps: ['intraoral_scan', 'design'],
|
2026-07-06 20:40:19 +03:30
|
|
|
|
},
|
|
|
|
|
|
{
|
2026-09-01 21:03:04 +03:30
|
|
|
|
code: 'mockup',
|
|
|
|
|
|
sortOrder: 72,
|
|
|
|
|
|
category: 'digital',
|
|
|
|
|
|
chartRegion: 'arch',
|
|
|
|
|
|
stackGroup: 'arch',
|
2026-09-02 17:24:01 +03:30
|
|
|
|
manufacturingSteps: ['intraoral_scan', 'design', 'model_create'],
|
2026-09-01 21:03:04 +03:30
|
|
|
|
},
|
|
|
|
|
|
// Legacy leaves — labels for historical cases; hidden from the picker.
|
|
|
|
|
|
{
|
|
|
|
|
|
code: 'veneer_zirconia',
|
|
|
|
|
|
sortOrder: 200,
|
|
|
|
|
|
isActive: false,
|
|
|
|
|
|
category: 'indirect',
|
|
|
|
|
|
subcategory: 'veneer',
|
|
|
|
|
|
chartRegion: 'crown',
|
|
|
|
|
|
stackGroup: 'restoration',
|
2026-09-02 17:24:01 +03:30
|
|
|
|
manufacturingSteps: KEEP_LAYERED,
|
2026-07-06 20:40:19 +03:30
|
|
|
|
},
|
|
|
|
|
|
{
|
2026-09-01 21:03:04 +03:30
|
|
|
|
code: 'veneer_ips_press',
|
|
|
|
|
|
sortOrder: 201,
|
|
|
|
|
|
isActive: false,
|
|
|
|
|
|
category: 'indirect',
|
|
|
|
|
|
subcategory: 'veneer',
|
|
|
|
|
|
chartRegion: 'crown',
|
|
|
|
|
|
stackGroup: 'restoration',
|
2026-09-02 17:24:01 +03:30
|
|
|
|
manufacturingSteps: [
|
|
|
|
|
|
'intraoral_scan',
|
|
|
|
|
|
'design',
|
|
|
|
|
|
'printer_resin',
|
|
|
|
|
|
'build_up',
|
|
|
|
|
|
'stain',
|
|
|
|
|
|
'glaze',
|
|
|
|
|
|
'polish_prep',
|
|
|
|
|
|
'pressing',
|
|
|
|
|
|
],
|
2026-07-06 20:40:19 +03:30
|
|
|
|
},
|
|
|
|
|
|
{
|
2026-09-01 21:03:04 +03:30
|
|
|
|
code: 'veneer_ips_cad',
|
|
|
|
|
|
sortOrder: 202,
|
|
|
|
|
|
isActive: false,
|
|
|
|
|
|
category: 'indirect',
|
|
|
|
|
|
subcategory: 'veneer',
|
|
|
|
|
|
chartRegion: 'crown',
|
|
|
|
|
|
stackGroup: 'restoration',
|
2026-09-02 17:24:01 +03:30
|
|
|
|
manufacturingSteps: KEEP_FULL_CONTOUR,
|
2026-07-06 20:40:19 +03:30
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
code: 'zirconia_overlay',
|
2026-09-01 21:03:04 +03:30
|
|
|
|
sortOrder: 203,
|
|
|
|
|
|
isActive: false,
|
|
|
|
|
|
category: 'indirect',
|
|
|
|
|
|
subcategory: 'overlay',
|
|
|
|
|
|
chartRegion: 'crown',
|
|
|
|
|
|
stackGroup: 'restoration',
|
2026-09-02 17:24:01 +03:30
|
|
|
|
manufacturingSteps: [
|
|
|
|
|
|
'intraoral_scan',
|
|
|
|
|
|
'design',
|
|
|
|
|
|
'milling_dry',
|
|
|
|
|
|
'sinter',
|
|
|
|
|
|
'stain',
|
|
|
|
|
|
'glaze',
|
|
|
|
|
|
'polish_prep',
|
|
|
|
|
|
],
|
2026-07-06 20:40:19 +03:30
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
code: 'ips_overlay',
|
2026-09-01 21:03:04 +03:30
|
|
|
|
sortOrder: 204,
|
|
|
|
|
|
isActive: false,
|
|
|
|
|
|
category: 'indirect',
|
|
|
|
|
|
subcategory: 'overlay',
|
|
|
|
|
|
chartRegion: 'crown',
|
|
|
|
|
|
stackGroup: 'restoration',
|
2026-09-02 17:24:01 +03:30
|
|
|
|
manufacturingSteps: KEEP_FULL_CONTOUR,
|
2026-07-06 20:40:19 +03:30
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
|
2026-09-02 17:24:01 +03:30
|
|
|
|
const PACK_SHIP = ['packing', 'shipping'] as const;
|
2026-07-06 20:40:19 +03:30
|
|
|
|
|
|
|
|
|
|
export function buildProsthesisStepCodes(type: ProsthesisTypeSeed): string[] {
|
2026-09-02 17:24:01 +03:30
|
|
|
|
const steps = [...type.manufacturingSteps];
|
2026-07-06 20:40:19 +03:30
|
|
|
|
if (!type.skipPackingShipping) {
|
2026-09-02 17:24:01 +03:30
|
|
|
|
steps.push(...PACK_SHIP);
|
2026-07-06 20:40:19 +03:30
|
|
|
|
}
|
|
|
|
|
|
return steps;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-01 21:03:04 +03:30
|
|
|
|
export {
|
|
|
|
|
|
unionWorkflowStepCodes,
|
|
|
|
|
|
prosthesisGroupKey,
|
|
|
|
|
|
splitProsthesisGroupKey,
|
|
|
|
|
|
} from '../src/common/prosthesis-group';
|
|
|
|
|
|
|
2026-07-06 20:40:19 +03:30
|
|
|
|
const TREATMENT_LABELS: Record<string, Record<string, string>> = {
|
|
|
|
|
|
restoration: { en: 'Restoration', fa: 'ترمیم', nl: 'Restauratie' },
|
feat(voice): adapt voice entry to the stacked-jobs prosthesis model
Authored by the /orchestrate builder agent, committed unrepaired so the
fixes that follow are reviewable against it.
Backend: replaces the flat prosthesisDefaultType/prosthesisOverrides wire
shape with a prosthesis: ProsthesisAssignment[] list whose targets can be a
tooth or a jaw; adds resolveAssignmentTarget / classifyTypeCode /
resolveProsthesisAssignment for leaf-vs-category classification, region
validity with mixed-region deferral, and assignmentIndex on unresolved
items; adds PROSTHESIS_CATEGORY and PROSTHESIS_SUBCATEGORY to
CatalogEntityKind with a migration and seeded fa/en/nl translations; and
rewrites the extraction prompt to render the catalog as a tree.
Frontend: merged "teeth and prosthesis" row, stack preview through the
existing applyLeafToJobs, three chip-fold paths, rewritten applyVoiceResult
and voiceForEditor, and the two carried-forward recording fixes — the
container fallback that refused Safari and the render gate that never
checked isMediaRecorderSupported().
Adds Vitest for the frontend's pure helpers, and updates CLAUDE.md.
Gate was green: backend 16 suites / 209 tests, nest build, prisma validate;
frontend 37 Vitest tests, tsc --noEmit, next build.
KNOWN DEFECTS, fixed in the commits that follow:
- VoiceReviewSheet.tsx:169 — a picked tooth chip is dropped on Apply
- VoiceReviewSheet.tsx:213 / TreatmentWorkspace.tsx:2215 — decision 41's
type-row lock is missing, so unticking it saves prosthesis lab rows on a
non-prosthesis detail
Reviewed on the correctness lens only; regression-risk never ran. The
migration was validated but never applied.
Spec: docs/specs/voice-treatment-entry/spec.md
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 12:23:58 +08:00
|
|
|
|
specialized_restoration: {
|
|
|
|
|
|
en: 'Specialized Restoration',
|
|
|
|
|
|
fa: 'ترمیم تخصصی',
|
|
|
|
|
|
nl: 'Gespecialiseerde Restauratie',
|
|
|
|
|
|
},
|
2026-07-06 20:40:19 +03:30
|
|
|
|
radiography: { en: 'Radiography', fa: 'رادیوگرافی', nl: 'Röntgen' },
|
|
|
|
|
|
endo: { en: 'Endo', fa: 'اندو', nl: 'Endo' },
|
|
|
|
|
|
surgery: { en: 'Surgery', fa: 'جراحی', nl: 'Chirurgie' },
|
|
|
|
|
|
prosthesis: { en: 'Prosthesis', fa: 'پروتز', nl: 'Prothese' },
|
|
|
|
|
|
implant: { en: 'Implant', fa: 'ایمپلنت', nl: 'Implantaat' },
|
|
|
|
|
|
orthodontics: { en: 'Orthodontics', fa: 'ارتودنسی', nl: 'Orthodontie' },
|
|
|
|
|
|
perio: { en: 'Perio', fa: 'پریو', nl: 'Paro' },
|
|
|
|
|
|
pediatrics: { en: 'Pediatrics', fa: 'اطفال', nl: 'Kinderen' },
|
|
|
|
|
|
extraction: { en: 'Extraction', fa: 'کشیدن', nl: 'Extractie' },
|
feat(voice): adapt voice entry to the stacked-jobs prosthesis model
Authored by the /orchestrate builder agent, committed unrepaired so the
fixes that follow are reviewable against it.
Backend: replaces the flat prosthesisDefaultType/prosthesisOverrides wire
shape with a prosthesis: ProsthesisAssignment[] list whose targets can be a
tooth or a jaw; adds resolveAssignmentTarget / classifyTypeCode /
resolveProsthesisAssignment for leaf-vs-category classification, region
validity with mixed-region deferral, and assignmentIndex on unresolved
items; adds PROSTHESIS_CATEGORY and PROSTHESIS_SUBCATEGORY to
CatalogEntityKind with a migration and seeded fa/en/nl translations; and
rewrites the extraction prompt to render the catalog as a tree.
Frontend: merged "teeth and prosthesis" row, stack preview through the
existing applyLeafToJobs, three chip-fold paths, rewritten applyVoiceResult
and voiceForEditor, and the two carried-forward recording fixes — the
container fallback that refused Safari and the render gate that never
checked isMediaRecorderSupported().
Adds Vitest for the frontend's pure helpers, and updates CLAUDE.md.
Gate was green: backend 16 suites / 209 tests, nest build, prisma validate;
frontend 37 Vitest tests, tsc --noEmit, next build.
KNOWN DEFECTS, fixed in the commits that follow:
- VoiceReviewSheet.tsx:169 — a picked tooth chip is dropped on Apply
- VoiceReviewSheet.tsx:213 / TreatmentWorkspace.tsx:2215 — decision 41's
type-row lock is missing, so unticking it saves prosthesis lab rows on a
non-prosthesis detail
Reviewed on the correctness lens only; regression-risk never ran. The
migration was validated but never applied.
Spec: docs/specs/voice-treatment-entry/spec.md
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 12:23:58 +08:00
|
|
|
|
clinic_visit: {
|
|
|
|
|
|
en: 'Clinic Visit',
|
|
|
|
|
|
fa: 'ویزیت درمانگاه',
|
|
|
|
|
|
nl: 'Kliniekbezoek',
|
|
|
|
|
|
},
|
|
|
|
|
|
continue_treatment: {
|
|
|
|
|
|
en: 'Continue Treatment',
|
|
|
|
|
|
fa: 'ادامه درمان',
|
|
|
|
|
|
nl: 'Behandeling Voortzetten',
|
|
|
|
|
|
},
|
2026-07-06 20:40:19 +03:30
|
|
|
|
consultation: { en: 'Consultation', fa: 'مشاوره', nl: 'Consult' },
|
|
|
|
|
|
filling: { en: 'Filling', fa: 'پر کردن', nl: 'Vulling' },
|
|
|
|
|
|
visit: { en: 'Visit', fa: 'ویزیت', nl: 'Bezoek' },
|
|
|
|
|
|
hygiene: { en: 'Hygiene', fa: 'بهداشت', nl: 'Hygiëne' },
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-09-01 21:03:04 +03:30
|
|
|
|
export const PROSTHESIS_LABELS: Record<string, Record<string, string>> = {
|
2026-07-06 20:40:19 +03:30
|
|
|
|
pfm_crown: { en: 'PFM Crown', fa: 'روکش PFM', nl: 'PFM Kroon' },
|
|
|
|
|
|
pfz_crown: { en: 'PFZ Crown', fa: 'روکش PFZ', nl: 'PFZ Kroon' },
|
feat(voice): adapt voice entry to the stacked-jobs prosthesis model
Authored by the /orchestrate builder agent, committed unrepaired so the
fixes that follow are reviewable against it.
Backend: replaces the flat prosthesisDefaultType/prosthesisOverrides wire
shape with a prosthesis: ProsthesisAssignment[] list whose targets can be a
tooth or a jaw; adds resolveAssignmentTarget / classifyTypeCode /
resolveProsthesisAssignment for leaf-vs-category classification, region
validity with mixed-region deferral, and assignmentIndex on unresolved
items; adds PROSTHESIS_CATEGORY and PROSTHESIS_SUBCATEGORY to
CatalogEntityKind with a migration and seeded fa/en/nl translations; and
rewrites the extraction prompt to render the catalog as a tree.
Frontend: merged "teeth and prosthesis" row, stack preview through the
existing applyLeafToJobs, three chip-fold paths, rewritten applyVoiceResult
and voiceForEditor, and the two carried-forward recording fixes — the
container fallback that refused Safari and the render gate that never
checked isMediaRecorderSupported().
Adds Vitest for the frontend's pure helpers, and updates CLAUDE.md.
Gate was green: backend 16 suites / 209 tests, nest build, prisma validate;
frontend 37 Vitest tests, tsc --noEmit, next build.
KNOWN DEFECTS, fixed in the commits that follow:
- VoiceReviewSheet.tsx:169 — a picked tooth chip is dropped on Apply
- VoiceReviewSheet.tsx:213 / TreatmentWorkspace.tsx:2215 — decision 41's
type-row lock is missing, so unticking it saves prosthesis lab rows on a
non-prosthesis detail
Reviewed on the correctness lens only; regression-risk never ran. The
migration was validated but never applied.
Spec: docs/specs/voice-treatment-entry/spec.md
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 12:23:58 +08:00
|
|
|
|
monolithic_zirconia: {
|
|
|
|
|
|
en: 'Monolithic Zirconia',
|
|
|
|
|
|
fa: 'زیرکونیا مونولیتیک',
|
|
|
|
|
|
nl: 'Monolithisch Zirconia',
|
|
|
|
|
|
},
|
|
|
|
|
|
glass_ceramic_crown: {
|
|
|
|
|
|
en: 'Glass Ceramic Crown',
|
|
|
|
|
|
fa: 'روکش سرامیک شیشهای',
|
|
|
|
|
|
nl: 'Glaskeramische Kroon',
|
|
|
|
|
|
},
|
|
|
|
|
|
full_metal_crown: {
|
|
|
|
|
|
en: 'Full Metal Crown',
|
|
|
|
|
|
fa: 'روکش تمام فلز',
|
|
|
|
|
|
nl: 'Volledige Metalen Kroon',
|
|
|
|
|
|
},
|
|
|
|
|
|
temporary_resin_crown: {
|
|
|
|
|
|
en: 'Temporary Resin Crown',
|
|
|
|
|
|
fa: 'روکش موقت رزینی',
|
|
|
|
|
|
nl: 'Tijdelijke Harskroon',
|
|
|
|
|
|
},
|
2026-07-06 20:40:19 +03:30
|
|
|
|
pmma: { en: 'PMMA', fa: 'PMMA', nl: 'PMMA' },
|
|
|
|
|
|
peek_crown: { en: 'PEEK Crown', fa: 'روکش PEEK', nl: 'PEEK Kroon' },
|
feat(voice): adapt voice entry to the stacked-jobs prosthesis model
Authored by the /orchestrate builder agent, committed unrepaired so the
fixes that follow are reviewable against it.
Backend: replaces the flat prosthesisDefaultType/prosthesisOverrides wire
shape with a prosthesis: ProsthesisAssignment[] list whose targets can be a
tooth or a jaw; adds resolveAssignmentTarget / classifyTypeCode /
resolveProsthesisAssignment for leaf-vs-category classification, region
validity with mixed-region deferral, and assignmentIndex on unresolved
items; adds PROSTHESIS_CATEGORY and PROSTHESIS_SUBCATEGORY to
CatalogEntityKind with a migration and seeded fa/en/nl translations; and
rewrites the extraction prompt to render the catalog as a tree.
Frontend: merged "teeth and prosthesis" row, stack preview through the
existing applyLeafToJobs, three chip-fold paths, rewritten applyVoiceResult
and voiceForEditor, and the two carried-forward recording fixes — the
container fallback that refused Safari and the render gate that never
checked isMediaRecorderSupported().
Adds Vitest for the frontend's pure helpers, and updates CLAUDE.md.
Gate was green: backend 16 suites / 209 tests, nest build, prisma validate;
frontend 37 Vitest tests, tsc --noEmit, next build.
KNOWN DEFECTS, fixed in the commits that follow:
- VoiceReviewSheet.tsx:169 — a picked tooth chip is dropped on Apply
- VoiceReviewSheet.tsx:213 / TreatmentWorkspace.tsx:2215 — decision 41's
type-row lock is missing, so unticking it saves prosthesis lab rows on a
non-prosthesis detail
Reviewed on the correctness lens only; regression-risk never ran. The
migration was validated but never applied.
Spec: docs/specs/voice-treatment-entry/spec.md
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 12:23:58 +08:00
|
|
|
|
press_ceramic: {
|
|
|
|
|
|
en: 'Pressed Ceramic / IPS e.max',
|
|
|
|
|
|
fa: 'سرامیک پرس / IPS e.max',
|
|
|
|
|
|
nl: 'Perskeramiek / IPS e.max',
|
|
|
|
|
|
},
|
|
|
|
|
|
veneer_full_contour: {
|
|
|
|
|
|
en: 'Veneer · Full contour',
|
|
|
|
|
|
fa: 'ونیر · تمامکانتور',
|
|
|
|
|
|
nl: 'Veneer · Full contour',
|
|
|
|
|
|
},
|
|
|
|
|
|
veneer_layered: {
|
|
|
|
|
|
en: 'Veneer · Layered ceramic',
|
|
|
|
|
|
fa: 'ونیر · سرامیک لایهای',
|
|
|
|
|
|
nl: 'Veneer · Gelaagd keramiek',
|
|
|
|
|
|
},
|
|
|
|
|
|
inlay_full_contour: {
|
|
|
|
|
|
en: 'Inlay · Full contour',
|
|
|
|
|
|
fa: 'اینلی · تمامکانتور',
|
|
|
|
|
|
nl: 'Inlay · Full contour',
|
|
|
|
|
|
},
|
|
|
|
|
|
inlay_layered: {
|
|
|
|
|
|
en: 'Inlay · Layered ceramic',
|
|
|
|
|
|
fa: 'اینلی · سرامیک لایهای',
|
|
|
|
|
|
nl: 'Inlay · Gelaagd keramiek',
|
|
|
|
|
|
},
|
|
|
|
|
|
onlay_full_contour: {
|
|
|
|
|
|
en: 'Onlay · Full contour',
|
|
|
|
|
|
fa: 'آنلی · تمامکانتور',
|
|
|
|
|
|
nl: 'Onlay · Full contour',
|
|
|
|
|
|
},
|
|
|
|
|
|
onlay_layered: {
|
|
|
|
|
|
en: 'Onlay · Layered ceramic',
|
|
|
|
|
|
fa: 'آنلی · سرامیک لایهای',
|
|
|
|
|
|
nl: 'Onlay · Gelaagd keramiek',
|
|
|
|
|
|
},
|
|
|
|
|
|
overlay_full_contour: {
|
|
|
|
|
|
en: 'Overlay · Full contour',
|
|
|
|
|
|
fa: 'اورلی · تمامکانتور',
|
|
|
|
|
|
nl: 'Overlay · Full contour',
|
|
|
|
|
|
},
|
|
|
|
|
|
overlay_layered: {
|
|
|
|
|
|
en: 'Overlay · Layered ceramic',
|
|
|
|
|
|
fa: 'اورلی · سرامیک لایهای',
|
|
|
|
|
|
nl: 'Overlay · Gelaagd keramiek',
|
|
|
|
|
|
},
|
|
|
|
|
|
cast_post_core: {
|
|
|
|
|
|
en: 'Cast Post & Core',
|
|
|
|
|
|
fa: 'پست و کور ریختگی',
|
|
|
|
|
|
nl: 'Gegoten Stiftopbouw',
|
|
|
|
|
|
},
|
|
|
|
|
|
fiber_post_core: {
|
|
|
|
|
|
en: 'Fiber Post & Core',
|
|
|
|
|
|
fa: 'پست و کور فایبر',
|
|
|
|
|
|
nl: 'Fiber Stiftopbouw',
|
|
|
|
|
|
},
|
|
|
|
|
|
customized_abutment: {
|
|
|
|
|
|
en: 'Custom Abutment (Titanium)',
|
|
|
|
|
|
fa: 'اباتمنت سفارشی (تیتانیوم)',
|
|
|
|
|
|
nl: 'Aangepast abutment (titanium)',
|
|
|
|
|
|
},
|
|
|
|
|
|
prefabricated_abutment: {
|
|
|
|
|
|
en: 'Prefabricated Abutment',
|
|
|
|
|
|
fa: 'اباتمنت آماده',
|
|
|
|
|
|
nl: 'Prefab Abutment',
|
|
|
|
|
|
},
|
|
|
|
|
|
ti_base_abutment: {
|
|
|
|
|
|
en: 'Ti Base Abutment',
|
|
|
|
|
|
fa: 'اباتمنت پایه تیتانیوم',
|
|
|
|
|
|
nl: 'Ti Basis Abutment',
|
|
|
|
|
|
},
|
|
|
|
|
|
multi_unit_abutment: {
|
|
|
|
|
|
en: 'Multi Unit Abutment',
|
|
|
|
|
|
fa: 'اباتمنت مولتی یونیت',
|
|
|
|
|
|
nl: 'Multi Unit Abutment',
|
|
|
|
|
|
},
|
|
|
|
|
|
zirconia_abutment: {
|
|
|
|
|
|
en: 'Custom Abutment (Zirconia)',
|
|
|
|
|
|
fa: 'اباتمنت سفارشی (زیرکونیا)',
|
|
|
|
|
|
nl: 'Aangepast abutment (zirconia)',
|
|
|
|
|
|
},
|
2026-07-06 20:40:19 +03:30
|
|
|
|
screw_retained: { en: 'Screw Retained', fa: 'پیچی', nl: 'Schroefgehouden' },
|
feat(voice): adapt voice entry to the stacked-jobs prosthesis model
Authored by the /orchestrate builder agent, committed unrepaired so the
fixes that follow are reviewable against it.
Backend: replaces the flat prosthesisDefaultType/prosthesisOverrides wire
shape with a prosthesis: ProsthesisAssignment[] list whose targets can be a
tooth or a jaw; adds resolveAssignmentTarget / classifyTypeCode /
resolveProsthesisAssignment for leaf-vs-category classification, region
validity with mixed-region deferral, and assignmentIndex on unresolved
items; adds PROSTHESIS_CATEGORY and PROSTHESIS_SUBCATEGORY to
CatalogEntityKind with a migration and seeded fa/en/nl translations; and
rewrites the extraction prompt to render the catalog as a tree.
Frontend: merged "teeth and prosthesis" row, stack preview through the
existing applyLeafToJobs, three chip-fold paths, rewritten applyVoiceResult
and voiceForEditor, and the two carried-forward recording fixes — the
container fallback that refused Safari and the render gate that never
checked isMediaRecorderSupported().
Adds Vitest for the frontend's pure helpers, and updates CLAUDE.md.
Gate was green: backend 16 suites / 209 tests, nest build, prisma validate;
frontend 37 Vitest tests, tsc --noEmit, next build.
KNOWN DEFECTS, fixed in the commits that follow:
- VoiceReviewSheet.tsx:169 — a picked tooth chip is dropped on Apply
- VoiceReviewSheet.tsx:213 / TreatmentWorkspace.tsx:2215 — decision 41's
type-row lock is missing, so unticking it saves prosthesis lab rows on a
non-prosthesis detail
Reviewed on the correctness lens only; regression-risk never ran. The
migration was validated but never applied.
Spec: docs/specs/voice-treatment-entry/spec.md
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 12:23:58 +08:00
|
|
|
|
complete_denture: {
|
|
|
|
|
|
en: 'Complete Denture',
|
|
|
|
|
|
fa: 'دنچر کامل',
|
|
|
|
|
|
nl: 'Volledige prothese',
|
|
|
|
|
|
},
|
|
|
|
|
|
partial_denture: {
|
|
|
|
|
|
en: 'Partial Denture',
|
|
|
|
|
|
fa: 'دنچر پارسیل',
|
|
|
|
|
|
nl: 'Partiële prothese',
|
|
|
|
|
|
},
|
2026-09-01 21:03:04 +03:30
|
|
|
|
overdenture: { en: 'Overdenture', fa: 'اوردنچر', nl: 'Overkappingsprothese' },
|
feat(voice): adapt voice entry to the stacked-jobs prosthesis model
Authored by the /orchestrate builder agent, committed unrepaired so the
fixes that follow are reviewable against it.
Backend: replaces the flat prosthesisDefaultType/prosthesisOverrides wire
shape with a prosthesis: ProsthesisAssignment[] list whose targets can be a
tooth or a jaw; adds resolveAssignmentTarget / classifyTypeCode /
resolveProsthesisAssignment for leaf-vs-category classification, region
validity with mixed-region deferral, and assignmentIndex on unresolved
items; adds PROSTHESIS_CATEGORY and PROSTHESIS_SUBCATEGORY to
CatalogEntityKind with a migration and seeded fa/en/nl translations; and
rewrites the extraction prompt to render the catalog as a tree.
Frontend: merged "teeth and prosthesis" row, stack preview through the
existing applyLeafToJobs, three chip-fold paths, rewritten applyVoiceResult
and voiceForEditor, and the two carried-forward recording fixes — the
container fallback that refused Safari and the render gate that never
checked isMediaRecorderSupported().
Adds Vitest for the frontend's pure helpers, and updates CLAUDE.md.
Gate was green: backend 16 suites / 209 tests, nest build, prisma validate;
frontend 37 Vitest tests, tsc --noEmit, next build.
KNOWN DEFECTS, fixed in the commits that follow:
- VoiceReviewSheet.tsx:169 — a picked tooth chip is dropped on Apply
- VoiceReviewSheet.tsx:213 / TreatmentWorkspace.tsx:2215 — decision 41's
type-row lock is missing, so unticking it saves prosthesis lab rows on a
non-prosthesis detail
Reviewed on the correctness lens only; regression-risk never ran. The
migration was validated but never applied.
Spec: docs/specs/voice-treatment-entry/spec.md
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 12:23:58 +08:00
|
|
|
|
night_guard_soft: {
|
|
|
|
|
|
en: 'Night Guard · Soft',
|
|
|
|
|
|
fa: 'نایت گارد · نرم',
|
|
|
|
|
|
nl: 'Nachtbeugel · Zacht',
|
|
|
|
|
|
},
|
|
|
|
|
|
night_guard_hard: {
|
|
|
|
|
|
en: 'Night Guard · Hard',
|
|
|
|
|
|
fa: 'نایت گارد · سخت',
|
|
|
|
|
|
nl: 'Nachtbeugel · Hard',
|
|
|
|
|
|
},
|
|
|
|
|
|
night_guard_dual: {
|
|
|
|
|
|
en: 'Night Guard · Dual laminate',
|
|
|
|
|
|
fa: 'نایت گارد · دو لایه',
|
|
|
|
|
|
nl: 'Nachtbeugel · Dual laminate',
|
|
|
|
|
|
},
|
2026-09-01 21:03:04 +03:30
|
|
|
|
bleaching_tray: { en: 'Bleaching Tray', fa: 'تری بلیچینگ', nl: 'Bleeklepel' },
|
feat(voice): adapt voice entry to the stacked-jobs prosthesis model
Authored by the /orchestrate builder agent, committed unrepaired so the
fixes that follow are reviewable against it.
Backend: replaces the flat prosthesisDefaultType/prosthesisOverrides wire
shape with a prosthesis: ProsthesisAssignment[] list whose targets can be a
tooth or a jaw; adds resolveAssignmentTarget / classifyTypeCode /
resolveProsthesisAssignment for leaf-vs-category classification, region
validity with mixed-region deferral, and assignmentIndex on unresolved
items; adds PROSTHESIS_CATEGORY and PROSTHESIS_SUBCATEGORY to
CatalogEntityKind with a migration and seeded fa/en/nl translations; and
rewrites the extraction prompt to render the catalog as a tree.
Frontend: merged "teeth and prosthesis" row, stack preview through the
existing applyLeafToJobs, three chip-fold paths, rewritten applyVoiceResult
and voiceForEditor, and the two carried-forward recording fixes — the
container fallback that refused Safari and the render gate that never
checked isMediaRecorderSupported().
Adds Vitest for the frontend's pure helpers, and updates CLAUDE.md.
Gate was green: backend 16 suites / 209 tests, nest build, prisma validate;
frontend 37 Vitest tests, tsc --noEmit, next build.
KNOWN DEFECTS, fixed in the commits that follow:
- VoiceReviewSheet.tsx:169 — a picked tooth chip is dropped on Apply
- VoiceReviewSheet.tsx:213 / TreatmentWorkspace.tsx:2215 — decision 41's
type-row lock is missing, so unticking it saves prosthesis lab rows on a
non-prosthesis detail
Reviewed on the correctness lens only; regression-risk never ran. The
migration was validated but never applied.
Spec: docs/specs/voice-treatment-entry/spec.md
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 12:23:58 +08:00
|
|
|
|
clear_aligner: {
|
|
|
|
|
|
en: 'Clear Aligner',
|
|
|
|
|
|
fa: 'الاینر شفاف',
|
|
|
|
|
|
nl: 'Clear aligner',
|
|
|
|
|
|
},
|
|
|
|
|
|
soft_structure: {
|
|
|
|
|
|
en: 'Soft Structure',
|
|
|
|
|
|
fa: 'ساختار نرم',
|
|
|
|
|
|
nl: 'Zachte Structuur',
|
|
|
|
|
|
},
|
|
|
|
|
|
surgical_guide: {
|
|
|
|
|
|
en: 'Surgical Guide',
|
|
|
|
|
|
fa: 'گاید جراحی',
|
|
|
|
|
|
nl: 'Chirurgische mal',
|
|
|
|
|
|
},
|
2026-07-06 20:40:19 +03:30
|
|
|
|
smile_design: { en: 'Smile Design', fa: 'طراحی لبخند', nl: 'Smile Design' },
|
|
|
|
|
|
mockup: { en: 'Mockup', fa: 'ماکاپ', nl: 'Mockup' },
|
feat(voice): adapt voice entry to the stacked-jobs prosthesis model
Authored by the /orchestrate builder agent, committed unrepaired so the
fixes that follow are reviewable against it.
Backend: replaces the flat prosthesisDefaultType/prosthesisOverrides wire
shape with a prosthesis: ProsthesisAssignment[] list whose targets can be a
tooth or a jaw; adds resolveAssignmentTarget / classifyTypeCode /
resolveProsthesisAssignment for leaf-vs-category classification, region
validity with mixed-region deferral, and assignmentIndex on unresolved
items; adds PROSTHESIS_CATEGORY and PROSTHESIS_SUBCATEGORY to
CatalogEntityKind with a migration and seeded fa/en/nl translations; and
rewrites the extraction prompt to render the catalog as a tree.
Frontend: merged "teeth and prosthesis" row, stack preview through the
existing applyLeafToJobs, three chip-fold paths, rewritten applyVoiceResult
and voiceForEditor, and the two carried-forward recording fixes — the
container fallback that refused Safari and the render gate that never
checked isMediaRecorderSupported().
Adds Vitest for the frontend's pure helpers, and updates CLAUDE.md.
Gate was green: backend 16 suites / 209 tests, nest build, prisma validate;
frontend 37 Vitest tests, tsc --noEmit, next build.
KNOWN DEFECTS, fixed in the commits that follow:
- VoiceReviewSheet.tsx:169 — a picked tooth chip is dropped on Apply
- VoiceReviewSheet.tsx:213 / TreatmentWorkspace.tsx:2215 — decision 41's
type-row lock is missing, so unticking it saves prosthesis lab rows on a
non-prosthesis detail
Reviewed on the correctness lens only; regression-risk never ran. The
migration was validated but never applied.
Spec: docs/specs/voice-treatment-entry/spec.md
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 12:23:58 +08:00
|
|
|
|
veneer_zirconia: {
|
|
|
|
|
|
en: 'Veneer Zirconia',
|
|
|
|
|
|
fa: 'ونیر زیرکونیا',
|
|
|
|
|
|
nl: 'Veneer Zirconia',
|
|
|
|
|
|
},
|
|
|
|
|
|
veneer_ips_press: {
|
|
|
|
|
|
en: 'Veneer IPS Press',
|
|
|
|
|
|
fa: 'ونیر IPS پرس',
|
|
|
|
|
|
nl: 'Veneer IPS Press',
|
|
|
|
|
|
},
|
|
|
|
|
|
veneer_ips_cad: {
|
|
|
|
|
|
en: 'Veneer IPS CAD',
|
|
|
|
|
|
fa: 'ونیر IPS CAD',
|
|
|
|
|
|
nl: 'Veneer IPS CAD',
|
|
|
|
|
|
},
|
|
|
|
|
|
zirconia_overlay: {
|
|
|
|
|
|
en: 'Zirconia Overlay',
|
|
|
|
|
|
fa: 'اورلی زیرکونیا',
|
|
|
|
|
|
nl: 'Zirconia Overlay',
|
|
|
|
|
|
},
|
2026-09-01 21:03:04 +03:30
|
|
|
|
ips_overlay: { en: 'IPS Overlay', fa: 'اورلی IPS', nl: 'IPS Overlay' },
|
2026-07-06 20:40:19 +03:30
|
|
|
|
};
|
|
|
|
|
|
|
2026-09-01 21:03:04 +03:30
|
|
|
|
export const WORKFLOW_STEP_LABELS: Record<string, Record<string, string>> = {
|
feat(voice): adapt voice entry to the stacked-jobs prosthesis model
Authored by the /orchestrate builder agent, committed unrepaired so the
fixes that follow are reviewable against it.
Backend: replaces the flat prosthesisDefaultType/prosthesisOverrides wire
shape with a prosthesis: ProsthesisAssignment[] list whose targets can be a
tooth or a jaw; adds resolveAssignmentTarget / classifyTypeCode /
resolveProsthesisAssignment for leaf-vs-category classification, region
validity with mixed-region deferral, and assignmentIndex on unresolved
items; adds PROSTHESIS_CATEGORY and PROSTHESIS_SUBCATEGORY to
CatalogEntityKind with a migration and seeded fa/en/nl translations; and
rewrites the extraction prompt to render the catalog as a tree.
Frontend: merged "teeth and prosthesis" row, stack preview through the
existing applyLeafToJobs, three chip-fold paths, rewritten applyVoiceResult
and voiceForEditor, and the two carried-forward recording fixes — the
container fallback that refused Safari and the render gate that never
checked isMediaRecorderSupported().
Adds Vitest for the frontend's pure helpers, and updates CLAUDE.md.
Gate was green: backend 16 suites / 209 tests, nest build, prisma validate;
frontend 37 Vitest tests, tsc --noEmit, next build.
KNOWN DEFECTS, fixed in the commits that follow:
- VoiceReviewSheet.tsx:169 — a picked tooth chip is dropped on Apply
- VoiceReviewSheet.tsx:213 / TreatmentWorkspace.tsx:2215 — decision 41's
type-row lock is missing, so unticking it saves prosthesis lab rows on a
non-prosthesis detail
Reviewed on the correctness lens only; regression-risk never ran. The
migration was validated but never applied.
Spec: docs/specs/voice-treatment-entry/spec.md
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 12:23:58 +08:00
|
|
|
|
intraoral_scan: {
|
|
|
|
|
|
en: 'Intraoral Scan',
|
|
|
|
|
|
fa: 'اسکن داخل دهان',
|
|
|
|
|
|
nl: 'Intraorale Scan',
|
|
|
|
|
|
},
|
2026-09-02 17:24:01 +03:30
|
|
|
|
choosing_abutment: {
|
|
|
|
|
|
en: 'Choosing Abutment',
|
|
|
|
|
|
fa: 'انتخاب اباتمنت',
|
|
|
|
|
|
nl: 'Abutment kiezen',
|
|
|
|
|
|
},
|
2026-07-06 20:40:19 +03:30
|
|
|
|
design: { en: 'Design', fa: 'طراحی', nl: 'Ontwerp' },
|
2026-09-02 17:24:01 +03:30
|
|
|
|
model_create: { en: 'Model Create', fa: 'ساخت مدل', nl: 'Model maken' },
|
2026-07-06 20:40:19 +03:30
|
|
|
|
milling_dry: { en: 'Milling Dry', fa: 'فرز خشک', nl: 'Droog Frezen' },
|
|
|
|
|
|
milling_wet: { en: 'Milling Wet', fa: 'فرز تر', nl: 'Nat Frezen' },
|
|
|
|
|
|
printer_resin: { en: 'Printer Resin', fa: 'پرینتر رزین', nl: 'Harsprinter' },
|
|
|
|
|
|
printer_metal: { en: 'Printer Metal', fa: 'پرینتر فلز', nl: 'Metaalprinter' },
|
|
|
|
|
|
sinter: { en: 'Sinter', fa: 'سینتر', nl: 'Sinteren' },
|
|
|
|
|
|
build_up: { en: 'Build Up', fa: 'بیلدآپ', nl: 'Opbouw' },
|
|
|
|
|
|
stain: { en: 'Stain', fa: 'رنگآمیزی', nl: 'Kleuren' },
|
|
|
|
|
|
glaze: { en: 'Glaze', fa: 'گلیز', nl: 'Glazuur' },
|
feat(voice): adapt voice entry to the stacked-jobs prosthesis model
Authored by the /orchestrate builder agent, committed unrepaired so the
fixes that follow are reviewable against it.
Backend: replaces the flat prosthesisDefaultType/prosthesisOverrides wire
shape with a prosthesis: ProsthesisAssignment[] list whose targets can be a
tooth or a jaw; adds resolveAssignmentTarget / classifyTypeCode /
resolveProsthesisAssignment for leaf-vs-category classification, region
validity with mixed-region deferral, and assignmentIndex on unresolved
items; adds PROSTHESIS_CATEGORY and PROSTHESIS_SUBCATEGORY to
CatalogEntityKind with a migration and seeded fa/en/nl translations; and
rewrites the extraction prompt to render the catalog as a tree.
Frontend: merged "teeth and prosthesis" row, stack preview through the
existing applyLeafToJobs, three chip-fold paths, rewritten applyVoiceResult
and voiceForEditor, and the two carried-forward recording fixes — the
container fallback that refused Safari and the render gate that never
checked isMediaRecorderSupported().
Adds Vitest for the frontend's pure helpers, and updates CLAUDE.md.
Gate was green: backend 16 suites / 209 tests, nest build, prisma validate;
frontend 37 Vitest tests, tsc --noEmit, next build.
KNOWN DEFECTS, fixed in the commits that follow:
- VoiceReviewSheet.tsx:169 — a picked tooth chip is dropped on Apply
- VoiceReviewSheet.tsx:213 / TreatmentWorkspace.tsx:2215 — decision 41's
type-row lock is missing, so unticking it saves prosthesis lab rows on a
non-prosthesis detail
Reviewed on the correctness lens only; regression-risk never ran. The
migration was validated but never applied.
Spec: docs/specs/voice-treatment-entry/spec.md
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 12:23:58 +08:00
|
|
|
|
polish_prep: {
|
|
|
|
|
|
en: 'Polish/Prep',
|
|
|
|
|
|
fa: 'پولیش/آمادهسازی',
|
|
|
|
|
|
nl: 'Polijsten/Voorbereiding',
|
|
|
|
|
|
},
|
2026-07-06 20:40:19 +03:30
|
|
|
|
packing: { en: 'Packing', fa: 'بستهبندی', nl: 'Verpakken' },
|
|
|
|
|
|
shipping: { en: 'Shipping', fa: 'ارسال', nl: 'Verzending' },
|
|
|
|
|
|
pressing: { en: 'Pressing', fa: 'پرس', nl: 'Persen' },
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
function labelsToTranslations(
|
|
|
|
|
|
entityKind: CatalogEntityKind,
|
|
|
|
|
|
labels: Record<string, Record<string, string>>,
|
|
|
|
|
|
): CatalogTranslationSeed[] {
|
|
|
|
|
|
const out: CatalogTranslationSeed[] = [];
|
|
|
|
|
|
for (const [entityCode, locales] of Object.entries(labels)) {
|
|
|
|
|
|
for (const [locale, label] of Object.entries(locales)) {
|
|
|
|
|
|
out.push({ entityKind, entityCode, locale, label });
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return out;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
feat(voice): adapt voice entry to the stacked-jobs prosthesis model
Authored by the /orchestrate builder agent, committed unrepaired so the
fixes that follow are reviewable against it.
Backend: replaces the flat prosthesisDefaultType/prosthesisOverrides wire
shape with a prosthesis: ProsthesisAssignment[] list whose targets can be a
tooth or a jaw; adds resolveAssignmentTarget / classifyTypeCode /
resolveProsthesisAssignment for leaf-vs-category classification, region
validity with mixed-region deferral, and assignmentIndex on unresolved
items; adds PROSTHESIS_CATEGORY and PROSTHESIS_SUBCATEGORY to
CatalogEntityKind with a migration and seeded fa/en/nl translations; and
rewrites the extraction prompt to render the catalog as a tree.
Frontend: merged "teeth and prosthesis" row, stack preview through the
existing applyLeafToJobs, three chip-fold paths, rewritten applyVoiceResult
and voiceForEditor, and the two carried-forward recording fixes — the
container fallback that refused Safari and the render gate that never
checked isMediaRecorderSupported().
Adds Vitest for the frontend's pure helpers, and updates CLAUDE.md.
Gate was green: backend 16 suites / 209 tests, nest build, prisma validate;
frontend 37 Vitest tests, tsc --noEmit, next build.
KNOWN DEFECTS, fixed in the commits that follow:
- VoiceReviewSheet.tsx:169 — a picked tooth chip is dropped on Apply
- VoiceReviewSheet.tsx:213 / TreatmentWorkspace.tsx:2215 — decision 41's
type-row lock is missing, so unticking it saves prosthesis lab rows on a
non-prosthesis detail
Reviewed on the correctness lens only; regression-risk never ran. The
migration was validated but never applied.
Spec: docs/specs/voice-treatment-entry/spec.md
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 12:23:58 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* The 7 prosthesis categories, worded from `frontend/messages/*.json`'s `category_*` keys
|
|
|
|
|
|
* (decision 47) so the extraction prompt and the manual picker read identically on day one.
|
|
|
|
|
|
* The frontend keeps its own message keys — migrating it to read these is out of scope
|
|
|
|
|
|
* (decision 48) — this seed is written *from* those keys precisely so the two agree at the
|
|
|
|
|
|
* point they diverge.
|
|
|
|
|
|
*/
|
|
|
|
|
|
export const PROSTHESIS_CATEGORY_LABELS: Record<
|
|
|
|
|
|
string,
|
|
|
|
|
|
Record<string, string>
|
|
|
|
|
|
> = {
|
|
|
|
|
|
crown: { en: 'Crowns', fa: 'روکشها', nl: 'Kronen' },
|
|
|
|
|
|
indirect: {
|
|
|
|
|
|
en: 'Veneer/Inlay/Onlay/Overlay',
|
|
|
|
|
|
fa: 'ونیر/اینلی/آنلی/اورلی',
|
|
|
|
|
|
nl: 'Veneer/Inlay/Onlay/Overlay',
|
|
|
|
|
|
},
|
|
|
|
|
|
implant: { en: 'Implants', fa: 'ایمپلنت', nl: 'Implantaten' },
|
|
|
|
|
|
post_core: { en: 'Post & core', fa: 'پست و کور', nl: 'Stiftopbouw' },
|
|
|
|
|
|
removable: { en: 'Removable', fa: 'متحرک', nl: 'Uitneembaar' },
|
|
|
|
|
|
appliance: { en: 'Appliances', fa: 'اپلاینسها', nl: 'Apparatuur' },
|
|
|
|
|
|
digital: { en: 'Digital', fa: 'دیجیتال', nl: 'Digitaal' },
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Only the 5 real subcategories — the `sub_*` message key set is wider (it also carries
|
|
|
|
|
|
* technique names and two leaf codes); those are not subcategory catalog entities.
|
|
|
|
|
|
*/
|
|
|
|
|
|
export const PROSTHESIS_SUBCATEGORY_LABELS: Record<
|
|
|
|
|
|
string,
|
|
|
|
|
|
Record<string, string>
|
|
|
|
|
|
> = {
|
|
|
|
|
|
veneer: { en: 'Veneer', fa: 'ونیر', nl: 'Veneer' },
|
|
|
|
|
|
inlay: { en: 'Inlay', fa: 'اینلی', nl: 'Inlay' },
|
|
|
|
|
|
onlay: { en: 'Onlay', fa: 'آنلی', nl: 'Onlay' },
|
|
|
|
|
|
overlay: { en: 'Overlay', fa: 'اورلی', nl: 'Overlay' },
|
|
|
|
|
|
night_guard: { en: 'Night guard', fa: 'نایت گارد', nl: 'Nachtbeugel' },
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-06 20:40:19 +03:30
|
|
|
|
export const CATALOG_TRANSLATIONS: CatalogTranslationSeed[] = [
|
|
|
|
|
|
...labelsToTranslations(CatalogEntityKind.TREATMENT_TYPE, TREATMENT_LABELS),
|
|
|
|
|
|
...labelsToTranslations(CatalogEntityKind.PROSTHESIS_TYPE, PROSTHESIS_LABELS),
|
feat(voice): adapt voice entry to the stacked-jobs prosthesis model
Authored by the /orchestrate builder agent, committed unrepaired so the
fixes that follow are reviewable against it.
Backend: replaces the flat prosthesisDefaultType/prosthesisOverrides wire
shape with a prosthesis: ProsthesisAssignment[] list whose targets can be a
tooth or a jaw; adds resolveAssignmentTarget / classifyTypeCode /
resolveProsthesisAssignment for leaf-vs-category classification, region
validity with mixed-region deferral, and assignmentIndex on unresolved
items; adds PROSTHESIS_CATEGORY and PROSTHESIS_SUBCATEGORY to
CatalogEntityKind with a migration and seeded fa/en/nl translations; and
rewrites the extraction prompt to render the catalog as a tree.
Frontend: merged "teeth and prosthesis" row, stack preview through the
existing applyLeafToJobs, three chip-fold paths, rewritten applyVoiceResult
and voiceForEditor, and the two carried-forward recording fixes — the
container fallback that refused Safari and the render gate that never
checked isMediaRecorderSupported().
Adds Vitest for the frontend's pure helpers, and updates CLAUDE.md.
Gate was green: backend 16 suites / 209 tests, nest build, prisma validate;
frontend 37 Vitest tests, tsc --noEmit, next build.
KNOWN DEFECTS, fixed in the commits that follow:
- VoiceReviewSheet.tsx:169 — a picked tooth chip is dropped on Apply
- VoiceReviewSheet.tsx:213 / TreatmentWorkspace.tsx:2215 — decision 41's
type-row lock is missing, so unticking it saves prosthesis lab rows on a
non-prosthesis detail
Reviewed on the correctness lens only; regression-risk never ran. The
migration was validated but never applied.
Spec: docs/specs/voice-treatment-entry/spec.md
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 12:23:58 +08:00
|
|
|
|
...labelsToTranslations(
|
|
|
|
|
|
CatalogEntityKind.LAB_WORKFLOW_STEP,
|
|
|
|
|
|
WORKFLOW_STEP_LABELS,
|
|
|
|
|
|
),
|
|
|
|
|
|
...labelsToTranslations(
|
|
|
|
|
|
CatalogEntityKind.PROSTHESIS_CATEGORY,
|
|
|
|
|
|
PROSTHESIS_CATEGORY_LABELS,
|
|
|
|
|
|
),
|
|
|
|
|
|
...labelsToTranslations(
|
|
|
|
|
|
CatalogEntityKind.PROSTHESIS_SUBCATEGORY,
|
|
|
|
|
|
PROSTHESIS_SUBCATEGORY_LABELS,
|
|
|
|
|
|
),
|
2026-07-06 20:40:19 +03:30
|
|
|
|
];
|