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:
@@ -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> => {
|
||||
|
||||
@@ -161,6 +161,7 @@ export interface IStorage {
|
||||
patientId: number,
|
||||
category: string
|
||||
): Promise<CloudFile[]>;
|
||||
listRecentAttachmentFiles(limit?: number): Promise<CloudFile[]>;
|
||||
}
|
||||
|
||||
/* ------------------------------- Implementation ------------------------------- */
|
||||
@@ -663,6 +664,25 @@ export const cloudStorageStorage: IStorage = {
|
||||
return files.map(serializeFile) as unknown as CloudFile[];
|
||||
},
|
||||
|
||||
async listRecentAttachmentFiles(limit: number = 20) {
|
||||
const files = await db.cloudFile.findMany({
|
||||
where: { folder: { name: "Attachments" }, isComplete: true },
|
||||
orderBy: { createdAt: "desc" },
|
||||
take: limit,
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
mimeType: true,
|
||||
fileSize: true,
|
||||
folderId: true,
|
||||
isComplete: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
});
|
||||
return files.map(serializeFile) as unknown as CloudFile[];
|
||||
},
|
||||
|
||||
// --- STREAM ---
|
||||
async streamFileTo(resStream: NodeJS.WritableStream, fileId: number) {
|
||||
const file = await db.cloudFile.findUnique({
|
||||
|
||||
Reference in New Issue
Block a user