Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Routers/routes/ConsultationRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,11 @@ const consultationRoutes: AppRoutes = {
),
"/facility/:facilityId/patient/:patientId/encounter/:encounterId/report/template/:templateSlug":
({ encounterId, templateSlug }) => (
<ReportViewer encounterId={encounterId} templateSlug={templateSlug} />
<ReportViewer associatingId={encounterId} templateSlug={templateSlug} />
),
"/facility/:facilityId/patient/:patientId/encounter/:encounterId/report/:reportId":
({ encounterId, reportId }) => (
<ReportViewer encounterId={encounterId} reportId={reportId} />
<ReportViewer associatingId={encounterId} reportId={reportId} />
),
"/facility/:facilityId/patient/:patientId/encounter/:encounterId/questionnaire":
({ facilityId, encounterId, patientId }) => (
Expand Down
12 changes: 12 additions & 0 deletions src/Routers/routes/FacilityRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import MedicationDispenseRedirect from "@/pages/Facility/billing/account/compone
import BedAvailabilityDashboard from "@/pages/Facility/BedAvailabilityDashboard";

import { AppRoutes } from "@/Routers/AppRouter";
import ReportViewer from "@/pages/Encounters/ReportViewer";
import TemplateBuilder from "@/pages/Encounters/TemplateBuilder/TemplateBuilder";
import TemplatePage from "@/pages/Encounters/TemplateBuilder/TemplatePage";
import AccountList from "@/pages/Facility/billing/account/AccountList";
Expand All @@ -28,6 +29,7 @@ import DiagnosticReportPrint from "@/pages/Facility/services/diagnosticReports/D
import DiagnosticReportView from "@/pages/Facility/services/diagnosticReports/DiagnosticReportView";
import ServiceRequestShow from "@/pages/Facility/services/serviceRequests/ServiceRequestShow";
import { SettingsLayout } from "@/pages/Facility/settings/layout";
import { ReportType } from "@/types/emr/report/report";

const FacilityRoutes: AppRoutes = {
"/facility": () => <Redirect to="/" />,
Expand Down Expand Up @@ -104,6 +106,16 @@ const FacilityRoutes: AppRoutes = {
facilityId,
accountId,
}) => <PrintChargeItems facilityId={facilityId} accountId={accountId} />,
"/facility/:facilityId/billing/account/:accountId/reports/:reportId": ({
accountId,
reportId,
}) => (
<ReportViewer
associatingId={accountId}
reportId={reportId}
reportType={ReportType.ACCOUNT_REPORT}
/>
),
Comment thread
coderabbitai[bot] marked this conversation as resolved.
"/facility/:facilityId/billing/account/:accountId/:tab": ({
facilityId,
accountId,
Expand Down
37 changes: 0 additions & 37 deletions src/components/Common/FilePreviewDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ import {
FileReadMinimal,
getVideoMimeType,
} from "@/types/files/file";
import { ShortcutBadge } from "@/Utils/keyboardShortcutComponents";
import { Printer } from "lucide-react";

const PDFViewer = lazy(() => import("@/components/Common/PDFViewer"));
export interface StateInterface {
Expand Down Expand Up @@ -247,41 +245,6 @@ export default function FilePreviewDialog(props: FilePreviewProps) {
)}
</div>
<div className="flex gap-2">
{file_state.extension === "pdf" && (
<Button
variant="outline"
onClick={async () => {
try {
const response = await fetch(fileUrl);
const blob = await response.blob();
const blobUrl = window.URL.createObjectURL(blob);
const iframe = document.createElement("iframe");
iframe.style.display = "none";
iframe.src = blobUrl;

iframe.onload = () => {
try {
iframe.contentWindow?.print();
} catch {
window.open(blobUrl, "_blank");
}
setTimeout(() => {
document.body.removeChild(iframe);
window.URL.revokeObjectURL(blobUrl);
}, 10000);
};

document.body.appendChild(iframe);
} catch {
toast.error(t("print_failed"));
}
}}
>
<Printer className="size-4" />
<span className="hidden sm:block">{t("print")}</span>
<ShortcutBadge actionId="print-button" />
</Button>
)}
{file_state.extension === "pdf" && fileUrl && (
<Button
variant="outline"
Expand Down
8 changes: 5 additions & 3 deletions src/components/Files/ReportSubTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ interface ReportTabProps {
facilityId?: string;
patientId?: string;
encounterId?: string;
getViewUrl?: (reportId: string) => string;
}

export function ReportSubTab({
Expand All @@ -58,6 +59,7 @@ export function ReportSubTab({
facilityId,
patientId,
encounterId,
getViewUrl,
}: ReportTabProps) {
const { t } = useTranslation();
const { facility } = useCurrentFacilitySilently();
Expand Down Expand Up @@ -98,10 +100,10 @@ export function ReportSubTab({
return iconMap[reportType] || "l-file-alt";
};

const canNavigateToPreview = !!(facilityId && patientId && encounterId);

const handleView = (report: ReportReadList) => {
if (canNavigateToPreview) {
if (getViewUrl) {
navigate(getViewUrl(report.id));
} else if (facilityId && patientId && encounterId) {
navigate(
`/facility/${facilityId}/patient/${patientId}/encounter/${encounterId}/report/${report.id}`,
);
Expand Down
56 changes: 40 additions & 16 deletions src/pages/Encounters/ReportViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import { EmptyState } from "@/components/ui/empty-state";
import { usePermissions } from "@/context/PermissionContext";
import { cn } from "@/lib/utils";
import { useCurrentFacilitySilently } from "@/pages/Facility/utils/useCurrentFacility";
import { ReportReadList } from "@/types/emr/report/report";
import { ReportReadList, ReportType } from "@/types/emr/report/report";
import reportApi from "@/types/emr/report/reportApi";
import { TemplateBaseRead } from "@/types/emr/template/template";
import templateApi from "@/types/emr/template/templateApi";
Expand All @@ -52,15 +52,17 @@ const POLL_INTERVAL_MS = 2000;
const POLL_TIMEOUT_MS = 30000;

interface ReportViewerProps {
encounterId: string;
associatingId: string;
templateSlug?: string;
reportId?: string;
reportType?: ReportType;
}
Comment thread
abhimanyurajeesh marked this conversation as resolved.

export default function ReportViewer({
encounterId,
associatingId,
templateSlug,
reportId,
reportType = ReportType.DISCHARGE_SUMMARY,
}: ReportViewerProps) {
const { t } = useTranslation();
const queryClient = useQueryClient();
Expand All @@ -75,6 +77,7 @@ export default function ReportViewer({
const [drawerOpen, setDrawerOpen] = useState(false);

const [isGenerating, setIsGenerating] = useState(false);
const [isPrinting, setIsPrinting] = useState(false);
const [autoGenTriggered, setAutoGenTriggered] = useState(false);
const pollIntervalRef = useRef<NodeJS.Timeout | null>(null);
const pollTimeoutRef = useRef<NodeJS.Timeout | null>(null);
Expand Down Expand Up @@ -114,18 +117,18 @@ export default function ReportViewer({
isLoading: isLoadingReports,
refetch: refetchReports,
} = useQuery({
queryKey: ["reports", encounterId, "template", effectiveTSlug],
queryKey: ["reports", associatingId, "template", effectiveTSlug],
Comment thread
abhimanyurajeesh marked this conversation as resolved.
Outdated
Comment thread
abhimanyurajeesh marked this conversation as resolved.
Outdated
queryFn: query(reportApi.listReports, {
queryParams: {
Comment thread
abhimanyurajeesh marked this conversation as resolved.
associating_id: encounterId,
associating_id: associatingId,
upload_completed: "true",
report_type: "discharge_summary",
report_type: reportType,
is_archived: "false",
template: effectiveTSlug,
limit: 50,
},
}),
enabled: !!encounterId && !!effectiveTSlug,
enabled: !!associatingId && !!effectiveTSlug,
Comment thread
abhimanyurajeesh marked this conversation as resolved.
Outdated
});

const reports = useMemo(
Expand Down Expand Up @@ -184,7 +187,7 @@ export default function ReportViewer({
const response = await callApi(reportApi.createReport, {
body: {
template_id: tmpl.id,
associating_id: encounterId,
associating_id: associatingId,
output_format: tmpl.default_format,
options: JSON.stringify({}),
force: false,
Expand All @@ -200,16 +203,16 @@ export default function ReportViewer({
const freshData = await queryClient.fetchQuery({
queryKey: [
"reports",
encounterId,
associatingId,
"template",
effectiveTSlug,
Comment thread
abhimanyurajeesh marked this conversation as resolved.
Comment on lines 210 to 214
"fresh",
],
Comment thread
abhimanyurajeesh marked this conversation as resolved.
Comment thread
abhimanyurajeesh marked this conversation as resolved.
queryFn: query(reportApi.listReports, {
queryParams: {
associating_id: encounterId,
associating_id: associatingId,
upload_completed: "true",
report_type: "discharge_summary",
report_type: reportType,
is_archived: "false",
template: effectiveTSlug,
limit: 1,
Expand All @@ -236,7 +239,15 @@ export default function ReportViewer({
// Continue polling on transient errors
}
},
[encounterId, effectiveTSlug, stopPolling, queryClient, refetchReports, t],
[
associatingId,
effectiveTSlug,
reportType,
stopPolling,
queryClient,
refetchReports,
t,
],
);

const startPolling = useCallback(
Expand Down Expand Up @@ -279,7 +290,7 @@ export default function ReportViewer({
triggerGeneration(
{
template_id: tmpl.id,
associating_id: encounterId,
associating_id: associatingId,
output_format: tmpl.default_format,
options: JSON.stringify({}),
force: false,
Expand All @@ -292,7 +303,7 @@ export default function ReportViewer({
},
);
},
[isGenerating, encounterId, triggerGeneration, startPolling, t],
[isGenerating, associatingId, triggerGeneration, startPolling, t],
);

// Auto-generate report on first load if none exist
Expand Down Expand Up @@ -327,6 +338,9 @@ export default function ReportViewer({
}

const response = await fetch(pdfUrl);
if (!response.ok) {
throw new Error("Download failed");
}
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const anchor = document.createElement("a");
Expand All @@ -345,8 +359,12 @@ export default function ReportViewer({
const handlePrint = useCallback(async () => {
if (!pdfUrl) return;

setIsPrinting(true);
try {
const response = await fetch(pdfUrl);
if (!response.ok) {
throw new Error("Print failed");
}
const blob = await response.blob();
const blobUrl = window.URL.createObjectURL(blob);
const iframe = document.createElement("iframe");
Expand All @@ -358,6 +376,7 @@ export default function ReportViewer({
} catch {
window.open(blobUrl, "_blank");
Comment thread
abhimanyurajeesh marked this conversation as resolved.
}
Comment thread
abhimanyurajeesh marked this conversation as resolved.
setIsPrinting(false);
setTimeout(() => {
document.body.removeChild(iframe);
window.URL.revokeObjectURL(blobUrl);
Expand All @@ -366,6 +385,7 @@ export default function ReportViewer({

document.body.appendChild(iframe);
} catch {
setIsPrinting(false);
toast.error(t("PRINTABLE_QR_CODE__print_error"));
Comment thread
abhimanyurajeesh marked this conversation as resolved.
Outdated
}
Comment thread
abhimanyurajeesh marked this conversation as resolved.
}, [pdfUrl, t]);
Comment thread
abhimanyurajeesh marked this conversation as resolved.
Expand Down Expand Up @@ -423,9 +443,13 @@ export default function ReportViewer({
variant="outline"
onClick={handlePrint}
aria-label={t("print")}
disabled={!pdfUrl}
disabled={!pdfUrl || isPrinting}
>
<Printer className="size-4" />
{isPrinting ? (
<Loader className="size-4 animate-spin" />
) : (
<Printer className="size-4" />
)}
<span className="hidden md:inline">{t("print")}</span>
<ShortcutBadge actionId="print-button" />
</Button>
Expand Down
3 changes: 3 additions & 0 deletions src/pages/Facility/billing/account/AccountShow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,9 @@ export function AccountShow({
<ReportSubTab
associatingId={accountId}
reportType={ReportType.ACCOUNT_REPORT}
getViewUrl={(reportId) =>
`/facility/${facilityId}/billing/account/${accountId}/reports/${reportId}`
}
/>
),
shortcutId: "switch-to-reports-tab",
Expand Down
Loading