routes fixed - user based fixed done

This commit is contained in:
2025-08-28 22:28:41 +05:30
parent 269cdf29b3
commit 848e4362e5
12 changed files with 478 additions and 283 deletions

View File

@@ -1,62 +1,14 @@
import { Router } from "express";
import type { Request, Response } from "express";
import { storage } from "../storage";
import {
AppointmentUncheckedCreateInputObjectSchema,
PatientUncheckedCreateInputObjectSchema,
} from "@repo/db/usedSchemas";
import { z } from "zod";
import {
insertAppointmentSchema,
updateAppointmentSchema,
} from "@repo/db/types";
const router = Router();
//creating types out of schema auto generated.
type Appointment = z.infer<typeof AppointmentUncheckedCreateInputObjectSchema>;
const insertAppointmentSchema = (
AppointmentUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
).omit({
id: true,
createdAt: true,
});
type InsertAppointment = z.infer<typeof insertAppointmentSchema>;
const updateAppointmentSchema = (
AppointmentUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
)
.omit({
id: true,
createdAt: true,
})
.partial();
type UpdateAppointment = z.infer<typeof updateAppointmentSchema>;
const PatientSchema = (
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
).omit({
appointments: true,
});
type Patient = z.infer<typeof PatientSchema>;
const insertPatientSchema = (
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
).omit({
id: true,
createdAt: true,
});
type InsertPatient = z.infer<typeof insertPatientSchema>;
const updatePatientSchema = (
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
)
.omit({
id: true,
createdAt: true,
userId: true,
})
.partial();
type UpdatePatient = z.infer<typeof updatePatientSchema>;
// Get all appointments
router.get("/all", async (req: Request, res: Response): Promise<any> => {
try {
@@ -162,9 +114,7 @@ router.get("/appointments/recent", async (req: Request, res: Response) => {
const offset = Math.max(0, parseInt(req.query.offset as string) || 0);
const all = await storage.getRecentAppointments(limit, offset);
const filtered = all.filter((a) => a.userId === req.user!.id);
res.json({ data: filtered, limit, offset });
res.json({ data: all, limit, offset });
} catch (err) {
res.status(500).json({ message: "Failed to get recent appointments" });
}

View File

@@ -166,8 +166,8 @@ router.get("/recent", async (req: Request, res: Response) => {
const offset = parseInt(req.query.offset as string) || 0;
const [claims, totalCount] = await Promise.all([
storage.getRecentClaimsByUser(req.user!.id, limit, offset),
storage.getTotalClaimCountByUser(req.user!.id),
storage.getRecentClaims(limit, offset),
storage.getTotalClaimCount(),
]);
res.json({ claims, totalCount });
@@ -210,13 +210,13 @@ router.get(
}
);
// Get all claims for the logged-in user
// Get all claims count.
router.get("/all", async (req: Request, res: Response) => {
try {
const claims = await storage.getTotalClaimCountByUser(req.user!.id);
const claims = await storage.getTotalClaimCount();
res.json(claims);
} catch (error) {
res.status(500).json({ message: "Failed to retrieve claims" });
res.status(500).json({ message: "Failed to retrieve claims count" });
}
});

View File

@@ -1,31 +1,29 @@
import express, { Request, Response, NextFunction } from "express";
import express, { Request, Response } from "express";
import { storage } from "../storage";
import { InsuranceCredentialUncheckedCreateInputObjectSchema } from "@repo/db/usedSchemas";
import { z } from "zod";
import {
insertInsuranceCredentialSchema,
InsuranceCredential,
} from "@repo/db/types";
const router = express.Router();
// ✅ Types
type InsuranceCredential = z.infer<typeof InsuranceCredentialUncheckedCreateInputObjectSchema>;
const insertInsuranceCredentialSchema = (
InsuranceCredentialUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
).omit({ id: true });
type InsertInsuranceCredential = z.infer<typeof insertInsuranceCredentialSchema>;
// ✅ Get all credentials for a user
router.get("/", async (req: Request, res: Response): Promise<any> => {
try {
if (!req.user || !req.user.id) {
return res.status(401).json({ message: "Unauthorized: user info missing" });
return res
.status(401)
.json({ message: "Unauthorized: user info missing" });
}
const userId = req.user.id;
const credentials = await storage.getInsuranceCredentialsByUser(userId);
return res.status(200).json(credentials);
} catch (err) {
return res.status(500).json({ error: "Failed to fetch credentials", details: String(err) });
return res
.status(500)
.json({ error: "Failed to fetch credentials", details: String(err) });
}
});
@@ -33,13 +31,20 @@ router.get("/", async (req: Request, res: Response):Promise<any> => {
router.post("/", async (req: Request, res: Response): Promise<any> => {
try {
if (!req.user || !req.user.id) {
return res.status(401).json({ message: "Unauthorized: user info missing" });
return res
.status(401)
.json({ message: "Unauthorized: user info missing" });
}
const userId = req.user.id;
const parseResult = insertInsuranceCredentialSchema.safeParse({ ...req.body, userId });
const parseResult = insertInsuranceCredentialSchema.safeParse({
...req.body,
userId,
});
if (!parseResult.success) {
const flat = (parseResult as typeof parseResult & { error: z.ZodError<any> }).error.flatten();
const flat = (
parseResult as typeof parseResult & { error: z.ZodError<any> }
).error.flatten();
const firstError =
Object.values(flat.fieldErrors)[0]?.[0] || "Invalid input";
@@ -49,7 +54,9 @@ router.post("/", async (req: Request, res: Response):Promise<any> => {
});
}
const credential = await storage.createInsuranceCredential(parseResult.data);
const credential = await storage.createInsuranceCredential(
parseResult.data
);
return res.status(201).json(credential);
} catch (err: any) {
if (err.code === "P2002") {
@@ -57,7 +64,9 @@ router.post("/", async (req: Request, res: Response):Promise<any> => {
message: `Credential with this ${err.meta?.target?.join(", ")} already exists.`,
});
}
return res.status(500).json({ error: "Failed to create credential", details: String(err) });
return res
.status(500)
.json({ error: "Failed to create credential", details: String(err) });
}
});
@@ -71,20 +80,45 @@ router.put("/:id", async (req: Request, res: Response):Promise<any> => {
const credential = await storage.updateInsuranceCredential(id, updates);
return res.status(200).json(credential);
} catch (err) {
return res.status(500).json({ error: "Failed to update credential", details: String(err) });
return res
.status(500)
.json({ error: "Failed to update credential", details: String(err) });
}
});
// ✅ Delete a credential
router.delete("/:id", async (req: Request, res: Response): Promise<any> => {
try {
const userId = (req as any).user?.id;
if (!userId) return res.status(401).json({ message: "Unauthorized" });
const id = Number(req.params.id);
if (isNaN(id)) return res.status(400).send("Invalid ID");
await storage.deleteInsuranceCredential(id);
// 1) Check existence
const existing = await storage.getInsuranceCredential(userId);
if (!existing)
return res.status(404).json({ message: "Credential not found" });
// 2) Ownership check
if (existing.userId !== userId) {
return res
.status(403)
.json({ message: "Forbidden: Not your credential" });
}
// 3) Delete (storage method enforces userId + id)
const ok = await storage.deleteInsuranceCredential(userId, id);
if (!ok) {
return res
.status(404)
.json({ message: "Credential not found or already deleted" });
}
return res.status(204).send();
} catch (err) {
return res.status(500).json({ error: "Failed to delete credential", details: String(err) });
return res
.status(500)
.json({ error: "Failed to delete credential", details: String(err) });
}
});

View File

@@ -1,34 +1,69 @@
import { Router, Request, Response } from "express";
import { prisma } from "@repo/db/client";
import { storage } from "../storage";
const router = Router();
router.get("/", async (req, res) => {
router.get("/", async (req: Request, res: Response): Promise<any> => {
try {
const userId = (req as any).user?.id;
const notifications = await prisma.notification.findMany({
where: { userId },
orderBy: { createdAt: "desc" },
take: 20,
});
if (!userId) return res.status(401).json({ message: "Unauthorized" });
const notifications = await storage.getNotifications(userId, 20, 0);
res.json(notifications);
} catch (err) {
console.error("Failed to fetch notifications:", err);
res.status(500).json({ message: "Failed to fetch notifications" });
}
});
router.post("/:id/read", async (req, res) => {
// Mark one notification as read
router.post("/:id/read", async (req: Request, res: Response): Promise<any> => {
try {
const userId = (req as any).user?.id;
await prisma.notification.updateMany({
where: { id: Number(req.params.id), userId },
data: { read: true },
});
if (!userId) return res.status(401).json({ message: "Unauthorized" });
const success = await storage.markNotificationRead(
userId,
Number(req.params.id)
);
if (!success)
return res.status(404).json({ message: "Notification not found" });
res.json({ success: true });
} catch (err) {
console.error("Failed to mark notification as read:", err);
res.status(500).json({ message: "Failed to mark notification as read" });
}
});
router.post("/read-all", async (req, res) => {
// Mark all notifications as read
router.post("/read-all", async (req: Request, res: Response): Promise<any> => {
try {
const userId = (req as any).user?.id;
await prisma.notification.updateMany({
where: { userId },
data: { read: true },
});
res.json({ success: true });
if (!userId) return res.status(401).json({ message: "Unauthorized" });
const count = await storage.markAllNotificationsRead(userId);
res.json({ success: true, updatedCount: count });
} catch (err) {
console.error("Failed to mark all notifications read:", err);
res.status(500).json({ message: "Failed to mark all notifications read" });
}
});
router.delete(
"/delete-all",
async (req: Request, res: Response): Promise<any> => {
try {
const userId = (req as any).user?.id;
if (!userId) return res.status(401).json({ message: "Unauthorized" });
const deletedCount = await storage.deleteAllNotifications(userId);
res.json({ success: true, deletedCount });
} catch (err) {
console.error("Failed to delete notifications:", err);
res.status(500).json({ message: "Failed to delete notifications" });
}
}
);
export default router;

View File

@@ -1,63 +1,11 @@
import { Router } from "express";
import type { Request, Response } from "express";
import { storage } from "../storage";
import {
AppointmentUncheckedCreateInputObjectSchema,
PatientUncheckedCreateInputObjectSchema,
} from "@repo/db/usedSchemas";
import { z } from "zod";
import { extractDobParts } from "../utils/DobParts";
import { insertPatientSchema, updatePatientSchema } from "@repo/db/types";
const router = Router();
//creating types out of schema auto generated.
type Appointment = z.infer<typeof AppointmentUncheckedCreateInputObjectSchema>;
const insertAppointmentSchema = (
AppointmentUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
).omit({
id: true,
createdAt: true,
});
type InsertAppointment = z.infer<typeof insertAppointmentSchema>;
const updateAppointmentSchema = (
AppointmentUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
)
.omit({
id: true,
createdAt: true,
})
.partial();
type UpdateAppointment = z.infer<typeof updateAppointmentSchema>;
const PatientSchema = (
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
).omit({
appointments: true,
});
type Patient = z.infer<typeof PatientSchema>;
const insertPatientSchema = (
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
).omit({
id: true,
createdAt: true,
});
type InsertPatient = z.infer<typeof insertPatientSchema>;
const updatePatientSchema = (
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
)
.omit({
id: true,
createdAt: true,
userId: true,
})
.partial();
type UpdatePatient = z.infer<typeof updatePatientSchema>;
// Patient Routes
// Get all patients for the logged-in user
router.get("/", async (req, res) => {
@@ -100,9 +48,7 @@ router.get("/search", async (req: Request, res: Response): Promise<any> => {
offset = "0",
} = req.query as Record<string, string>;
const filters: any = {
userId: req.user!.id,
};
const filters: any = {};
if (term) {
filters.OR = [
@@ -159,9 +105,10 @@ router.get("/search", async (req: Request, res: Response): Promise<any> => {
}
});
// get patient by insurance id
router.get("/by-insurance-id", async (req: Request, res: Response): Promise<any> => {
router.get(
"/by-insurance-id",
async (req: Request, res: Response): Promise<any> => {
const insuranceId = req.query.insuranceId?.toString();
if (!insuranceId) {
@@ -180,7 +127,8 @@ router.get("/by-insurance-id", async (req: Request, res: Response): Promise<any>
console.error("Failed to lookup patient:", err);
return res.status(500).json({ error: "Internal server error" });
}
});
}
);
// Get a single patient by ID
router.get(
@@ -334,12 +282,12 @@ router.delete(
}
if (existingPatient.userId !== req.user!.id) {
console.warn(
`User ${req.user!.id} tried to delete patient ${patientId} owned by ${existingPatient.userId}`
);
return res
.status(403)
.json({ message: "Forbidden: Patient belongs to a different user" });
.json({
message:
"Forbidden: Patient belongs to a different user, you can't delete this.",
});
}
// Delete patient
@@ -351,18 +299,6 @@ router.delete(
}
}
);
// Appointment Routes
// Get all appointments for the logged-in user
router.get("/appointments", async (req, res) => {
try {
const appointments = await storage.getAppointmentsByUserId(req.user!.id);
res.json(appointments);
} catch (error) {
res.status(500).json({ message: "Failed to retrieve appointments" });
}
});
// Get appointments for a specific patient
router.get(
"/:patientId/appointments",

View File

@@ -46,15 +46,12 @@ const router = Router();
// GET /api/payments/recent
router.get("/recent", async (req: Request, res: Response): Promise<any> => {
try {
const userId = req.user?.id;
if (!userId) return res.status(401).json({ message: "Unauthorized" });
const limit = parseInt(req.query.limit as string) || 10;
const offset = parseInt(req.query.offset as string) || 0;
const [payments, totalCount] = await Promise.all([
storage.getRecentPaymentsByUser(userId, limit, offset),
storage.getTotalPaymentCountByUser(userId),
storage.getRecentPayments(limit, offset),
storage.getTotalPaymentCount(),
]);
res.status(200).json({ payments, totalCount });
@@ -69,9 +66,6 @@ router.get(
"/claim/:claimId",
async (req: Request, res: Response): Promise<any> => {
try {
const userId = req.user?.id;
if (!userId) return res.status(401).json({ message: "Unauthorized" });
const parsedClaimId = parseIntOrError(req.params.claimId, "Claim ID");
const payments = await storage.getPaymentsByClaimId(parsedClaimId);
@@ -122,9 +116,6 @@ router.get(
// GET /api/payments/filter
router.get("/filter", async (req: Request, res: Response): Promise<any> => {
try {
const userId = req.user?.id;
if (!userId) return res.status(401).json({ message: "Unauthorized" });
const validated = paymentFilterSchema.safeParse(req.query);
if (!validated.success) {
return res.status(400).json({
@@ -148,9 +139,6 @@ router.get("/filter", async (req: Request, res: Response): Promise<any> => {
// GET /api/payments/:id
router.get("/:id", async (req: Request, res: Response): Promise<any> => {
try {
const userId = req.user?.id;
if (!userId) return res.status(401).json({ message: "Unauthorized" });
const id = parseIntOrError(req.params.id, "Payment ID");
const payment = await storage.getPaymentById(id);
@@ -351,6 +339,19 @@ router.delete("/:id", async (req: Request, res: Response): Promise<any> => {
if (!userId) return res.status(401).json({ message: "Unauthorized" });
const id = parseIntOrError(req.params.id, "Payment ID");
// Check if payment exists and belongs to this user
const existingPayment = await storage.getPayment(id);
if (!existingPayment) {
return res.status(404).json({ message: "Payment not found" });
}
if (existingPayment.userId !== req.user!.id) {
return res.status(403).json({
message:
"Forbidden: Payment belongs to a different user, you can't delete this.",
});
}
await storage.deletePayment(id, userId);
res.status(200).json({ message: "Payment deleted successfully" });

View File

@@ -57,13 +57,43 @@ router.put("/:id", async (req: Request, res: Response): Promise<any> => {
}
});
const parseIdOr400 = (raw: any, label: string) => {
const n = Number(raw);
if (!Number.isFinite(n)) throw new Error(`${label} is invalid`);
return n;
};
router.delete("/:id", async (req: Request, res: Response): Promise<any> => {
try {
const userId = req.user!.id;
const id = parseIdOr400(req.params.id, "Staff ID");
const parsedStaffId = Number(req.params.id);
if (isNaN(parsedStaffId)) {
return res.status(400).send("Invalid staff ID");
}
const existing = await storage.getStaff(id); // must include createdById
if (!existing) return res.status(404).json({ message: "Staff not found" });
if (existing.userId !== userId) {
return res.status(403).json({
message:
"Forbidden: Staff was created by a different user; you cannot delete it.",
});
}
const [apptCount, claimCount] = await Promise.all([
storage.countAppointmentsByStaffId(id),
storage.countClaimsByStaffId(id),
]);
if (apptCount || claimCount) {
return res.status(409).json({
message: `Cannot delete staff with linked records. Appointment of this staff : ${apptCount} and Claims ${claimCount}`,
hint: "Archive this staff, or reassign linked records, then delete.",
});
}
const deleted = await storage.deleteStaff(parsedStaffId);
if (!deleted) return res.status(404).send("Staff not found");

View File

@@ -107,6 +107,8 @@ export interface IStorage {
createStaff(staff: Staff): Promise<Staff>;
updateStaff(id: number, updates: Partial<Staff>): Promise<Staff | undefined>;
deleteStaff(id: number): Promise<boolean>;
countAppointmentsByStaffId(staffId: number): Promise<number>;
countClaimsByStaffId(staffId: number): Promise<number>;
// Claim methods
getClaim(id: number): Promise<Claim | undefined>;
@@ -118,17 +120,14 @@ export interface IStorage {
getTotalClaimCountByPatient(patientId: number): Promise<number>;
getClaimsByAppointmentId(appointmentId: number): Promise<Claim[]>;
getRecentClaimsByUser(
userId: number,
limit: number,
offset: number
): Promise<Claim[]>;
getTotalClaimCountByUser(userId: number): Promise<number>;
getRecentClaims(limit: number, offset: number): Promise<Claim[]>;
getTotalClaimCount(): Promise<number>;
createClaim(claim: InsertClaim): Promise<Claim>;
updateClaim(id: number, updates: UpdateClaim): Promise<Claim>;
deleteClaim(id: number): Promise<void>;
// InsuranceCredential methods
getInsuranceCredential(id: number): Promise<InsuranceCredential | null>;
getInsuranceCredentialsByUser(userId: number): Promise<InsuranceCredential[]>;
createInsuranceCredential(
data: InsertInsuranceCredential
@@ -136,8 +135,8 @@ export interface IStorage {
updateInsuranceCredential(
id: number,
updates: Partial<InsuranceCredential>
): Promise<InsuranceCredential>;
deleteInsuranceCredential(id: number): Promise<void>;
): Promise<InsuranceCredential | null>;
deleteInsuranceCredential(userId: number, id: number): Promise<boolean>;
getInsuranceCredentialByUserAndSiteKey(
userId: number,
siteKey: string
@@ -179,6 +178,7 @@ export interface IStorage {
deletePdfGroup(id: number): Promise<boolean>;
// Payment methods:
getPayment(id: number): Promise<Payment | undefined>;
createPayment(data: InsertPayment): Promise<Payment>;
updatePayment(id: number, updates: UpdatePayment): Promise<Payment>;
deletePayment(id: number, userId: number): Promise<void>;
@@ -190,13 +190,12 @@ export interface IStorage {
): Promise<PaymentWithExtras[] | null>;
getTotalPaymentCountByPatient(patientId: number): Promise<number>;
getPaymentsByClaimId(claimId: number): Promise<PaymentWithExtras | null>;
getRecentPaymentsByUser(
userId: number,
getRecentPayments(
limit: number,
offset: number
): Promise<PaymentWithExtras[]>;
getPaymentsByDateRange(from: Date, to: Date): Promise<PaymentWithExtras[]>;
getTotalPaymentCountByUser(userId: number): Promise<number>;
getTotalPaymentCount(): Promise<number>;
// Database Backup methods
createBackup(userId: number): Promise<DatabaseBackup>;
@@ -224,6 +223,7 @@ export interface IStorage {
userId: number,
type: NotificationTypes
): Promise<number>;
deleteAllNotifications(userId: number): Promise<number>;
}
export const storage: IStorage = {
@@ -534,6 +534,14 @@ export const storage: IStorage = {
}
},
async countAppointmentsByStaffId(staffId: number): Promise<number> {
return await db.appointment.count({ where: { staffId } });
},
async countClaimsByStaffId(staffId: number): Promise<number> {
return await db.claim.count({ where: { staffId } });
},
// Claim methods implementation
async getClaim(id: number): Promise<Claim | undefined> {
const claim = await db.claim.findUnique({ where: { id } });
@@ -567,13 +575,11 @@ export const storage: IStorage = {
return await db.claim.findMany({ where: { appointmentId } });
},
async getRecentClaimsByUser(
userId: number,
async getRecentClaims(
limit: number,
offset: number
): Promise<ClaimWithServiceLines[]> {
return db.claim.findMany({
where: { userId },
orderBy: { createdAt: "desc" },
skip: offset,
take: limit,
@@ -581,8 +587,8 @@ export const storage: IStorage = {
});
},
async getTotalClaimCountByUser(userId: number): Promise<number> {
return db.claim.count({ where: { userId } });
async getTotalClaimCount(): Promise<number> {
return db.claim.count();
},
async createClaim(claim: InsertClaim): Promise<Claim> {
@@ -609,6 +615,10 @@ export const storage: IStorage = {
},
// Insurance Creds
async getInsuranceCredential(id: number) {
return await db.insuranceCredential.findUnique({ where: { id } });
},
async getInsuranceCredentialsByUser(userId: number) {
return await db.insuranceCredential.findMany({ where: { userId } });
},
@@ -629,8 +639,13 @@ export const storage: IStorage = {
});
},
async deleteInsuranceCredential(id: number) {
await db.insuranceCredential.delete({ where: { id } });
async deleteInsuranceCredential(userId: number, id: number) {
try {
await db.insuranceCredential.delete({ where: { userId, id } });
return true;
} catch {
return false;
}
},
async getInsuranceCredentialByUserAndSiteKey(
@@ -759,6 +774,11 @@ export const storage: IStorage = {
},
// Payment Methods
async getPayment(id: number): Promise<Payment | undefined> {
const payment = await db.payment.findUnique({ where: { id } });
return payment ?? undefined;
},
async createPayment(payment: InsertPayment): Promise<Payment> {
return db.payment.create({ data: payment as Payment });
},
@@ -881,13 +901,11 @@ export const storage: IStorage = {
};
},
async getRecentPaymentsByUser(
userId: number,
async getRecentPayments(
limit: number,
offset: number
): Promise<PaymentWithExtras[]> {
const payments = await db.payment.findMany({
where: { userId },
orderBy: { createdAt: "desc" },
skip: offset,
take: limit,
@@ -949,8 +967,8 @@ export const storage: IStorage = {
}));
},
async getTotalPaymentCountByUser(userId: number): Promise<number> {
return db.payment.count({ where: { userId } });
async getTotalPaymentCount(): Promise<number> {
return db.payment.count();
},
// ==============================
@@ -1024,4 +1042,11 @@ export const storage: IStorage = {
});
return result.count;
},
async deleteAllNotifications(userId: number): Promise<number> {
const result = await db.notification.deleteMany({
where: { userId },
});
return result.count;
},
};

View File

@@ -44,6 +44,7 @@ import {
applyComboToForm,
getDescriptionForCode,
} from "@/utils/procedureCombosMapping";
import { COMBO_CATEGORIES, PROCEDURE_COMBOS } from "@/utils/procedureCombos";
interface ClaimFormData {
patientId: number;
@@ -707,8 +708,17 @@ export function ClaimForm({
+ Add Service Line
</Button>
<div className="flex gap-2 mt-10 mb-10">
{COMBO_BUTTONS.map((b) => (
<div className="space-y-8 mt-10 mb-10">
{Object.entries(COMBO_CATEGORIES).map(([section, ids]) => (
<div key={section}>
<div className="mb-3 text-sm font-semibold opacity-70">
{section}
</div>
<div className="flex flex-wrap gap-2">
{ids.map((id) => {
const b = PROCEDURE_COMBOS[id];
if(!b){return}
return (
<Button
key={b.id}
variant="secondary"
@@ -728,13 +738,21 @@ export function ClaimForm({
>
{b.label}
</Button>
);
})}
</div>
</div>
))}
<div className="pt-4">
<Button variant="success" onClick={onMapPrice}>
Map Price
</Button>
</div>
</div>
</div>
{/* File Upload Section */}
<div className="mt-4 bg-gray-100 p-4 rounded-md space-y-4">
<p className="text-sm text-gray-500">

View File

@@ -10,28 +10,186 @@ export const PROCEDURE_COMBOS: Record<
"D1120",
"D0272",
"D1208",
"D2331",
"D0120",
"D1120",
"D0272",
"D1208",
"D2331",
"D0120",
"D1120",
"D0272",
"D1208",
"D2331",
],
},
adultProphy: {
id: "adultProphy",
label: "Adult Prophy",
codes: ["D0150", "D1110", "D0274", "D1208"],
adultRecall: {
id: "adultRecall",
label: "Adult Recall",
codes: ["D0120", "D0220", "D0230", "D0274", "D1110"],
},
bitewingsOnly: {
id: "bitewingsOnly",
label: "Bitewings Only",
codes: ["D0272"],
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"],
},
//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"],
},
// 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"],
},
// 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"],
},
// add more…
};
// Which combos appear under which heading
export const COMBO_CATEGORIES: Record<string, (keyof typeof PROCEDURE_COMBOS)[]> = {
"Recalls & New Patients": [
"childRecall",
"adultRecall",
"newAdultPatientPano",
"newAdultPatientFMX",
"newChildPatient",
],
"Composite Fillings (Front)": [
"oneSurfCompFront",
"twoSurfCompFront",
"threeSurfCompFront",
"fourSurfCompFront",
],
"Composite Fillings (Back)": [
"oneSurfCompBack",
"twoSurfCompBack",
"threeSurfCompBack",
"fourSurfCompBack",
],
"Dentures / Partials (>21 price)": [
"fu",
"fl",
"puResin",
"puCast",
"plResin",
"plCast",
],
Endodontics: ["rctAnterior", "rctPremolar", "rctMolar", "postCore"],
Prosthodontics: ["crown"],
Periodontics: ["deepCleaning"],
Extractions: ["simpleExtraction", "surgicalExtraction", "babyTeethExtraction"],
};

View File

@@ -25,6 +25,7 @@ model User {
password String
patients Patient[]
appointments Appointment[]
staff Staff[]
claims Claim[]
insuranceCredentials InsuranceCredential[]
updatedPayments Payment[] @relation("PaymentUpdatedBy")
@@ -87,11 +88,13 @@ model Appointment {
model Staff {
id Int @id @default(autoincrement())
userId Int?
name String
email String?
role String // e.g., "Dentist", "Hygienist", "Assistant"
phone String?
createdAt DateTime @default(now())
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
appointments Appointment[]
claims Claim[] @relation("ClaimStaff")
}

5
readmeForMigration.txt Normal file
View File

@@ -0,0 +1,5 @@
for staff model migration:
after npm run db migrate,
either by sql or manually add userid to each row of staff model.