feat: replace DOB with Age in Lab RX form, auto-calculated from patient DOB

Age is pre-filled when opening the RX popup by calculating from patient.dateOfBirth.
The field is editable in case of override. Both preview card and print output
now show Age instead of DOB.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-03 21:06:44 -04:00
parent 305a07874d
commit 191ed129d8
2 changed files with 37 additions and 8 deletions

View File

@@ -27,7 +27,17 @@ const EMPTY_TEMPLATE = {
name: "", labName: "", labPhone: "", labFax: "", name: "", labName: "", labPhone: "", labFax: "",
labAddress: "", labAccount: "", caseType: "", material: "", instructions: "Please make\nThank you!", labAddress: "", labAccount: "", caseType: "", material: "", instructions: "Please make\nThank you!",
}; };
const EMPTY_RX = { toothNumbers: "", shade: "", dueDate: "", doctorName: "", additionalNotes: "Please make\nThank you!" }; const EMPTY_RX = { toothNumbers: "", shade: "", dueDate: "", doctorName: "", age: "", additionalNotes: "Please make\nThank you!" };
const calcAge = (dob) => {
if (!dob) return "";
const birth = new Date(dob);
const today = new Date();
let age = today.getFullYear() - birth.getFullYear();
const m = today.getMonth() - birth.getMonth();
if (m < 0 || (m === 0 && today.getDate() < birth.getDate())) age--;
return String(age);
};
const CASE_TYPES = ["Crown", "Bridge", "Veneer", "Inlay/Onlay", "Full Denture", "Partial Denture", "Night Guard", "Retainer", "Implant Crown", "Other"]; const CASE_TYPES = ["Crown", "Bridge", "Veneer", "Inlay/Onlay", "Full Denture", "Partial Denture", "Night Guard", "Retainer", "Implant Crown", "Other"];
const MATERIALS = ["Zirconia", "PFM (Porcelain-Fused-to-Metal)", "E-Max", "Acrylic", "Cast Metal", "Composite", "PMMA", "Other"]; const MATERIALS = ["Zirconia", "PFM (Porcelain-Fused-to-Metal)", "E-Max", "Acrylic", "Cast Metal", "Composite", "PMMA", "Other"];
@@ -131,7 +141,7 @@ export function LabRxModal({ open, onClose, patient }) {
const openRx = (t) => { const openRx = (t) => {
if (renamingId !== null) return; if (renamingId !== null) return;
setRxTemplate(t); setRxTemplate(t);
setRx({ ...EMPTY_RX, doctorName: providers[0]?.providerName ?? "" }); setRx({ ...EMPTY_RX, doctorName: providers[0]?.providerName ?? "", age: calcAge(patient.dateOfBirth) });
}; };
const startRename = (e, t) => { const startRename = (e, t) => {
@@ -451,6 +461,10 @@ export function LabRxModal({ open, onClose, patient }) {
<Label className="text-xs text-gray-600">Shade</Label> <Label className="text-xs text-gray-600">Shade</Label>
<Input placeholder="e.g. A2, B1" value={rx.shade} onChange={(e) => setRx((r) => ({ ...r, shade: e.target.value }))} className="h-9 text-sm" /> <Input placeholder="e.g. A2, B1" value={rx.shade} onChange={(e) => setRx((r) => ({ ...r, shade: e.target.value }))} className="h-9 text-sm" />
</div> </div>
<div className="space-y-1">
<Label className="text-xs text-gray-600">Age</Label>
<Input placeholder="e.g. 72" value={rx.age} onChange={(e) => setRx((r) => ({ ...r, age: e.target.value }))} className="h-9 text-sm" />
</div>
<div className="space-y-1"> <div className="space-y-1">
<Label className="text-xs text-gray-600">Due Date</Label> <Label className="text-xs text-gray-600">Due Date</Label>
<Input type="date" value={rx.dueDate} onChange={(e) => setRx((r) => ({ ...r, dueDate: e.target.value }))} className="h-9 text-sm" /> <Input type="date" value={rx.dueDate} onChange={(e) => setRx((r) => ({ ...r, dueDate: e.target.value }))} className="h-9 text-sm" />
@@ -499,7 +513,7 @@ export function LabRxModal({ open, onClose, patient }) {
</div> </div>
<div className="grid grid-cols-2 gap-x-6 gap-y-1"> <div className="grid grid-cols-2 gap-x-6 gap-y-1">
<div><span className="text-gray-400 text-xs">Patient</span><p className="font-medium">{patientName}</p></div> <div><span className="text-gray-400 text-xs">Patient</span><p className="font-medium">{patientName}</p></div>
<div><span className="text-gray-400 text-xs">DOB</span><p>{patient.dateOfBirth ? new Date(patient.dateOfBirth).toLocaleDateString("en-US") : "—"}</p></div> <div><span className="text-gray-400 text-xs">Age</span><p>{rx.age || "—"}</p></div>
<div className="col-span-2 mt-1 border-t pt-2"><span className="text-gray-400 text-xs">Lab</span><p className="font-medium">{rxTemplate.labName || "—"}</p></div> <div className="col-span-2 mt-1 border-t pt-2"><span className="text-gray-400 text-xs">Lab</span><p className="font-medium">{rxTemplate.labName || "—"}</p></div>
<div><span className="text-gray-400 text-xs">Phone</span><p>{rxTemplate.labPhone || "—"}</p></div> <div><span className="text-gray-400 text-xs">Phone</span><p>{rxTemplate.labPhone || "—"}</p></div>
{rxTemplate.labFax && <div><span className="text-gray-400 text-xs">Fax</span><p>{rxTemplate.labFax}</p></div>} {rxTemplate.labFax && <div><span className="text-gray-400 text-xs">Fax</span><p>{rxTemplate.labFax}</p></div>}
@@ -537,7 +551,7 @@ export function LabRxModal({ open, onClose, patient }) {
<div className="section-title">Patient Information</div> <div className="section-title">Patient Information</div>
<div className="row"> <div className="row">
<div className="field"><label>Patient Name</label><span>{patientName}</span></div> <div className="field"><label>Patient Name</label><span>{patientName}</span></div>
<div className="field"><label>Date of Birth</label><span>{patient.dateOfBirth ? new Date(patient.dateOfBirth).toLocaleDateString("en-US") : ""}</span></div> <div className="field"><label>Age</label><span>{rx.age}</span></div>
</div> </div>
</div> </div>
<div className="section"> <div className="section">

View File

@@ -55,6 +55,7 @@ type RxFields = {
shade: string; shade: string;
dueDate: string; dueDate: string;
doctorName: string; doctorName: string;
age: string;
additionalNotes: string; additionalNotes: string;
}; };
@@ -62,7 +63,17 @@ const EMPTY_TEMPLATE: TemplateFormState = {
name: "", labName: "", labPhone: "", labFax: "", name: "", labName: "", labPhone: "", labFax: "",
labAddress: "", labAccount: "", caseType: "", material: "", instructions: "Please make\nThank you!", labAddress: "", labAccount: "", caseType: "", material: "", instructions: "Please make\nThank you!",
}; };
const EMPTY_RX: RxFields = { toothNumbers: "", shade: "", dueDate: "", doctorName: "", additionalNotes: "Please make\nThank you!" }; const EMPTY_RX: RxFields = { toothNumbers: "", shade: "", dueDate: "", doctorName: "", age: "", additionalNotes: "Please make\nThank you!" };
const calcAge = (dob: string | Date | null | undefined): string => {
if (!dob) return "";
const birth = new Date(dob);
const today = new Date();
let age = today.getFullYear() - birth.getFullYear();
const m = today.getMonth() - birth.getMonth();
if (m < 0 || (m === 0 && today.getDate() < birth.getDate())) age--;
return String(age);
};
const CASE_TYPES = ["Crown", "Bridge", "Veneer", "Inlay/Onlay", "Full Denture", "Partial Denture", "Night Guard", "Retainer", "Implant Crown", "Other"]; const CASE_TYPES = ["Crown", "Bridge", "Veneer", "Inlay/Onlay", "Full Denture", "Partial Denture", "Night Guard", "Retainer", "Implant Crown", "Other"];
const MATERIALS = ["Zirconia", "PFM (Porcelain-Fused-to-Metal)", "E-Max", "Acrylic", "Cast Metal", "Composite", "PMMA", "Other"]; const MATERIALS = ["Zirconia", "PFM (Porcelain-Fused-to-Metal)", "E-Max", "Acrylic", "Cast Metal", "Composite", "PMMA", "Other"];
@@ -175,7 +186,7 @@ export function LabRxModal({ open, onClose, patient }: Props) {
const openRx = (t: LabRxTemplate) => { const openRx = (t: LabRxTemplate) => {
if (renamingId !== null) return; if (renamingId !== null) return;
setRxTemplate(t); setRxTemplate(t);
setRx({ ...EMPTY_RX, doctorName: providers[0]?.providerName ?? "" }); setRx({ ...EMPTY_RX, doctorName: providers[0]?.providerName ?? "", age: calcAge(patient.dateOfBirth) });
}; };
const startRename = (e: React.MouseEvent, t: LabRxTemplate) => { const startRename = (e: React.MouseEvent, t: LabRxTemplate) => {
@@ -495,6 +506,10 @@ export function LabRxModal({ open, onClose, patient }: Props) {
<Label className="text-xs text-gray-600">Shade</Label> <Label className="text-xs text-gray-600">Shade</Label>
<Input placeholder="e.g. A2, B1" value={rx.shade} onChange={(e) => setRx((r) => ({ ...r, shade: e.target.value }))} className="h-9 text-sm" /> <Input placeholder="e.g. A2, B1" value={rx.shade} onChange={(e) => setRx((r) => ({ ...r, shade: e.target.value }))} className="h-9 text-sm" />
</div> </div>
<div className="space-y-1">
<Label className="text-xs text-gray-600">Age</Label>
<Input placeholder="e.g. 72" value={rx.age} onChange={(e) => setRx((r) => ({ ...r, age: e.target.value }))} className="h-9 text-sm" />
</div>
<div className="space-y-1"> <div className="space-y-1">
<Label className="text-xs text-gray-600">Due Date</Label> <Label className="text-xs text-gray-600">Due Date</Label>
<Input type="date" value={rx.dueDate} onChange={(e) => setRx((r) => ({ ...r, dueDate: e.target.value }))} className="h-9 text-sm" /> <Input type="date" value={rx.dueDate} onChange={(e) => setRx((r) => ({ ...r, dueDate: e.target.value }))} className="h-9 text-sm" />
@@ -543,7 +558,7 @@ export function LabRxModal({ open, onClose, patient }: Props) {
</div> </div>
<div className="grid grid-cols-2 gap-x-6 gap-y-1"> <div className="grid grid-cols-2 gap-x-6 gap-y-1">
<div><span className="text-gray-400 text-xs">Patient</span><p className="font-medium">{patientName}</p></div> <div><span className="text-gray-400 text-xs">Patient</span><p className="font-medium">{patientName}</p></div>
<div><span className="text-gray-400 text-xs">DOB</span><p>{patient.dateOfBirth ? new Date(patient.dateOfBirth).toLocaleDateString("en-US") : "—"}</p></div> <div><span className="text-gray-400 text-xs">Age</span><p>{rx.age || "—"}</p></div>
<div className="col-span-2 mt-1 border-t pt-2"><span className="text-gray-400 text-xs">Lab</span><p className="font-medium">{rxTemplate.labName || "—"}</p></div> <div className="col-span-2 mt-1 border-t pt-2"><span className="text-gray-400 text-xs">Lab</span><p className="font-medium">{rxTemplate.labName || "—"}</p></div>
<div><span className="text-gray-400 text-xs">Phone</span><p>{rxTemplate.labPhone || "—"}</p></div> <div><span className="text-gray-400 text-xs">Phone</span><p>{rxTemplate.labPhone || "—"}</p></div>
{rxTemplate.labFax && <div><span className="text-gray-400 text-xs">Fax</span><p>{rxTemplate.labFax}</p></div>} {rxTemplate.labFax && <div><span className="text-gray-400 text-xs">Fax</span><p>{rxTemplate.labFax}</p></div>}
@@ -581,7 +596,7 @@ export function LabRxModal({ open, onClose, patient }: Props) {
<div className="section-title">Patient Information</div> <div className="section-title">Patient Information</div>
<div className="row"> <div className="row">
<div className="field"><label>Patient Name</label><span>{patientName}</span></div> <div className="field"><label>Patient Name</label><span>{patientName}</span></div>
<div className="field"><label>Date of Birth</label><span>{patient.dateOfBirth ? new Date(patient.dateOfBirth).toLocaleDateString("en-US") : ""}</span></div> <div className="field"><label>Age</label><span>{rx.age}</span></div>
</div> </div>
</div> </div>
<div className="section"> <div className="section">