Files
DentalManagementMH07/apps/Backend/src/routes/npiProviders.ts
Gitead 75c49ab1df feat: chatbot rendering provider override and NPI provider ordering
- AI chat extracts 'with provider <name>' and routes claim to that provider
- Claim form reads provider from sessionStorage before any async effects run,
  preventing saved claim/procedure data from overriding the chatbot selection
- NPI provider settings table shows Provider #1 / #2 labels with up/down
  reorder buttons; Provider #1 is always the default for claims
- Default provider now uses sortOrder instead of hardcoded 'Mary Scannell'
- Added sortOrder column to NpiProvider schema with migration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 13:17:05 -04:00

116 lines
3.1 KiB
TypeScript
Executable File

import express, { Request, Response } from "express";
import { z } from "zod";
import { storage } from "../storage";
import { insertNpiProviderSchema } from "@repo/db/types";
const router = express.Router();
router.get("/", async (req: Request, res: Response) => {
try {
if (!req.user?.id) {
return res.status(401).json({ message: "Unauthorized" });
}
const providers = await storage.getNpiProvidersByUser(req.user.id);
res.status(200).json(providers);
} catch (err) {
res.status(500).json({
error: "Failed to fetch NPI providers",
details: String(err),
});
}
});
router.post("/", async (req: Request, res: Response) => {
try {
if (!req.user?.id) {
return res.status(401).json({ message: "Unauthorized" });
}
const parsed = insertNpiProviderSchema.safeParse({
...req.body,
userId: req.user.id,
});
if (!parsed.success) {
const flat = parsed.error.flatten();
const firstError =
Object.values(flat.fieldErrors)[0]?.[0] || "Invalid input";
return res.status(400).json({
message: firstError,
details: flat.fieldErrors,
});
}
const provider = await storage.createNpiProvider(parsed.data);
res.status(201).json(provider);
} catch (err: any) {
if (err.code === "P2002") {
return res.status(400).json({
message: "This NPI already exists for the user",
});
}
res.status(500).json({
error: "Failed to create NPI provider",
details: String(err),
});
}
});
router.put("/:id", async (req: Request, res: Response) => {
try {
const id = Number(req.params.id);
if (isNaN(id)) return res.status(400).send("Invalid ID");
const provider = await storage.updateNpiProvider(id, req.body);
res.status(200).json(provider);
} catch (err) {
res.status(500).json({
error: "Failed to update NPI provider",
details: String(err),
});
}
});
router.post("/reorder", async (req: Request, res: Response) => {
try {
if (!req.user?.id) {
return res.status(401).json({ message: "Unauthorized" });
}
const { orderedIds } = req.body;
if (!Array.isArray(orderedIds)) {
return res.status(400).json({ message: "orderedIds must be an array" });
}
await storage.reorderNpiProviders(req.user.id, orderedIds.map(Number));
res.status(200).json({ ok: true });
} catch (err) {
res.status(500).json({ error: "Failed to reorder NPI providers", details: String(err) });
}
});
router.delete("/:id", async (req: Request, res: Response) => {
try {
if (!req.user?.id) {
return res.status(401).json({ message: "Unauthorized" });
}
const id = Number(req.params.id);
if (isNaN(id)) return res.status(400).send("Invalid ID");
const ok = await storage.deleteNpiProvider(req.user.id, id);
if (!ok) {
return res.status(404).json({ message: "NPI provider not found" });
}
res.status(204).send();
} catch (err) {
res.status(500).json({
error: "Failed to delete NPI provider",
details: String(err),
});
}
});
export default router;