feat: add "Choose from Server" attachment picker to claim/preauth forms and chatbot

Native file inputs can't be pointed at a default server folder for security
reasons, so instead the Browse buttons on the Claim/PreAuth/Select Procedures
forms and the AI chatbot's attach button now also offer picking from
previously saved Cloud Storage attachments (e.g. Copy Agent screenshots) via
a new server-side picker modal, scoped per-patient on the claim forms and
unscoped (recent across all patients) in the chatbot.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 00:36:43 -04:00
co-authored by Claude Sonnet 5
parent 8b245d00c6
commit 34312b62bc
6 changed files with 332 additions and 11 deletions
+43
View File
@@ -158,6 +158,49 @@ router.post(
}
);
// GET /api/claims/patient-attachments?patientId=123
// Lists this patient's previously saved Cloud Storage "Attachments" (e.g. Copy Agent screenshots)
// so they can be picked and re-attached to a claim/preauth without re-uploading from disk.
router.get(
"/patient-attachments",
async (req: Request, res: Response): Promise<any> => {
if (!req.user?.id) return res.status(401).json({ error: "Unauthorized" });
const patientId = Number(req.query.patientId);
if (!patientId || isNaN(patientId)) {
return res.status(400).json({ error: "Invalid patientId" });
}
try {
const files = await storage.listPatientCategoryCloudFiles(patientId, "Attachments");
return res.json({ error: false, data: files });
} catch (err: any) {
console.error("[patient-attachments]", err);
return res.status(500).json({ error: "Failed to list attachments", message: err?.message });
}
}
);
// GET /api/claims/recent-attachments?limit=20
// Lists recently saved Cloud Storage "Attachments" across all patients (e.g. Copy Agent
// screenshots), for use in contexts (like the AI chatbot) that don't have a patient in scope yet.
router.get(
"/recent-attachments",
async (req: Request, res: Response): Promise<any> => {
if (!req.user?.id) return res.status(401).json({ error: "Unauthorized" });
const limit = Number(req.query.limit) || 20;
try {
const files = await storage.listRecentAttachmentFiles(limit);
return res.json({ error: false, data: files });
} catch (err: any) {
console.error("[recent-attachments]", err);
return res.status(500).json({ error: "Failed to list attachments", message: err?.message });
}
}
);
router.post(
"/mh-provider-login",
async (req: Request, res: Response): Promise<any> => {