feat: MassHealth PDF import auto-pays full balance + patient name fix
- PDF import now marks payments as PAID when MassHealth patient's mhPaidAmount >= totalBilled (no patient balance) - Newly created patients from MH vouchers get insuranceProvider = 'MassHealth' - Existing patients with blank insuranceProvider get it filled on import - Fix: update patient name from PDF if existing record has empty name - Various frontend/selenium/route updates Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,51 @@ import { prisma } from "@repo/db/client";
|
|||||||
import { PaymentStatusSchema } from "@repo/db/types";
|
import { PaymentStatusSchema } from "@repo/db/types";
|
||||||
import * as paymentService from "../services/paymentService";
|
import * as paymentService from "../services/paymentService";
|
||||||
import { callPythonSync } from "../queue/processors/_shared";
|
import { callPythonSync } from "../queue/processors/_shared";
|
||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import axios from "axios";
|
||||||
|
import FormData from "form-data";
|
||||||
|
|
||||||
|
const VOUCHER_DIR = path.join(__dirname, "..", "..", "uploads", "MHVoucher");
|
||||||
|
const IMPORTED_LOG = path.join(VOUCHER_DIR, "imported_vouchers.json");
|
||||||
|
const OCR_BASE_URL = process.env.OCR_SERVICE_BASE_URL || "http://localhost:5003";
|
||||||
|
|
||||||
|
function loadImportedVouchers(): Set<string> {
|
||||||
|
try {
|
||||||
|
if (!fs.existsSync(IMPORTED_LOG)) return new Set();
|
||||||
|
const data = JSON.parse(fs.readFileSync(IMPORTED_LOG, "utf-8"));
|
||||||
|
return new Set(data.imported ?? []);
|
||||||
|
} catch {
|
||||||
|
return new Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveImportedVoucher(voucher: string) {
|
||||||
|
const existing = loadImportedVouchers();
|
||||||
|
existing.add(voucher);
|
||||||
|
fs.writeFileSync(IMPORTED_LOG, JSON.stringify({ imported: [...existing].sort() }, null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function extractAndImportVoucherPdf(filePath: string, userId: number): Promise<{ paymentIds: number[]; rowCount: number }> {
|
||||||
|
const buffer = fs.readFileSync(filePath);
|
||||||
|
const filename = path.basename(filePath);
|
||||||
|
|
||||||
|
const form = new FormData();
|
||||||
|
form.append("files", buffer, { filename, contentType: "application/pdf", knownLength: buffer.length });
|
||||||
|
|
||||||
|
const resp = await axios.post<{ rows: any[] }>(
|
||||||
|
`${OCR_BASE_URL}/extract/pdf/json`,
|
||||||
|
form,
|
||||||
|
{ headers: form.getHeaders(), maxBodyLength: Infinity, maxContentLength: Infinity, timeout: 120_000 }
|
||||||
|
);
|
||||||
|
|
||||||
|
const rows = resp.data?.rows ?? [];
|
||||||
|
console.log(`[mh-batch] Extracted ${rows.length} rows from ${filename}`);
|
||||||
|
if (rows.length === 0) return { paymentIds: [], rowCount: 0 };
|
||||||
|
|
||||||
|
const paymentIds = await paymentService.pdfImportService.importRows(rows, userId);
|
||||||
|
return { paymentIds, rowCount: rows.length };
|
||||||
|
}
|
||||||
|
|
||||||
const paymentFilterSchema = z.object({
|
const paymentFilterSchema = z.object({
|
||||||
from: z.string().datetime(),
|
from: z.string().datetime(),
|
||||||
@@ -192,6 +237,76 @@ router.post(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// POST /api/payments/mh-batch-payment-check
|
||||||
|
router.post(
|
||||||
|
"/mh-batch-payment-check",
|
||||||
|
async (req: Request, res: Response): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const userId = req.user?.id;
|
||||||
|
if (!userId) return res.status(401).json({ message: "Unauthorized" });
|
||||||
|
|
||||||
|
const { fromDate, toDate } = req.body;
|
||||||
|
|
||||||
|
const credentials = await storage.getInsuranceCredentialByUserAndSiteKey(userId, "MH");
|
||||||
|
if (!credentials) {
|
||||||
|
return res.status(404).json({
|
||||||
|
message: "No MassHealth credentials found. Please add them in Settings.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const seleniumResult = await callPythonSync("/mh-batch-payment-check", {
|
||||||
|
data: {
|
||||||
|
massdhpUsername: credentials.username,
|
||||||
|
massdhpPassword: credentials.password,
|
||||||
|
fromDate,
|
||||||
|
toDate,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- PDF import phase ---
|
||||||
|
const alreadyImported = loadImportedVouchers();
|
||||||
|
const allPdfs = fs.existsSync(VOUCHER_DIR)
|
||||||
|
? fs.readdirSync(VOUCHER_DIR).filter((f) => f.endsWith(".pdf") && !f.startsWith("remittance_search_"))
|
||||||
|
: [];
|
||||||
|
const newPdfs = allPdfs.filter((f) => !alreadyImported.has(f.replace(".pdf", "")));
|
||||||
|
|
||||||
|
console.log(`[mh-batch] ${allPdfs.length} voucher PDFs total, ${newPdfs.length} new to import`);
|
||||||
|
|
||||||
|
const importResults: { voucher: string; paymentIds: number[]; rowCount: number; error?: string }[] = [];
|
||||||
|
|
||||||
|
for (const pdfFile of newPdfs) {
|
||||||
|
const voucher = pdfFile.replace(".pdf", "");
|
||||||
|
const filePath = path.join(VOUCHER_DIR, pdfFile);
|
||||||
|
console.log(`[mh-batch] Extracting and importing: ${voucher}`);
|
||||||
|
try {
|
||||||
|
const { paymentIds, rowCount } = await extractAndImportVoucherPdf(filePath, userId);
|
||||||
|
saveImportedVoucher(voucher);
|
||||||
|
importResults.push({ voucher, paymentIds, rowCount });
|
||||||
|
console.log(`[mh-batch] ✓ ${voucher}: ${rowCount} rows → ${paymentIds.length} payment(s)`);
|
||||||
|
} catch (err: any) {
|
||||||
|
const errMsg = err?.response?.data?.detail ?? err.message ?? "Unknown error";
|
||||||
|
console.error(`[mh-batch] ✗ ${voucher}: ${errMsg}`);
|
||||||
|
importResults.push({ voucher, paymentIds: [], rowCount: 0, error: errMsg });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const succeeded = importResults.filter((r) => !r.error);
|
||||||
|
const failed = importResults.filter((r) => r.error);
|
||||||
|
|
||||||
|
return res.json({
|
||||||
|
...seleniumResult,
|
||||||
|
importResults,
|
||||||
|
importSummary: newPdfs.length === 0
|
||||||
|
? "All PDFs already imported."
|
||||||
|
: `${succeeded.length} of ${newPdfs.length} PDFs imported successfully${failed.length ? `, ${failed.length} failed` : ""}.`,
|
||||||
|
});
|
||||||
|
} catch (err: unknown) {
|
||||||
|
const message = err instanceof Error ? err.message : "MH batch payment check failed";
|
||||||
|
return res.status(500).json({ message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// POST /api/payments/:claimId
|
// POST /api/payments/:claimId
|
||||||
router.post("/:claimId", async (req: Request, res: Response): Promise<any> => {
|
router.post("/:claimId", async (req: Request, res: Response): Promise<any> => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -261,27 +261,44 @@ export const pdfImportService = {
|
|||||||
data: {
|
data: {
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
insuranceId: memberNo || null,
|
insuranceId: memberNo || null,
|
||||||
|
insuranceProvider: "MassHealth",
|
||||||
dateOfBirth: new Date(Date.UTC(1900, 0, 1)),
|
dateOfBirth: new Date(Date.UTC(1900, 0, 1)),
|
||||||
gender: "",
|
gender: "",
|
||||||
phone: "",
|
phone: "",
|
||||||
userId,
|
userId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
const updates: Record<string, string> = {};
|
||||||
|
if (patientName && !patient.firstName && !patient.lastName) {
|
||||||
|
const { firstName, lastName } = parsePdfName(patientName);
|
||||||
|
updates.firstName = firstName;
|
||||||
|
updates.lastName = lastName;
|
||||||
|
}
|
||||||
|
if (!patient.insuranceProvider) {
|
||||||
|
updates.insuranceProvider = "MassHealth";
|
||||||
|
}
|
||||||
|
if (Object.keys(updates).length > 0) {
|
||||||
|
patient = await tx.patient.update({ where: { id: patient.id }, data: updates });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isMassHealth = patient.insuranceProvider?.startsWith("MassHealth") ?? false;
|
||||||
|
const paidInFull = isMassHealth && totalMhPaid.gte(totalBilled);
|
||||||
|
|
||||||
// 2. Create payment
|
// 2. Create payment
|
||||||
const payment = await tx.payment.create({
|
const payment = await tx.payment.create({
|
||||||
data: {
|
data: {
|
||||||
patientId: patient.id,
|
patientId: patient.id,
|
||||||
userId,
|
userId,
|
||||||
totalBilled,
|
totalBilled,
|
||||||
totalPaid: new Decimal(0),
|
totalPaid: paidInFull ? totalMhPaid : new Decimal(0),
|
||||||
totalAdjusted: new Decimal(0),
|
totalAdjusted: new Decimal(0),
|
||||||
totalDue: totalBilled,
|
totalDue: paidInFull ? new Decimal(0) : totalBilled,
|
||||||
mhPaidAmount: totalMhPaid,
|
mhPaidAmount: totalMhPaid,
|
||||||
adjustment,
|
adjustment,
|
||||||
status: "PENDING",
|
status: paidInFull ? "PAID" : "PENDING",
|
||||||
notes: `PDF import from ${first["Source File"] ?? "unknown file"}`,
|
notes: `PDF import from ${first["Source File"] ?? "unknown file"}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -290,7 +307,8 @@ export const pdfImportService = {
|
|||||||
for (const row of patientRows) {
|
for (const row of patientRows) {
|
||||||
const billed = new Decimal(row["Submitted Amount"] || 0);
|
const billed = new Decimal(row["Submitted Amount"] || 0);
|
||||||
const mhPaid = new Decimal(row["Paid Amount"] || 0);
|
const mhPaid = new Decimal(row["Paid Amount"] || 0);
|
||||||
const due = Decimal.max(0, billed.minus(mhPaid));
|
const linePaidInFull = isMassHealth && mhPaid.gte(billed);
|
||||||
|
const due = linePaidInFull ? new Decimal(0) : Decimal.max(0, billed.minus(mhPaid));
|
||||||
const { toothNumber, toothSurface } = parseTooth(row["Tooth"]);
|
const { toothNumber, toothSurface } = parseTooth(row["Tooth"]);
|
||||||
|
|
||||||
const allowed = new Decimal(row["Allowed Amount"] || 0);
|
const allowed = new Decimal(row["Allowed Amount"] || 0);
|
||||||
@@ -309,7 +327,7 @@ export const pdfImportService = {
|
|||||||
totalPaid: mhPaid,
|
totalPaid: mhPaid,
|
||||||
totalAdjusted: new Decimal(0),
|
totalAdjusted: new Decimal(0),
|
||||||
totalDue: due,
|
totalDue: due,
|
||||||
status: mhPaid.gt(0) ? "PARTIALLY_PAID" : "UNPAID",
|
status: linePaidInFull ? "PAID" : mhPaid.gt(0) ? "PARTIALLY_PAID" : "UNPAID",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,10 @@ const ReportsPage = lazy(() => import("./pages/reports-page"));
|
|||||||
const CloudStoragePage = lazy(() => import("./pages/cloud-storage-page"));
|
const CloudStoragePage = lazy(() => import("./pages/cloud-storage-page"));
|
||||||
const JobMonitorPage = lazy(() => import("./pages/job-monitor-page"));
|
const JobMonitorPage = lazy(() => import("./pages/job-monitor-page"));
|
||||||
const ChartPage = lazy(() => import("./pages/chart-page"));
|
const ChartPage = lazy(() => import("./pages/chart-page"));
|
||||||
|
const AiInputAgentPage = lazy(() => import("./pages/ai-input-agent-page"));
|
||||||
const DentalShoppingSearchTagPage = lazy(() => import("./pages/dental-shopping-search-tag-page"));
|
const DentalShoppingSearchTagPage = lazy(() => import("./pages/dental-shopping-search-tag-page"));
|
||||||
const DentalShoppingLoginInfoPage = lazy(() => import("./pages/dental-shopping-login-info-page"));
|
const DentalShoppingLoginInfoPage = lazy(() => import("./pages/dental-shopping-login-info-page"));
|
||||||
|
const ActivationPage = lazy(() => import("./pages/activation-page"));
|
||||||
const NotFound = lazy(() => import("./pages/not-found"));
|
const NotFound = lazy(() => import("./pages/not-found"));
|
||||||
function Router() {
|
function Router() {
|
||||||
return (<Switch>
|
return (<Switch>
|
||||||
@@ -46,8 +48,10 @@ function Router() {
|
|||||||
<ProtectedRoute path="/database-management" component={() => <DatabaseManagementPage />} adminOnly/>
|
<ProtectedRoute path="/database-management" component={() => <DatabaseManagementPage />} adminOnly/>
|
||||||
<ProtectedRoute path="/reports" component={() => <ReportsPage />}/>
|
<ProtectedRoute path="/reports" component={() => <ReportsPage />}/>
|
||||||
<ProtectedRoute path="/cloud-storage" component={() => <CloudStoragePage />}/>
|
<ProtectedRoute path="/cloud-storage" component={() => <CloudStoragePage />}/>
|
||||||
|
<ProtectedRoute path="/ai-input-agent" component={() => <AiInputAgentPage />}/>
|
||||||
<ProtectedRoute path="/dental-shopping/search-tag" component={() => <DentalShoppingSearchTagPage />}/>
|
<ProtectedRoute path="/dental-shopping/search-tag" component={() => <DentalShoppingSearchTagPage />}/>
|
||||||
<ProtectedRoute path="/dental-shopping/login-info" component={() => <DentalShoppingLoginInfoPage />}/>
|
<ProtectedRoute path="/dental-shopping/login-info" component={() => <DentalShoppingLoginInfoPage />}/>
|
||||||
|
<ProtectedRoute path="/activation" component={() => <ActivationPage />} adminOnly/>
|
||||||
<ProtectedRoute path="/job-monitor" component={() => <JobMonitorPage />} adminOnly/>
|
<ProtectedRoute path="/job-monitor" component={() => <JobMonitorPage />} adminOnly/>
|
||||||
<Route path="/auth" component={() => <AuthPage />}/>
|
<Route path="/auth" component={() => <AuthPage />}/>
|
||||||
<Route component={() => <NotFound />}/>
|
<Route component={() => <NotFound />}/>
|
||||||
|
|||||||
@@ -18,22 +18,38 @@ export const PatientForm = forwardRef(({ patient, extractedInfo, onSubmit }, ref
|
|||||||
: insertPatientSchema.extend({ userId: z.number().optional() }), [isEditing]);
|
: insertPatientSchema.extend({ userId: z.number().optional() }), [isEditing]);
|
||||||
const normalizeInsuranceProvider = (val) => {
|
const normalizeInsuranceProvider = (val) => {
|
||||||
const p = (val || "").toLowerCase().trim();
|
const p = (val || "").toLowerCase().trim();
|
||||||
if (p.includes("masshealth") || p === "mh" || p === "mass health") return "MassHealth";
|
if (p.includes("masshealth") || p === "mh" || p === "mass health")
|
||||||
if (p.includes("commonwealth care alliance") || p === "cca") return "CCA";
|
return "MassHealth";
|
||||||
if (p.includes("ddma")) return "DDMA";
|
if (p.includes("commonwealth care alliance") || p === "cca")
|
||||||
if (p.includes("delta dental") || p.includes("delta ins") || p === "deltains") return "DeltaIns";
|
return "CCA";
|
||||||
if (p.includes("tufts") || p.includes("dentaquest") || p === "tuftssco") return "TuftsSCO";
|
if (p.includes("ddma"))
|
||||||
if (p.includes("united sco") || p === "unitedsco") return "UnitedSCO";
|
return "DDMA";
|
||||||
if (p.includes("cmsp")) return "CMSP";
|
if (p.includes("delta dental") || p.includes("delta ins") || p === "deltains")
|
||||||
if (p.includes("bcbs") || p.includes("blue cross")) return "BCBS";
|
return "DeltaIns";
|
||||||
if (p.includes("united aapr") || p === "unitedaapr") return "UnitedAAPR";
|
if (p.includes("tufts") || p.includes("dentaquest") || p === "tuftssco")
|
||||||
if (p.includes("aetna")) return "Aetna";
|
return "TuftsSCO";
|
||||||
if (p.includes("altus")) return "Altus";
|
if (p.includes("united sco") || p === "unitedsco")
|
||||||
if (p.includes("metlife")) return "MetlifeDental";
|
return "UnitedSCO";
|
||||||
if (p.includes("cigna")) return "Cigna";
|
if (p.includes("cmsp"))
|
||||||
if (p.includes("delta wa") || p === "deltawa") return "DeltaWA";
|
return "CMSP";
|
||||||
if (p.includes("delta il") || p === "deltail") return "DeltaIL";
|
if (p.includes("bcbs") || p.includes("blue cross"))
|
||||||
if (p.includes("other")) return "Others";
|
return "BCBS";
|
||||||
|
if (p.includes("united aapr") || p === "unitedaapr")
|
||||||
|
return "UnitedAAPR";
|
||||||
|
if (p.includes("aetna"))
|
||||||
|
return "Aetna";
|
||||||
|
if (p.includes("altus"))
|
||||||
|
return "Altus";
|
||||||
|
if (p.includes("metlife"))
|
||||||
|
return "MetlifeDental";
|
||||||
|
if (p.includes("cigna"))
|
||||||
|
return "Cigna";
|
||||||
|
if (p.includes("delta wa") || p === "deltawa")
|
||||||
|
return "DeltaWA";
|
||||||
|
if (p.includes("delta il") || p === "deltail")
|
||||||
|
return "DeltaIL";
|
||||||
|
if (p.includes("other"))
|
||||||
|
return "Others";
|
||||||
return val || "";
|
return val || "";
|
||||||
};
|
};
|
||||||
const computedDefaultValues = useMemo(() => {
|
const computedDefaultValues = useMemo(() => {
|
||||||
@@ -87,15 +103,16 @@ export const PatientForm = forwardRef(({ patient, extractedInfo, onSubmit }, ref
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (patient) {
|
if (patient) {
|
||||||
const { id, userId, createdAt, ...sanitizedPatient } = patient;
|
const { id, userId, createdAt, ...sanitizedPatient } = patient;
|
||||||
|
const normalized = normalizeInsuranceProvider(patient.insuranceProvider);
|
||||||
const resetValues = {
|
const resetValues = {
|
||||||
...sanitizedPatient,
|
...sanitizedPatient,
|
||||||
dateOfBirth: patient.dateOfBirth
|
dateOfBirth: patient.dateOfBirth
|
||||||
? formatLocalDate(new Date(patient.dateOfBirth))
|
? formatLocalDate(new Date(patient.dateOfBirth))
|
||||||
: null,
|
: null,
|
||||||
insuranceProvider: normalizeInsuranceProvider(patient.insuranceProvider),
|
insuranceProvider: normalized,
|
||||||
};
|
};
|
||||||
const normalized = normalizeInsuranceProvider(patient.insuranceProvider);
|
|
||||||
form.reset(resetValues);
|
form.reset(resetValues);
|
||||||
|
// Explicit setValue ensures the controlled Select re-renders with the new value
|
||||||
form.setValue("insuranceProvider", normalized);
|
form.setValue("insuranceProvider", normalized);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -579,7 +579,7 @@ export default function PaymentsRecentTable({
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isMhChecking ? "Checking..." : "Check MH Payment"}
|
{isMhChecking ? "Checking..." : "Check Single MH Payment"}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|||||||
@@ -31,12 +31,21 @@ import { apiRequest } from "@/lib/queryClient";
|
|||||||
import { toast } from "@/hooks/use-toast";
|
import { toast } from "@/hooks/use-toast";
|
||||||
import PaymentEditModal from "@/components/payments/payment-edit-modal";
|
import PaymentEditModal from "@/components/payments/payment-edit-modal";
|
||||||
|
|
||||||
|
function formatDateInput(raw: string): string {
|
||||||
|
const digits = raw.replace(/\D/g, "").slice(0, 8);
|
||||||
|
if (digits.length <= 2) return digits;
|
||||||
|
if (digits.length <= 4) return `${digits.slice(0, 2)}/${digits.slice(2)}`;
|
||||||
|
return `${digits.slice(0, 2)}/${digits.slice(2, 4)}/${digits.slice(4)}`;
|
||||||
|
}
|
||||||
|
|
||||||
export default function PaymentsPage() {
|
export default function PaymentsPage() {
|
||||||
const [paymentPeriod, setPaymentPeriod] = useState<string>("all-time");
|
const [paymentPeriod, setPaymentPeriod] = useState<string>("all-time");
|
||||||
|
|
||||||
// Check Payments Online date range
|
// Check Payments Online date range
|
||||||
const [mhFromDate, setMhFromDate] = useState<Date | undefined>(undefined);
|
const [mhFromDate, setMhFromDate] = useState<Date | undefined>(undefined);
|
||||||
const [mhToDate, setMhToDate] = useState<Date | undefined>(undefined);
|
const [mhToDate, setMhToDate] = useState<Date | undefined>(undefined);
|
||||||
|
const [mhFromText, setMhFromText] = useState("");
|
||||||
|
const [mhToText, setMhToText] = useState("");
|
||||||
const [fromCalendarOpen, setFromCalendarOpen] = useState(false);
|
const [fromCalendarOpen, setFromCalendarOpen] = useState(false);
|
||||||
const [toCalendarOpen, setToCalendarOpen] = useState(false);
|
const [toCalendarOpen, setToCalendarOpen] = useState(false);
|
||||||
|
|
||||||
@@ -229,7 +238,7 @@ export default function PaymentsPage() {
|
|||||||
{/* Check Payments Online */}
|
{/* Check Payments Online */}
|
||||||
<Card className="mb-6">
|
<Card className="mb-6">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>Check Payments Online</CardTitle>
|
<CardTitle>Check MH Payments</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
Select a date range and check MH payment status online
|
Select a date range and check MH payment status online
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
@@ -239,69 +248,115 @@ export default function PaymentsPage() {
|
|||||||
{/* From date picker */}
|
{/* From date picker */}
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="text-sm font-medium text-gray-600 whitespace-nowrap">From:</span>
|
<span className="text-sm font-medium text-gray-600 whitespace-nowrap">From:</span>
|
||||||
<Popover open={fromCalendarOpen} onOpenChange={setFromCalendarOpen}>
|
<div className="flex items-center gap-1">
|
||||||
<PopoverTrigger asChild>
|
<input
|
||||||
<Button
|
type="text"
|
||||||
variant="outline"
|
placeholder="MM/DD/YYYY"
|
||||||
className="w-[160px] justify-start text-left font-normal"
|
className="border rounded-md px-2 py-1.5 text-sm w-[110px] focus:outline-none focus:ring-2 focus:ring-ring"
|
||||||
>
|
value={mhFromText}
|
||||||
<CalendarIcon className="mr-2 h-4 w-4 text-gray-400" />
|
onChange={(e) => {
|
||||||
{mhFromDate
|
const formatted = formatDateInput(e.target.value);
|
||||||
? mhFromDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })
|
setMhFromText(formatted);
|
||||||
: <span className="text-muted-foreground">Pick a date</span>}
|
if (formatted === "") { setMhFromDate(undefined); return; }
|
||||||
</Button>
|
if (/^\d{2}\/\d{2}\/\d{4}$/.test(formatted)) {
|
||||||
</PopoverTrigger>
|
const parsed = new Date(formatted);
|
||||||
<PopoverContent className="w-auto p-0" align="start">
|
if (!isNaN(parsed.getTime())) setMhFromDate(parsed);
|
||||||
<Calendar
|
}
|
||||||
mode="single"
|
}}
|
||||||
selected={mhFromDate}
|
/>
|
||||||
onSelect={(d) => {
|
<Popover open={fromCalendarOpen} onOpenChange={setFromCalendarOpen}>
|
||||||
setMhFromDate(d);
|
<PopoverTrigger asChild>
|
||||||
setFromCalendarOpen(false);
|
<Button type="button" variant="outline" size="icon" className="h-8 w-8 shrink-0">
|
||||||
}}
|
<CalendarIcon className="h-4 w-4" />
|
||||||
onClose={() => setFromCalendarOpen(false)}
|
</Button>
|
||||||
/>
|
</PopoverTrigger>
|
||||||
</PopoverContent>
|
<PopoverContent className="w-auto p-0" align="start">
|
||||||
</Popover>
|
<Calendar
|
||||||
|
mode="single"
|
||||||
|
selected={mhFromDate}
|
||||||
|
onSelect={(d) => {
|
||||||
|
setMhFromDate(d);
|
||||||
|
setMhFromText(d ? d.toLocaleDateString("en-US", { month: "2-digit", day: "2-digit", year: "numeric" }) : "");
|
||||||
|
setFromCalendarOpen(false);
|
||||||
|
}}
|
||||||
|
onClose={() => setFromCalendarOpen(false)}
|
||||||
|
/>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* To date picker */}
|
{/* To date picker */}
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="text-sm font-medium text-gray-600 whitespace-nowrap">To:</span>
|
<span className="text-sm font-medium text-gray-600 whitespace-nowrap">To:</span>
|
||||||
<Popover open={toCalendarOpen} onOpenChange={setToCalendarOpen}>
|
<div className="flex items-center gap-1">
|
||||||
<PopoverTrigger asChild>
|
<input
|
||||||
<Button
|
type="text"
|
||||||
variant="outline"
|
placeholder="MM/DD/YYYY"
|
||||||
className="w-[160px] justify-start text-left font-normal"
|
className="border rounded-md px-2 py-1.5 text-sm w-[110px] focus:outline-none focus:ring-2 focus:ring-ring"
|
||||||
>
|
value={mhToText}
|
||||||
<CalendarIcon className="mr-2 h-4 w-4 text-gray-400" />
|
onChange={(e) => {
|
||||||
{mhToDate
|
const formatted = formatDateInput(e.target.value);
|
||||||
? mhToDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })
|
setMhToText(formatted);
|
||||||
: <span className="text-muted-foreground">Pick a date</span>}
|
if (formatted === "") { setMhToDate(undefined); return; }
|
||||||
</Button>
|
if (/^\d{2}\/\d{2}\/\d{4}$/.test(formatted)) {
|
||||||
</PopoverTrigger>
|
const parsed = new Date(formatted);
|
||||||
<PopoverContent className="w-auto p-0" align="start">
|
if (!isNaN(parsed.getTime())) setMhToDate(parsed);
|
||||||
<Calendar
|
}
|
||||||
mode="single"
|
}}
|
||||||
selected={mhToDate}
|
/>
|
||||||
onSelect={(d) => {
|
<Popover open={toCalendarOpen} onOpenChange={setToCalendarOpen}>
|
||||||
setMhToDate(d);
|
<PopoverTrigger asChild>
|
||||||
setToCalendarOpen(false);
|
<Button type="button" variant="outline" size="icon" className="h-8 w-8 shrink-0">
|
||||||
}}
|
<CalendarIcon className="h-4 w-4" />
|
||||||
onClose={() => setToCalendarOpen(false)}
|
</Button>
|
||||||
/>
|
</PopoverTrigger>
|
||||||
</PopoverContent>
|
<PopoverContent className="w-auto p-0" align="start">
|
||||||
</Popover>
|
<Calendar
|
||||||
|
mode="single"
|
||||||
|
selected={mhToDate}
|
||||||
|
onSelect={(d) => {
|
||||||
|
setMhToDate(d);
|
||||||
|
setMhToText(d ? d.toLocaleDateString("en-US", { month: "2-digit", day: "2-digit", year: "numeric" }) : "");
|
||||||
|
setToCalendarOpen(false);
|
||||||
|
}}
|
||||||
|
onClose={() => setToCalendarOpen(false)}
|
||||||
|
/>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Check All MH Payment button */}
|
{/* MH Batch Payment Check button */}
|
||||||
<Button
|
<Button
|
||||||
variant="default"
|
variant="default"
|
||||||
onClick={() => {
|
onClick={async () => {
|
||||||
// Logic to be defined later
|
if (!mhFromDate || !mhToDate) {
|
||||||
|
toast({ title: "Please select both From and To dates", variant: "destructive" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await apiRequest("POST", "/api/payments/mh-batch-payment-check", {
|
||||||
|
fromDate: mhFromDate.toISOString().split("T")[0],
|
||||||
|
toDate: mhToDate.toISOString().split("T")[0],
|
||||||
|
});
|
||||||
|
if (!res.ok) {
|
||||||
|
const err = await res.json();
|
||||||
|
toast({ title: "MH Batch Payment Check Failed", description: err.message, variant: "destructive" });
|
||||||
|
} else {
|
||||||
|
const result = await res.json();
|
||||||
|
if (result.noResults) {
|
||||||
|
toast({ title: "No Results", description: result.message, variant: "destructive" });
|
||||||
|
} else {
|
||||||
|
toast({ title: "MH Batch Payment Check", description: result.importSummary ?? result.message ?? "Done" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
toast({ title: "Error", description: e.message, variant: "destructive" });
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Check All MH Payment
|
Go
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ function isDateOnlyString(s) {
|
|||||||
}
|
}
|
||||||
// ---------- formatDateToHumanReadable ----------
|
// ---------- formatDateToHumanReadable ----------
|
||||||
/**
|
/**
|
||||||
* Frontend-safe human readable formatter.
|
* Frontend-safe date formatter. Output format: "MM/DD/YYYY" (e.g. "03/01/1980").
|
||||||
*
|
*
|
||||||
* Rules:
|
* Rules:
|
||||||
* - If input is a date-only string "YYYY-MM-DD", format it directly (no TZ math).
|
* - If input is a date-only string "YYYY-MM-DD", format it directly (no TZ math).
|
||||||
@@ -118,37 +118,35 @@ function isDateOnlyString(s) {
|
|||||||
* - If input is any other string (ISO/timestamp), DO NOT call new Date(isoString) directly
|
* - If input is any other string (ISO/timestamp), DO NOT call new Date(isoString) directly
|
||||||
* for display. Instead, use parseLocalDate(dateInput) to extract the local calendar day
|
* for display. Instead, use parseLocalDate(dateInput) to extract the local calendar day
|
||||||
* (strip time portion) and render that. This prevents off-by-one day drift.
|
* (strip time portion) and render that. This prevents off-by-one day drift.
|
||||||
*
|
|
||||||
* Output example: "Oct 7, 2025"
|
|
||||||
*/
|
*/
|
||||||
export function formatDateToHumanReadable(dateInput) {
|
export function formatDateToHumanReadable(dateInput) {
|
||||||
if (!dateInput)
|
if (!dateInput)
|
||||||
return "N/A";
|
return "N/A";
|
||||||
// date-only string -> show as-is using MONTH_SHORT
|
// date-only string "YYYY-MM-DD" -> m and d are already zero-padded
|
||||||
if (typeof dateInput === "string" && isDateOnlyString(dateInput)) {
|
if (typeof dateInput === "string" && isDateOnlyString(dateInput)) {
|
||||||
const [y, m, d] = dateInput.split("-");
|
const [y, m, d] = dateInput.split("-");
|
||||||
if (!y || !m || !d)
|
if (!y || !m || !d)
|
||||||
return "Invalid Date";
|
return "Invalid Date";
|
||||||
return `${MONTH_SHORT[parseInt(m, 10) - 1]} ${d}, ${y}`;
|
return `${m}/${d}/${y}`;
|
||||||
}
|
}
|
||||||
// Date object -> use local calendar fields
|
// Date object -> use local calendar fields
|
||||||
if (dateInput instanceof Date) {
|
if (dateInput instanceof Date) {
|
||||||
if (isNaN(dateInput.getTime()))
|
if (isNaN(dateInput.getTime()))
|
||||||
return "Invalid Date";
|
return "Invalid Date";
|
||||||
const dd = String(dateInput.getDate());
|
const dd = String(dateInput.getDate()).padStart(2, "0");
|
||||||
const mm = MONTH_SHORT[dateInput.getMonth()];
|
const mm = String(dateInput.getMonth() + 1).padStart(2, "0");
|
||||||
const yy = dateInput.getFullYear();
|
const yy = dateInput.getFullYear();
|
||||||
return `${mm} ${dd}, ${yy}`;
|
return `${mm}/${dd}/${yy}`;
|
||||||
}
|
}
|
||||||
// Other string (likely ISO/timestamp) -> normalize via parseLocalDate
|
// Other string (likely ISO/timestamp) -> normalize via parseLocalDate
|
||||||
// This preserves the calendar day the user expects (no timezone drift).
|
// This preserves the calendar day the user expects (no timezone drift).
|
||||||
if (typeof dateInput === "string") {
|
if (typeof dateInput === "string") {
|
||||||
try {
|
try {
|
||||||
const localDate = parseLocalDate(dateInput);
|
const localDate = parseLocalDate(dateInput);
|
||||||
const dd = String(localDate.getDate());
|
const dd = String(localDate.getDate()).padStart(2, "0");
|
||||||
const mm = MONTH_SHORT[localDate.getMonth()];
|
const mm = String(localDate.getMonth() + 1).padStart(2, "0");
|
||||||
const yy = localDate.getFullYear();
|
const yy = localDate.getFullYear();
|
||||||
return `${mm} ${dd}, ${yy}`;
|
return `${mm}/${dd}/${yy}`;
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
console.error("Invalid date input provided:", dateInput, err);
|
console.error("Invalid date input provided:", dateInput, err);
|
||||||
|
|||||||
@@ -220,6 +220,27 @@ export const PROCEDURE_COMBOS = {
|
|||||||
label: "PL (Cast)",
|
label: "PL (Cast)",
|
||||||
codes: ["D5214"],
|
codes: ["D5214"],
|
||||||
},
|
},
|
||||||
|
// Implants
|
||||||
|
implantFull: {
|
||||||
|
id: "implantFull",
|
||||||
|
label: "Implant/Abut/Crown",
|
||||||
|
codes: ["D6010", "D6057", "D6058"],
|
||||||
|
},
|
||||||
|
implantFixture: {
|
||||||
|
id: "implantFixture",
|
||||||
|
label: "Implant Fixture",
|
||||||
|
codes: ["D6010"],
|
||||||
|
},
|
||||||
|
implantAbutment: {
|
||||||
|
id: "implantAbutment",
|
||||||
|
label: "Abutment",
|
||||||
|
codes: ["D6057"],
|
||||||
|
},
|
||||||
|
implantCrown: {
|
||||||
|
id: "implantCrown",
|
||||||
|
label: "Implant Crown",
|
||||||
|
codes: ["D6058"],
|
||||||
|
},
|
||||||
// Endodontics
|
// Endodontics
|
||||||
rctAnterior: {
|
rctAnterior: {
|
||||||
id: "rctAnterior",
|
id: "rctAnterior",
|
||||||
@@ -353,6 +374,7 @@ export const COMBO_CATEGORIES = {
|
|||||||
"plResin",
|
"plResin",
|
||||||
"plCast",
|
"plCast",
|
||||||
],
|
],
|
||||||
|
Implants: ["implantFull", "implantFixture", "implantAbutment", "implantCrown"],
|
||||||
Endodontics: ["rctAnterior", "rctAnteriorPostCrown", "rctPremolar", "rctPremolarPostCrown", "rctMolar", "rctMolarPostCrown", "postCore", "coreBU"],
|
Endodontics: ["rctAnterior", "rctAnteriorPostCrown", "rctPremolar", "rctPremolarPostCrown", "rctMolar", "rctMolarPostCrown", "postCore", "coreBU"],
|
||||||
Prosthodontics: ["crown"],
|
Prosthodontics: ["crown"],
|
||||||
Periodontics: ["deepCleaning"],
|
Periodontics: ["deepCleaning"],
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
import Decimal from "decimal.js";
|
import Decimal from "decimal.js";
|
||||||
import rawCodeTable from "@/assets/data/procedureCodesMH.json";
|
import rawCodeTable from "@/assets/data/procedureCodesMH.json";
|
||||||
|
import rawCCACodeTable from "@/assets/data/procedureCodesCCA.json";
|
||||||
|
import rawDDMACodeTable from "@/assets/data/procedureCodesDDMA.json";
|
||||||
|
import rawUnitedDHCodeTable from "@/assets/data/procedureCodesUnitedDH.json";
|
||||||
|
import rawTuftsSCOCodeTable from "@/assets/data/procedureCodesTuftsSCO.json";
|
||||||
import { PROCEDURE_COMBOS } from "./procedureCombos";
|
import { PROCEDURE_COMBOS } from "./procedureCombos";
|
||||||
const CODE_TABLE = rawCodeTable;
|
const CODE_TABLE = rawCodeTable;
|
||||||
|
const CCA_CODE_TABLE = rawCCACodeTable;
|
||||||
|
const DDMA_CODE_TABLE = rawDDMACodeTable;
|
||||||
|
const UNITEDDH_CODE_TABLE = rawUnitedDHCodeTable;
|
||||||
|
const TUFTSSCO_CODE_TABLE = rawTuftsSCOCodeTable;
|
||||||
/* ----------------------------- Helpers ----------------------------- */
|
/* ----------------------------- Helpers ----------------------------- */
|
||||||
export const COMBO_BUTTONS = Object.values(PROCEDURE_COMBOS).map((c) => ({
|
export const COMBO_BUTTONS = Object.values(PROCEDURE_COMBOS).map((c) => ({
|
||||||
id: c.id,
|
id: c.id,
|
||||||
@@ -18,6 +26,55 @@ const CODE_MAP = (() => {
|
|||||||
}
|
}
|
||||||
return m;
|
return m;
|
||||||
})();
|
})();
|
||||||
|
const CCA_CODE_MAP = (() => {
|
||||||
|
const m = new Map();
|
||||||
|
for (const r of CCA_CODE_TABLE) {
|
||||||
|
const k = normalizeCode(String(r["Procedure Code"] || ""));
|
||||||
|
if (k && !m.has(k))
|
||||||
|
m.set(k, r);
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
})();
|
||||||
|
const DDMA_CODE_MAP = (() => {
|
||||||
|
const m = new Map();
|
||||||
|
for (const r of DDMA_CODE_TABLE) {
|
||||||
|
const k = normalizeCode(String(r["Procedure Code"] || ""));
|
||||||
|
if (k && !m.has(k))
|
||||||
|
m.set(k, r);
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
})();
|
||||||
|
const UNITEDDH_CODE_MAP = (() => {
|
||||||
|
const m = new Map();
|
||||||
|
for (const r of UNITEDDH_CODE_TABLE) {
|
||||||
|
const k = normalizeCode(String(r["Procedure Code"] || ""));
|
||||||
|
if (k && !m.has(k))
|
||||||
|
m.set(k, r);
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
})();
|
||||||
|
const TUFTSSCO_CODE_MAP = (() => {
|
||||||
|
const m = new Map();
|
||||||
|
for (const r of TUFTSSCO_CODE_TABLE) {
|
||||||
|
const k = normalizeCode(String(r["Procedure Code"] || ""));
|
||||||
|
if (k && !m.has(k))
|
||||||
|
m.set(k, r);
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
})();
|
||||||
|
/** Return the correct fee-schedule map for the given insurance type. */
|
||||||
|
function getCodeMap(insuranceSiteKey) {
|
||||||
|
const k = (insuranceSiteKey ?? "").replace(/_/g, "").toLowerCase();
|
||||||
|
if (k === "cca")
|
||||||
|
return CCA_CODE_MAP;
|
||||||
|
if (k === "ddma")
|
||||||
|
return DDMA_CODE_MAP;
|
||||||
|
if (k === "unitedsco" || k === "uniteddh" || k === "dentalhub")
|
||||||
|
return UNITEDDH_CODE_MAP;
|
||||||
|
if (k === "tuftssco" || k === "tufts")
|
||||||
|
return TUFTSSCO_CODE_MAP;
|
||||||
|
return CODE_MAP; // default: MassHealth
|
||||||
|
}
|
||||||
// this function is solely for abbrevations feature in claim-form
|
// this function is solely for abbrevations feature in claim-form
|
||||||
export function getDescriptionForCode(code) {
|
export function getDescriptionForCode(code) {
|
||||||
if (!code)
|
if (!code)
|
||||||
@@ -83,44 +140,35 @@ const ageOnDate = (dob, on) => {
|
|||||||
export function pickPriceForRowByAge(row, age, normalizedCode) {
|
export function pickPriceForRowByAge(row, age, normalizedCode) {
|
||||||
// Special-case rules (add more codes here if needed)
|
// Special-case rules (add more codes here if needed)
|
||||||
if (normalizedCode) {
|
if (normalizedCode) {
|
||||||
// D1110: only valid for age >=14 (14..21 => PriceLTEQ21, >21 => PriceGT21)
|
// D1110: only valid for age >=14
|
||||||
if (normalizedCode === "D1110") {
|
if (normalizedCode === "D1110") {
|
||||||
if (age < 14) {
|
if (age < 14)
|
||||||
// D1110 not applicable to children <14 (those belong to D1120)
|
return new Decimal(0); // D1110 not for children <14
|
||||||
return new Decimal(0);
|
// age >= 14: use age-split if present, then flat Price
|
||||||
}
|
if (age <= 21 && !isBlankPrice(row.PriceLTEQ21))
|
||||||
if (age >= 14 && age <= 21) {
|
return toDecimalOrZero(row.PriceLTEQ21);
|
||||||
// use PriceLTEQ21 only if present
|
if (age > 21 && !isBlankPrice(row.PriceGT21))
|
||||||
if (!isBlankPrice(row.PriceLTEQ21))
|
|
||||||
return toDecimalOrZero(row.PriceLTEQ21);
|
|
||||||
return new Decimal(0);
|
|
||||||
}
|
|
||||||
// age > 21
|
|
||||||
if (!isBlankPrice(row.PriceGT21))
|
|
||||||
return toDecimalOrZero(row.PriceGT21);
|
return toDecimalOrZero(row.PriceGT21);
|
||||||
|
if (!isBlankPrice(row.Price))
|
||||||
|
return toDecimalOrZero(row.Price);
|
||||||
return new Decimal(0);
|
return new Decimal(0);
|
||||||
}
|
}
|
||||||
// D1120: child 0-13 => PriceLTEQ21, otherwise no price (NC)
|
// D1120: valid for child 0-13 only
|
||||||
if (normalizedCode === "D1120") {
|
if (normalizedCode === "D1120") {
|
||||||
if (age < 14) {
|
if (age >= 14)
|
||||||
if (!isBlankPrice(row.PriceLTEQ21))
|
return new Decimal(0); // NC for adults
|
||||||
return toDecimalOrZero(row.PriceLTEQ21);
|
if (!isBlankPrice(row.PriceLTEQ21))
|
||||||
return new Decimal(0);
|
return toDecimalOrZero(row.PriceLTEQ21);
|
||||||
}
|
if (!isBlankPrice(row.Price))
|
||||||
// age >= 14 => NC / no price
|
return toDecimalOrZero(row.Price);
|
||||||
return new Decimal(0);
|
return new Decimal(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Generic/default behavior (unchanged)
|
// Generic/default: age-split first, flat Price as fallback
|
||||||
if (age <= 21) {
|
if (age <= 21 && !isBlankPrice(row.PriceLTEQ21))
|
||||||
if (!isBlankPrice(row.PriceLTEQ21))
|
return toDecimalOrZero(row.PriceLTEQ21);
|
||||||
return toDecimalOrZero(row.PriceLTEQ21);
|
if (age > 21 && !isBlankPrice(row.PriceGT21))
|
||||||
}
|
return toDecimalOrZero(row.PriceGT21);
|
||||||
else {
|
|
||||||
if (!isBlankPrice(row.PriceGT21))
|
|
||||||
return toDecimalOrZero(row.PriceGT21);
|
|
||||||
}
|
|
||||||
// Fallback to Price if tiered not available/blank
|
|
||||||
if (!isBlankPrice(row.Price))
|
if (!isBlankPrice(row.Price))
|
||||||
return toDecimalOrZero(row.Price);
|
return toDecimalOrZero(row.Price);
|
||||||
return new Decimal(0);
|
return new Decimal(0);
|
||||||
@@ -158,7 +206,8 @@ const ensureCapacity = (lines, min, lineDate) => {
|
|||||||
* Returns a NEW form object (immutable).
|
* Returns a NEW form object (immutable).
|
||||||
*/
|
*/
|
||||||
export function mapPricesForForm(params) {
|
export function mapPricesForForm(params) {
|
||||||
const { form, patientDOB } = params;
|
const { form, patientDOB, insuranceSiteKey } = params;
|
||||||
|
const map = getCodeMap(insuranceSiteKey);
|
||||||
return {
|
return {
|
||||||
...form,
|
...form,
|
||||||
serviceLines: form.serviceLines.map((ln) => {
|
serviceLines: form.serviceLines.map((ln) => {
|
||||||
@@ -166,7 +215,7 @@ export function mapPricesForForm(params) {
|
|||||||
const code = normalizeCode(ln.procedureCode || "");
|
const code = normalizeCode(ln.procedureCode || "");
|
||||||
if (!code)
|
if (!code)
|
||||||
return { ...ln };
|
return { ...ln };
|
||||||
const price = getPriceForCodeWithAgeFromMap(CODE_MAP, code, age);
|
const price = getPriceForCodeWithAgeFromMap(map, code, age);
|
||||||
return { ...ln, procedureCode: code, totalBilled: price };
|
return { ...ln, procedureCode: code, totalBilled: price };
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
@@ -175,7 +224,7 @@ export function mapPricesForForm(params) {
|
|||||||
* Apply a preset combo (fills codes & prices) using patientDOB and serviceDate.
|
* Apply a preset combo (fills codes & prices) using patientDOB and serviceDate.
|
||||||
* Returns a NEW form object (immutable).
|
* Returns a NEW form object (immutable).
|
||||||
*/
|
*/
|
||||||
export function applyComboToForm(form, comboId, patientDOB, options = {}) {
|
export function applyComboToForm(form, comboId, patientDOB, options = {}, insuranceSiteKey) {
|
||||||
const preset = PROCEDURE_COMBOS[String(comboId)];
|
const preset = PROCEDURE_COMBOS[String(comboId)];
|
||||||
if (!preset)
|
if (!preset)
|
||||||
return form;
|
return form;
|
||||||
@@ -205,8 +254,8 @@ export function applyComboToForm(form, comboId, patientDOB, options = {}) {
|
|||||||
} // if replaceAll, insertAt stays 0
|
} // if replaceAll, insertAt stays 0
|
||||||
// Make sure we have enough rows for the whole combo
|
// Make sure we have enough rows for the whole combo
|
||||||
ensureCapacity(next.serviceLines, insertAt + preset.codes.length, lineDate);
|
ensureCapacity(next.serviceLines, insertAt + preset.codes.length, lineDate);
|
||||||
// Age on the specific line date we will set
|
const age = options.skipPrice ? 0 : ageOnDate(patientDOB, lineDate);
|
||||||
const age = ageOnDate(patientDOB, lineDate);
|
const map = options.skipPrice ? CODE_MAP : getCodeMap(insuranceSiteKey);
|
||||||
for (let j = 0; j < preset.codes.length; j++) {
|
for (let j = 0; j < preset.codes.length; j++) {
|
||||||
const i = insertAt + j;
|
const i = insertAt + j;
|
||||||
if (i >= next.serviceLines.length)
|
if (i >= next.serviceLines.length)
|
||||||
@@ -215,7 +264,9 @@ export function applyComboToForm(form, comboId, patientDOB, options = {}) {
|
|||||||
if (!codeRaw)
|
if (!codeRaw)
|
||||||
continue;
|
continue;
|
||||||
const code = normalizeCode(codeRaw);
|
const code = normalizeCode(codeRaw);
|
||||||
const price = getPriceForCodeWithAgeFromMap(CODE_MAP, code, age);
|
const price = options.skipPrice
|
||||||
|
? new Decimal(0)
|
||||||
|
: getPriceForCodeWithAgeFromMap(map, code, age);
|
||||||
const original = next.serviceLines[i];
|
const original = next.serviceLines[i];
|
||||||
next.serviceLines[i] = {
|
next.serviceLines[i] = {
|
||||||
...original,
|
...original,
|
||||||
@@ -238,4 +289,32 @@ export function applyComboToForm(form, comboId, patientDOB, options = {}) {
|
|||||||
}
|
}
|
||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
export { CODE_MAP, getPriceForCodeWithAgeFromMap };
|
export { CODE_MAP, CCA_CODE_MAP, DDMA_CODE_MAP, UNITEDDH_CODE_MAP, TUFTSSCO_CODE_MAP, getCodeMap, getPriceForCodeWithAgeFromMap };
|
||||||
|
/** Compare each service line's totalBilled against the fee schedule.
|
||||||
|
* Returns lines where the entered price differs from the schedule price.
|
||||||
|
* Returns empty array if the siteKey has no schedule (United, Tufts, etc.). */
|
||||||
|
export function findPriceMismatches(serviceLines, insuranceSiteKey, patientDOB, serviceDate) {
|
||||||
|
const supported = ["MH", "MASSHEALTH", "CCA", "DDMA", "UNITEDDH", "UNITEDSCO", "TUFTSSCO"];
|
||||||
|
if (!insuranceSiteKey || !supported.includes(insuranceSiteKey.toUpperCase()))
|
||||||
|
return [];
|
||||||
|
const map = getCodeMap(insuranceSiteKey);
|
||||||
|
const mismatches = [];
|
||||||
|
for (const line of serviceLines) {
|
||||||
|
const code = normalizeCode(line.procedureCode || "");
|
||||||
|
if (!code)
|
||||||
|
continue;
|
||||||
|
const enteredPrice = new Decimal(Number(line.totalBilled) || 0);
|
||||||
|
if (enteredPrice.isZero())
|
||||||
|
continue;
|
||||||
|
const age = ageOnDate(patientDOB, serviceDate);
|
||||||
|
const schedulePrice = getPriceForCodeWithAgeFromMap(map, code, age);
|
||||||
|
if (!schedulePrice.isZero() && !enteredPrice.equals(schedulePrice)) {
|
||||||
|
mismatches.push({
|
||||||
|
procedureCode: code,
|
||||||
|
enteredPrice: enteredPrice.toNumber(),
|
||||||
|
schedulePrice: schedulePrice.toNumber(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mismatches;
|
||||||
|
}
|
||||||
|
|||||||
@@ -78,10 +78,22 @@ export default {
|
|||||||
height: "0",
|
height: "0",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
blob: {
|
||||||
|
"0%, 100%": { transform: "translate(0, 0) scale(1)", borderRadius: "40% 60% 70% 30% / 40% 50% 60% 50%" },
|
||||||
|
"25%": { transform: "translate(80px, -60px) scale(1.25)", borderRadius: "60% 40% 30% 70% / 60% 30% 70% 40%" },
|
||||||
|
"50%": { transform: "translate(-50px, 50px) scale(0.85)", borderRadius: "30% 60% 40% 70% / 50% 60% 30% 60%" },
|
||||||
|
"75%": { transform: "translate(40px, 30px) scale(1.1)", borderRadius: "50% 40% 60% 30% / 40% 70% 50% 60%" },
|
||||||
|
},
|
||||||
|
"blob-spin": {
|
||||||
|
"0%": { transform: "rotate(0deg)" },
|
||||||
|
"100%": { transform: "rotate(360deg)" },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
animation: {
|
animation: {
|
||||||
"accordion-down": "accordion-down 0.2s ease-out",
|
"accordion-down": "accordion-down 0.2s ease-out",
|
||||||
"accordion-up": "accordion-up 0.2s ease-out",
|
"accordion-up": "accordion-up 0.2s ease-out",
|
||||||
|
blob: "blob 10s ease-in-out infinite",
|
||||||
|
"blob-spin": "blob-spin 20s linear infinite",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -11,7 +11,10 @@ export default defineConfig(({ mode }) => {
|
|||||||
fs: {
|
fs: {
|
||||||
allow: [".."],
|
allow: [".."],
|
||||||
},
|
},
|
||||||
allowedHosts: ["communitydentistsoflowell.mydentalofficemanagement.com"],
|
allowedHosts: [
|
||||||
|
...(env.VITE_CLOUDFLARE_HOST ? [env.VITE_CLOUDFLARE_HOST] : []),
|
||||||
|
"192.168.0.94",
|
||||||
|
],
|
||||||
proxy: {
|
proxy: {
|
||||||
"/api": {
|
"/api": {
|
||||||
target: env.VITE_API_BASE_URL_BACKEND || "http://localhost:5000",
|
target: env.VITE_API_BASE_URL_BACKEND || "http://localhost:5000",
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ from selenium_MH_eligibilityHistoryCheckWorker import AutomationMassHealthEligib
|
|||||||
from selenium_CMSP_eligibilityHistoryRemainingCheckWorker import AutomationCMSPEligibilityHistoryRemainingCheck
|
from selenium_CMSP_eligibilityHistoryRemainingCheckWorker import AutomationCMSPEligibilityHistoryRemainingCheck
|
||||||
from selenium_claimStatusCheckWorker import AutomationMassHealthClaimStatusCheck
|
from selenium_claimStatusCheckWorker import AutomationMassHealthClaimStatusCheck
|
||||||
from selenium_preAuthWorker import AutomationMassHealthPreAuth
|
from selenium_preAuthWorker import AutomationMassHealthPreAuth
|
||||||
from selenium_MHPaymentCheckWorker import AutomationMassHealthPaymentCheck
|
from selenium_MHSinglePaymentCheckWorker import AutomationMassHealthSinglePaymentCheck
|
||||||
|
from selenium_MHBatchPaymentCheckWorker import AutomationMassHealthBatchPaymentCheck
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
import helpers_ddma_eligibility as hddma
|
import helpers_ddma_eligibility as hddma
|
||||||
@@ -290,7 +291,35 @@ async def mh_payment_check(request: Request):
|
|||||||
waiting_jobs -= 1
|
waiting_jobs -= 1
|
||||||
active_jobs += 1
|
active_jobs += 1
|
||||||
try:
|
try:
|
||||||
bot = AutomationMassHealthPaymentCheck(data)
|
bot = AutomationMassHealthSinglePaymentCheck(data)
|
||||||
|
result = bot.main_workflow("https://provider.masshealth-dental.org/mh_provider_login")
|
||||||
|
|
||||||
|
if result.get("status") != "success":
|
||||||
|
return {"status": "error", "message": result.get("message")}
|
||||||
|
|
||||||
|
return result
|
||||||
|
except Exception as e:
|
||||||
|
return {"status": "error", "message": str(e)}
|
||||||
|
finally:
|
||||||
|
async with lock:
|
||||||
|
active_jobs -= 1
|
||||||
|
|
||||||
|
|
||||||
|
# Endpoint: 6 — Check MassHealth payments in batch by date range
|
||||||
|
@app.post("/mh-batch-payment-check")
|
||||||
|
async def mh_batch_payment_check(request: Request):
|
||||||
|
global active_jobs, waiting_jobs
|
||||||
|
data = await request.json()
|
||||||
|
|
||||||
|
async with lock:
|
||||||
|
waiting_jobs += 1
|
||||||
|
|
||||||
|
async with semaphore:
|
||||||
|
async with lock:
|
||||||
|
waiting_jobs -= 1
|
||||||
|
active_jobs += 1
|
||||||
|
try:
|
||||||
|
bot = AutomationMassHealthBatchPaymentCheck(data)
|
||||||
result = bot.main_workflow("https://provider.masshealth-dental.org/mh_provider_login")
|
result = bot.main_workflow("https://provider.masshealth-dental.org/mh_provider_login")
|
||||||
|
|
||||||
if result.get("status") != "success":
|
if result.get("status") != "success":
|
||||||
|
|||||||
@@ -1,218 +0,0 @@
|
|||||||
from selenium import webdriver
|
|
||||||
from selenium.webdriver.chrome.service import Service
|
|
||||||
from selenium.webdriver.common.by import By
|
|
||||||
from selenium.webdriver.support.ui import WebDriverWait
|
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
|
||||||
from webdriver_manager.chrome import ChromeDriverManager
|
|
||||||
import time
|
|
||||||
import os
|
|
||||||
import base64
|
|
||||||
|
|
||||||
|
|
||||||
class AutomationMassHealthPaymentCheck:
|
|
||||||
def __init__(self, data):
|
|
||||||
self.headless = False
|
|
||||||
self.driver = None
|
|
||||||
self.extracted_data = {}
|
|
||||||
|
|
||||||
self.data = data.get("data")
|
|
||||||
|
|
||||||
self.massdhp_username = self.data.get("massdhpUsername", "")
|
|
||||||
self.massdhp_password = self.data.get("massdhpPassword", "")
|
|
||||||
self.claim_number = self.data.get("claimNumber", "")
|
|
||||||
|
|
||||||
self.download_dir = os.path.abspath("downloads")
|
|
||||||
os.makedirs(self.download_dir, exist_ok=True)
|
|
||||||
|
|
||||||
def config_driver(self):
|
|
||||||
options = webdriver.ChromeOptions()
|
|
||||||
if self.headless:
|
|
||||||
options.add_argument("--headless")
|
|
||||||
|
|
||||||
prefs = {
|
|
||||||
"download.default_directory": self.download_dir,
|
|
||||||
"plugins.always_open_pdf_externally": False,
|
|
||||||
"download.prompt_for_download": False,
|
|
||||||
"download.directory_upgrade": True,
|
|
||||||
}
|
|
||||||
options.add_experimental_option("prefs", prefs)
|
|
||||||
|
|
||||||
s = Service(ChromeDriverManager().install())
|
|
||||||
self.driver = webdriver.Chrome(service=s, options=options)
|
|
||||||
|
|
||||||
def login(self):
|
|
||||||
wait = WebDriverWait(self.driver, 30)
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Step 1: Click the SIGN IN button on the initial page
|
|
||||||
signin_button = wait.until(
|
|
||||||
EC.element_to_be_clickable(
|
|
||||||
(
|
|
||||||
By.CSS_SELECTOR,
|
|
||||||
"a.btn.btn-block.btn-primary[href='https://connectsso.masshealth-dental.org/mhprovider/index.html']",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
signin_button.click()
|
|
||||||
|
|
||||||
time.sleep(3)
|
|
||||||
|
|
||||||
# Step 2: Enter username
|
|
||||||
email_field = wait.until(EC.presence_of_element_located((By.ID, "User")))
|
|
||||||
email_field.clear()
|
|
||||||
email_field.send_keys(self.massdhp_username)
|
|
||||||
|
|
||||||
# Step 3: Enter password
|
|
||||||
password_field = wait.until(
|
|
||||||
EC.presence_of_element_located((By.ID, "Password"))
|
|
||||||
)
|
|
||||||
password_field.clear()
|
|
||||||
password_field.send_keys(self.massdhp_password)
|
|
||||||
|
|
||||||
# Step 4: Click login button
|
|
||||||
login_button = wait.until(
|
|
||||||
EC.element_to_be_clickable(
|
|
||||||
(
|
|
||||||
By.CSS_SELECTOR,
|
|
||||||
"input[type='submit'][name='submit'][value='Login']",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
login_button.click()
|
|
||||||
|
|
||||||
return "Success"
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error while logging in: {e}")
|
|
||||||
return "ERROR:LOGIN FAILED"
|
|
||||||
|
|
||||||
def navigate_to_payments(self):
|
|
||||||
"""
|
|
||||||
TODO: Navigate to the payments / remittance section after login.
|
|
||||||
Inspect the portal and fill in the correct selectors below.
|
|
||||||
"""
|
|
||||||
wait = WebDriverWait(self.driver, 30)
|
|
||||||
substep = "init"
|
|
||||||
|
|
||||||
try:
|
|
||||||
print(f"[navigate_to_payments] URL after login: {self.driver.current_url}")
|
|
||||||
|
|
||||||
substep = "financial_menu"
|
|
||||||
financial_menu = wait.until(
|
|
||||||
EC.presence_of_element_located(
|
|
||||||
(By.XPATH, '//*[@id="navbar-desktop"]/div/div[3]/div/strong')
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.driver.execute_script("arguments[0].scrollIntoView(true);", financial_menu)
|
|
||||||
self.driver.execute_script("arguments[0].click();", financial_menu)
|
|
||||||
time.sleep(2)
|
|
||||||
|
|
||||||
substep = "payments_link"
|
|
||||||
payments_link = wait.until(
|
|
||||||
EC.presence_of_element_located(
|
|
||||||
(By.XPATH, '//*[@id="navbar-desktop"]/div/div[3]/div/div/div[2]/div/a')
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.driver.execute_script("arguments[0].click();", payments_link)
|
|
||||||
time.sleep(2)
|
|
||||||
|
|
||||||
return "Success"
|
|
||||||
except Exception as e:
|
|
||||||
print(f"[navigate_to_payments] FAILED at substep='{substep}': {e}")
|
|
||||||
print(f"[navigate_to_payments] URL at failure: {self.driver.current_url}")
|
|
||||||
return f"ERROR:NAVIGATE_TO_PAYMENTS:{substep}"
|
|
||||||
|
|
||||||
def step1_search_claim(self):
|
|
||||||
"""Enter claim number and click SEARCH on the Search Claims/Prior Authorizations page."""
|
|
||||||
wait = WebDriverWait(self.driver, 30)
|
|
||||||
substep = "init"
|
|
||||||
|
|
||||||
try:
|
|
||||||
print(f"[step1] URL: {self.driver.current_url}")
|
|
||||||
|
|
||||||
substep = "claim_number_input"
|
|
||||||
claim_input = wait.until(
|
|
||||||
EC.presence_of_element_located(
|
|
||||||
(By.XPATH, "/html/body/div[1]/div/div/div/form/fieldset/div[4]/div[2]/input")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
claim_input.clear()
|
|
||||||
claim_input.send_keys(self.claim_number)
|
|
||||||
print(f"[step1] entered claim number: {self.claim_number}")
|
|
||||||
|
|
||||||
substep = "search_button"
|
|
||||||
search_button = wait.until(
|
|
||||||
EC.element_to_be_clickable(
|
|
||||||
(By.XPATH, "/html/body/div[1]/div/div/div/form/fieldset/div[7]/div/button[2]")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.driver.execute_script("arguments[0].click();", search_button)
|
|
||||||
print("[step1] clicked SEARCH")
|
|
||||||
time.sleep(3)
|
|
||||||
|
|
||||||
return "Success"
|
|
||||||
except Exception as e:
|
|
||||||
print(f"[step1] FAILED at substep='{substep}': {e}")
|
|
||||||
print(f"[step1] URL at failure: {self.driver.current_url}")
|
|
||||||
return f"ERROR:STEP1:{substep}"
|
|
||||||
|
|
||||||
def step2_extract_paid_amount(self):
|
|
||||||
"""Read the totalPaidAmount from the search results table."""
|
|
||||||
wait = WebDriverWait(self.driver, 30)
|
|
||||||
substep = "init"
|
|
||||||
|
|
||||||
try:
|
|
||||||
substep = "wait_results_table"
|
|
||||||
wait.until(
|
|
||||||
EC.presence_of_element_located(
|
|
||||||
(By.XPATH, '//td[@ng-bind="item.totalPaidAmount | currency"]')
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
substep = "read_paid_amount"
|
|
||||||
paid_cell = self.driver.find_element(
|
|
||||||
By.XPATH, '//td[@ng-bind="item.totalPaidAmount | currency"]'
|
|
||||||
)
|
|
||||||
raw_text = paid_cell.text.strip()
|
|
||||||
print(f"[step2] raw paid amount text: '{raw_text}'")
|
|
||||||
|
|
||||||
# Strip currency symbol and commas, e.g. "$1,234.56" → 1234.56
|
|
||||||
numeric_str = raw_text.replace("$", "").replace(",", "").strip()
|
|
||||||
try:
|
|
||||||
paid_amount = float(numeric_str)
|
|
||||||
except ValueError:
|
|
||||||
paid_amount = 0.0
|
|
||||||
print(f"[step2] could not parse '{raw_text}' as float, defaulting to 0.0")
|
|
||||||
|
|
||||||
return {"status": "success", "mhPaidAmount": paid_amount, "mhPaidAmountRaw": raw_text}
|
|
||||||
except Exception as e:
|
|
||||||
print(f"[step2] FAILED at substep='{substep}': {e}")
|
|
||||||
return f"ERROR:STEP2:{substep}"
|
|
||||||
|
|
||||||
def main_workflow(self, url):
|
|
||||||
try:
|
|
||||||
self.config_driver()
|
|
||||||
self.driver.maximize_window()
|
|
||||||
self.driver.get(url)
|
|
||||||
time.sleep(3)
|
|
||||||
|
|
||||||
login_result = self.login()
|
|
||||||
if login_result.startswith("ERROR"):
|
|
||||||
return {"status": "error", "message": login_result}
|
|
||||||
|
|
||||||
nav_result = self.navigate_to_payments()
|
|
||||||
if nav_result.startswith("ERROR"):
|
|
||||||
return {"status": "error", "message": nav_result}
|
|
||||||
|
|
||||||
step1_result = self.step1_search_claim()
|
|
||||||
if step1_result.startswith("ERROR"):
|
|
||||||
return {"status": "error", "message": step1_result}
|
|
||||||
|
|
||||||
step2_result = self.step2_extract_paid_amount()
|
|
||||||
if isinstance(step2_result, str) and step2_result.startswith("ERROR"):
|
|
||||||
return {"status": "error", "message": step2_result}
|
|
||||||
|
|
||||||
return step2_result
|
|
||||||
except Exception as e:
|
|
||||||
return {"status": "error", "message": str(e)}
|
|
||||||
finally:
|
|
||||||
self.driver.quit()
|
|
||||||
225
package-lock.json
generated
225
package-lock.json
generated
@@ -38,8 +38,10 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@google/generative-ai": "^0.24.1",
|
"@google/generative-ai": "^0.24.1",
|
||||||
|
"@langchain/anthropic": "^1.4.0",
|
||||||
"@langchain/google-genai": "^2.1.30",
|
"@langchain/google-genai": "^2.1.30",
|
||||||
"@langchain/langgraph": "^1.2.9",
|
"@langchain/langgraph": "^1.2.9",
|
||||||
|
"@langchain/openai": "^1.4.7",
|
||||||
"archiver": "^7.0.1",
|
"archiver": "^7.0.1",
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.9.0",
|
||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
@@ -220,6 +222,27 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@anthropic-ai/sdk": {
|
||||||
|
"version": "0.95.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.95.2.tgz",
|
||||||
|
"integrity": "sha512-Egddwo3sheo1PzUrMkZnH6VkQYwS0h/b/i8vSK8Ta9M45UQipAMeDFH57dYuDAfXMEUUGeKw6CMlremgMZgrSQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"json-schema-to-ts": "^3.1.1",
|
||||||
|
"standardwebhooks": "^1.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"anthropic-ai-sdk": "bin/cli"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"zod": "^3.25.0 || ^4.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"zod": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
"version": "7.29.0",
|
"version": "7.29.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
|
||||||
@@ -251,7 +274,6 @@
|
|||||||
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.29.0",
|
"@babel/code-frame": "^7.29.0",
|
||||||
"@babel/generator": "^7.29.0",
|
"@babel/generator": "^7.29.0",
|
||||||
@@ -536,7 +558,8 @@
|
|||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.1.tgz",
|
||||||
"integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==",
|
"integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/@chevrotain/cst-dts-gen": {
|
"node_modules/@chevrotain/cst-dts-gen": {
|
||||||
"version": "10.5.0",
|
"version": "10.5.0",
|
||||||
@@ -605,8 +628,7 @@
|
|||||||
"version": "0.3.15",
|
"version": "0.3.15",
|
||||||
"resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.3.15.tgz",
|
"resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.3.15.tgz",
|
||||||
"integrity": "sha512-Cj++n1Mekf9ETfdc16TlDi+cDDQF0W7EcbyRHYOAeZdsAe8M/FJg18itDTSwyHfar2WIezawM9o0EKaRGVKygQ==",
|
"integrity": "sha512-Cj++n1Mekf9ETfdc16TlDi+cDDQF0W7EcbyRHYOAeZdsAe8M/FJg18itDTSwyHfar2WIezawM9o0EKaRGVKygQ==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@electric-sql/pglite-socket": {
|
"node_modules/@electric-sql/pglite-socket": {
|
||||||
"version": "0.0.20",
|
"version": "0.0.20",
|
||||||
@@ -636,7 +658,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -653,7 +674,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -670,7 +690,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -687,7 +706,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -704,7 +722,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -721,7 +738,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -738,7 +754,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -755,7 +770,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -772,7 +786,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -789,7 +802,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -806,7 +818,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -823,7 +834,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -840,7 +850,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"mips64el"
|
"mips64el"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -857,7 +866,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -874,7 +882,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -891,7 +898,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -908,7 +914,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -925,7 +930,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -942,7 +946,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -959,7 +962,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -976,7 +978,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -993,7 +994,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1010,7 +1010,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1027,7 +1026,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1044,7 +1042,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1061,7 +1058,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1416,18 +1412,31 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@langchain/anthropic": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@langchain/anthropic/-/anthropic-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-rs1yVydrHjyiD31uChdCnKZpmDuKa0Bpz8Raiy9GvqnqmfXPMe0oOrap/2paE+NRSinDbtax8mMpP/yv8EbO1A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@anthropic-ai/sdk": "^0.95.1",
|
||||||
|
"zod": "^3.25.76 || ^4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@langchain/core": "^1.1.47"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@langchain/core": {
|
"node_modules/@langchain/core": {
|
||||||
"version": "1.1.44",
|
"version": "1.1.48",
|
||||||
"resolved": "https://registry.npmjs.org/@langchain/core/-/core-1.1.44.tgz",
|
"resolved": "https://registry.npmjs.org/@langchain/core/-/core-1.1.48.tgz",
|
||||||
"integrity": "sha512-RePW1IjGCHr9ua2vcby3aE8mOOz3EnwDZxMEGbNDT91kf14eqkJqxDXvaZFviGdcN9DTrxM5RPQNAHmwSm4tbg==",
|
"integrity": "sha512-fQU6Guyb1pwc2fEplmA8FPbKfOMAofjnyJzExevro0FxEiuGHE18Ov/ZHmT9trWCDTZRI9eW1VIc6aChxV8pAQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cfworker/json-schema": "^4.0.2",
|
"@cfworker/json-schema": "^4.0.2",
|
||||||
"@standard-schema/spec": "^1.1.0",
|
"@standard-schema/spec": "^1.1.0",
|
||||||
"ansi-styles": "^5.0.0",
|
|
||||||
"camelcase": "6",
|
|
||||||
"decamelize": "1.2.0",
|
|
||||||
"js-tiktoken": "^1.0.12",
|
"js-tiktoken": "^1.0.12",
|
||||||
"langsmith": ">=0.5.0 <1.0.0",
|
"langsmith": ">=0.5.0 <1.0.0",
|
||||||
"mustache": "^4.2.0",
|
"mustache": "^4.2.0",
|
||||||
@@ -1438,18 +1447,6 @@
|
|||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@langchain/core/node_modules/ansi-styles": {
|
|
||||||
"version": "5.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
|
|
||||||
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@langchain/google-genai": {
|
"node_modules/@langchain/google-genai": {
|
||||||
"version": "2.1.30",
|
"version": "2.1.30",
|
||||||
"resolved": "https://registry.npmjs.org/@langchain/google-genai/-/google-genai-2.1.30.tgz",
|
"resolved": "https://registry.npmjs.org/@langchain/google-genai/-/google-genai-2.1.30.tgz",
|
||||||
@@ -1616,6 +1613,23 @@
|
|||||||
"uuid": "dist/bin/uuid"
|
"uuid": "dist/bin/uuid"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@langchain/openai": {
|
||||||
|
"version": "1.4.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-1.4.7.tgz",
|
||||||
|
"integrity": "sha512-i1YLV4pWbGC6W8m0ZNpLObJuf1nyU4o8aWyX4AF9fHn7eM67HfIJWQ5n5XzcCpuSa41otrxA9jvH5XRKwI1qDA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"js-tiktoken": "^1.0.12",
|
||||||
|
"openai": "^6.37.0",
|
||||||
|
"zod": "^3.25.76 || ^4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@langchain/core": "^1.1.48"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@mapbox/node-pre-gyp": {
|
"node_modules/@mapbox/node-pre-gyp": {
|
||||||
"version": "1.0.11",
|
"version": "1.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
|
||||||
@@ -4338,6 +4352,12 @@
|
|||||||
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
|
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@stablelib/base64": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@standard-schema/spec": {
|
"node_modules/@standard-schema/spec": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
|
||||||
@@ -4966,7 +4986,6 @@
|
|||||||
"integrity": "sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g==",
|
"integrity": "sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"hoist-non-react-statics": "^3.3.0"
|
"hoist-non-react-statics": "^3.3.0"
|
||||||
},
|
},
|
||||||
@@ -5019,7 +5038,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.11.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.11.tgz",
|
||||||
"integrity": "sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw==",
|
"integrity": "sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.19.2"
|
"undici-types": "~6.19.2"
|
||||||
}
|
}
|
||||||
@@ -5084,7 +5102,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
|
||||||
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
|
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.2.2"
|
"csstype": "^3.2.2"
|
||||||
}
|
}
|
||||||
@@ -5095,7 +5112,6 @@
|
|||||||
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^19.2.0"
|
"@types/react": "^19.2.0"
|
||||||
}
|
}
|
||||||
@@ -5234,7 +5250,6 @@
|
|||||||
"integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==",
|
"integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.56.1",
|
"@typescript-eslint/scope-manager": "8.56.1",
|
||||||
"@typescript-eslint/types": "8.56.1",
|
"@typescript-eslint/types": "8.56.1",
|
||||||
@@ -5543,7 +5558,6 @@
|
|||||||
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@@ -6089,7 +6103,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.9.0",
|
"baseline-browser-mapping": "^2.9.0",
|
||||||
"caniuse-lite": "^1.0.30001759",
|
"caniuse-lite": "^1.0.30001759",
|
||||||
@@ -6263,18 +6276,6 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/camelcase": {
|
|
||||||
"version": "6.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
|
|
||||||
"integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/camelcase-css": {
|
"node_modules/camelcase-css": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
|
||||||
@@ -6946,15 +6947,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/decamelize": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
|
||||||
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/decimal.js": {
|
"node_modules/decimal.js": {
|
||||||
"version": "10.6.0",
|
"version": "10.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
|
||||||
@@ -7239,8 +7231,7 @@
|
|||||||
"version": "8.6.0",
|
"version": "8.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz",
|
||||||
"integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==",
|
"integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/embla-carousel-react": {
|
"node_modules/embla-carousel-react": {
|
||||||
"version": "8.6.0",
|
"version": "8.6.0",
|
||||||
@@ -7515,7 +7506,6 @@
|
|||||||
"integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==",
|
"integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.8.0",
|
"@eslint-community/eslint-utils": "^4.8.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
@@ -8002,6 +7992,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-sha256": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==",
|
||||||
|
"license": "Unlicense"
|
||||||
|
},
|
||||||
"node_modules/fast-uri": {
|
"node_modules/fast-uri": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
|
||||||
@@ -8733,7 +8729,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/hono/-/hono-4.11.4.tgz",
|
"resolved": "https://registry.npmjs.org/hono/-/hono-4.11.4.tgz",
|
||||||
"integrity": "sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA==",
|
"integrity": "sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.9.0"
|
"node": ">=16.9.0"
|
||||||
}
|
}
|
||||||
@@ -9127,6 +9122,19 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/json-schema-to-ts": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.18.3",
|
||||||
|
"ts-algebra": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/json-schema-traverse": {
|
"node_modules/json-schema-traverse": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||||
@@ -9245,6 +9253,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.6.0.tgz",
|
||||||
"integrity": "sha512-GGaj5IMRfLv2HXXFzGk9diISMYLTpSTh6fzCZGKxWYW/NqEztIFtnXLq6G/RVhzFRmCykLap1fuC67LVKoQLcg==",
|
"integrity": "sha512-GGaj5IMRfLv2HXXFzGk9diISMYLTpSTh6fzCZGKxWYW/NqEztIFtnXLq6G/RVhzFRmCykLap1fuC67LVKoQLcg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"p-queue": "6.6.2"
|
"p-queue": "6.6.2"
|
||||||
},
|
},
|
||||||
@@ -10096,6 +10105,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
|
||||||
"integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==",
|
"integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"mustache": "bin/mustache"
|
"mustache": "bin/mustache"
|
||||||
}
|
}
|
||||||
@@ -10435,6 +10445,24 @@
|
|||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/openai": {
|
||||||
|
"version": "6.42.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/openai/-/openai-6.42.0.tgz",
|
||||||
|
"integrity": "sha512-1WFEt/uXMXOLhYRNkgJWo08Y2YNvNwpVU72K7ibrWgWpNOXd4VojXLbe6SQ4bLiUQ3Y8jz4IiyVkylJCL1DtZg==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"peerDependencies": {
|
||||||
|
"ws": "^8.18.0",
|
||||||
|
"zod": "^3.25 || ^4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"ws": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"zod": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/optionator": {
|
"node_modules/optionator": {
|
||||||
"version": "0.9.4",
|
"version": "0.9.4",
|
||||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
||||||
@@ -10499,6 +10527,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz",
|
||||||
"integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==",
|
"integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"eventemitter3": "^4.0.4",
|
"eventemitter3": "^4.0.4",
|
||||||
"p-timeout": "^3.2.0"
|
"p-timeout": "^3.2.0"
|
||||||
@@ -10530,6 +10559,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz",
|
||||||
"integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==",
|
"integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"p-finally": "^1.0.0"
|
"p-finally": "^1.0.0"
|
||||||
},
|
},
|
||||||
@@ -10704,7 +10734,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-3.11.174.tgz",
|
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-3.11.174.tgz",
|
||||||
"integrity": "sha512-TdTZPf1trZ8/UFu5Cx/GXB7GZM30LT+wWUNfsi6Bq8ePLnb+woNKtDymI2mxZYBpMbonNFqKmiz684DIfnd8dA==",
|
"integrity": "sha512-TdTZPf1trZ8/UFu5Cx/GXB7GZM30LT+wWUNfsi6Bq8ePLnb+woNKtDymI2mxZYBpMbonNFqKmiz684DIfnd8dA==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
},
|
},
|
||||||
@@ -10737,7 +10766,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/pg/-/pg-8.18.0.tgz",
|
||||||
"integrity": "sha512-xqrUDL1b9MbkydY/s+VZ6v+xiMUmOUk7SS9d/1kpyQxoJ6U9AO1oIJyUWVZojbfe5Cc/oluutcgFG4L9RDP1iQ==",
|
"integrity": "sha512-xqrUDL1b9MbkydY/s+VZ6v+xiMUmOUk7SS9d/1kpyQxoJ6U9AO1oIJyUWVZojbfe5Cc/oluutcgFG4L9RDP1iQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pg-connection-string": "^2.11.0",
|
"pg-connection-string": "^2.11.0",
|
||||||
"pg-pool": "^3.11.0",
|
"pg-pool": "^3.11.0",
|
||||||
@@ -10902,7 +10930,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.11",
|
"nanoid": "^3.3.11",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
@@ -11132,7 +11159,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz",
|
||||||
"integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
|
"integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"prettier": "bin/prettier.cjs"
|
"prettier": "bin/prettier.cjs"
|
||||||
},
|
},
|
||||||
@@ -11825,7 +11851,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
|
||||||
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
|
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -11917,7 +11942,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
|
||||||
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
|
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": "^0.27.0"
|
"scheduler": "^0.27.0"
|
||||||
},
|
},
|
||||||
@@ -11930,7 +11954,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.71.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.71.2.tgz",
|
||||||
"integrity": "sha512-1CHvcDYzuRUNOflt4MOq3ZM46AronNJtQ1S7tnX6YN4y72qhgiUItpacZUAQ0TyWYci3yz1X+rXaSxiuEm86PA==",
|
"integrity": "sha512-1CHvcDYzuRUNOflt4MOq3ZM46AronNJtQ1S7tnX6YN4y72qhgiUItpacZUAQ0TyWYci3yz1X+rXaSxiuEm86PA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0"
|
"node": ">=18.0.0"
|
||||||
},
|
},
|
||||||
@@ -11962,7 +11985,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
||||||
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
|
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/use-sync-external-store": "^0.0.6",
|
"@types/use-sync-external-store": "^0.0.6",
|
||||||
"use-sync-external-store": "^1.4.0"
|
"use-sync-external-store": "^1.4.0"
|
||||||
@@ -12243,8 +12265,7 @@
|
|||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
|
||||||
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
|
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/redux-thunk": {
|
"node_modules/redux-thunk": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
@@ -12933,6 +12954,16 @@
|
|||||||
"integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==",
|
"integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/standardwebhooks": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@stablelib/base64": "^1.0.0",
|
||||||
|
"fast-sha256": "^1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/statuses": {
|
"node_modules/statuses": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
||||||
@@ -13166,7 +13197,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz",
|
||||||
"integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==",
|
"integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alloc/quick-lru": "^5.2.0",
|
"@alloc/quick-lru": "^5.2.0",
|
||||||
"arg": "^5.0.2",
|
"arg": "^5.0.2",
|
||||||
@@ -13460,7 +13490,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -13505,6 +13534,12 @@
|
|||||||
"tree-kill": "cli.js"
|
"tree-kill": "cli.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ts-algebra": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/ts-api-utils": {
|
"node_modules/ts-api-utils": {
|
||||||
"version": "2.4.0",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz",
|
||||||
@@ -13732,7 +13767,6 @@
|
|||||||
"integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
|
"integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "~0.27.0",
|
"esbuild": "~0.27.0",
|
||||||
"get-tsconfig": "^4.7.5"
|
"get-tsconfig": "^4.7.5"
|
||||||
@@ -13914,7 +13948,6 @@
|
|||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -14205,7 +14238,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
|
||||||
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
|
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"fdir": "^6.4.4",
|
"fdir": "^6.4.4",
|
||||||
@@ -14754,7 +14786,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -14993,7 +15024,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
|
||||||
"integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
|
"integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
},
|
},
|
||||||
@@ -15116,7 +15146,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user