Amin Mousavi dc23c3dd1c fix(voice): prime the lab-drafts ref before the save that reads it
"برای دندون ۱۲ و ۱۴ یه فول متال کران" previewed correctly, then Apply failed
with TREATMENT_TOOTH_NOT_ON_DETAIL. The form still showed both teeth and both
crowns — that is React state. The database got a detail with no teeth.

applyVoiceResult called setLabCaseDrafts but never wrote
labCaseDraftsRef.current, which is only refreshed in the render body.
persistDraft runs in the same tick and starts by pruning each lab-dependent
detail down to the teeth its jobs cover, reading that stale ref. It found no
draft for the brand-new detail, so jobs were empty, so the detail was saved
with teeth: []. persistLabCases then posted rows for 12 and 14 against it,
and the server — which re-reads the detail's teeth from the database, because
the lab-case endpoint carries no teeth field — correctly refused.

detailsRef was already written by hand two lines above for exactly this
reason (3911477). This is the other half of the same mistake.

Two sibling handlers had the same gap and are fixed with it: adopting an
orphan lab case, and creating a new lab draft. Both call setLabCaseDrafts and
then persistDraft in the same tick. handleLabCasesChange was the only site
that already primed the ref, and it is the pattern the others now follow.

Adds a comment at the ref declaration, because the coupling between this ref
and persistDraft's prune is invisible at the call sites and has now cost two
bugs.

No automated test: this is state-and-ref ordering inside a React component,
and the frontend's Vitest scope is pure helpers only — no React, no DOM.
Mirroring the server's tooth rule on the client to make it testable would
duplicate a rule across layers, which decision 38 rules out.

Gates: tsc --noEmit clean, 37 Vitest tests, next build clean, ESLint warnings
unchanged at 10 (all pre-existing, count verified against HEAD).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 17:18:02 +08:00

Dyolink

Monorepo: NestJS backend (backend/), Next.js frontend (frontend/), Docker stack under infrastructure/..

Local development: see backend/README.md and frontend/README.md.

Cursor AI: project conventions for agents are in AGENTS.md, .cursor/rules/, and .cursor/skills/.


Production deploy (https://nudentic.ir)

Full guide: infrastructure/DEPLOY.md

Git tag v1.0.1 → Gitea workflow .gitea/workflows/prod-tag-deploy.yml builds images (URLs baked for nudentic.ir), pushes to Gitea registry, SSH-deploys the Linux stack.

First-time SSL / Hub fallback: same DEPLOY.md.


Staging deploy (https://wixur.ir)

Full guide: infrastructure/STAGING-DEPLOY.md

Merge or push to master → Gitea Actions builds images → deploys to https://wixur.ir (Gitea: http://wixur.ir:3000).

Workflow: .gitea/workflows/registry-build-deploy.yml


Path Role
backend/Dockerfile API image
frontend/Dockerfile Web image
infrastructure/STAGING-DEPLOY.md Staging setup, CI variables, testing
infrastructure/docker-compose.registry.yml Pull-only staging stack (registry images + nginx + postgres)
infrastructure/deploy.registry.env.example Template for deploy.registry.env
Description
The whole dockerized structure of the project repo
Readme 10 MiB
Languages
TypeScript 97.4%
Shell 1.1%
CSS 0.8%
JavaScript 0.3%
Dockerfile 0.3%
Other 0.1%