fix: persist and correctly forward appointment attachments to insurance claims
- Add "surg ext" alias mapping to D7210 in the AI CDT lookup. - Add GET/DELETE endpoints for appointment attachments, and load existing attachments in the appointment edit modal (they were uploaded correctly but never re-fetched, so they appeared to vanish on reopen). - Fix claim-form.tsx so previously-saved appointment attachments are always loaded (even when a claim already exists) instead of only when creating a brand-new claim, preventing them from being silently wiped on save. - Merge existing appointment attachments into every insurance submit handler (MassHealth, CCA, DDMA, UnitedDH, TuftsSCO) so Selenium submission always includes files saved earlier via the Schedule editor, not just files uploaded in the current claim-form session. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -125,6 +125,7 @@ const ALIAS_MAP: Record<string, string> = {
|
|||||||
"simple extraction": "extraction erupted",
|
"simple extraction": "extraction erupted",
|
||||||
"surgical extraction": "extraction bone",
|
"surgical extraction": "extraction bone",
|
||||||
"surgical ext": "extraction bone",
|
"surgical ext": "extraction bone",
|
||||||
|
"surg ext": "extraction bone",
|
||||||
"baby tooth ext": "extraction primary",
|
"baby tooth ext": "extraction primary",
|
||||||
"baby tooth extraction": "extraction primary",
|
"baby tooth extraction": "extraction primary",
|
||||||
"primary tooth ext": "extraction primary",
|
"primary tooth ext": "extraction primary",
|
||||||
@@ -211,6 +212,7 @@ const DIRECT_CODE_MAP: Record<string, string> = {
|
|||||||
"primary tooth ext": "D7111",
|
"primary tooth ext": "D7111",
|
||||||
"surgical extraction": "D7210",
|
"surgical extraction": "D7210",
|
||||||
"surgical ext": "D7210",
|
"surgical ext": "D7210",
|
||||||
|
"surg ext": "D7210",
|
||||||
"soft tissue impaction": "D7220",
|
"soft tissue impaction": "D7220",
|
||||||
"partial bony": "D7230",
|
"partial bony": "D7230",
|
||||||
"complete bony": "D7240",
|
"complete bony": "D7240",
|
||||||
|
|||||||
@@ -489,6 +489,27 @@ router.patch("/:id/confirm", async (req: Request, res: Response): Promise<any> =
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// List attachments for an appointment
|
||||||
|
router.get(
|
||||||
|
"/:appointmentId/files",
|
||||||
|
async (req: Request, res: Response): Promise<any> => {
|
||||||
|
if (!req.user?.id) return res.status(401).json({ error: "Unauthorized" });
|
||||||
|
|
||||||
|
const appointmentId = parseInt(req.params.appointmentId || "", 10);
|
||||||
|
if (isNaN(appointmentId)) {
|
||||||
|
return res.status(400).json({ error: "Invalid appointment ID" });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const files = await storage.getAppointmentFiles(appointmentId);
|
||||||
|
return res.json({ error: false, data: files });
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error("[appointment-files]", err);
|
||||||
|
return res.status(500).json({ error: "Failed to load attachments", message: err?.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// Add a single attachment to an appointment (incremental insert, does not touch existing files)
|
// Add a single attachment to an appointment (incremental insert, does not touch existing files)
|
||||||
router.post(
|
router.post(
|
||||||
"/:appointmentId/files",
|
"/:appointmentId/files",
|
||||||
@@ -555,6 +576,29 @@ router.post(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Delete a single attachment from an appointment
|
||||||
|
router.delete(
|
||||||
|
"/:appointmentId/files/:fileId",
|
||||||
|
async (req: Request, res: Response): Promise<any> => {
|
||||||
|
if (!req.user?.id) return res.status(401).json({ error: "Unauthorized" });
|
||||||
|
|
||||||
|
const appointmentId = parseInt(req.params.appointmentId || "", 10);
|
||||||
|
const fileId = parseInt(req.params.fileId || "", 10);
|
||||||
|
if (isNaN(appointmentId) || isNaN(fileId)) {
|
||||||
|
return res.status(400).json({ error: "Invalid appointment or file ID" });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const deleted = await storage.deleteAppointmentFile(appointmentId, fileId);
|
||||||
|
if (!deleted) return res.status(404).json({ error: "Attachment not found" });
|
||||||
|
return res.json({ error: false });
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error("[appointment-files]", err);
|
||||||
|
return res.status(500).json({ error: "Failed to delete attachment", message: err?.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// Delete an appointment
|
// Delete an appointment
|
||||||
router.delete("/:id", async (req: Request, res: Response): Promise<any> => {
|
router.delete("/:id", async (req: Request, res: Response): Promise<any> => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -860,7 +860,11 @@ router.post(
|
|||||||
// straight from disk via filePath (unlike MH/CCA, which get base64 buffers above).
|
// straight from disk via filePath (unlike MH/CCA, which get base64 buffers above).
|
||||||
const diskClaimFiles = allFileMeta.filter((f) => {
|
const diskClaimFiles = allFileMeta.filter((f) => {
|
||||||
if (!f.filePath) return false;
|
if (!f.filePath) return false;
|
||||||
return fs.existsSync(path.join(process.cwd(), f.filePath));
|
const exists = fs.existsSync(path.join(process.cwd(), f.filePath));
|
||||||
|
if (!exists) {
|
||||||
|
console.warn(`[batch-column] attachment not found on disk: ${path.join(process.cwd(), f.filePath)}`);
|
||||||
|
}
|
||||||
|
return exists;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Base claim data shared across all insurance pathways
|
// Base claim data shared across all insurance pathways
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import {
|
|||||||
UpdateAppointmentProcedure,
|
UpdateAppointmentProcedure,
|
||||||
} from "@repo/db/types";
|
} from "@repo/db/types";
|
||||||
import { prisma as db } from "@repo/db/client";
|
import { prisma as db } from "@repo/db/client";
|
||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
export interface AppointmentFileMeta {
|
export interface AppointmentFileMeta {
|
||||||
filename: string;
|
filename: string;
|
||||||
@@ -50,6 +52,7 @@ export interface IAppointmentProceduresStorage {
|
|||||||
appointmentId: number,
|
appointmentId: number,
|
||||||
file: AppointmentFileMeta
|
file: AppointmentFileMeta
|
||||||
): Promise<AppointmentFileMeta & { id: number }>;
|
): Promise<AppointmentFileMeta & { id: number }>;
|
||||||
|
deleteAppointmentFile(appointmentId: number, fileId: number): Promise<boolean>;
|
||||||
getAppointmentIdsWithProcedures(ids: number[]): Promise<Set<number>>;
|
getAppointmentIdsWithProcedures(ids: number[]): Promise<Set<number>>;
|
||||||
getProcedureCodesByAppointmentIds(ids: number[]): Promise<Map<number, string[]>>;
|
getProcedureCodesByAppointmentIds(ids: number[]): Promise<Map<number, string[]>>;
|
||||||
}
|
}
|
||||||
@@ -225,4 +228,30 @@ export const appointmentProceduresStorage: IAppointmentProceduresStorage = {
|
|||||||
filePath: row.filePath,
|
filePath: row.filePath,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async deleteAppointmentFile(appointmentId: number, fileId: number): Promise<boolean> {
|
||||||
|
const file = await db.appointmentFile.findFirst({
|
||||||
|
where: { id: fileId, appointmentId },
|
||||||
|
});
|
||||||
|
if (!file) return false;
|
||||||
|
|
||||||
|
await db.appointmentFile.delete({ where: { id: fileId } });
|
||||||
|
|
||||||
|
// Best-effort cleanup of the underlying cloud-storage file/disk copy.
|
||||||
|
if (file.filePath) {
|
||||||
|
const cloudFile = await db.cloudFile.findFirst({
|
||||||
|
where: { diskPath: file.filePath },
|
||||||
|
select: { id: true, diskPath: true },
|
||||||
|
});
|
||||||
|
if (cloudFile) {
|
||||||
|
if (cloudFile.diskPath) {
|
||||||
|
const abs = path.join(process.cwd(), cloudFile.diskPath);
|
||||||
|
if (fs.existsSync(abs)) fs.unlinkSync(abs);
|
||||||
|
}
|
||||||
|
await db.cloudFile.delete({ where: { id: cloudFile.id } }).catch(() => null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import ReactCrop, { Crop, PixelCrop, centerCrop, makeAspectCrop } from "react-image-crop";
|
import ReactCrop, { Crop, PixelCrop, centerCrop, makeAspectCrop } from "react-image-crop";
|
||||||
import "react-image-crop/dist/ReactCrop.css";
|
import "react-image-crop/dist/ReactCrop.css";
|
||||||
import { Paperclip, Image as ImageIcon, Camera, Loader2, Check, RotateCcw, FileText } from "lucide-react";
|
import { Paperclip, Image as ImageIcon, Camera, Loader2, Check, RotateCcw, FileText, Trash2 } from "lucide-react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
@@ -26,7 +26,29 @@ interface AttachmentItem {
|
|||||||
localId: string;
|
localId: string;
|
||||||
filename: string;
|
filename: string;
|
||||||
status: "uploading" | "done" | "error";
|
status: "uploading" | "done" | "error";
|
||||||
file: File;
|
file?: File;
|
||||||
|
fileId?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchAppointmentAttachments(appointmentId: number): Promise<AttachmentItem[]> {
|
||||||
|
const res = await apiRequest("GET", `/api/appointments/${appointmentId}/files`);
|
||||||
|
if (!res.ok) return [];
|
||||||
|
const body = await res.json().catch(() => null);
|
||||||
|
const files: Array<{ id: number; filename: string }> = body?.data ?? [];
|
||||||
|
return files.map((f) => ({
|
||||||
|
localId: `existing-${f.id}`,
|
||||||
|
filename: f.filename,
|
||||||
|
status: "done" as const,
|
||||||
|
fileId: f.id,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteAppointmentAttachment(appointmentId: number, fileId: number) {
|
||||||
|
const res = await apiRequest("DELETE", `/api/appointments/${appointmentId}/files/${fileId}`);
|
||||||
|
if (!res.ok) {
|
||||||
|
const body = await res.json().catch(() => null);
|
||||||
|
throw new Error(body?.message || body?.error || "Failed to delete attachment");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCroppedDataUrl(image: HTMLImageElement, crop: PixelCrop): string {
|
function getCroppedDataUrl(image: HTMLImageElement, crop: PixelCrop): string {
|
||||||
@@ -80,6 +102,18 @@ export function AppointmentAttachments({ appointmentId, patientId }: Appointment
|
|||||||
const [completedCrop, setCompletedCrop] = useState<PixelCrop>();
|
const [completedCrop, setCompletedCrop] = useState<PixelCrop>();
|
||||||
const imgRef = useRef<HTMLImageElement | null>(null);
|
const imgRef = useRef<HTMLImageElement | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let cancelled = false;
|
||||||
|
if (appointmentId) {
|
||||||
|
void fetchAppointmentAttachments(appointmentId).then((existing) => {
|
||||||
|
if (!cancelled) setItems((prev) => [...existing, ...prev]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
};
|
||||||
|
}, [appointmentId]);
|
||||||
|
|
||||||
const handleImageLoad = (e: React.SyntheticEvent<HTMLImageElement>) => {
|
const handleImageLoad = (e: React.SyntheticEvent<HTMLImageElement>) => {
|
||||||
imgRef.current = e.currentTarget;
|
imgRef.current = e.currentTarget;
|
||||||
const { width, height } = e.currentTarget;
|
const { width, height } = e.currentTarget;
|
||||||
@@ -107,8 +141,12 @@ export function AppointmentAttachments({ appointmentId, patientId }: Appointment
|
|||||||
const localId = crypto.randomUUID();
|
const localId = crypto.randomUUID();
|
||||||
setItems((prev) => [...prev, { localId, filename: file.name, status: "uploading", file }]);
|
setItems((prev) => [...prev, { localId, filename: file.name, status: "uploading", file }]);
|
||||||
try {
|
try {
|
||||||
await uploadAppointmentAttachment(appointmentId, patientId, file);
|
const created = await uploadAppointmentAttachment(appointmentId, patientId, file);
|
||||||
setItems((prev) => prev.map((it) => (it.localId === localId ? { ...it, status: "done" } : it)));
|
setItems((prev) =>
|
||||||
|
prev.map((it) =>
|
||||||
|
it.localId === localId ? { ...it, status: "done", fileId: created?.data?.id } : it
|
||||||
|
)
|
||||||
|
);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
setItems((prev) => prev.map((it) => (it.localId === localId ? { ...it, status: "error" } : it)));
|
setItems((prev) => prev.map((it) => (it.localId === localId ? { ...it, status: "error" } : it)));
|
||||||
toast({
|
toast({
|
||||||
@@ -120,10 +158,27 @@ export function AppointmentAttachments({ appointmentId, patientId }: Appointment
|
|||||||
};
|
};
|
||||||
|
|
||||||
const retryUpload = (item: AttachmentItem) => {
|
const retryUpload = (item: AttachmentItem) => {
|
||||||
|
if (!item.file) return;
|
||||||
setItems((prev) => prev.filter((it) => it.localId !== item.localId));
|
setItems((prev) => prev.filter((it) => it.localId !== item.localId));
|
||||||
void runUpload(item.file);
|
void runUpload(item.file);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const deleteItem = async (item: AttachmentItem) => {
|
||||||
|
if (!item.fileId) return;
|
||||||
|
const prevItems = items;
|
||||||
|
setItems((prev) => prev.filter((it) => it.localId !== item.localId));
|
||||||
|
try {
|
||||||
|
await deleteAppointmentAttachment(appointmentId, item.fileId);
|
||||||
|
} catch (err: any) {
|
||||||
|
setItems(prevItems);
|
||||||
|
toast({
|
||||||
|
title: "Delete failed",
|
||||||
|
description: err?.message ?? `Failed to delete ${item.filename}.`,
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleDiskFilesSelected = (files: FileList | null) => {
|
const handleDiskFilesSelected = (files: FileList | null) => {
|
||||||
if (!files?.length) return;
|
if (!files?.length) return;
|
||||||
Array.from(files).forEach((file) => void runUpload(file));
|
Array.from(files).forEach((file) => void runUpload(file));
|
||||||
@@ -239,6 +294,18 @@ export function AppointmentAttachments({ appointmentId, patientId }: Appointment
|
|||||||
<RotateCcw className="h-3.5 w-3.5 text-destructive" />
|
<RotateCcw className="h-3.5 w-3.5 text-destructive" />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
{item.status === "done" && item.fileId != null && (
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="h-5 w-5 shrink-0"
|
||||||
|
title="Delete attachment"
|
||||||
|
onClick={() => deleteItem(item)}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-3.5 w-3.5 text-destructive" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -463,10 +463,12 @@ export function ClaimForm({
|
|||||||
}, [appointmentId]);
|
}, [appointmentId]);
|
||||||
|
|
||||||
// 2b. Prefill procedures from AppointmentProcedure records.
|
// 2b. Prefill procedures from AppointmentProcedure records.
|
||||||
// Skipped when an existing claim was already loaded above.
|
// Service lines/NPI are skipped when an existing claim was already loaded above
|
||||||
|
// (claim data takes priority), but appointmentFiles must always be fetched here —
|
||||||
|
// otherwise existingAppointmentFiles stays empty and handleProceduresSave's merge
|
||||||
|
// logic silently wipes previously-attached files on the next save.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!appointmentId) return;
|
if (!appointmentId) return;
|
||||||
if (existingClaimId) return; // existing claim takes priority
|
|
||||||
|
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
|
|
||||||
@@ -482,6 +484,12 @@ export function ClaimForm({
|
|||||||
|
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
|
|
||||||
|
if (data.appointmentFiles?.length) {
|
||||||
|
setExistingAppointmentFiles(data.appointmentFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingClaimId) return; // existing claim takes priority for the rest
|
||||||
|
|
||||||
const mappedLines = (data.procedures || []).map((p: any) => ({
|
const mappedLines = (data.procedures || []).map((p: any) => ({
|
||||||
procedureCode: p.procedureCode,
|
procedureCode: p.procedureCode,
|
||||||
procedureDate: serviceDate,
|
procedureDate: serviceDate,
|
||||||
@@ -494,10 +502,6 @@ export function ClaimForm({
|
|||||||
totalPaid: new Decimal(0),
|
totalPaid: new Decimal(0),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (data.appointmentFiles?.length) {
|
|
||||||
setExistingAppointmentFiles(data.appointmentFiles);
|
|
||||||
}
|
|
||||||
|
|
||||||
setForm((prev) => ({
|
setForm((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
serviceLines: mappedLines.length > 0 ? mappedLines : prev.serviceLines,
|
serviceLines: mappedLines.length > 0 ? mappedLines : prev.serviceLines,
|
||||||
@@ -909,6 +913,19 @@ export function ClaimForm({
|
|||||||
const [existingAppointmentFiles, setExistingAppointmentFiles] = useState<ClaimFileMeta[]>([]);
|
const [existingAppointmentFiles, setExistingAppointmentFiles] = useState<ClaimFileMeta[]>([]);
|
||||||
const pendingClaimAction = useRef<(() => void) | null>(null);
|
const pendingClaimAction = useRef<(() => void) | null>(null);
|
||||||
|
|
||||||
|
// Merges attachments already saved on the appointment (e.g. via the Schedule editor) with
|
||||||
|
// any newly uploaded in this claim-form session, so Selenium submission always gets every
|
||||||
|
// file — not just ones uploaded through this form's own file picker in the current session.
|
||||||
|
const mergeWithExistingAttachments = useCallback(
|
||||||
|
(newFiles: ClaimFileMeta[]): ClaimFileMeta[] => {
|
||||||
|
const byFilename = new Map<string, ClaimFileMeta>();
|
||||||
|
for (const f of existingAppointmentFiles) byFilename.set(f.filename, f);
|
||||||
|
for (const f of newFiles) byFilename.set(f.filename, f);
|
||||||
|
return Array.from(byFilename.values());
|
||||||
|
},
|
||||||
|
[existingAppointmentFiles],
|
||||||
|
);
|
||||||
|
|
||||||
// NO validation here — the upload zone handles validation, toasts, max files, sizes, etc.
|
// NO validation here — the upload zone handles validation, toasts, max files, sizes, etc.
|
||||||
const handleFilesChange = useCallback((files: File[]) => {
|
const handleFilesChange = useCallback((files: File[]) => {
|
||||||
setForm((prev) => ({ ...prev, uploadedFiles: files }));
|
setForm((prev) => ({ ...prev, uploadedFiles: files }));
|
||||||
@@ -972,9 +989,9 @@ export function ClaimForm({
|
|||||||
} = f;
|
} = f;
|
||||||
|
|
||||||
// Save uploaded files to disk (uploads/patients/<name>/) and record their paths
|
// Save uploaded files to disk (uploads/patients/<name>/) and record their paths
|
||||||
const claimFilesMeta: ClaimFileMeta[] = uploadedFiles?.length
|
const claimFilesMeta: ClaimFileMeta[] = mergeWithExistingAttachments(
|
||||||
? await uploadAttachmentsToLocalFolder(uploadedFiles)
|
uploadedFiles?.length ? await uploadAttachmentsToLocalFolder(uploadedFiles) : [],
|
||||||
: [];
|
);
|
||||||
|
|
||||||
const selectedNpiProviderId = npiProvider?.npiNumber
|
const selectedNpiProviderId = npiProvider?.npiNumber
|
||||||
? npiProviders.find((p) => p.npiNumber === npiProvider.npiNumber)?.id ?? null
|
? npiProviders.find((p) => p.npiNumber === npiProvider.npiNumber)?.id ?? null
|
||||||
@@ -1103,10 +1120,12 @@ export function ClaimForm({
|
|||||||
const appointmentIdToUse = appointmentId ?? null;
|
const appointmentIdToUse = appointmentId ?? null;
|
||||||
|
|
||||||
const { uploadedFiles, insuranceSiteKey, npiProvider, ...formToCreateClaim } = f;
|
const { uploadedFiles, insuranceSiteKey, npiProvider, ...formToCreateClaim } = f;
|
||||||
const claimFilesMeta: ClaimFileMeta[] = (uploadedFiles || []).map((file) => ({
|
const claimFilesMeta: ClaimFileMeta[] = mergeWithExistingAttachments(
|
||||||
filename: file.name,
|
(uploadedFiles || []).map((file) => ({
|
||||||
mimeType: file.type,
|
filename: file.name,
|
||||||
}));
|
mimeType: file.type,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
const selectedNpiProviderId = npiProvider?.npiNumber
|
const selectedNpiProviderId = npiProvider?.npiNumber
|
||||||
? npiProviders.find((p) => p.npiNumber === npiProvider.npiNumber)?.id ?? null
|
? npiProviders.find((p) => p.npiNumber === npiProvider.npiNumber)?.id ?? null
|
||||||
@@ -1172,9 +1191,9 @@ export function ClaimForm({
|
|||||||
const { uploadedFiles, insuranceSiteKey, npiProvider, ...formToCreateClaim } = f;
|
const { uploadedFiles, insuranceSiteKey, npiProvider, ...formToCreateClaim } = f;
|
||||||
|
|
||||||
// Upload files to server so we get local filePaths for Selenium
|
// Upload files to server so we get local filePaths for Selenium
|
||||||
const claimFilesMeta: ClaimFileMeta[] = uploadedFiles?.length
|
const claimFilesMeta: ClaimFileMeta[] = mergeWithExistingAttachments(
|
||||||
? await uploadAttachmentsToLocalFolder(uploadedFiles)
|
uploadedFiles?.length ? await uploadAttachmentsToLocalFolder(uploadedFiles) : [],
|
||||||
: [];
|
);
|
||||||
|
|
||||||
const selectedNpiProviderId = npiProvider?.npiNumber
|
const selectedNpiProviderId = npiProvider?.npiNumber
|
||||||
? npiProviders.find((p) => p.npiNumber === npiProvider.npiNumber)?.id ?? null
|
? npiProviders.find((p) => p.npiNumber === npiProvider.npiNumber)?.id ?? null
|
||||||
@@ -1238,9 +1257,9 @@ export function ClaimForm({
|
|||||||
|
|
||||||
const { uploadedFiles, insuranceSiteKey, npiProvider, ...formToCreateClaim } = f;
|
const { uploadedFiles, insuranceSiteKey, npiProvider, ...formToCreateClaim } = f;
|
||||||
|
|
||||||
const claimFilesMeta: ClaimFileMeta[] = uploadedFiles?.length
|
const claimFilesMeta: ClaimFileMeta[] = mergeWithExistingAttachments(
|
||||||
? await uploadAttachmentsToLocalFolder(uploadedFiles)
|
uploadedFiles?.length ? await uploadAttachmentsToLocalFolder(uploadedFiles) : [],
|
||||||
: [];
|
);
|
||||||
|
|
||||||
const selectedNpiProviderId = npiProvider?.npiNumber
|
const selectedNpiProviderId = npiProvider?.npiNumber
|
||||||
? npiProviders.find((p) => p.npiNumber === npiProvider.npiNumber)?.id ?? null
|
? npiProviders.find((p) => p.npiNumber === npiProvider.npiNumber)?.id ?? null
|
||||||
@@ -1304,9 +1323,9 @@ export function ClaimForm({
|
|||||||
|
|
||||||
const { uploadedFiles, insuranceSiteKey, npiProvider, ...formToCreateClaim } = f;
|
const { uploadedFiles, insuranceSiteKey, npiProvider, ...formToCreateClaim } = f;
|
||||||
|
|
||||||
const claimFilesMeta: ClaimFileMeta[] = uploadedFiles?.length
|
const claimFilesMeta: ClaimFileMeta[] = mergeWithExistingAttachments(
|
||||||
? await uploadAttachmentsToLocalFolder(uploadedFiles)
|
uploadedFiles?.length ? await uploadAttachmentsToLocalFolder(uploadedFiles) : [],
|
||||||
: [];
|
);
|
||||||
|
|
||||||
const selectedNpiProviderId = npiProvider?.npiNumber
|
const selectedNpiProviderId = npiProvider?.npiNumber
|
||||||
? npiProviders.find((p) => p.npiNumber === npiProvider.npiNumber)?.id ?? null
|
? npiProviders.find((p) => p.npiNumber === npiProvider.npiNumber)?.id ?? null
|
||||||
@@ -1573,9 +1592,9 @@ export function ClaimForm({
|
|||||||
|
|
||||||
const { uploadedFiles, insuranceSiteKey, npiProvider, ...formToSave } = form;
|
const { uploadedFiles, insuranceSiteKey, npiProvider, ...formToSave } = form;
|
||||||
|
|
||||||
const claimFilesMeta: ClaimFileMeta[] = uploadedFiles?.length
|
const claimFilesMeta: ClaimFileMeta[] = mergeWithExistingAttachments(
|
||||||
? await uploadAttachmentsToLocalFolder(uploadedFiles)
|
uploadedFiles?.length ? await uploadAttachmentsToLocalFolder(uploadedFiles) : [],
|
||||||
: [];
|
);
|
||||||
|
|
||||||
// Find the npiProviderId matching the currently selected NPI provider
|
// Find the npiProviderId matching the currently selected NPI provider
|
||||||
const selectedNpiProviderId = npiProvider?.npiNumber
|
const selectedNpiProviderId = npiProvider?.npiNumber
|
||||||
@@ -1717,9 +1736,9 @@ export function ClaimForm({
|
|||||||
|
|
||||||
const { uploadedFiles, insuranceSiteKey, npiProvider, ...formToCreateClaim } = form;
|
const { uploadedFiles, insuranceSiteKey, npiProvider, ...formToCreateClaim } = form;
|
||||||
|
|
||||||
const claimFilesMeta: ClaimFileMeta[] = uploadedFiles?.length
|
const claimFilesMeta: ClaimFileMeta[] = mergeWithExistingAttachments(
|
||||||
? await uploadAttachmentsToLocalFolder(uploadedFiles)
|
uploadedFiles?.length ? await uploadAttachmentsToLocalFolder(uploadedFiles) : [],
|
||||||
: [];
|
);
|
||||||
|
|
||||||
const selectedNpiProviderId = npiProvider?.npiNumber
|
const selectedNpiProviderId = npiProvider?.npiNumber
|
||||||
? npiProviders.find((p) => p.npiNumber === npiProvider.npiNumber)?.id ?? null
|
? npiProviders.find((p) => p.npiNumber === npiProvider.npiNumber)?.id ?? null
|
||||||
|
|||||||
Reference in New Issue
Block a user