selenium being fixed
This commit is contained in:
@@ -1,12 +1,14 @@
|
||||
import { Switch, Route } from "wouter";
|
||||
import React, { Suspense, lazy } from "react";
|
||||
import { Provider } from "react-redux";
|
||||
import { store } from "./redux/store";
|
||||
import { queryClient } from "./lib/queryClient";
|
||||
import { QueryClientProvider } from "@tanstack/react-query";
|
||||
import { Toaster } from "./components/ui/toaster";
|
||||
import { TooltipProvider } from "./components/ui/tooltip";
|
||||
import { ProtectedRoute } from "./lib/protected-route";
|
||||
import { AuthProvider } from "./hooks/use-auth";
|
||||
import Dashboard from "./pages/dashboard";
|
||||
import Dashboard from "./pages/dashboard";
|
||||
import LoadingScreen from "./components/ui/LoadingScreen";
|
||||
|
||||
const AuthPage = lazy(() => import("./pages/auth-page"));
|
||||
@@ -14,7 +16,9 @@ const AppointmentsPage = lazy(() => import("./pages/appointments-page"));
|
||||
const PatientsPage = lazy(() => import("./pages/patients-page"));
|
||||
const SettingsPage = lazy(() => import("./pages/settings-page"));
|
||||
const ClaimsPage = lazy(() => import("./pages/claims-page"));
|
||||
const PreAuthorizationsPage = lazy(() => import("./pages/preauthorizations-page"));
|
||||
const PreAuthorizationsPage = lazy(
|
||||
() => import("./pages/preauthorizations-page")
|
||||
);
|
||||
const PaymentsPage = lazy(() => import("./pages/payments-page"));
|
||||
const DocumentPage = lazy(() => import("./pages/documents-page"));
|
||||
const NotFound = lazy(() => import("./pages/not-found"));
|
||||
@@ -23,32 +27,39 @@ function Router() {
|
||||
return (
|
||||
<Switch>
|
||||
<ProtectedRoute path="/" component={() => <Dashboard />} />
|
||||
<ProtectedRoute path="/appointments" component={() => <AppointmentsPage />} />
|
||||
<ProtectedRoute
|
||||
path="/appointments"
|
||||
component={() => <AppointmentsPage />}
|
||||
/>
|
||||
<ProtectedRoute path="/patients" component={() => <PatientsPage />} />
|
||||
<ProtectedRoute path="/settings" component={() => <SettingsPage />} />
|
||||
<ProtectedRoute path="/claims" component={() => <ClaimsPage />} />
|
||||
<ProtectedRoute path="/preauthorizations" component={() => <PreAuthorizationsPage />} />
|
||||
<ProtectedRoute
|
||||
path="/preauthorizations"
|
||||
component={() => <PreAuthorizationsPage />}
|
||||
/>
|
||||
<ProtectedRoute path="/payments" component={() => <PaymentsPage />} />
|
||||
<ProtectedRoute path="/documents" component={() => <DocumentPage/>}/>
|
||||
<ProtectedRoute path="/documents" component={() => <DocumentPage />} />
|
||||
<Route path="/auth" component={() => <AuthPage />} />
|
||||
<Route component={() => <NotFound />} />
|
||||
</Switch>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AuthProvider>
|
||||
<TooltipProvider>
|
||||
<Toaster />
|
||||
<Suspense fallback={<LoadingScreen />}>
|
||||
<Router />
|
||||
</Suspense>
|
||||
</TooltipProvider>
|
||||
</AuthProvider>
|
||||
</QueryClientProvider>
|
||||
<Provider store={store}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AuthProvider>
|
||||
<TooltipProvider>
|
||||
<Toaster />
|
||||
<Suspense fallback={<LoadingScreen />}>
|
||||
<Router />
|
||||
</Suspense>
|
||||
</TooltipProvider>
|
||||
</AuthProvider>
|
||||
</QueryClientProvider>
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
50
apps/Frontend/src/components/claims/selenium-task-banner.tsx
Normal file
50
apps/Frontend/src/components/claims/selenium-task-banner.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { RootState } from "@/redux/store";
|
||||
import { clearTaskStatus } from "@/redux/slices/seleniumTaskSlice";
|
||||
import { Loader2, CheckCircle, XCircle } from "lucide-react";
|
||||
|
||||
export const SeleniumTaskBanner = () => {
|
||||
const { status, message, show } = useSelector(
|
||||
(state: RootState) => state.seleniumTask
|
||||
);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
if (!show) return null;
|
||||
|
||||
const getIcon = () => {
|
||||
switch (status) {
|
||||
case "pending":
|
||||
return <Loader2 className="w-5 h-5 animate-spin text-blue-500" />;
|
||||
case "success":
|
||||
return <CheckCircle className="w-5 h-5 text-green-500" />;
|
||||
case "error":
|
||||
return <XCircle className="w-5 h-5 text-red-500" />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-white border border-gray-200 shadow-md rounded-lg p-3 mb-4 flex items-start justify-between">
|
||||
<div className="flex items-start gap-3">
|
||||
{getIcon()}
|
||||
<div>
|
||||
<div className="font-medium text-gray-800">
|
||||
{status === "pending"
|
||||
? "Selenium Task In Progress"
|
||||
: status === "success"
|
||||
? "Selenium Task Completed"
|
||||
: "Selenium Task Error"}
|
||||
</div>
|
||||
<p className="text-gray-600 text-sm">{message}</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => dispatch(clearTaskStatus())}
|
||||
className="text-sm text-gray-500 hover:text-gray-800"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -18,6 +18,12 @@ import { apiRequest, queryClient } from "@/lib/queryClient";
|
||||
import { useLocation } from "wouter";
|
||||
import RecentClaims from "@/components/claims/recent-claims";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useDispatch } from "react-redux";
|
||||
import {
|
||||
setTaskStatus,
|
||||
clearTaskStatus,
|
||||
} from "@/redux/slices/seleniumTaskSlice";
|
||||
import { SeleniumTaskBanner } from "@/components/claims/selenium-task-banner";
|
||||
|
||||
//creating types out of schema auto generated.
|
||||
type Appointment = z.infer<typeof AppointmentUncheckedCreateInputObjectSchema>;
|
||||
@@ -71,8 +77,8 @@ type UpdatePatient = z.infer<typeof updatePatientSchema>;
|
||||
export default function ClaimsPage() {
|
||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
||||
const [isClaimFormOpen, setIsClaimFormOpen] = useState(false);
|
||||
const [isMhPopupOpen, setIsMhPopupOpen] = useState(false);
|
||||
const [selectedPatient, setSelectedPatient] = useState<number | null>(null);
|
||||
const dispatch = useDispatch();
|
||||
const { toast } = useToast();
|
||||
const { user } = useAuth();
|
||||
const [claimFormData, setClaimFormData] = useState<any>({
|
||||
@@ -215,38 +221,6 @@ export default function ClaimsPage() {
|
||||
},
|
||||
});
|
||||
|
||||
// selenium pdf download handler
|
||||
const handleSeleniumPopup = async (actionType: string) => {
|
||||
try {
|
||||
if (!claimRes?.id) {
|
||||
throw new Error("Missing claimId");
|
||||
}
|
||||
if (!selectedPatient) {
|
||||
throw new Error("Missing patientId");
|
||||
}
|
||||
const res = await apiRequest("POST", "/api/claims/selenium/fetchpdf", {
|
||||
action: actionType,
|
||||
patientId: selectedPatient,
|
||||
claimId: claimRes.id,
|
||||
});
|
||||
const result = await res.json();
|
||||
|
||||
toast({
|
||||
title: "Success",
|
||||
description: "MH action completed.",
|
||||
});
|
||||
|
||||
setIsMhPopupOpen(false);
|
||||
setSelectedPatient(null);
|
||||
} catch (error: any) {
|
||||
toast({
|
||||
title: "Error",
|
||||
description: error.message,
|
||||
variant: "destructive",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Converts local date to exact UTC date with no offset issues
|
||||
function parseLocalDate(dateInput: Date | string): Date {
|
||||
if (dateInput instanceof Date) return dateInput;
|
||||
@@ -332,6 +306,7 @@ export default function ClaimsPage() {
|
||||
return res.json();
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
console.log(data)
|
||||
setClaimRes(data);
|
||||
toast({
|
||||
title: "Claim submitted successfully",
|
||||
@@ -519,7 +494,6 @@ export default function ClaimsPage() {
|
||||
const handleSelenium = async (data: any) => {
|
||||
const formData = new FormData();
|
||||
formData.append("data", JSON.stringify(data));
|
||||
|
||||
const uploadedFiles: File[] = data.uploadedFiles ?? [];
|
||||
|
||||
uploadedFiles.forEach((file: File) => {
|
||||
@@ -531,22 +505,43 @@ export default function ClaimsPage() {
|
||||
});
|
||||
|
||||
try {
|
||||
dispatch(
|
||||
setTaskStatus({
|
||||
status: "pending",
|
||||
message: "Submitting claim to Selenium...",
|
||||
})
|
||||
);
|
||||
const response = await apiRequest(
|
||||
"POST",
|
||||
"/api/claims/selenium",
|
||||
formData
|
||||
);
|
||||
const result = await response.json();
|
||||
const result1 = await response.json();
|
||||
if (result1.error) throw new Error(result1.error);
|
||||
|
||||
dispatch(
|
||||
setTaskStatus({
|
||||
status: "pending",
|
||||
message: "Submitted to Selenium. Awaiting PDF...",
|
||||
})
|
||||
);
|
||||
|
||||
toast({
|
||||
title: "Selenium service notified",
|
||||
description:
|
||||
"Your claim data was successfully sent to Selenium, Wait for its response.",
|
||||
"Your claim data was successfully sent to Selenium, Waitinig for its response.",
|
||||
variant: "default",
|
||||
});
|
||||
setIsMhPopupOpen(true);
|
||||
return result;
|
||||
|
||||
// const result2 = await handleSeleniumPdfDownload(result1);
|
||||
// return result2;
|
||||
} catch (error: any) {
|
||||
dispatch(
|
||||
setTaskStatus({
|
||||
status: "error",
|
||||
message: error.message || "Selenium submission failed",
|
||||
})
|
||||
);
|
||||
toast({
|
||||
title: "Selenium service error",
|
||||
description: error.message || "An error occurred.",
|
||||
@@ -555,6 +550,61 @@ export default function ClaimsPage() {
|
||||
}
|
||||
};
|
||||
|
||||
// selenium pdf download handler
|
||||
const handleSeleniumPdfDownload = async (data: any) => {
|
||||
try {
|
||||
if (!claimRes?.id) {
|
||||
throw new Error("Missing claimId2s");
|
||||
}
|
||||
if (!selectedPatient) {
|
||||
throw new Error("Missing patientId");
|
||||
}
|
||||
|
||||
dispatch(
|
||||
setTaskStatus({
|
||||
status: "pending",
|
||||
message: "Downloading PDF from Selenium...",
|
||||
})
|
||||
);
|
||||
|
||||
const res = await apiRequest("POST", "/api/claims/selenium/fetchpdf", {
|
||||
patientId: selectedPatient,
|
||||
claimId: claimRes.id,
|
||||
pdf_url: data["pdf_url"],
|
||||
});
|
||||
const result = await res.json();
|
||||
if (result.error) throw new Error(result.error);
|
||||
|
||||
dispatch(
|
||||
setTaskStatus({
|
||||
status: "success",
|
||||
message: "Claim submitted & PDF downloaded successfully.",
|
||||
})
|
||||
);
|
||||
|
||||
toast({
|
||||
title: "Success",
|
||||
description: "Claim Submitted and Pdf Downloaded completed.",
|
||||
});
|
||||
|
||||
setSelectedPatient(null);
|
||||
|
||||
return result;
|
||||
} catch (error: any) {
|
||||
dispatch(
|
||||
setTaskStatus({
|
||||
status: "error",
|
||||
message: error.message || "Failed to download PDF",
|
||||
})
|
||||
);
|
||||
toast({
|
||||
title: "Error",
|
||||
description: error.message || "Failed to fetch PDF",
|
||||
variant: "destructive",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-screen overflow-hidden bg-gray-100">
|
||||
<Sidebar
|
||||
@@ -565,6 +615,8 @@ export default function ClaimsPage() {
|
||||
<div className="flex-1 flex flex-col overflow-hidden">
|
||||
<TopAppBar toggleMobileMenu={toggleMobileMenu} />
|
||||
|
||||
<SeleniumTaskBanner />
|
||||
|
||||
<main className="flex-1 overflow-y-auto p-4">
|
||||
{/* Header */}
|
||||
<div className="mb-6">
|
||||
@@ -680,31 +732,6 @@ export default function ClaimsPage() {
|
||||
onHandleForSelenium={handleSelenium}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isMhPopupOpen && (
|
||||
<div className="fixed inset-0 flex items-center justify-center z-50 bg-black bg-opacity-50">
|
||||
<div className="bg-white rounded-lg shadow-lg p-6 max-w-md w-full">
|
||||
<h2 className="text-xl font-semibold mb-4">Selenium Handler</h2>
|
||||
|
||||
<div className="flex justify-between space-x-4">
|
||||
<Button
|
||||
className="flex-1"
|
||||
onClick={() => handleSeleniumPopup("submit")}
|
||||
>
|
||||
Download the PDF.
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
className="flex-1"
|
||||
variant="secondary"
|
||||
onClick={() => setIsMhPopupOpen(false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
5
apps/Frontend/src/redux/hooks.ts
Normal file
5
apps/Frontend/src/redux/hooks.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
|
||||
import type { RootState, AppDispatch } from "./store";
|
||||
|
||||
export const useAppDispatch: () => AppDispatch = useDispatch;
|
||||
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
|
||||
32
apps/Frontend/src/redux/slices/seleniumTaskSlice.ts
Normal file
32
apps/Frontend/src/redux/slices/seleniumTaskSlice.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
|
||||
export type TaskStatus = "idle" | "pending" | "success" | "error";
|
||||
|
||||
export interface SeleniumTaskState {
|
||||
status: TaskStatus;
|
||||
message: string;
|
||||
show: boolean;
|
||||
}
|
||||
|
||||
const initialState: SeleniumTaskState = {
|
||||
status: "idle",
|
||||
message: "",
|
||||
show: false,
|
||||
};
|
||||
|
||||
const seleniumTaskSlice = createSlice({
|
||||
name: "seleniumTask",
|
||||
initialState,
|
||||
reducers: {
|
||||
setTaskStatus: (
|
||||
state: SeleniumTaskState,
|
||||
action: PayloadAction<Partial<SeleniumTaskState>>
|
||||
) => {
|
||||
return { ...state, ...action.payload, show: true };
|
||||
},
|
||||
clearTaskStatus: () => initialState,
|
||||
},
|
||||
});
|
||||
|
||||
export const { setTaskStatus, clearTaskStatus } = seleniumTaskSlice.actions;
|
||||
export default seleniumTaskSlice.reducer;
|
||||
11
apps/Frontend/src/redux/store.ts
Normal file
11
apps/Frontend/src/redux/store.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { configureStore } from "@reduxjs/toolkit";
|
||||
import seleniumTaskReducer from "./slices/seleniumTaskSlice";
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
seleniumTask: seleniumTaskReducer,
|
||||
},
|
||||
});
|
||||
|
||||
export type RootState = ReturnType<typeof store.getState>;
|
||||
export type AppDispatch = typeof store.dispatch;
|
||||
Reference in New Issue
Block a user