fix(UI, PdfGroups) - Schema updated, ui changed, backend Route changed
This commit is contained in:
@@ -127,21 +127,18 @@ router.post(
|
||||
|
||||
const groupTitle = "Insurance Claim";
|
||||
const groupTitleKey = "INSURANCE_CLAIM";
|
||||
const groupCategory = "CLAIM";
|
||||
|
||||
// ✅ Find or create PDF group for this claim
|
||||
let group = await storage.findPdfGroupByPatientTitleKeyAndCategory(
|
||||
let group = await storage.findPdfGroupByPatientTitleKey(
|
||||
parsedPatientId,
|
||||
groupTitleKey,
|
||||
groupCategory
|
||||
groupTitleKey
|
||||
);
|
||||
|
||||
if (!group) {
|
||||
group = await storage.createPdfGroup(
|
||||
parsedPatientId,
|
||||
groupTitle,
|
||||
groupTitleKey,
|
||||
groupCategory
|
||||
groupTitleKey
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Router } from "express";
|
||||
import { Request, Response } from "express";
|
||||
import { storage } from "../storage";
|
||||
import multer from "multer";
|
||||
import { PdfFile } from "../../../../packages/db/types/pdf-types";
|
||||
|
||||
const upload = multer({ storage: multer.memoryStorage() });
|
||||
|
||||
@@ -12,18 +13,17 @@ router.post(
|
||||
"/pdf-groups",
|
||||
async (req: Request, res: Response): Promise<any> => {
|
||||
try {
|
||||
const { patientId, groupTitle, groupTitleKey, groupCategory } = req.body;
|
||||
if (!patientId || !groupTitle || groupTitleKey || !groupCategory) {
|
||||
const { patientId, groupTitle, groupTitleKey } = req.body;
|
||||
if (!patientId || !groupTitle || groupTitleKey) {
|
||||
return res
|
||||
.status(400)
|
||||
.json({ error: "Missing title, titleKey, category, or patientId" });
|
||||
.json({ error: "Missing title, titleKey, or patientId" });
|
||||
}
|
||||
|
||||
const group = await storage.createPdfGroup(
|
||||
parseInt(patientId),
|
||||
groupTitle,
|
||||
groupTitleKey,
|
||||
groupCategory
|
||||
groupTitleKey
|
||||
);
|
||||
|
||||
res.json(group);
|
||||
@@ -90,11 +90,10 @@ router.put(
|
||||
return res.status(400).json({ error: "Missing ID" });
|
||||
}
|
||||
const id = parseInt(idParam);
|
||||
const { title, category, titleKey } = req.body;
|
||||
const { title, titleKey } = req.body;
|
||||
|
||||
const updates: any = {};
|
||||
updates.title = title;
|
||||
updates.category = category;
|
||||
updates.titleKey = titleKey;
|
||||
|
||||
const updated = await storage.updatePdfGroup(id, updates);
|
||||
@@ -168,6 +167,69 @@ router.get(
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* GET /pdf-files/group/:groupId
|
||||
* Query params:
|
||||
* - limit (optional, defaults to 5): number of items per page (max 1000)
|
||||
* - offset (optional, defaults to 0): offset for pagination
|
||||
*
|
||||
* Response: { total: number, data: PdfFile[] }
|
||||
*/
|
||||
router.get(
|
||||
"/recent-pdf-files/group/:groupId",
|
||||
async (req: Request, res: Response): Promise<any> => {
|
||||
try {
|
||||
const rawGroupId = req.params.groupId;
|
||||
if (!rawGroupId) {
|
||||
return res.status(400).json({ error: "Missing groupId param" });
|
||||
}
|
||||
|
||||
const groupId = Number(rawGroupId);
|
||||
if (Number.isNaN(groupId) || groupId <= 0) {
|
||||
return res.status(400).json({ error: "Invalid groupId" });
|
||||
}
|
||||
|
||||
// Parse & sanitize query params
|
||||
const limitQuery = req.query.limit;
|
||||
const offsetQuery = req.query.offset;
|
||||
|
||||
const limit =
|
||||
limitQuery !== undefined
|
||||
? Math.min(Math.max(Number(limitQuery), 1), 1000) // 1..1000
|
||||
: undefined; // if undefined -> treat as "no pagination" (return all)
|
||||
const offset =
|
||||
offsetQuery !== undefined ? Math.max(Number(offsetQuery), 0) : 0;
|
||||
|
||||
// Decide whether client asked for paginated response
|
||||
const wantsPagination = typeof limit === "number";
|
||||
|
||||
if (wantsPagination) {
|
||||
// storage.getPdfFilesByGroupId with pagination should return { total, data }
|
||||
const result = await storage.getPdfFilesByGroupId(groupId, {
|
||||
limit,
|
||||
offset,
|
||||
withGroup: false, // do not include group relation in listing
|
||||
});
|
||||
|
||||
// result should be { total, data }, but handle unexpected shapes defensively
|
||||
if (Array.isArray(result)) {
|
||||
// fallback: storage returned full array; compute total
|
||||
return res.json({ total: result.length, data: result });
|
||||
}
|
||||
|
||||
return res.json(result);
|
||||
} else {
|
||||
// no limit requested -> return all files for the group
|
||||
const all = (await storage.getPdfFilesByGroupId(groupId)) as PdfFile[];
|
||||
return res.json({ total: all.length, data: all });
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("GET /pdf-files/group/:groupId error:", err);
|
||||
return res.status(500).json({ error: "Internal server error" });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
router.get(
|
||||
"/pdf-files/:id",
|
||||
async (req: Request, res: Response): Promise<any> => {
|
||||
|
||||
@@ -60,14 +60,12 @@ router.post(
|
||||
if (result.pdf_path && result.pdf_path.endsWith(".pdf")) {
|
||||
const pdfBuffer = await fs.readFile(result.pdf_path);
|
||||
|
||||
const groupTitle = "Insurance Status PDFs";
|
||||
const groupTitleKey = "INSURANCE_STATUS_PDFs";
|
||||
const groupCategory = "ELIGIBILITY_STATUS";
|
||||
const groupTitle = "Eligibility Status PDFs";
|
||||
const groupTitleKey = "ELIGIBILITY_STATUS";
|
||||
|
||||
let group = await storage.findPdfGroupByPatientTitleKeyAndCategory(
|
||||
let group = await storage.findPdfGroupByPatientTitleKey(
|
||||
patient.id,
|
||||
groupTitleKey,
|
||||
groupCategory
|
||||
groupTitleKey
|
||||
);
|
||||
|
||||
// Step 2b: Create group if it doesn’t exist
|
||||
@@ -75,8 +73,7 @@ router.post(
|
||||
group = await storage.createPdfGroup(
|
||||
patient.id,
|
||||
groupTitle,
|
||||
groupTitleKey,
|
||||
groupCategory
|
||||
groupTitleKey
|
||||
);
|
||||
}
|
||||
|
||||
@@ -220,13 +217,11 @@ router.post(
|
||||
|
||||
if (pdfBuffer && generatedPdfPath) {
|
||||
const groupTitle = "Insurance Status PDFs";
|
||||
const groupTitleKey = "INSURANCE_STATUS_PDFs";
|
||||
const groupCategory = "CLAIM_STATUS";
|
||||
const groupTitleKey = "CLAIM_STATUS";
|
||||
|
||||
let group = await storage.findPdfGroupByPatientTitleKeyAndCategory(
|
||||
let group = await storage.findPdfGroupByPatientTitleKey(
|
||||
patient.id,
|
||||
groupTitleKey,
|
||||
groupCategory
|
||||
groupTitleKey
|
||||
);
|
||||
|
||||
// Create group if missing
|
||||
@@ -234,8 +229,7 @@ router.post(
|
||||
group = await storage.createPdfGroup(
|
||||
patient.id,
|
||||
groupTitle,
|
||||
groupTitleKey,
|
||||
groupCategory
|
||||
groupTitleKey
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { prisma as db } from "@repo/db/client";
|
||||
import { PdfCategory, PdfTitle } from "@repo/db/generated/prisma";
|
||||
import { PdfTitleKey } from "@repo/db/generated/prisma";
|
||||
import {
|
||||
Appointment,
|
||||
Claim,
|
||||
@@ -150,7 +150,10 @@ export interface IStorage {
|
||||
pdfData: Buffer
|
||||
): Promise<PdfFile>;
|
||||
getPdfFileById(id: number): Promise<PdfFile | undefined>;
|
||||
getPdfFilesByGroupId(groupId: number): Promise<PdfFile[]>;
|
||||
getPdfFilesByGroupId(
|
||||
groupId: number,
|
||||
opts?: { limit?: number; offset?: number; withGroup?: boolean }
|
||||
): Promise<PdfFile[] | { total: number; data: PdfFile[] }>;
|
||||
getRecentPdfFiles(limit: number, offset: number): Promise<PdfFile[]>;
|
||||
deletePdfFile(id: number): Promise<boolean>;
|
||||
updatePdfFile(
|
||||
@@ -162,20 +165,18 @@ export interface IStorage {
|
||||
createPdfGroup(
|
||||
patientId: number,
|
||||
title: string,
|
||||
titleKey: PdfTitle,
|
||||
category: PdfCategory
|
||||
titleKey: PdfTitleKey
|
||||
): Promise<PdfGroup>;
|
||||
findPdfGroupByPatientTitleKeyAndCategory(
|
||||
findPdfGroupByPatientTitleKey(
|
||||
patientId: number,
|
||||
titleKey: PdfTitle,
|
||||
category: PdfCategory
|
||||
titleKey: PdfTitleKey
|
||||
): Promise<PdfGroup | undefined>;
|
||||
getAllPdfGroups(): Promise<PdfGroup[]>;
|
||||
getPdfGroupById(id: number): Promise<PdfGroup | undefined>;
|
||||
getPdfGroupsByPatientId(patientId: number): Promise<PdfGroup[]>;
|
||||
updatePdfGroup(
|
||||
id: number,
|
||||
updates: Partial<Pick<PdfGroup, "title" | "category">>
|
||||
updates: Partial<Pick<PdfGroup, "title">>
|
||||
): Promise<PdfGroup | undefined>;
|
||||
deletePdfGroup(id: number): Promise<boolean>;
|
||||
|
||||
@@ -699,11 +700,70 @@ export const storage: IStorage = {
|
||||
return (await db.pdfFile.findUnique({ where: { id } })) ?? undefined;
|
||||
},
|
||||
|
||||
async getPdfFilesByGroupId(groupId) {
|
||||
return db.pdfFile.findMany({
|
||||
where: { groupId },
|
||||
orderBy: { uploadedAt: "desc" },
|
||||
});
|
||||
/**
|
||||
* getPdfFilesByGroupId: supports
|
||||
* - getPdfFilesByGroupId(groupId) => Promise<PdfFile[]>
|
||||
* - getPdfFilesByGroupId(groupId, { limit, offset }) => Promise<{ total, data }>
|
||||
* - getPdfFilesByGroupId(groupId, { limit, offset, withGroup: true }) => Promise<{ total, data: PdfFileWithGroup[] }>
|
||||
*/
|
||||
async getPdfFilesByGroupId(groupId, opts) {
|
||||
// if pagination is requested (limit provided) return total + page
|
||||
const wantsPagination =
|
||||
!!opts &&
|
||||
(typeof opts.limit === "number" || typeof opts.offset === "number");
|
||||
|
||||
if (wantsPagination) {
|
||||
const limit = Math.min(Number(opts?.limit ?? 5), 1000);
|
||||
const offset = Number(opts?.offset ?? 0);
|
||||
|
||||
if (opts?.withGroup) {
|
||||
// return total + data with group included
|
||||
const [total, data] = await Promise.all([
|
||||
db.pdfFile.count({ where: { groupId } }),
|
||||
db.pdfFile.findMany({
|
||||
where: { groupId },
|
||||
orderBy: { uploadedAt: "desc" },
|
||||
take: limit,
|
||||
skip: offset,
|
||||
include: { group: true }, // only include
|
||||
}),
|
||||
]);
|
||||
|
||||
return { total, data };
|
||||
} else {
|
||||
// return total + data with limited fields via select
|
||||
const [total, data] = await Promise.all([
|
||||
db.pdfFile.count({ where: { groupId } }),
|
||||
db.pdfFile.findMany({
|
||||
where: { groupId },
|
||||
orderBy: { uploadedAt: "desc" },
|
||||
take: limit,
|
||||
skip: offset,
|
||||
select: { id: true, filename: true, uploadedAt: true }, // only select
|
||||
}),
|
||||
]);
|
||||
|
||||
// Note: selected shape won't have all PdfFile fields; cast if needed
|
||||
return { total, data: data as unknown as PdfFile[] };
|
||||
}
|
||||
}
|
||||
|
||||
// non-paginated: return all files (keep descending order)
|
||||
if (opts?.withGroup) {
|
||||
const all = await db.pdfFile.findMany({
|
||||
where: { groupId },
|
||||
orderBy: { uploadedAt: "desc" },
|
||||
include: { group: true },
|
||||
});
|
||||
return all as PdfFile[];
|
||||
} else {
|
||||
const all = await db.pdfFile.findMany({
|
||||
where: { groupId },
|
||||
orderBy: { uploadedAt: "desc" },
|
||||
// no select or include -> returns full PdfFile
|
||||
});
|
||||
return all as PdfFile[];
|
||||
}
|
||||
},
|
||||
|
||||
async getRecentPdfFiles(limit: number, offset: number): Promise<PdfFile[]> {
|
||||
@@ -739,28 +799,22 @@ export const storage: IStorage = {
|
||||
// PdfGroup CRUD
|
||||
// ----------------------
|
||||
|
||||
async createPdfGroup(patientId, title, titleKey, category) {
|
||||
async createPdfGroup(patientId, title, titleKey) {
|
||||
return db.pdfGroup.create({
|
||||
data: {
|
||||
patientId,
|
||||
title,
|
||||
titleKey,
|
||||
category,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
async findPdfGroupByPatientTitleKeyAndCategory(
|
||||
patientId,
|
||||
titleKey,
|
||||
category
|
||||
) {
|
||||
async findPdfGroupByPatientTitleKey(patientId, titleKey) {
|
||||
return (
|
||||
(await db.pdfGroup.findFirst({
|
||||
where: {
|
||||
patientId,
|
||||
titleKey,
|
||||
category,
|
||||
},
|
||||
})) ?? undefined
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user