feat: add missing backend route and frontend utility/config files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
adb5801023
commit
fcb049273a
@@ -0,0 +1,245 @@
|
||||
/**
|
||||
* Use parseLocalDate when you need a Date object at local midnight
|
||||
* (for calendars, date pickers, Date math in the browser).
|
||||
*
|
||||
*
|
||||
* Parse a date string in yyyy-MM-dd format (assumed local) into a JS Date object.
|
||||
* No timezone conversion is applied. Returns a Date at midnight local time.
|
||||
*
|
||||
* * Accepts:
|
||||
* - "YYYY-MM-DD"
|
||||
* - ISO/timestamp string (will take left-of-'T' date portion)
|
||||
* - Date object (will return a new Date set to that local calendar day at midnight)
|
||||
*/
|
||||
export function parseLocalDate(input) {
|
||||
if (input instanceof Date) {
|
||||
return new Date(input.getFullYear(), input.getMonth(), input.getDate());
|
||||
}
|
||||
if (typeof input === "string") {
|
||||
const dateString = input?.split("T")[0] ?? "";
|
||||
const parts = dateString.split("-");
|
||||
const [yearStr, monthStr, dayStr] = parts;
|
||||
// Validate all parts are defined and valid strings
|
||||
if (!yearStr || !monthStr || !dayStr) {
|
||||
throw new Error("Invalid date string format. Expected yyyy-MM-dd.");
|
||||
}
|
||||
const year = parseInt(yearStr, 10);
|
||||
const month = parseInt(monthStr, 10) - 1; // JS Date months are 0-based
|
||||
const day = parseInt(dayStr, 10);
|
||||
if (Number.isNaN(year) || Number.isNaN(month) || Number.isNaN(day)) {
|
||||
throw new Error("Invalid numeric values in date string.");
|
||||
}
|
||||
return new Date(year, month, day);
|
||||
}
|
||||
throw new Error("Unsupported input to parseLocalDate. Expected string or Date.");
|
||||
}
|
||||
/**
|
||||
* Use formatLocalDate when you need a date-only string "YYYY-MM-DD" (for displaying stable date values in UI lists,
|
||||
* sending to APIs, storing in sessionStorage/DB where date-only is required).
|
||||
*
|
||||
*
|
||||
* Format a date value into a "YYYY-MM-DD" string with **no timezone shifts**.
|
||||
*
|
||||
* Handles all common input cases:
|
||||
* - "YYYY-MM-DD" string → returned as-is.
|
||||
* - ISO/timestamp string → takes the date portion before "T" (safe, no TZ math).
|
||||
* - Date object:
|
||||
* - If created via `new Date("2025-07-15T00:00:00Z")` (ISO instant),
|
||||
* UTC vs local calendar components may differ. In this case, use UTC
|
||||
* fields so the original calendar day (15th) is preserved across timezones.
|
||||
* - If created via `parseLocalDate("2025-07-15")` or `new Date(2025, 6, 15)`
|
||||
* (local-midnight Date), UTC and local calendar components match,
|
||||
* so local fields are safe to use.
|
||||
*
|
||||
* This hybrid logic ensures:
|
||||
* - DOBs and other date-only values will never appear off by one day
|
||||
* due to timezone differences.
|
||||
* - Works with both string and Date inputs without requiring code changes elsewhere.
|
||||
*/
|
||||
export function formatLocalDate(input) {
|
||||
if (!input)
|
||||
return "";
|
||||
// Case 1: already "YYYY-MM-DD" string
|
||||
if (typeof input === "string" && /^\d{4}-\d{2}-\d{2}$/.test(input)) {
|
||||
return input;
|
||||
}
|
||||
// Case 2: ISO/timestamp string -> take the left-of-T portion
|
||||
if (typeof input === "string") {
|
||||
const dateString = input.split("T")[0] ?? "";
|
||||
return dateString;
|
||||
}
|
||||
// Case 3: Date object
|
||||
if (input instanceof Date) {
|
||||
if (isNaN(input.getTime()))
|
||||
return "";
|
||||
// HYBRID LOGIC:
|
||||
// - If this Date was likely created from an ISO instant at UTC midnight
|
||||
// (e.g. "2025-10-15T00:00:00Z"), then getUTCHours() === 0 but getHours()
|
||||
// will be non-zero in most non-UTC timezones. In that case use UTC date
|
||||
// parts to preserve the original calendar day.
|
||||
// - Otherwise use the local calendar fields (safe for local-midnight Dates).
|
||||
const utcHours = input.getUTCHours();
|
||||
const localHours = input.getHours();
|
||||
const useUTC = utcHours === 0 && localHours !== 0;
|
||||
const year = useUTC ? input.getUTCFullYear() : input.getFullYear();
|
||||
const month = useUTC ? input.getUTCMonth() + 1 : input.getMonth() + 1;
|
||||
const day = useUTC ? input.getUTCDate() : input.getDate();
|
||||
const m = `${month}`.padStart(2, "0");
|
||||
const d = `${day}`.padStart(2, "0");
|
||||
return `${year}-${m}-${d}`;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
// ---------- helpers ----------
|
||||
const MONTH_SHORT = [
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Apr",
|
||||
"May",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
"Oct",
|
||||
"Nov",
|
||||
"Dec",
|
||||
];
|
||||
function isDateOnlyString(s) {
|
||||
return /^\d{4}-\d{2}-\d{2}$/.test(s);
|
||||
}
|
||||
// ---------- formatDateToHumanReadable ----------
|
||||
/**
|
||||
* Frontend-safe human readable formatter.
|
||||
*
|
||||
* Rules:
|
||||
* - If input is a date-only string "YYYY-MM-DD", format it directly (no TZ math).
|
||||
* - If input is a Date object, use its local calendar fields (getFullYear/getMonth/getDate).
|
||||
* - If input is any other string (ISO/timestamp), DO NOT call new Date(isoString) directly
|
||||
* for display. Instead, use parseLocalDate(dateInput) to extract the local calendar day
|
||||
* (strip time portion) and render that. This prevents off-by-one day drift.
|
||||
*
|
||||
* Output example: "Oct 7, 2025"
|
||||
*/
|
||||
export function formatDateToHumanReadable(dateInput) {
|
||||
if (!dateInput)
|
||||
return "N/A";
|
||||
// date-only string -> show as-is using MONTH_SHORT
|
||||
if (typeof dateInput === "string" && isDateOnlyString(dateInput)) {
|
||||
const [y, m, d] = dateInput.split("-");
|
||||
if (!y || !m || !d)
|
||||
return "Invalid Date";
|
||||
return `${MONTH_SHORT[parseInt(m, 10) - 1]} ${d}, ${y}`;
|
||||
}
|
||||
// Date object -> use local calendar fields
|
||||
if (dateInput instanceof Date) {
|
||||
if (isNaN(dateInput.getTime()))
|
||||
return "Invalid Date";
|
||||
const dd = String(dateInput.getDate());
|
||||
const mm = MONTH_SHORT[dateInput.getMonth()];
|
||||
const yy = dateInput.getFullYear();
|
||||
return `${mm} ${dd}, ${yy}`;
|
||||
}
|
||||
// Other string (likely ISO/timestamp) -> normalize via parseLocalDate
|
||||
// This preserves the calendar day the user expects (no timezone drift).
|
||||
if (typeof dateInput === "string") {
|
||||
try {
|
||||
const localDate = parseLocalDate(dateInput);
|
||||
const dd = String(localDate.getDate());
|
||||
const mm = MONTH_SHORT[localDate.getMonth()];
|
||||
const yy = localDate.getFullYear();
|
||||
return `${mm} ${dd}, ${yy}`;
|
||||
}
|
||||
catch (err) {
|
||||
console.error("Invalid date input provided:", dateInput, err);
|
||||
return "Invalid Date";
|
||||
}
|
||||
}
|
||||
return "Invalid Date";
|
||||
}
|
||||
// ---------------- OCR Date helper --------------------------
|
||||
/**
|
||||
* Convert any OCR numeric-ish value into a number.
|
||||
* Handles string | number | null | undefined gracefully.
|
||||
*/
|
||||
export function toNum(val) {
|
||||
if (val == null || val === "")
|
||||
return 0;
|
||||
if (typeof val === "number")
|
||||
return val;
|
||||
const parsed = Number(val);
|
||||
return isNaN(parsed) ? 0 : parsed;
|
||||
}
|
||||
/**
|
||||
* Convert any OCR string-like value into a safe string.
|
||||
*/
|
||||
export function toStr(val) {
|
||||
if (val == null)
|
||||
return "";
|
||||
return String(val).trim();
|
||||
}
|
||||
/**
|
||||
* Convert OCR date strings like "070825" (MMDDYY) into a JS Date object.
|
||||
* Example: "070825" → 2025-08-07.
|
||||
*/
|
||||
export function convertOCRDate(input) {
|
||||
const raw = toStr(input);
|
||||
if (!/^\d{6}$/.test(raw)) {
|
||||
throw new Error(`Invalid OCR date format: ${raw}`);
|
||||
}
|
||||
const month = parseInt(raw.slice(0, 2), 10) - 1;
|
||||
const day = parseInt(raw.slice(2, 4), 10);
|
||||
const year2 = parseInt(raw.slice(4, 6), 10);
|
||||
const year = year2 < 50 ? 2000 + year2 : 1900 + year2;
|
||||
return new Date(year, month, day);
|
||||
}
|
||||
/**
|
||||
* Format a Date or date string into "HH:mm" (24-hour) string.
|
||||
*
|
||||
* Options:
|
||||
* - By default, hours/minutes are taken in local time.
|
||||
* - Pass { asUTC: true } to format using UTC hours/minutes.
|
||||
*
|
||||
* Examples:
|
||||
* formatLocalTime(new Date(2025, 6, 15, 9, 5)) → "09:05"
|
||||
* formatLocalTime("2025-07-15") → "00:00"
|
||||
* formatLocalTime("2025-07-15T14:30:00Z") → "20:30" (in +06:00)
|
||||
* formatLocalTime("2025-07-15T14:30:00Z", { asUTC:true }) → "14:30"
|
||||
*/
|
||||
export function formatLocalTime(d, opts = {}) {
|
||||
if (!d)
|
||||
return "";
|
||||
const { asUTC = false } = opts;
|
||||
const pad2 = (n) => n.toString().padStart(2, "0");
|
||||
let dateObj;
|
||||
if (d instanceof Date) {
|
||||
if (isNaN(d.getTime()))
|
||||
return "";
|
||||
dateObj = d;
|
||||
}
|
||||
else if (typeof d === "string") {
|
||||
const raw = d.trim();
|
||||
const isDateOnly = /^\d{4}-\d{2}-\d{2}$/.test(raw);
|
||||
if (isDateOnly) {
|
||||
// Parse yyyy-MM-dd safely as local midnight
|
||||
try {
|
||||
dateObj = parseLocalDate(raw);
|
||||
}
|
||||
catch {
|
||||
dateObj = new Date(raw); // fallback
|
||||
}
|
||||
}
|
||||
else {
|
||||
// For full ISO/timestamp strings, let Date handle TZ
|
||||
dateObj = new Date(raw);
|
||||
}
|
||||
if (isNaN(dateObj.getTime()))
|
||||
return "";
|
||||
}
|
||||
else {
|
||||
return "";
|
||||
}
|
||||
const hours = asUTC ? dateObj.getUTCHours() : dateObj.getHours();
|
||||
const minutes = asUTC ? dateObj.getUTCMinutes() : dateObj.getMinutes();
|
||||
return `${pad2(hours)}:${pad2(minutes)}`;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
export function getPageNumbers(current, total, maxButtons = 7) {
|
||||
const pages = [];
|
||||
if (total <= maxButtons) {
|
||||
for (let i = 1; i <= total; i++)
|
||||
pages.push(i);
|
||||
return pages;
|
||||
}
|
||||
const delta = 2;
|
||||
const start = Math.max(2, current - delta);
|
||||
const end = Math.min(total - 1, current + delta);
|
||||
pages.push(1);
|
||||
if (start > 2)
|
||||
pages.push("...");
|
||||
for (let i = start; i <= end; i++)
|
||||
pages.push(i);
|
||||
if (end < total - 1)
|
||||
pages.push("...");
|
||||
if (total > 1)
|
||||
pages.push(total);
|
||||
return pages;
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
export const PROCEDURE_COMBOS = {
|
||||
childRecall: {
|
||||
id: "childRecall",
|
||||
label: "Child Recall",
|
||||
codes: ["D0120", "D1120", "D0272", "D1208"],
|
||||
},
|
||||
childRecallDirect: {
|
||||
id: "childRecallDirect",
|
||||
label: "Child Recall Direct(no x-ray)",
|
||||
codes: ["D0120", "D1120", "D1208"],
|
||||
},
|
||||
childRecallDirect2BW: {
|
||||
id: "childRecallDirect2BW",
|
||||
label: "Child Recall Direct 2BW",
|
||||
codes: ["D0120", "D1120", "D1208", "D0272"],
|
||||
},
|
||||
childRecallDirect4BW: {
|
||||
id: "childRecallDirect4BW",
|
||||
label: "Child Recall Direct 4BW",
|
||||
codes: ["D0120", "D1120", "D1208", "D0274"],
|
||||
},
|
||||
childRecallDirect2PA2BW: {
|
||||
id: "childRecallDirect2PA2BW",
|
||||
label: "Child Recall Direct 2PA 2BW",
|
||||
codes: ["D0120", "D1120", "D1208", "D0220", "D0230", "D0272"],
|
||||
toothNumbers: [null, null, null, "9", "24", null], // only these two need values
|
||||
},
|
||||
childRecallDirect2PA4BW: {
|
||||
id: "childRecallDirect2PA4BW",
|
||||
label: "Child Recall Direct 2PA 4BW",
|
||||
codes: ["D0120", "D1120", "D1208", "D0220", "D0230", "D0274"],
|
||||
toothNumbers: [null, null, null, "9", "24", null], // only these two need values
|
||||
},
|
||||
childRecallDirect3PA2BW: {
|
||||
id: "childRecallDirect3PA2BW",
|
||||
label: "Child Recall Direct 3PA 2BW",
|
||||
codes: [
|
||||
"D0120", // exam
|
||||
"D1120", // prophy
|
||||
"D1208", // fluoride
|
||||
"D0220",
|
||||
"D0230",
|
||||
"D0230", // extra PA
|
||||
"D0272", // 2BW
|
||||
],
|
||||
},
|
||||
childRecallDirect4PA: {
|
||||
id: "childRecallDirect4PA",
|
||||
label: "Child Recall Direct 4PA",
|
||||
codes: ["D0120", "D1120", "D1208", "D0220", "D0230", "D0230", "D0230"],
|
||||
},
|
||||
childRecallDirect3PA: {
|
||||
id: "childRecallDirect3PA",
|
||||
label: "Child Recall Direct 3PA",
|
||||
codes: ["D0120", "D1120", "D1208", "D0220", "D0230", "D0230"],
|
||||
},
|
||||
childRecallDirectPANO: {
|
||||
id: "childRecallDirectPANO",
|
||||
label: "Child Recall Direct PANO",
|
||||
codes: ["D0120", "D1120", "D1208", "D0330"],
|
||||
},
|
||||
adultRecall: {
|
||||
id: "adultRecall",
|
||||
label: "Adult Recall",
|
||||
codes: ["D0120", "D0220", "D0230", "D0274", "D1110"],
|
||||
toothNumbers: [null, "9", "24", null, null], // only these two need values
|
||||
},
|
||||
adultRecallDirect: {
|
||||
id: "adultRecallDirect",
|
||||
label: "Adult Recall Direct(no x-ray)",
|
||||
codes: ["D0120", "D1110"],
|
||||
},
|
||||
adultRecallDirect2BW: {
|
||||
id: "adultRecallDirect2BW",
|
||||
label: "Adult Recall Direct - 2bw (no x-ray)",
|
||||
codes: ["D0120", "D1110", "D0272"],
|
||||
},
|
||||
adultRecallDirect4BW: {
|
||||
id: "adultRecallDirect4BW",
|
||||
label: "Adult Recall Direct - 4bw (no x-ray)",
|
||||
codes: ["D0120", "D1110", "D0274"],
|
||||
},
|
||||
adultRecallDirect2PA2BW: {
|
||||
id: "adultRecallDirect2PA2BW",
|
||||
label: "Adult Recall Direct - 2PA 2BW",
|
||||
codes: ["D0120", "D0220", "D0230", "D0272", "D1110"],
|
||||
toothNumbers: [null, "9", "24", null, null], // only these two need values
|
||||
},
|
||||
adultRecallDirect2PA4BW: {
|
||||
id: "adultRecallDirect2PA4BW",
|
||||
label: "Adult Recall Direct - 2PA 4BW",
|
||||
codes: ["D0120", "D0220", "D0230", "D0274", "D1110"],
|
||||
toothNumbers: [null, "9", "24", null, null], // only these two need values
|
||||
},
|
||||
adultRecallDirect4PA: {
|
||||
id: "adultRecallDirect4PA",
|
||||
label: "Adult Recall Direct 4PA",
|
||||
codes: ["D0120", "D1110", "D0220", "D0230", "D0230", "D0230"],
|
||||
},
|
||||
adultRecallDirectPano: {
|
||||
id: "adultRecallDirectPano",
|
||||
label: "Adult Recall Direct - PANO",
|
||||
codes: ["D0120", "D1110", "D0330"],
|
||||
},
|
||||
newChildPatient: {
|
||||
id: "newChildPatient",
|
||||
label: "New Child Patient",
|
||||
codes: ["D0150", "D1120", "D1208"],
|
||||
},
|
||||
newAdultPatientPano: {
|
||||
id: "newAdultPatientPano",
|
||||
label: "New Adult Patient - PANO",
|
||||
codes: ["D0150", "D0330", "D1110"],
|
||||
},
|
||||
newAdultPatientFMX: {
|
||||
id: "newAdultPatientFMX",
|
||||
label: "New Adult Patient (FMX)",
|
||||
codes: ["D0150", "D0210", "D1110"],
|
||||
},
|
||||
newPatientLimitedPano: {
|
||||
id: "newPatientLimitedPano",
|
||||
label: "Patient (Limited exam+Pano)",
|
||||
codes: ["D0140", "D0330"],
|
||||
},
|
||||
newAdultPatientLimited1PA: {
|
||||
id: "newAdultPatientLimited1PA",
|
||||
label: "Patient (Limited exam+1PA)",
|
||||
codes: ["D0140", "D0220"],
|
||||
},
|
||||
patientD9110_1PA: {
|
||||
id: "patientD9110_1PA",
|
||||
label: "Patient (D9110+1PA)",
|
||||
codes: ["D9110", "D0220"],
|
||||
},
|
||||
//Compostie
|
||||
oneSurfCompFront: {
|
||||
id: "oneSurfCompFront",
|
||||
label: "One Surface Composite (Front)",
|
||||
codes: ["D2330"],
|
||||
},
|
||||
oneSurfCompBack: {
|
||||
id: "oneSurfCompBack",
|
||||
label: "One Surface Composite (Back)",
|
||||
codes: ["D2391"],
|
||||
},
|
||||
twoSurfCompFront: {
|
||||
id: "twoSurfCompFront",
|
||||
label: "Two Surface Composite (Front)",
|
||||
codes: ["D2331"],
|
||||
},
|
||||
twoSurfCompBack: {
|
||||
id: "twoSurfCompBack",
|
||||
label: "Two Surface Composite (Back)",
|
||||
codes: ["D2392"],
|
||||
},
|
||||
threeSurfCompFront: {
|
||||
id: "threeSurfCompFront",
|
||||
label: "Three Surface Composite (Front)",
|
||||
codes: ["D2332"],
|
||||
},
|
||||
threeSurfCompBack: {
|
||||
id: "threeSurfCompBack",
|
||||
label: "Three Surface Composite (Back)",
|
||||
codes: ["D2393"],
|
||||
},
|
||||
fourSurfCompFront: {
|
||||
id: "fourSurfCompFront",
|
||||
label: "Four Surface Composite (Front)",
|
||||
codes: ["D2335"],
|
||||
},
|
||||
fourSurfCompBack: {
|
||||
id: "fourSurfCompBack",
|
||||
label: "Four Surface Composite (Back)",
|
||||
codes: ["D2394"],
|
||||
},
|
||||
// Pedo
|
||||
pedoSealants: {
|
||||
id: "pedoSealants",
|
||||
label: "Sealants",
|
||||
codes: ["D1351"],
|
||||
},
|
||||
pedoPulpotomy: {
|
||||
id: "pedoPulpotomy",
|
||||
label: "Pulpotomy",
|
||||
codes: ["D3220"],
|
||||
},
|
||||
pedoSSCrown: {
|
||||
id: "pedoSSCrown",
|
||||
label: "Stainless Steel Crown",
|
||||
codes: ["D2930"],
|
||||
},
|
||||
// Dentures / Partials
|
||||
fu: {
|
||||
id: "fu",
|
||||
label: "FU",
|
||||
codes: ["D5110"],
|
||||
},
|
||||
fl: {
|
||||
id: "fl",
|
||||
label: "FL",
|
||||
codes: ["D5120"],
|
||||
},
|
||||
puResin: {
|
||||
id: "puResin",
|
||||
label: "PU (Resin)",
|
||||
codes: ["D5211"],
|
||||
},
|
||||
puCast: {
|
||||
id: "puCast",
|
||||
label: "PU (Cast)",
|
||||
codes: ["D5213"],
|
||||
},
|
||||
plResin: {
|
||||
id: "plResin",
|
||||
label: "PL (Resin)",
|
||||
codes: ["D5212"],
|
||||
},
|
||||
plCast: {
|
||||
id: "plCast",
|
||||
label: "PL (Cast)",
|
||||
codes: ["D5214"],
|
||||
},
|
||||
// Endodontics
|
||||
rctAnterior: {
|
||||
id: "rctAnterior",
|
||||
label: "RCT Anterior",
|
||||
codes: ["D3310"],
|
||||
},
|
||||
rctPremolar: {
|
||||
id: "rctPremolar",
|
||||
label: "RCT PreM",
|
||||
codes: ["D3320"],
|
||||
},
|
||||
rctMolar: {
|
||||
id: "rctMolar",
|
||||
label: "RCT Molar",
|
||||
codes: ["D3330"],
|
||||
},
|
||||
postCore: {
|
||||
id: "postCore",
|
||||
label: "Post/Core",
|
||||
codes: ["D2954"],
|
||||
},
|
||||
coreBU: {
|
||||
id: "coreBU",
|
||||
label: "Core BU",
|
||||
codes: ["D2950"],
|
||||
},
|
||||
// Prostho / Perio / Oral Surgery
|
||||
crown: {
|
||||
id: "crown",
|
||||
label: "Crown",
|
||||
codes: ["D2740"],
|
||||
},
|
||||
deepCleaning: {
|
||||
id: "deepCleaning",
|
||||
label: "Deep Cleaning",
|
||||
codes: ["D4341"],
|
||||
},
|
||||
simpleExtraction: {
|
||||
id: "simpleExtraction",
|
||||
label: "Simple EXT",
|
||||
codes: ["D7140"],
|
||||
},
|
||||
surgicalExtraction: {
|
||||
id: "surgicalExtraction",
|
||||
label: "Surg EXT",
|
||||
codes: ["D7210"],
|
||||
},
|
||||
babyTeethExtraction: {
|
||||
id: "babyTeethExtraction",
|
||||
label: "Baby Teeth EXT",
|
||||
codes: ["D7111"],
|
||||
},
|
||||
fullBonyExtraction: {
|
||||
id: "fullBonyExtraction",
|
||||
label: "Full Bony EXT",
|
||||
codes: ["D7240"],
|
||||
},
|
||||
// Orthodontics
|
||||
orthPreExamDirect: {
|
||||
id: "orthPreExamDirect",
|
||||
label: "Direct Pre-Orth Exam",
|
||||
codes: ["D9310"],
|
||||
},
|
||||
orthRecordDirect: {
|
||||
id: "orthRecordDirect",
|
||||
label: "Direct Orth Record",
|
||||
codes: ["D8660"],
|
||||
},
|
||||
orthPerioVisitDirect: {
|
||||
id: "orthPerioVisitDirect",
|
||||
label: "Direct Perio Orth Visit ",
|
||||
codes: ["D8670"],
|
||||
},
|
||||
orthRetentionDirect: {
|
||||
id: "orthRetentionDirect",
|
||||
label: "Direct Orth Retention",
|
||||
codes: ["D8680"],
|
||||
},
|
||||
orthPA: {
|
||||
id: "orthPA",
|
||||
label: "Orth PA",
|
||||
codes: ["D8080", "D8670", "D8660"],
|
||||
},
|
||||
// add more…
|
||||
};
|
||||
// Which combos appear under which heading
|
||||
export const COMBO_CATEGORIES = {
|
||||
"Recalls & New Patients": [
|
||||
"childRecall",
|
||||
"adultRecall",
|
||||
"newChildPatient",
|
||||
"newAdultPatientPano",
|
||||
"newAdultPatientFMX",
|
||||
"newPatientLimitedPano",
|
||||
"newAdultPatientLimited1PA",
|
||||
"patientD9110_1PA",
|
||||
],
|
||||
"Composite Fillings (Front)": [
|
||||
"oneSurfCompFront",
|
||||
"twoSurfCompFront",
|
||||
"threeSurfCompFront",
|
||||
"fourSurfCompFront",
|
||||
],
|
||||
"Composite Fillings (Back)": [
|
||||
"oneSurfCompBack",
|
||||
"twoSurfCompBack",
|
||||
"threeSurfCompBack",
|
||||
"fourSurfCompBack",
|
||||
],
|
||||
Pedo: ["pedoSealants", "pedoPulpotomy", "pedoSSCrown"],
|
||||
"Dentures / Partials (>21 price)": [
|
||||
"fu",
|
||||
"fl",
|
||||
"puResin",
|
||||
"puCast",
|
||||
"plResin",
|
||||
"plCast",
|
||||
],
|
||||
Endodontics: ["rctAnterior", "rctPremolar", "rctMolar", "postCore", "coreBU"],
|
||||
Prosthodontics: ["crown"],
|
||||
Periodontics: ["deepCleaning"],
|
||||
Extractions: [
|
||||
"simpleExtraction",
|
||||
"surgicalExtraction",
|
||||
"babyTeethExtraction",
|
||||
"fullBonyExtraction",
|
||||
],
|
||||
Orthodontics: ["orthPA"],
|
||||
};
|
||||
Reference in New Issue
Block a user