@eka-care/medical-records-ui 1.0.43 → 1.0.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -59,7 +59,7 @@ function TextViewer({ url }) {
59
59
 
60
60
  // src/views/RecordPreview/DocumentViewer.tsx
61
61
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
62
- var PdfViewer = lazy(() => import("./PdfViewer-WIV5AUJB.mjs").then((m) => ({ default: m.PdfViewer })));
62
+ var PdfViewer = lazy(() => import("./PdfViewer-CG2577E4.mjs").then((m) => ({ default: m.PdfViewer })));
63
63
  var PAGE_WIDTH = 595;
64
64
  var ZOOM_STEP = 0.25;
65
65
  var ZOOM_MIN = 0.5;
@@ -195,4 +195,4 @@ function DocumentViewer({ files, title, isHtml }) {
195
195
  export {
196
196
  DocumentViewer
197
197
  };
198
- //# sourceMappingURL=DocumentViewer-OM6D4CSI.mjs.map
198
+ //# sourceMappingURL=DocumentViewer-5AXKL75U.mjs.map
@@ -59,7 +59,7 @@ function TextViewer({ url }) {
59
59
 
60
60
  // src/views/RecordPreview/DocumentViewer.tsx
61
61
 
62
- var PdfViewer = _react.lazy.call(void 0, () => Promise.resolve().then(() => _interopRequireWildcard(require("./PdfViewer-EAN6EQAB.js"))).then((m) => ({ default: m.PdfViewer })));
62
+ var PdfViewer = _react.lazy.call(void 0, () => Promise.resolve().then(() => _interopRequireWildcard(require("./PdfViewer-GIWRL6FQ.js"))).then((m) => ({ default: m.PdfViewer })));
63
63
  var PAGE_WIDTH = 595;
64
64
  var ZOOM_STEP = 0.25;
65
65
  var ZOOM_MIN = 0.5;
@@ -195,4 +195,4 @@ function DocumentViewer({ files, title, isHtml }) {
195
195
 
196
196
 
197
197
  exports.DocumentViewer = DocumentViewer;
198
- //# sourceMappingURL=DocumentViewer-D7OJVVZM.js.map
198
+ //# sourceMappingURL=DocumentViewer-Y3SQ2BMS.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/medical-records-ui/medical-records-ui/dist/DocumentViewer-D7OJVVZM.js","../src/views/RecordPreview/DocumentViewer.tsx","../src/views/RecordPreview/TextViewer.tsx"],"names":["jsx"],"mappings":"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","file":"/home/runner/work/medical-records-ui/medical-records-ui/dist/DocumentViewer-D7OJVVZM.js","sourcesContent":[null,"import { lazy, Suspense, useCallback, useEffect, useRef, useState } from 'react';\nimport {\n ChevronLeft,\n ChevronRight,\n Minus,\n Plus,\n Download,\n Maximize,\n Minimize,\n Globe,\n} from 'lucide-react';\nimport { TextViewer } from './TextViewer';\nimport type { PreviewFile } from '../../connection';\n\nconst PdfViewer = lazy(() => import('./PdfViewer').then((m) => ({ default: m.PdfViewer })));\n\nexport interface DocumentViewerProps {\n files: PreviewFile[];\n title: string;\n isHtml?: boolean;\n}\n\nconst PAGE_WIDTH = 595; \nconst ZOOM_STEP = 0.25;\nconst ZOOM_MIN = 0.5;\nconst ZOOM_MAX = 3;\n\nexport function DocumentViewer({ files, title, isHtml }: DocumentViewerProps) {\n const [zoom, setZoom] = useState(1);\n const [pageInfo, setPageInfo] = useState({ current: 1, total: 0 });\n const [scrollToPage, setScrollToPage] = useState(1);\n const [isFullscreen, setIsFullscreen] = useState(false);\n const rootRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const onChange = () => setIsFullscreen(document.fullscreenElement === rootRef.current);\n document.addEventListener('fullscreenchange', onChange);\n return () => document.removeEventListener('fullscreenchange', onChange);\n }, []);\n\n const toggleFullscreen = () => {\n if (document.fullscreenElement) void document.exitFullscreen();\n else void rootRef.current?.requestFullscreen?.();\n };\n\n const primary = files[0] ?? null;\n const isPdf = primary?.kind === 'pdf';\n const images = files.filter((f) => f.kind === 'image');\n const isZoomable = isPdf || images.length > 0;\n\n const zoomOut = () => setZoom((z) => Math.max(ZOOM_MIN, +(z - ZOOM_STEP).toFixed(2)));\n const zoomIn = () => setZoom((z) => Math.min(ZOOM_MAX, +(z + ZOOM_STEP).toFixed(2)));\n\n const prevPage = () => setScrollToPage(Math.max(1, pageInfo.current - 1));\n const nextPage = () => setScrollToPage(Math.min(pageInfo.total, pageInfo.current + 1));\n\n // Download / print fetch the file into a blob URL rather than re-opening the\n // signed URL directly, so it works even when the document is already loaded.\n const withBlobUrl = useCallback(async (fn: (objUrl: string) => void) => {\n if (!primary) return;\n try {\n const res = await fetch(primary.url);\n const blob = await res.blob();\n const objUrl = URL.createObjectURL(blob);\n fn(objUrl);\n // Revoke after the consumer has had a chance to use it.\n setTimeout(() => URL.revokeObjectURL(objUrl), 120_000);\n } catch {\n // Fall back to opening the original URL.\n fn(primary.url);\n }\n }, [primary]);\n\n const handleDownload = () =>\n withBlobUrl((objUrl) => {\n const a = document.createElement('a');\n a.href = objUrl;\n a.download = title || 'document';\n a.click();\n });\n\n return (\n <div className={`mr-doc-viewer${isHtml ? ' mr-doc-viewer--html' : ''}`} ref={rootRef}>\n {!isHtml && <div className=\"mr-doc-viewer__toolbar\">\n <span className=\"mr-doc-viewer__filename\">{title}</span>\n {isPdf && (\n <div className=\"mr-doc-viewer__pages\">\n <button\n type=\"button\"\n aria-label=\"Previous page\"\n onClick={prevPage}\n disabled={pageInfo.current <= 1}\n >\n <ChevronLeft size={18} aria-hidden />\n </button>\n <span className=\"mr-doc-viewer__page-badge\">{pageInfo.current}</span>\n <span>/</span>\n <span>{pageInfo.total || '—'}</span>\n <button\n type=\"button\"\n aria-label=\"Next page\"\n onClick={nextPage}\n disabled={pageInfo.current >= pageInfo.total}\n >\n <ChevronRight size={18} aria-hidden />\n </button>\n </div>\n )}\n {isZoomable && (\n <div className=\"mr-doc-viewer__zoom\">\n <button type=\"button\" aria-label=\"Zoom out\" onClick={zoomOut} disabled={zoom <= ZOOM_MIN}>\n <Minus size={20} aria-hidden />\n </button>\n <span className=\"mr-doc-viewer__zoom-badge\">{Math.round(zoom * 100)}%</span>\n <button type=\"button\" aria-label=\"Zoom in\" onClick={zoomIn} disabled={zoom >= ZOOM_MAX}>\n <Plus size={20} aria-hidden />\n </button>\n </div>\n )}\n <div className=\"mr-doc-viewer__actions\">\n <button type=\"button\" aria-label=\"Download\" onClick={handleDownload} disabled={!primary}>\n <Download size={20} aria-hidden />\n </button>\n <button\n type=\"button\"\n aria-label={isFullscreen ? 'Exit full screen' : 'Full screen'}\n onClick={toggleFullscreen}\n >\n {isFullscreen ? <Minimize size={20} aria-hidden /> : <Maximize size={20} aria-hidden />}\n </button>\n </div>\n </div>}\n\n {isHtml && (\n <div className=\"mr-doc-viewer__html-bar\">\n <div className=\"mr-doc-viewer__html-bar-left\">\n <Globe size={12} aria-hidden />\n <span>Previewing HTML file – you can copy to a note and edit this file. The original record that you are viewing now will not be affected.</span>\n </div>\n <button\n type=\"button\"\n className=\"mr-doc-viewer__html-download\"\n aria-label=\"Download\"\n onClick={handleDownload}\n disabled={!primary}\n >\n <Download size={16} aria-hidden />\n </button>\n </div>\n )}\n\n <div className=\"mr-doc-viewer__canvas\">\n {!primary ? (\n <div className=\"mr-doc-viewer__status\">No preview available for this record.</div>\n ) : isPdf ? (\n <Suspense fallback={<div className=\"mr-doc-viewer__status\">Loading viewer…</div>}>\n <PdfViewer\n url={primary.url}\n baseWidth={PAGE_WIDTH}\n scale={zoom}\n scrollToPage={scrollToPage}\n onPageInfo={setPageInfo}\n />\n </Suspense>\n ) : images.length > 0 ? (\n images.map((img, i) => (\n <img\n key={i}\n className=\"mr-doc-viewer__image\"\n src={img.url}\n alt={`${title} ${i + 1}`}\n style={{ transform: `scale(${zoom})`, transformOrigin: 'top center' }}\n />\n ))\n ) : (\n <TextViewer url={primary.url} />\n )}\n </div>\n </div>\n );\n}\n","import { useEffect, useState } from 'react';\n\nexport interface TextViewerProps {\n url: string;\n}\n\nexport function TextViewer({ url }: TextViewerProps) {\n const [state, setState] = useState<{ status: 'loading' | 'loaded' | 'error'; text: string }>({\n status: 'loading',\n text: '',\n });\n\n useEffect(() => {\n let cancelled = false;\n setState({ status: 'loading', text: '' });\n (async () => {\n try {\n const res = await fetch(url);\n if (!res.ok) throw new Error(`fetch ${res.status}`);\n const text = await res.text();\n if (text.trimStart().startsWith('%PDF')) throw new Error('binary');\n if (!cancelled) setState({ status: 'loaded', text });\n } catch {\n if (!cancelled) setState({ status: 'error', text: '' });\n }\n })();\n return () => {\n cancelled = true;\n };\n }, [url]);\n\n if (state.status === 'loading') {\n return <div className=\"mr-doc-viewer__status\" style={{ color: 'var(--mr-muted-foreground)' }}>Loading document...</div>;\n }\n if (state.status === 'error') {\n return <div className=\"mr-doc-viewer__status\" style={{ color: 'var(--mr-muted-foreground)' }}>Couldn't load this document.</div>;\n }\n if (!state.text.trim()) {\n return <div className=\"mr-doc-viewer__status\" style={{ color: 'var(--mr-muted-foreground)' }}>This record appears to be empty.</div>;\n }\n return (\n <iframe\n className=\"mr-doc-viewer__html-frame\"\n srcDoc={state.text}\n sandbox=\"allow-same-origin\"\n title=\"HTML preview\"\n />\n );\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/medical-records-ui/medical-records-ui/dist/DocumentViewer-Y3SQ2BMS.js","../src/views/RecordPreview/DocumentViewer.tsx","../src/views/RecordPreview/TextViewer.tsx"],"names":["jsx"],"mappings":"AAAA;ACAA,8BAAyE;AACzE;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA,2CACK;ADEP;AACA;AEbA;AAgCW,+CAAA;AA1BJ,SAAS,UAAA,CAAW,EAAE,IAAI,CAAA,EAAoB;AACnD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,EAAA,EAAI,6BAAA;AAAmE,IAC3F,MAAA,EAAQ,SAAA;AAAA,IACR,IAAA,EAAM;AAAA,EACR,CAAC,CAAA;AAED,EAAA,8BAAA,CAAU,EAAA,GAAM;AACd,IAAA,IAAI,UAAA,EAAY,KAAA;AAChB,IAAA,QAAA,CAAS,EAAE,MAAA,EAAQ,SAAA,EAAW,IAAA,EAAM,GAAG,CAAC,CAAA;AACxC,IAAA,CAAC,MAAA,CAAA,EAAA,GAAY;AACX,MAAA,IAAI;AACF,QAAA,MAAM,IAAA,EAAM,MAAM,KAAA,CAAM,GAAG,CAAA;AAC3B,QAAA,GAAA,CAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,IAAI,KAAA,CAAM,CAAA,MAAA,EAAS,GAAA,CAAI,MAAM,CAAA,CAAA;AACpB,QAAA;AACmB,QAAA;AACF,QAAA;AACvC,MAAA;AACsC,QAAA;AAC9C,MAAA;AACC,IAAA;AACU,IAAA;AACC,MAAA;AACd,IAAA;AACM,EAAA;AAEwB,EAAA;AACR,IAAA;AACxB,EAAA;AAC8B,EAAA;AACN,IAAA;AACxB,EAAA;AACwB,EAAA;AACA,IAAA;AACxB,EAAA;AAEE,EAAA;AAAC,IAAA;AAAA,IAAA;AACW,MAAA;AACI,MAAA;AACN,MAAA;AACF,MAAA;AAAA,IAAA;AACR,EAAA;AAEJ;AFUyD;AACA;ACyBjD;AAtE4B;AAQjB;AACD;AACD;AACA;AAE6D;AAC1C,EAAA;AACkB,EAAA;AACF,EAAA;AACI,EAAA;AACX,EAAA;AAE3B,EAAA;AACkC,IAAA;AACF,IAAA;AACJ,IAAA;AACvC,EAAA;AAE0B,EAAA;AACiB,IAAA;AACC,IAAA;AACjD,EAAA;AAE4B,EAAA;AACI,EAAA;AACqB,EAAA;AACT,EAAA;AAEE,EAAA;AACS,EAAA;AAEJ,EAAA;AACH,EAAA;AAIwB,EAAA;AACxD,IAAA;AACV,IAAA;AACiC,MAAA;AACP,MAAA;AACW,MAAA;AAC9B,MAAA;AAE4C,MAAA;AAC/C,IAAA;AAEQ,MAAA;AAChB,IAAA;AACU,EAAA;AAGc,EAAA;AACc,IAAA;AAC3B,IAAA;AACa,IAAA;AACd,IAAA;AACT,EAAA;AAGe,EAAA;AACa,IAAA;AACT,sBAAA;AAEC,MAAA;AACbA,wBAAAA;AAAC,UAAA;AAAA,UAAA;AACM,YAAA;AACM,YAAA;AACF,YAAA;AACqB,YAAA;AAE7B,YAAA;AAAkC,UAAA;AACrC,QAAA;AACgB,wBAAA;AACT,wBAAA;AACA,wBAAA;AACPA,wBAAAA;AAAC,UAAA;AAAA,UAAA;AACM,YAAA;AACM,YAAA;AACF,YAAA;AAC8B,YAAA;AAEtC,YAAA;AAAmC,UAAA;AACtC,QAAA;AACF,MAAA;AAGK,MAAA;AACmB,wBAAA;AAGN,wBAAA;AAAkD,UAAA;AAAE,UAAA;AAAC,QAAA;AAC/C,wBAAA;AAGxB,MAAA;AAEa,sBAAA;AACS,wBAAA;AAGtBA,wBAAAA;AAAC,UAAA;AAAA,UAAA;AACM,YAAA;AACsB,YAAA;AAClB,YAAA;AAEOA,YAAAA;AAAqE,UAAA;AACvF,QAAA;AACF,MAAA;AACF,IAAA;AAGiB,IAAA;AACE,sBAAA;AACI,wBAAA;AACX,wBAAA;AACR,MAAA;AACAA,sBAAAA;AAAC,QAAA;AAAA,QAAA;AACM,UAAA;AACK,UAAA;AACC,UAAA;AACF,UAAA;AACE,UAAA;AAED,UAAA;AAAsB,QAAA;AAClC,MAAA;AACF,IAAA;AAGa,oBAAA;AAKR,MAAA;AAAA,MAAA;AACc,QAAA;AACF,QAAA;AACJ,QAAA;AACP,QAAA;AACY,QAAA;AAAA,MAAA;AAKd,IAAA;AAAC,MAAA;AAAA,MAAA;AAEW,QAAA;AACD,QAAA;AACa,QAAA;AACgB,QAAA;AAA8B,MAAA;AAJ/D,MAAA;AAQgB,IAAA;AAG/B,EAAA;AAEJ;ADcyD;AACA;AACA","file":"/home/runner/work/medical-records-ui/medical-records-ui/dist/DocumentViewer-Y3SQ2BMS.js","sourcesContent":[null,"import { lazy, Suspense, useCallback, useEffect, useRef, useState } from 'react';\nimport {\n ChevronLeft,\n ChevronRight,\n Minus,\n Plus,\n Download,\n Maximize,\n Minimize,\n Globe,\n} from 'lucide-react';\nimport { TextViewer } from './TextViewer';\nimport type { PreviewFile } from '../../connection';\n\nconst PdfViewer = lazy(() => import('./PdfViewer').then((m) => ({ default: m.PdfViewer })));\n\nexport interface DocumentViewerProps {\n files: PreviewFile[];\n title: string;\n isHtml?: boolean;\n}\n\nconst PAGE_WIDTH = 595; \nconst ZOOM_STEP = 0.25;\nconst ZOOM_MIN = 0.5;\nconst ZOOM_MAX = 3;\n\nexport function DocumentViewer({ files, title, isHtml }: DocumentViewerProps) {\n const [zoom, setZoom] = useState(1);\n const [pageInfo, setPageInfo] = useState({ current: 1, total: 0 });\n const [scrollToPage, setScrollToPage] = useState(1);\n const [isFullscreen, setIsFullscreen] = useState(false);\n const rootRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const onChange = () => setIsFullscreen(document.fullscreenElement === rootRef.current);\n document.addEventListener('fullscreenchange', onChange);\n return () => document.removeEventListener('fullscreenchange', onChange);\n }, []);\n\n const toggleFullscreen = () => {\n if (document.fullscreenElement) void document.exitFullscreen();\n else void rootRef.current?.requestFullscreen?.();\n };\n\n const primary = files[0] ?? null;\n const isPdf = primary?.kind === 'pdf';\n const images = files.filter((f) => f.kind === 'image');\n const isZoomable = isPdf || images.length > 0;\n\n const zoomOut = () => setZoom((z) => Math.max(ZOOM_MIN, +(z - ZOOM_STEP).toFixed(2)));\n const zoomIn = () => setZoom((z) => Math.min(ZOOM_MAX, +(z + ZOOM_STEP).toFixed(2)));\n\n const prevPage = () => setScrollToPage(Math.max(1, pageInfo.current - 1));\n const nextPage = () => setScrollToPage(Math.min(pageInfo.total, pageInfo.current + 1));\n\n // Download / print fetch the file into a blob URL rather than re-opening the\n // signed URL directly, so it works even when the document is already loaded.\n const withBlobUrl = useCallback(async (fn: (objUrl: string) => void) => {\n if (!primary) return;\n try {\n const res = await fetch(primary.url);\n const blob = await res.blob();\n const objUrl = URL.createObjectURL(blob);\n fn(objUrl);\n // Revoke after the consumer has had a chance to use it.\n setTimeout(() => URL.revokeObjectURL(objUrl), 120_000);\n } catch {\n // Fall back to opening the original URL.\n fn(primary.url);\n }\n }, [primary]);\n\n const handleDownload = () =>\n withBlobUrl((objUrl) => {\n const a = document.createElement('a');\n a.href = objUrl;\n a.download = title || 'document';\n a.click();\n });\n\n return (\n <div className={`mr-doc-viewer${isHtml ? ' mr-doc-viewer--html' : ''}`} ref={rootRef}>\n {!isHtml && <div className=\"mr-doc-viewer__toolbar\">\n <span className=\"mr-doc-viewer__filename\">{title}</span>\n {isPdf && (\n <div className=\"mr-doc-viewer__pages\">\n <button\n type=\"button\"\n aria-label=\"Previous page\"\n onClick={prevPage}\n disabled={pageInfo.current <= 1}\n >\n <ChevronLeft size={18} aria-hidden />\n </button>\n <span className=\"mr-doc-viewer__page-badge\">{pageInfo.current}</span>\n <span>/</span>\n <span>{pageInfo.total || '—'}</span>\n <button\n type=\"button\"\n aria-label=\"Next page\"\n onClick={nextPage}\n disabled={pageInfo.current >= pageInfo.total}\n >\n <ChevronRight size={18} aria-hidden />\n </button>\n </div>\n )}\n {isZoomable && (\n <div className=\"mr-doc-viewer__zoom\">\n <button type=\"button\" aria-label=\"Zoom out\" onClick={zoomOut} disabled={zoom <= ZOOM_MIN}>\n <Minus size={20} aria-hidden />\n </button>\n <span className=\"mr-doc-viewer__zoom-badge\">{Math.round(zoom * 100)}%</span>\n <button type=\"button\" aria-label=\"Zoom in\" onClick={zoomIn} disabled={zoom >= ZOOM_MAX}>\n <Plus size={20} aria-hidden />\n </button>\n </div>\n )}\n <div className=\"mr-doc-viewer__actions\">\n <button type=\"button\" aria-label=\"Download\" onClick={handleDownload} disabled={!primary}>\n <Download size={20} aria-hidden />\n </button>\n <button\n type=\"button\"\n aria-label={isFullscreen ? 'Exit full screen' : 'Full screen'}\n onClick={toggleFullscreen}\n >\n {isFullscreen ? <Minimize size={20} aria-hidden /> : <Maximize size={20} aria-hidden />}\n </button>\n </div>\n </div>}\n\n {isHtml && (\n <div className=\"mr-doc-viewer__html-bar\">\n <div className=\"mr-doc-viewer__html-bar-left\">\n <Globe size={12} aria-hidden />\n <span>Previewing HTML file – you can copy to a note and edit this file. The original record that you are viewing now will not be affected.</span>\n </div>\n <button\n type=\"button\"\n className=\"mr-doc-viewer__html-download\"\n aria-label=\"Download\"\n onClick={handleDownload}\n disabled={!primary}\n >\n <Download size={16} aria-hidden />\n </button>\n </div>\n )}\n\n <div className=\"mr-doc-viewer__canvas\">\n {!primary ? (\n <div className=\"mr-doc-viewer__status\">No preview available for this record.</div>\n ) : isPdf ? (\n <Suspense fallback={<div className=\"mr-doc-viewer__status\">Loading viewer…</div>}>\n <PdfViewer\n url={primary.url}\n baseWidth={PAGE_WIDTH}\n scale={zoom}\n scrollToPage={scrollToPage}\n onPageInfo={setPageInfo}\n />\n </Suspense>\n ) : images.length > 0 ? (\n images.map((img, i) => (\n <img\n key={i}\n className=\"mr-doc-viewer__image\"\n src={img.url}\n alt={`${title} ${i + 1}`}\n style={{ transform: `scale(${zoom})`, transformOrigin: 'top center' }}\n />\n ))\n ) : (\n <TextViewer url={primary.url} />\n )}\n </div>\n </div>\n );\n}\n","import { useEffect, useState } from 'react';\n\nexport interface TextViewerProps {\n url: string;\n}\n\nexport function TextViewer({ url }: TextViewerProps) {\n const [state, setState] = useState<{ status: 'loading' | 'loaded' | 'error'; text: string }>({\n status: 'loading',\n text: '',\n });\n\n useEffect(() => {\n let cancelled = false;\n setState({ status: 'loading', text: '' });\n (async () => {\n try {\n const res = await fetch(url);\n if (!res.ok) throw new Error(`fetch ${res.status}`);\n const text = await res.text();\n if (text.trimStart().startsWith('%PDF')) throw new Error('binary');\n if (!cancelled) setState({ status: 'loaded', text });\n } catch {\n if (!cancelled) setState({ status: 'error', text: '' });\n }\n })();\n return () => {\n cancelled = true;\n };\n }, [url]);\n\n if (state.status === 'loading') {\n return <div className=\"mr-doc-viewer__status\" style={{ color: 'var(--mr-muted-foreground)' }}>Loading document...</div>;\n }\n if (state.status === 'error') {\n return <div className=\"mr-doc-viewer__status\" style={{ color: 'var(--mr-muted-foreground)' }}>Couldn't load this document.</div>;\n }\n if (!state.text.trim()) {\n return <div className=\"mr-doc-viewer__status\" style={{ color: 'var(--mr-muted-foreground)' }}>This record appears to be empty.</div>;\n }\n return (\n <iframe\n className=\"mr-doc-viewer__html-frame\"\n srcDoc={state.text}\n sandbox=\"allow-same-origin\"\n title=\"HTML preview\"\n />\n );\n}\n"]}
@@ -23,7 +23,7 @@ var _lucidereact = require('lucide-react');
23
23
  var _zustand = require('zustand');
24
24
  var _jsxruntime = require('react/jsx-runtime');
25
25
  var DocumentViewer = _react.lazy.call(void 0,
26
- () => Promise.resolve().then(() => _interopRequireWildcard(require("./DocumentViewer-D7OJVVZM.js"))).then((m) => ({
26
+ () => Promise.resolve().then(() => _interopRequireWildcard(require("./DocumentViewer-Y3SQ2BMS.js"))).then((m) => ({
27
27
  default: m.DocumentViewer
28
28
  }))
29
29
  );
@@ -607,4 +607,4 @@ function VitalReadingsExpanded({ vitalName, ekaId }) {
607
607
 
608
608
 
609
609
  exports.OriginalRecordPanel = OriginalRecordPanel;
610
- //# sourceMappingURL=OriginalRecordPanel-UHNIWIT3.js.map
610
+ //# sourceMappingURL=OriginalRecordPanel-SJIBGHYZ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/medical-records-ui/medical-records-ui/dist/OriginalRecordPanel-UHNIWIT3.js","../src/views/components/shared/OriginalRecordPanel.tsx"],"names":[],"mappings":"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","file":"/home/runner/work/medical-records-ui/medical-records-ui/dist/OriginalRecordPanel-UHNIWIT3.js","sourcesContent":[null,"\"use client\";\n\nimport { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { ChevronLeft, ChevronRight, ChevronDown, ExternalLink, Check, File, LoaderCircle } from \"lucide-react\";\nimport { useStore } from \"zustand\";\nimport { useSdk } from \"../../../connection/useSdk\";\nimport { useRecordPreview } from \"../../../connection/useRecordPreview\";\nimport { useRecordPanelTable } from \"../../hooks/care-panel/useRecordPanelTable\";\nimport { useParameterTrend } from \"../../hooks/care-panel/useParameterTrend\";\nimport { epochToDisplay } from \"../../../helpers/formatters\";\nimport type { ParameterTrendResponse } from \"@eka-care/medical-records-ts-sdk\";\nimport { useSmartRecordsActions } from \"../../context/SmartRecordsActionsContext\";\nimport { IconButton, Typography, SearchInput, Tooltip } from \"widgets\";\nimport type { RecordItem } from \"../../../helpers\";\nimport type { PanelRow } from \"../../../helpers/vitalsPanel\";\nimport SmartSparkles from \"icons/SmartSparkles\";\n\nconst DocumentViewer = lazy(() =>\n import(\"../../RecordPreview/DocumentViewer\").then((m) => ({\n default: m.DocumentViewer,\n })),\n);\n\ninterface OriginalRecordPanelProps {\n recordId: string;\n caseId?: string;\n onBack: () => void;\n}\n\ntype SrTab = \"all\" | \"oor\" | \"none\";\n\nfunction buildVitalsText(rows: PanelRow[]): string {\n return rows\n .map((r) => {\n const cell = r.cells[0];\n const ref = cell?.refRange ? ` (Ref: ${cell.refRange})` : \"\";\n return `${r.name}: ${cell?.value ?? \"—\"} ${r.unit}${ref}`;\n })\n .join(\"\\n\");\n}\n\nexport function OriginalRecordPanel({\n recordId,\n caseId,\n onBack,\n}: OriginalRecordPanelProps) {\n const { store } = useSdk();\n const record = useStore(store, (s) => s.records.byId[recordId]);\n const recordsById = useStore(store, (s) => s.records.byId);\n const preview = useRecordPreview(recordId);\n const { replaceOriginal, onCopyToNote, onCopyVitalsToNote, hasCopyToNote, onToast } =\n useSmartRecordsActions();\n\n // Smart report vitals (only fetched when record.isSmart)\n const { status: vitalsStatus, table } = useRecordPanelTable(\n record?.isSmart ? recordId : null,\n );\n const [smartPanelOpen, setSmartPanelOpen] = useState(true);\n const [srSearch, setSrSearch] = useState(\"\");\n const [srTab, setSrTab] = useState<SrTab>(\"all\");\n const [expandedVital, setExpandedVital] = useState<string | null>(null);\n const toggleExpand = useCallback((key: string) => {\n setExpandedVital((prev) => (prev === key ? null : key));\n }, []);\n const [srSelected, setSrSelected] = useState<Set<string>>(new Set());\n const srCopyBtnRef = useRef<HTMLButtonElement>(null);\n\n // Dropdown state\n const [dropdownOpen, setDropdownOpen] = useState(false);\n const dropdownRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!dropdownOpen) return;\n const handler = (e: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {\n setDropdownOpen(false);\n }\n };\n document.addEventListener(\"mousedown\", handler);\n return () => document.removeEventListener(\"mousedown\", handler);\n }, [dropdownOpen]);\n\n const caseRecords = useMemo<RecordItem[]>(() => {\n if (!caseId) return [];\n return Object.values(recordsById)\n .filter(\n (r): r is RecordItem =>\n r != null &&\n (caseId === \"__ungrouped__\" ? r.cases.length === 0 : r.cases.includes(caseId)),\n )\n .sort((a, b) => b.createdAtEpoch - a.createdAtEpoch);\n }, [recordsById, caseId]);\n\n const currentIndex = caseRecords.findIndex((r) => r.id === recordId);\n const total = caseRecords.length;\n const isUngrouped = caseId === \"__ungrouped__\";\n\n const goTo = useCallback(\n (r: RecordItem) => {\n setDropdownOpen(false);\n replaceOriginal(r.id, caseId);\n },\n [replaceOriginal, caseId],\n );\n\n const goPrev = useCallback(() => {\n if (currentIndex > 0) goTo(caseRecords[currentIndex - 1]);\n }, [currentIndex, caseRecords, goTo]);\n\n const goNext = useCallback(() => {\n if (currentIndex < total - 1) goTo(caseRecords[currentIndex + 1]);\n }, [currentIndex, total, caseRecords, goTo]);\n\n const showNavPicker = caseId && total > 0;\n\n // Smart report vitals filtering\n const allRows = table.rows;\n const oorRows = useMemo(\n () => allRows.filter((r) => r.latestStatus === \"high\" || r.latestStatus === \"low\"),\n [allRows],\n );\n const noInterpRows = useMemo(\n () => allRows.filter((r) => r.latestStatus === \"unknown\"),\n [allRows],\n );\n\n const baseRows = srTab === \"oor\" ? oorRows : srTab === \"none\" ? noInterpRows : allRows;\n\n const filteredRows = useMemo(() => {\n if (!srSearch) return baseRows;\n const q = srSearch.toLowerCase();\n return baseRows.filter((r) => r.name.toLowerCase().includes(q));\n }, [baseRows, srSearch]);\n\n const tabCounts = useMemo(() => {\n if (!srSearch) return { all: allRows.length, oor: oorRows.length, none: noInterpRows.length };\n const q = srSearch.toLowerCase();\n return {\n all: allRows.filter((r) => r.name.toLowerCase().includes(q)).length,\n oor: oorRows.filter((r) => r.name.toLowerCase().includes(q)).length,\n none: noInterpRows.filter((r) => r.name.toLowerCase().includes(q)).length,\n };\n }, [srSearch, allRows, oorRows, noInterpRows]);\n\n useEffect(() => {\n setSrSelected(new Set());\n }, [srTab, srSearch]);\n\n useEffect(() => {\n if (srTab === \"oor\" && oorRows.length === 0) setSrTab(\"all\");\n if (srTab === \"none\" && noInterpRows.length === 0) setSrTab(\"all\");\n }, [srTab, oorRows.length, noInterpRows.length]);\n\n const srAllSelected =\n filteredRows.length > 0 && filteredRows.every((r) => srSelected.has(r.key));\n const srSomeSelected = srSelected.size > 0;\n\n const toggleSrRow = useCallback((key: string) => {\n setSrSelected((prev) => {\n const next = new Set(prev);\n next.has(key) ? next.delete(key) : next.add(key);\n return next;\n });\n }, []);\n\n const toggleSrAll = useCallback(() => {\n setSrSelected(\n srAllSelected ? new Set() : new Set(filteredRows.map((r) => r.key)),\n );\n }, [srAllSelected, filteredRows]);\n\n const handleSrCopyToNote = useCallback(() => {\n const selectedRows = filteredRows.filter((r) => srSelected.has(r.key));\n if (!selectedRows.length) return;\n const text = buildVitalsText(selectedRows);\n if (onCopyVitalsToNote) {\n void onCopyVitalsToNote(text);\n } else if (onCopyToNote) {\n const rect = srCopyBtnRef.current?.getBoundingClientRect();\n void onCopyToNote(\n text,\n rect ? { x: rect.left + rect.width / 2, y: rect.bottom } : undefined,\n );\n }\n }, [filteredRows, srSelected, onCopyVitalsToNote, onCopyToNote]);\n\n const htmlCopyBtnRef = useRef<HTMLButtonElement>(null);\n const [isHtmlCopying, setIsHtmlCopying] = useState(false);\n\n const handleHtmlCopyToNote = useCallback(async () => {\n const url = preview.files[0]?.url;\n if (!url) return;\n setIsHtmlCopying(true);\n try {\n const res = await fetch(url);\n const html = await res.text();\n const doc = new DOMParser().parseFromString(html, \"text/html\");\n const text = doc.body.textContent?.trim() ?? \"\";\n if (!text) return;\n if (onCopyVitalsToNote) {\n void onCopyVitalsToNote(text);\n } else if (onCopyToNote) {\n const rect = htmlCopyBtnRef.current?.getBoundingClientRect();\n void onCopyToNote(\n text,\n rect ? { x: rect.left, y: rect.bottom } : undefined,\n );\n }\n } catch {\n onToast?.(\"Failed to copy document text\", \"error\");\n } finally {\n setIsHtmlCopying(false);\n }\n }, [preview.files, onCopyVitalsToNote, onCopyToNote, onToast]);\n\n const isSmart = record?.isSmart ?? false;\n const hasPreview = preview.status === \"loaded\" && preview.files.length > 0;\n\n return (\n <div className=\"mr-sv__panel\">\n <div className=\"mr-sv__panel-header\">\n <button type=\"button\" className=\"mr-sv__back-btn\" onClick={onBack}>\n <ChevronLeft size={16} aria-hidden />\n Back\n </button>\n\n {showNavPicker ? (\n <div className=\"mr-orig__nav-wrap\" ref={dropdownRef}>\n <div\n className={`mr-orig__nav-pill${dropdownOpen ? \" mr-orig__nav-pill--open\" : \"\"}`}\n role=\"button\"\n tabIndex={0}\n onClick={() => setDropdownOpen((v) => !v)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n setDropdownOpen((v) => !v);\n }\n }}\n >\n <button\n type=\"button\"\n className=\"mr-orig__nav-chevron\"\n onClick={(e) => { e.stopPropagation(); goPrev(); }}\n aria-label=\"Previous record\"\n disabled={currentIndex <= 0}\n >\n <ChevronLeft size={14} aria-hidden />\n </button>\n <div className=\"mr-orig__nav-text\">\n <span className=\"mr-orig__nav-title\">{record?.title ?? \"Record\"}</span>\n <span className=\"mr-orig__nav-counter\">\n {currentIndex + 1} of {total}\n </span>\n </div>\n <button\n type=\"button\"\n className=\"mr-orig__nav-chevron\"\n onClick={(e) => { e.stopPropagation(); goNext(); }}\n aria-label=\"Next record\"\n disabled={currentIndex >= total - 1}\n >\n <ChevronRight size={14} aria-hidden />\n </button>\n </div>\n\n {dropdownOpen && (\n <div className=\"mr-orig__dropdown\">\n <div className=\"mr-orig__dropdown-eyebrow\">\n {total} RECORD{total !== 1 ? \"S\" : \"\"}{\" \"}\n {isUngrouped ? \"WITH NO CARE CONTEXT\" : \"FOUND IN THIS ENCOUNTER\"}\n </div>\n <div className=\"mr-orig__dropdown-list\">\n {caseRecords.map((r) => {\n const isActive = r.id === recordId;\n return (\n <button\n key={r.id}\n type=\"button\"\n className={`mr-orig__dropdown-item${isActive ? \" mr-orig__dropdown-item--active\" : \"\"}`}\n onClick={() => goTo(r)}\n >\n <div className=\"mr-orig__dropdown-icon\">\n <File size={16} aria-hidden />\n {r.isSmart && (\n <SmartSparkles\n className=\"mr-orig__dropdown-smart-dot\"\n aria-label=\"Smart report\"\n />\n )}\n </div>\n <div className=\"mr-orig__dropdown-info\">\n <div className=\"mr-orig__dropdown-title-row\">\n <span className=\"mr-orig__dropdown-title\">{r.title}</span>\n {r.isSmart && (\n <span className=\"mr-orig__dropdown-smart-badge\">\n <SmartSparkles aria-hidden />\n Smart Report\n </span>\n )}\n {r.fileType?.toUpperCase() === \"HTML\" && (\n <span className=\"mr-tag-chip mr-tag-chip--html\">\n <span className=\"mr-tag-chip__label\">HTML</span>\n </span>\n )}\n </div>\n <div className=\"mr-orig__dropdown-meta\">\n <span>{epochToDisplay(r.createdAtEpoch)}</span>\n {r.isSmart && r.totalVitals > 0 && (\n <>\n <span className=\"mr-orig__dropdown-dot\">·</span>\n <span>{r.totalVitals} vitals</span>\n </>\n )}\n {r.isSmart && r.outOfRangeVitals > 0 && (\n <>\n <span className=\"mr-orig__dropdown-dot\">·</span>\n <span className=\"mr-orig__dropdown-oor\">\n {r.outOfRangeVitals} out of range\n </span>\n </>\n )}\n </div>\n </div>\n {isActive && (\n <Check\n size={14}\n className=\"mr-orig__dropdown-check\"\n aria-hidden\n />\n )}\n </button>\n );\n })}\n </div>\n </div>\n )}\n </div>\n ) : record ? (\n <div className=\"mr-sv__panel-info\">\n <Typography variant=\"label\" as=\"span\" className=\"mr-sv__panel-name\">\n {record.title}\n </Typography>\n <Typography variant=\"hint\" as=\"span\" className=\"mr-sv__panel-meta-text\">\n {epochToDisplay(record.createdAtEpoch)}\n </Typography>\n </div>\n ) : null}\n\n <div className=\"mr-orig__header-actions\">\n {preview.isHtml && hasCopyToNote && (\n <button\n ref={htmlCopyBtnRef}\n type=\"button\"\n className=\"mr-orig__html-copy-btn\"\n onClick={handleHtmlCopyToNote}\n disabled={isHtmlCopying}\n >\n {isHtmlCopying && <LoaderCircle size={12} className=\"mr-sv__loading-spinner\" aria-hidden />}\n Copy to note\n </button>\n )}\n {hasPreview && (\n <IconButton\n icon={<ExternalLink size={16} aria-hidden />}\n label=\"Open in new tab\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() =>\n window.open(preview.files[0].url, \"_blank\", \"noopener,noreferrer\")\n }\n />\n )}\n </div>\n </div>\n\n <div\n className=\"mr-sv__panel-body\"\n style={{ flex: 1, display: \"flex\", flexDirection: \"column\", overflow: \"hidden\" }}\n >\n {preview.status === \"loading\" && (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n height: \"100%\",\n }}\n >\n <LoaderCircle size={44} className=\"mr-sv__loading-spinner\" aria-hidden />\n </div>\n )}\n {preview.status === \"error\" && (\n <Typography variant=\"muted\" className=\"mr-sv__panel-empty\">\n Failed to load document preview.\n </Typography>\n )}\n {preview.status === \"loaded\" && preview.files.length === 0 && (\n <Typography variant=\"muted\" className=\"mr-sv__panel-empty\">\n No preview available for this record.\n </Typography>\n )}\n\n {/* Non-smart: full-width PDF */}\n {hasPreview && !isSmart && (\n <div\n className=\"mr-record-preview__body\"\n style={{ flex: 1, display: \"flex\", overflow: \"hidden\" }}\n >\n <Suspense fallback={null}>\n <DocumentViewer\n files={preview.files}\n title={record?.title ?? \"\"}\n isHtml={preview.isHtml}\n />\n </Suspense>\n </div>\n )}\n\n {/* Smart: split view — PDF left, smart report panel right */}\n {hasPreview && isSmart && (\n <div className=\"mr-orig__split\">\n <div className=\"mr-orig__pdf-side\">\n <Suspense fallback={null}>\n <DocumentViewer\n files={preview.files}\n title={record?.title ?? \"\"}\n isHtml={preview.isHtml}\n />\n </Suspense>\n </div>\n\n {smartPanelOpen ? (\n <div className=\"mr-orig__smart-side\">\n {/* Header */}\n <div className=\"mr-orig__smart-hdr\">\n <span className=\"mr-orig__smart-title-row\">\n <span className=\"mr-orig__smart-title\">Smart Report</span>\n <SmartSparkles className=\"mr-orig__smart-title-icon\" aria-hidden />\n </span>\n <button\n type=\"button\"\n className=\"mr-sv__sidebar-collapse-btn mr-orig__smart-expanded-btn\"\n aria-label=\"Collapse smart report panel\"\n aria-expanded={true}\n onClick={() => setSmartPanelOpen(false)}\n >\n <ChevronRight size={14} aria-hidden />\n </button>\n </div>\n\n {/* Search */}\n <SearchInput\n className=\"mr-orig__smart-search\"\n iconSize={16}\n placeholder=\"Search vitals\"\n value={srSearch}\n onChange={setSrSearch}\n />\n\n {/* Filter tabs — only when at least one sub-category has data */}\n {(oorRows.length > 0 || noInterpRows.length > 0) && (\n <div className=\"mr-orig__smart-tabs-wrap\">\n <div className=\"mr-orig__smart-tabs\">\n {([\"all\", \"oor\", \"none\"] as SrTab[]).map((tab) => {\n if (tab === \"oor\" && oorRows.length === 0) return null;\n if (tab === \"none\" && noInterpRows.length === 0) return null;\n const count =\n tab === \"all\"\n ? tabCounts.all\n : tab === \"oor\"\n ? tabCounts.oor\n : tabCounts.none;\n const label =\n tab === \"all\"\n ? \"All\"\n : tab === \"oor\"\n ? \"Out of range\"\n : \"No interpretation\";\n return (\n <button\n key={tab}\n type=\"button\"\n className={`mr-orig__smart-tab${srTab === tab ? \" mr-orig__smart-tab--active\" : \"\"}`}\n onClick={() => setSrTab(tab)}\n >\n {label} ({count})\n </button>\n );\n })}\n </div>\n </div>\n )}\n\n {/* Vital list */}\n <div className=\"mr-orig__smart-list\">\n {vitalsStatus === \"loading\" && (\n <div className=\"mr-orig__smart-loading\">\n <LoaderCircle\n size={20}\n className=\"mr-sv__loading-spinner\"\n aria-hidden\n />\n </div>\n )}\n {vitalsStatus === \"loaded\" && filteredRows.length === 0 && (\n <Typography variant=\"muted\" className=\"mr-orig__smart-empty\">\n No vitals match.\n </Typography>\n )}\n {vitalsStatus === \"loaded\" &&\n filteredRows.map((row, idx) => {\n const cell = row.cells[0];\n const isOor =\n row.latestStatus === \"high\" || row.latestStatus === \"low\";\n const isSelected = srSelected.has(row.key);\n const isLast = idx === filteredRows.length - 1;\n return (\n <div\n key={row.key}\n className={`mr-orig__vital-item${isLast ? \" mr-orig__vital-item--last\" : \"\"}`}\n >\n <div\n className={`mr-orig__vital-row mr-orig__vital-row--selectable${isSelected ? \" mr-orig__vital-row--selected\" : \"\"}`}\n onClick={() => toggleSrRow(row.key)}\n >\n <span\n className={`mr-sv__checkbox mr-cc-table__row-check${isSelected ? \" mr-sv__checkbox--checked\" : \"\"}`}\n role=\"checkbox\"\n aria-checked={isSelected}\n tabIndex={0}\n onClick={(e) => {\n e.stopPropagation();\n toggleSrRow(row.key);\n }}\n onKeyDown={(e) => {\n if (e.key === \" \" || e.key === \"Enter\") {\n e.preventDefault();\n e.stopPropagation();\n toggleSrRow(row.key);\n }\n }}\n >\n {isSelected && <Check size={10} strokeWidth={3} />}\n </span>\n <div className=\"mr-orig__vital-info\">\n <span className=\"mr-orig__vital-name\">{row.name}</span>\n {row.refRange && (\n <span className=\"mr-orig__vital-ref\">\n Ref {row.refRange}\n </span>\n )}\n </div>\n {cell && (\n <div className=\"mr-orig__vital-value\">\n <span\n className={`mr-orig__vital-val${isOor ? \" mr-orig__vital-val--oor\" : \"\"}`}\n >\n {cell.value}\n </span>\n {cell.unit && (\n <span className=\"mr-orig__vital-unit\">{cell.unit}</span>\n )}\n </div>\n )}\n {row.ekaId && (\n <button\n type=\"button\"\n className={`mr-orig__vital-expand-btn${expandedVital === row.key ? \" mr-orig__vital-expand-btn--open\" : \"\"}`}\n onClick={(e) => { e.stopPropagation(); toggleExpand(row.key); }}\n aria-label={expandedVital === row.key ? \"Collapse readings\" : \"Show latest readings\"}\n >\n <ChevronDown size={13} aria-hidden />\n </button>\n )}\n </div>\n {expandedVital === row.key && row.ekaId && (\n <VitalReadingsExpanded vitalName={row.name} ekaId={row.ekaId} />\n )}\n </div>\n );\n })}\n </div>\n\n {/* Footer */}\n <div className=\"mr-cc-table__footer\">\n <span\n className={`mr-sv__checkbox mr-cc-table__footer-check${srAllSelected ? \" mr-sv__checkbox--checked\" : \"\"}`}\n role=\"checkbox\"\n aria-checked={srAllSelected}\n tabIndex={0}\n onClick={toggleSrAll}\n onKeyDown={(e) => {\n if (e.key === \" \" || e.key === \"Enter\") {\n e.preventDefault();\n toggleSrAll();\n }\n }}\n >\n {srAllSelected && <Check size={10} strokeWidth={3} />}\n </span>\n <span className=\"mr-cc-table__footer-label\" onClick={toggleSrAll}>Select all</span>\n <span className=\"mr-cc-table__footer-count\">\n {srSelected.size} of {filteredRows.length} selected\n </span>\n <button\n ref={srCopyBtnRef}\n type=\"button\"\n className=\"mr-cc-table__copy-note-btn\"\n disabled={!srSomeSelected || !hasCopyToNote}\n onClick={handleSrCopyToNote}\n >\n Copy to note\n </button>\n </div>\n </div>\n ) : (\n <div className=\"mr-orig__smart-side mr-orig__smart-side--collapsed\">\n <Tooltip content=\"Show smart report\">\n <button\n type=\"button\"\n className=\"mr-sv__sidebar-collapse-btn mr-orig__smart-collapsed-btn\"\n aria-label=\"Expand smart report panel\"\n aria-expanded={false}\n onClick={() => setSmartPanelOpen(true)}\n >\n <span className=\"mr-orig__smart-collapsed-icon-wrap\">\n <SmartSparkles className=\"mr-orig__smart-collapsed-icon\" aria-hidden />\n </span>\n </button>\n </Tooltip>\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n );\n}\n\nfunction VitalReadingsExpanded({ vitalName, ekaId }: { vitalName: string; ekaId: string }) {\n const { data: trendData, status } = useParameterTrend(ekaId);\n\n const readings = useMemo(() => {\n const trend = trendData as ParameterTrendResponse | null;\n if (!trend?.vitals_info?.length) return [];\n return [...trend.vitals_info]\n .sort((a, b) => (b.date ?? 0) - (a.date ?? 0))\n .slice(0, 3)\n .map((info) => {\n const dr = (info.display_result ?? \"\").toLowerCase();\n const isOor = dr.includes(\"high\") || dr.includes(\"above\") || dr.includes(\"abnormal_high\") ||\n dr.includes(\"low\") || dr.includes(\"below\") || dr.includes(\"abnormal_low\");\n return {\n value: info.val != null ? String(info.val) : ((info as any).val_string ?? \"—\"),\n date: info.display_time || \"\",\n isOor,\n };\n });\n }, [trendData]);\n\n if (status === \"loading\") {\n return (\n <div className=\"mr-orig__vital-readings mr-orig__vital-readings--loading\">\n <LoaderCircle size={16} className=\"mr-sv__loading-spinner\" aria-hidden />\n </div>\n );\n }\n\n if (status === \"error\" || !readings.length) {\n return (\n <div className=\"mr-orig__vital-readings\">\n <Typography variant=\"muted\" className=\"mr-orig__vital-readings-empty\">\n No readings available\n </Typography>\n </div>\n );\n }\n\n return (\n <div className=\"mr-orig__vital-readings\">\n <div className=\"mr-orig__vital-readings-title\">\n LATEST READINGS FOR {vitalName.toUpperCase()}\n </div>\n <div className=\"mr-orig__vital-readings-grid\">\n {readings.map((r, i) => (\n <div key={i} className=\"mr-orig__vital-reading-item\">\n <span className={`mr-orig__vital-reading-value${r.isOor ? \" mr-orig__vital-reading-value--oor\" : \"\"}`}>{r.value}</span>\n <span className=\"mr-orig__vital-reading-date\">{r.date}</span>\n </div>\n ))}\n </div>\n </div>\n );\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/medical-records-ui/medical-records-ui/dist/OriginalRecordPanel-SJIBGHYZ.js","../src/views/components/shared/OriginalRecordPanel.tsx"],"names":[],"mappings":"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","file":"/home/runner/work/medical-records-ui/medical-records-ui/dist/OriginalRecordPanel-SJIBGHYZ.js","sourcesContent":[null,"\"use client\";\n\nimport { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { ChevronLeft, ChevronRight, ChevronDown, ExternalLink, Check, File, LoaderCircle } from \"lucide-react\";\nimport { useStore } from \"zustand\";\nimport { useSdk } from \"../../../connection/useSdk\";\nimport { useRecordPreview } from \"../../../connection/useRecordPreview\";\nimport { useRecordPanelTable } from \"../../hooks/care-panel/useRecordPanelTable\";\nimport { useParameterTrend } from \"../../hooks/care-panel/useParameterTrend\";\nimport { epochToDisplay } from \"../../../helpers/formatters\";\nimport type { ParameterTrendResponse } from \"@eka-care/medical-records-ts-sdk\";\nimport { useSmartRecordsActions } from \"../../context/SmartRecordsActionsContext\";\nimport { IconButton, Typography, SearchInput, Tooltip } from \"widgets\";\nimport type { RecordItem } from \"../../../helpers\";\nimport type { PanelRow } from \"../../../helpers/vitalsPanel\";\nimport SmartSparkles from \"icons/SmartSparkles\";\n\nconst DocumentViewer = lazy(() =>\n import(\"../../RecordPreview/DocumentViewer\").then((m) => ({\n default: m.DocumentViewer,\n })),\n);\n\ninterface OriginalRecordPanelProps {\n recordId: string;\n caseId?: string;\n onBack: () => void;\n}\n\ntype SrTab = \"all\" | \"oor\" | \"none\";\n\nfunction buildVitalsText(rows: PanelRow[]): string {\n return rows\n .map((r) => {\n const cell = r.cells[0];\n const ref = cell?.refRange ? ` (Ref: ${cell.refRange})` : \"\";\n return `${r.name}: ${cell?.value ?? \"—\"} ${r.unit}${ref}`;\n })\n .join(\"\\n\");\n}\n\nexport function OriginalRecordPanel({\n recordId,\n caseId,\n onBack,\n}: OriginalRecordPanelProps) {\n const { store } = useSdk();\n const record = useStore(store, (s) => s.records.byId[recordId]);\n const recordsById = useStore(store, (s) => s.records.byId);\n const preview = useRecordPreview(recordId);\n const { replaceOriginal, onCopyToNote, onCopyVitalsToNote, hasCopyToNote, onToast } =\n useSmartRecordsActions();\n\n // Smart report vitals (only fetched when record.isSmart)\n const { status: vitalsStatus, table } = useRecordPanelTable(\n record?.isSmart ? recordId : null,\n );\n const [smartPanelOpen, setSmartPanelOpen] = useState(true);\n const [srSearch, setSrSearch] = useState(\"\");\n const [srTab, setSrTab] = useState<SrTab>(\"all\");\n const [expandedVital, setExpandedVital] = useState<string | null>(null);\n const toggleExpand = useCallback((key: string) => {\n setExpandedVital((prev) => (prev === key ? null : key));\n }, []);\n const [srSelected, setSrSelected] = useState<Set<string>>(new Set());\n const srCopyBtnRef = useRef<HTMLButtonElement>(null);\n\n // Dropdown state\n const [dropdownOpen, setDropdownOpen] = useState(false);\n const dropdownRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!dropdownOpen) return;\n const handler = (e: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {\n setDropdownOpen(false);\n }\n };\n document.addEventListener(\"mousedown\", handler);\n return () => document.removeEventListener(\"mousedown\", handler);\n }, [dropdownOpen]);\n\n const caseRecords = useMemo<RecordItem[]>(() => {\n if (!caseId) return [];\n return Object.values(recordsById)\n .filter(\n (r): r is RecordItem =>\n r != null &&\n (caseId === \"__ungrouped__\" ? r.cases.length === 0 : r.cases.includes(caseId)),\n )\n .sort((a, b) => b.createdAtEpoch - a.createdAtEpoch);\n }, [recordsById, caseId]);\n\n const currentIndex = caseRecords.findIndex((r) => r.id === recordId);\n const total = caseRecords.length;\n const isUngrouped = caseId === \"__ungrouped__\";\n\n const goTo = useCallback(\n (r: RecordItem) => {\n setDropdownOpen(false);\n replaceOriginal(r.id, caseId);\n },\n [replaceOriginal, caseId],\n );\n\n const goPrev = useCallback(() => {\n if (currentIndex > 0) goTo(caseRecords[currentIndex - 1]);\n }, [currentIndex, caseRecords, goTo]);\n\n const goNext = useCallback(() => {\n if (currentIndex < total - 1) goTo(caseRecords[currentIndex + 1]);\n }, [currentIndex, total, caseRecords, goTo]);\n\n const showNavPicker = caseId && total > 0;\n\n // Smart report vitals filtering\n const allRows = table.rows;\n const oorRows = useMemo(\n () => allRows.filter((r) => r.latestStatus === \"high\" || r.latestStatus === \"low\"),\n [allRows],\n );\n const noInterpRows = useMemo(\n () => allRows.filter((r) => r.latestStatus === \"unknown\"),\n [allRows],\n );\n\n const baseRows = srTab === \"oor\" ? oorRows : srTab === \"none\" ? noInterpRows : allRows;\n\n const filteredRows = useMemo(() => {\n if (!srSearch) return baseRows;\n const q = srSearch.toLowerCase();\n return baseRows.filter((r) => r.name.toLowerCase().includes(q));\n }, [baseRows, srSearch]);\n\n const tabCounts = useMemo(() => {\n if (!srSearch) return { all: allRows.length, oor: oorRows.length, none: noInterpRows.length };\n const q = srSearch.toLowerCase();\n return {\n all: allRows.filter((r) => r.name.toLowerCase().includes(q)).length,\n oor: oorRows.filter((r) => r.name.toLowerCase().includes(q)).length,\n none: noInterpRows.filter((r) => r.name.toLowerCase().includes(q)).length,\n };\n }, [srSearch, allRows, oorRows, noInterpRows]);\n\n useEffect(() => {\n setSrSelected(new Set());\n }, [srTab, srSearch]);\n\n useEffect(() => {\n if (srTab === \"oor\" && oorRows.length === 0) setSrTab(\"all\");\n if (srTab === \"none\" && noInterpRows.length === 0) setSrTab(\"all\");\n }, [srTab, oorRows.length, noInterpRows.length]);\n\n const srAllSelected =\n filteredRows.length > 0 && filteredRows.every((r) => srSelected.has(r.key));\n const srSomeSelected = srSelected.size > 0;\n\n const toggleSrRow = useCallback((key: string) => {\n setSrSelected((prev) => {\n const next = new Set(prev);\n next.has(key) ? next.delete(key) : next.add(key);\n return next;\n });\n }, []);\n\n const toggleSrAll = useCallback(() => {\n setSrSelected(\n srAllSelected ? new Set() : new Set(filteredRows.map((r) => r.key)),\n );\n }, [srAllSelected, filteredRows]);\n\n const handleSrCopyToNote = useCallback(() => {\n const selectedRows = filteredRows.filter((r) => srSelected.has(r.key));\n if (!selectedRows.length) return;\n const text = buildVitalsText(selectedRows);\n if (onCopyVitalsToNote) {\n void onCopyVitalsToNote(text);\n } else if (onCopyToNote) {\n const rect = srCopyBtnRef.current?.getBoundingClientRect();\n void onCopyToNote(\n text,\n rect ? { x: rect.left + rect.width / 2, y: rect.bottom } : undefined,\n );\n }\n }, [filteredRows, srSelected, onCopyVitalsToNote, onCopyToNote]);\n\n const htmlCopyBtnRef = useRef<HTMLButtonElement>(null);\n const [isHtmlCopying, setIsHtmlCopying] = useState(false);\n\n const handleHtmlCopyToNote = useCallback(async () => {\n const url = preview.files[0]?.url;\n if (!url) return;\n setIsHtmlCopying(true);\n try {\n const res = await fetch(url);\n const html = await res.text();\n const doc = new DOMParser().parseFromString(html, \"text/html\");\n const text = doc.body.textContent?.trim() ?? \"\";\n if (!text) return;\n if (onCopyVitalsToNote) {\n void onCopyVitalsToNote(text);\n } else if (onCopyToNote) {\n const rect = htmlCopyBtnRef.current?.getBoundingClientRect();\n void onCopyToNote(\n text,\n rect ? { x: rect.left, y: rect.bottom } : undefined,\n );\n }\n } catch {\n onToast?.(\"Failed to copy document text\", \"error\");\n } finally {\n setIsHtmlCopying(false);\n }\n }, [preview.files, onCopyVitalsToNote, onCopyToNote, onToast]);\n\n const isSmart = record?.isSmart ?? false;\n const hasPreview = preview.status === \"loaded\" && preview.files.length > 0;\n\n return (\n <div className=\"mr-sv__panel\">\n <div className=\"mr-sv__panel-header\">\n <button type=\"button\" className=\"mr-sv__back-btn\" onClick={onBack}>\n <ChevronLeft size={16} aria-hidden />\n Back\n </button>\n\n {showNavPicker ? (\n <div className=\"mr-orig__nav-wrap\" ref={dropdownRef}>\n <div\n className={`mr-orig__nav-pill${dropdownOpen ? \" mr-orig__nav-pill--open\" : \"\"}`}\n role=\"button\"\n tabIndex={0}\n onClick={() => setDropdownOpen((v) => !v)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n setDropdownOpen((v) => !v);\n }\n }}\n >\n <button\n type=\"button\"\n className=\"mr-orig__nav-chevron\"\n onClick={(e) => { e.stopPropagation(); goPrev(); }}\n aria-label=\"Previous record\"\n disabled={currentIndex <= 0}\n >\n <ChevronLeft size={14} aria-hidden />\n </button>\n <div className=\"mr-orig__nav-text\">\n <span className=\"mr-orig__nav-title\">{record?.title ?? \"Record\"}</span>\n <span className=\"mr-orig__nav-counter\">\n {currentIndex + 1} of {total}\n </span>\n </div>\n <button\n type=\"button\"\n className=\"mr-orig__nav-chevron\"\n onClick={(e) => { e.stopPropagation(); goNext(); }}\n aria-label=\"Next record\"\n disabled={currentIndex >= total - 1}\n >\n <ChevronRight size={14} aria-hidden />\n </button>\n </div>\n\n {dropdownOpen && (\n <div className=\"mr-orig__dropdown\">\n <div className=\"mr-orig__dropdown-eyebrow\">\n {total} RECORD{total !== 1 ? \"S\" : \"\"}{\" \"}\n {isUngrouped ? \"WITH NO CARE CONTEXT\" : \"FOUND IN THIS ENCOUNTER\"}\n </div>\n <div className=\"mr-orig__dropdown-list\">\n {caseRecords.map((r) => {\n const isActive = r.id === recordId;\n return (\n <button\n key={r.id}\n type=\"button\"\n className={`mr-orig__dropdown-item${isActive ? \" mr-orig__dropdown-item--active\" : \"\"}`}\n onClick={() => goTo(r)}\n >\n <div className=\"mr-orig__dropdown-icon\">\n <File size={16} aria-hidden />\n {r.isSmart && (\n <SmartSparkles\n className=\"mr-orig__dropdown-smart-dot\"\n aria-label=\"Smart report\"\n />\n )}\n </div>\n <div className=\"mr-orig__dropdown-info\">\n <div className=\"mr-orig__dropdown-title-row\">\n <span className=\"mr-orig__dropdown-title\">{r.title}</span>\n {r.isSmart && (\n <span className=\"mr-orig__dropdown-smart-badge\">\n <SmartSparkles aria-hidden />\n Smart Report\n </span>\n )}\n {r.fileType?.toUpperCase() === \"HTML\" && (\n <span className=\"mr-tag-chip mr-tag-chip--html\">\n <span className=\"mr-tag-chip__label\">HTML</span>\n </span>\n )}\n </div>\n <div className=\"mr-orig__dropdown-meta\">\n <span>{epochToDisplay(r.createdAtEpoch)}</span>\n {r.isSmart && r.totalVitals > 0 && (\n <>\n <span className=\"mr-orig__dropdown-dot\">·</span>\n <span>{r.totalVitals} vitals</span>\n </>\n )}\n {r.isSmart && r.outOfRangeVitals > 0 && (\n <>\n <span className=\"mr-orig__dropdown-dot\">·</span>\n <span className=\"mr-orig__dropdown-oor\">\n {r.outOfRangeVitals} out of range\n </span>\n </>\n )}\n </div>\n </div>\n {isActive && (\n <Check\n size={14}\n className=\"mr-orig__dropdown-check\"\n aria-hidden\n />\n )}\n </button>\n );\n })}\n </div>\n </div>\n )}\n </div>\n ) : record ? (\n <div className=\"mr-sv__panel-info\">\n <Typography variant=\"label\" as=\"span\" className=\"mr-sv__panel-name\">\n {record.title}\n </Typography>\n <Typography variant=\"hint\" as=\"span\" className=\"mr-sv__panel-meta-text\">\n {epochToDisplay(record.createdAtEpoch)}\n </Typography>\n </div>\n ) : null}\n\n <div className=\"mr-orig__header-actions\">\n {preview.isHtml && hasCopyToNote && (\n <button\n ref={htmlCopyBtnRef}\n type=\"button\"\n className=\"mr-orig__html-copy-btn\"\n onClick={handleHtmlCopyToNote}\n disabled={isHtmlCopying}\n >\n {isHtmlCopying && <LoaderCircle size={12} className=\"mr-sv__loading-spinner\" aria-hidden />}\n Copy to note\n </button>\n )}\n {hasPreview && (\n <IconButton\n icon={<ExternalLink size={16} aria-hidden />}\n label=\"Open in new tab\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() =>\n window.open(preview.files[0].url, \"_blank\", \"noopener,noreferrer\")\n }\n />\n )}\n </div>\n </div>\n\n <div\n className=\"mr-sv__panel-body\"\n style={{ flex: 1, display: \"flex\", flexDirection: \"column\", overflow: \"hidden\" }}\n >\n {preview.status === \"loading\" && (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n height: \"100%\",\n }}\n >\n <LoaderCircle size={44} className=\"mr-sv__loading-spinner\" aria-hidden />\n </div>\n )}\n {preview.status === \"error\" && (\n <Typography variant=\"muted\" className=\"mr-sv__panel-empty\">\n Failed to load document preview.\n </Typography>\n )}\n {preview.status === \"loaded\" && preview.files.length === 0 && (\n <Typography variant=\"muted\" className=\"mr-sv__panel-empty\">\n No preview available for this record.\n </Typography>\n )}\n\n {/* Non-smart: full-width PDF */}\n {hasPreview && !isSmart && (\n <div\n className=\"mr-record-preview__body\"\n style={{ flex: 1, display: \"flex\", overflow: \"hidden\" }}\n >\n <Suspense fallback={null}>\n <DocumentViewer\n files={preview.files}\n title={record?.title ?? \"\"}\n isHtml={preview.isHtml}\n />\n </Suspense>\n </div>\n )}\n\n {/* Smart: split view — PDF left, smart report panel right */}\n {hasPreview && isSmart && (\n <div className=\"mr-orig__split\">\n <div className=\"mr-orig__pdf-side\">\n <Suspense fallback={null}>\n <DocumentViewer\n files={preview.files}\n title={record?.title ?? \"\"}\n isHtml={preview.isHtml}\n />\n </Suspense>\n </div>\n\n {smartPanelOpen ? (\n <div className=\"mr-orig__smart-side\">\n {/* Header */}\n <div className=\"mr-orig__smart-hdr\">\n <span className=\"mr-orig__smart-title-row\">\n <span className=\"mr-orig__smart-title\">Smart Report</span>\n <SmartSparkles className=\"mr-orig__smart-title-icon\" aria-hidden />\n </span>\n <button\n type=\"button\"\n className=\"mr-sv__sidebar-collapse-btn mr-orig__smart-expanded-btn\"\n aria-label=\"Collapse smart report panel\"\n aria-expanded={true}\n onClick={() => setSmartPanelOpen(false)}\n >\n <ChevronRight size={14} aria-hidden />\n </button>\n </div>\n\n {/* Search */}\n <SearchInput\n className=\"mr-orig__smart-search\"\n iconSize={16}\n placeholder=\"Search vitals\"\n value={srSearch}\n onChange={setSrSearch}\n />\n\n {/* Filter tabs — only when at least one sub-category has data */}\n {(oorRows.length > 0 || noInterpRows.length > 0) && (\n <div className=\"mr-orig__smart-tabs-wrap\">\n <div className=\"mr-orig__smart-tabs\">\n {([\"all\", \"oor\", \"none\"] as SrTab[]).map((tab) => {\n if (tab === \"oor\" && oorRows.length === 0) return null;\n if (tab === \"none\" && noInterpRows.length === 0) return null;\n const count =\n tab === \"all\"\n ? tabCounts.all\n : tab === \"oor\"\n ? tabCounts.oor\n : tabCounts.none;\n const label =\n tab === \"all\"\n ? \"All\"\n : tab === \"oor\"\n ? \"Out of range\"\n : \"No interpretation\";\n return (\n <button\n key={tab}\n type=\"button\"\n className={`mr-orig__smart-tab${srTab === tab ? \" mr-orig__smart-tab--active\" : \"\"}`}\n onClick={() => setSrTab(tab)}\n >\n {label} ({count})\n </button>\n );\n })}\n </div>\n </div>\n )}\n\n {/* Vital list */}\n <div className=\"mr-orig__smart-list\">\n {vitalsStatus === \"loading\" && (\n <div className=\"mr-orig__smart-loading\">\n <LoaderCircle\n size={20}\n className=\"mr-sv__loading-spinner\"\n aria-hidden\n />\n </div>\n )}\n {vitalsStatus === \"loaded\" && filteredRows.length === 0 && (\n <Typography variant=\"muted\" className=\"mr-orig__smart-empty\">\n No vitals match.\n </Typography>\n )}\n {vitalsStatus === \"loaded\" &&\n filteredRows.map((row, idx) => {\n const cell = row.cells[0];\n const isOor =\n row.latestStatus === \"high\" || row.latestStatus === \"low\";\n const isSelected = srSelected.has(row.key);\n const isLast = idx === filteredRows.length - 1;\n return (\n <div\n key={row.key}\n className={`mr-orig__vital-item${isLast ? \" mr-orig__vital-item--last\" : \"\"}`}\n >\n <div\n className={`mr-orig__vital-row mr-orig__vital-row--selectable${isSelected ? \" mr-orig__vital-row--selected\" : \"\"}`}\n onClick={() => toggleSrRow(row.key)}\n >\n <span\n className={`mr-sv__checkbox mr-cc-table__row-check${isSelected ? \" mr-sv__checkbox--checked\" : \"\"}`}\n role=\"checkbox\"\n aria-checked={isSelected}\n tabIndex={0}\n onClick={(e) => {\n e.stopPropagation();\n toggleSrRow(row.key);\n }}\n onKeyDown={(e) => {\n if (e.key === \" \" || e.key === \"Enter\") {\n e.preventDefault();\n e.stopPropagation();\n toggleSrRow(row.key);\n }\n }}\n >\n {isSelected && <Check size={10} strokeWidth={3} />}\n </span>\n <div className=\"mr-orig__vital-info\">\n <span className=\"mr-orig__vital-name\">{row.name}</span>\n {row.refRange && (\n <span className=\"mr-orig__vital-ref\">\n Ref {row.refRange}\n </span>\n )}\n </div>\n {cell && (\n <div className=\"mr-orig__vital-value\">\n <span\n className={`mr-orig__vital-val${isOor ? \" mr-orig__vital-val--oor\" : \"\"}`}\n >\n {cell.value}\n </span>\n {cell.unit && (\n <span className=\"mr-orig__vital-unit\">{cell.unit}</span>\n )}\n </div>\n )}\n {row.ekaId && (\n <button\n type=\"button\"\n className={`mr-orig__vital-expand-btn${expandedVital === row.key ? \" mr-orig__vital-expand-btn--open\" : \"\"}`}\n onClick={(e) => { e.stopPropagation(); toggleExpand(row.key); }}\n aria-label={expandedVital === row.key ? \"Collapse readings\" : \"Show latest readings\"}\n >\n <ChevronDown size={13} aria-hidden />\n </button>\n )}\n </div>\n {expandedVital === row.key && row.ekaId && (\n <VitalReadingsExpanded vitalName={row.name} ekaId={row.ekaId} />\n )}\n </div>\n );\n })}\n </div>\n\n {/* Footer */}\n <div className=\"mr-cc-table__footer\">\n <span\n className={`mr-sv__checkbox mr-cc-table__footer-check${srAllSelected ? \" mr-sv__checkbox--checked\" : \"\"}`}\n role=\"checkbox\"\n aria-checked={srAllSelected}\n tabIndex={0}\n onClick={toggleSrAll}\n onKeyDown={(e) => {\n if (e.key === \" \" || e.key === \"Enter\") {\n e.preventDefault();\n toggleSrAll();\n }\n }}\n >\n {srAllSelected && <Check size={10} strokeWidth={3} />}\n </span>\n <span className=\"mr-cc-table__footer-label\" onClick={toggleSrAll}>Select all</span>\n <span className=\"mr-cc-table__footer-count\">\n {srSelected.size} of {filteredRows.length} selected\n </span>\n <button\n ref={srCopyBtnRef}\n type=\"button\"\n className=\"mr-cc-table__copy-note-btn\"\n disabled={!srSomeSelected || !hasCopyToNote}\n onClick={handleSrCopyToNote}\n >\n Copy to note\n </button>\n </div>\n </div>\n ) : (\n <div className=\"mr-orig__smart-side mr-orig__smart-side--collapsed\">\n <Tooltip content=\"Show smart report\">\n <button\n type=\"button\"\n className=\"mr-sv__sidebar-collapse-btn mr-orig__smart-collapsed-btn\"\n aria-label=\"Expand smart report panel\"\n aria-expanded={false}\n onClick={() => setSmartPanelOpen(true)}\n >\n <span className=\"mr-orig__smart-collapsed-icon-wrap\">\n <SmartSparkles className=\"mr-orig__smart-collapsed-icon\" aria-hidden />\n </span>\n </button>\n </Tooltip>\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n );\n}\n\nfunction VitalReadingsExpanded({ vitalName, ekaId }: { vitalName: string; ekaId: string }) {\n const { data: trendData, status } = useParameterTrend(ekaId);\n\n const readings = useMemo(() => {\n const trend = trendData as ParameterTrendResponse | null;\n if (!trend?.vitals_info?.length) return [];\n return [...trend.vitals_info]\n .sort((a, b) => (b.date ?? 0) - (a.date ?? 0))\n .slice(0, 3)\n .map((info) => {\n const dr = (info.display_result ?? \"\").toLowerCase();\n const isOor = dr.includes(\"high\") || dr.includes(\"above\") || dr.includes(\"abnormal_high\") ||\n dr.includes(\"low\") || dr.includes(\"below\") || dr.includes(\"abnormal_low\");\n return {\n value: info.val != null ? String(info.val) : ((info as any).val_string ?? \"—\"),\n date: info.display_time || \"\",\n isOor,\n };\n });\n }, [trendData]);\n\n if (status === \"loading\") {\n return (\n <div className=\"mr-orig__vital-readings mr-orig__vital-readings--loading\">\n <LoaderCircle size={16} className=\"mr-sv__loading-spinner\" aria-hidden />\n </div>\n );\n }\n\n if (status === \"error\" || !readings.length) {\n return (\n <div className=\"mr-orig__vital-readings\">\n <Typography variant=\"muted\" className=\"mr-orig__vital-readings-empty\">\n No readings available\n </Typography>\n </div>\n );\n }\n\n return (\n <div className=\"mr-orig__vital-readings\">\n <div className=\"mr-orig__vital-readings-title\">\n LATEST READINGS FOR {vitalName.toUpperCase()}\n </div>\n <div className=\"mr-orig__vital-readings-grid\">\n {readings.map((r, i) => (\n <div key={i} className=\"mr-orig__vital-reading-item\">\n <span className={`mr-orig__vital-reading-value${r.isOor ? \" mr-orig__vital-reading-value--oor\" : \"\"}`}>{r.value}</span>\n <span className=\"mr-orig__vital-reading-date\">{r.date}</span>\n </div>\n ))}\n </div>\n </div>\n );\n}\n"]}
@@ -23,7 +23,7 @@ import { ChevronLeft, ChevronRight, ChevronDown, ExternalLink, Check, File, Load
23
23
  import { useStore } from "zustand";
24
24
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
25
25
  var DocumentViewer = lazy(
26
- () => import("./DocumentViewer-OM6D4CSI.mjs").then((m) => ({
26
+ () => import("./DocumentViewer-5AXKL75U.mjs").then((m) => ({
27
27
  default: m.DocumentViewer
28
28
  }))
29
29
  );
@@ -607,4 +607,4 @@ function VitalReadingsExpanded({ vitalName, ekaId }) {
607
607
  export {
608
608
  OriginalRecordPanel
609
609
  };
610
- //# sourceMappingURL=OriginalRecordPanel-XU6ZYBPB.mjs.map
610
+ //# sourceMappingURL=OriginalRecordPanel-UM74PRBO.mjs.map
@@ -33,6 +33,7 @@ function PdfViewer({ url, baseWidth, scale, scrollToPage, onPageInfo }) {
33
33
  const [current, setCurrent] = useState(1);
34
34
  const containerRef = useRef(null);
35
35
  const pageRefs = useRef([]);
36
+ const pdfRef = useRef(null);
36
37
  useEffect(() => {
37
38
  let cancelled = false;
38
39
  setStatus("loading");
@@ -42,6 +43,11 @@ function PdfViewer({ url, baseWidth, scale, scrollToPage, onPageInfo }) {
42
43
  await ensurePdfjs();
43
44
  const pdfjs = window.pdfjsLib;
44
45
  const pdf = await pdfjs.getDocument(url).promise;
46
+ if (cancelled) {
47
+ pdf.destroy();
48
+ return;
49
+ }
50
+ pdfRef.current = pdf;
45
51
  const numPages = pdf.numPages;
46
52
  const rendered = [];
47
53
  for (let i = 1; i <= numPages; i++) {
@@ -56,6 +62,7 @@ function PdfViewer({ url, baseWidth, scale, scrollToPage, onPageInfo }) {
56
62
  ctx.scale(dpr, dpr);
57
63
  await page.render({ canvasContext: ctx, canvas, viewport }).promise;
58
64
  rendered.push({ pageNum: i, dataUrl: canvas.toDataURL(), width: viewport.width, height: viewport.height });
65
+ page.cleanup();
59
66
  if (!cancelled) setPages([...rendered]);
60
67
  }
61
68
  if (!cancelled) setStatus("done");
@@ -65,6 +72,8 @@ function PdfViewer({ url, baseWidth, scale, scrollToPage, onPageInfo }) {
65
72
  })();
66
73
  return () => {
67
74
  cancelled = true;
75
+ pdfRef.current?.destroy();
76
+ pdfRef.current = null;
68
77
  };
69
78
  }, [url, baseWidth]);
70
79
  useEffect(() => {
@@ -138,4 +147,4 @@ function PdfViewer({ url, baseWidth, scale, scrollToPage, onPageInfo }) {
138
147
  export {
139
148
  PdfViewer
140
149
  };
141
- //# sourceMappingURL=PdfViewer-WIV5AUJB.mjs.map
150
+ //# sourceMappingURL=PdfViewer-CG2577E4.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/views/RecordPreview/PdfViewer.tsx","../src/views/RecordPreview/ensurePdfjs.ts"],"sourcesContent":["import { useState, useEffect, useRef, useCallback } from 'react';\nimport { ensurePdfjs } from './ensurePdfjs';\n\ndeclare global {\n interface Window {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n pdfjsLib: any;\n }\n}\n\nexport interface PdfViewerProps {\n url: string;\n baseWidth: number;\n scale: number;\n scrollToPage: number;\n onPageInfo?: (info: { current: number; total: number }) => void;\n}\n\ninterface RenderedPage {\n pageNum: number;\n dataUrl: string;\n width: number;\n height: number;\n}\n\nexport function PdfViewer({ url, baseWidth, scale, scrollToPage, onPageInfo }: PdfViewerProps) {\n const [pages, setPages] = useState<RenderedPage[]>([]);\n const [status, setStatus] = useState<'loading' | 'error' | 'done'>('loading');\n const [current, setCurrent] = useState(1);\n const containerRef = useRef<HTMLDivElement>(null);\n const pageRefs = useRef<(HTMLDivElement | null)[]>([]);\n const pdfRef = useRef<any>(null);\n\n useEffect(() => {\n let cancelled = false;\n setStatus('loading');\n setPages([]);\n\n (async () => {\n try {\n await ensurePdfjs();\n const pdfjs = window.pdfjsLib;\n const pdf = await pdfjs.getDocument(url).promise;\n if (cancelled) {\n pdf.destroy();\n return;\n }\n pdfRef.current = pdf;\n const numPages = pdf.numPages;\n const rendered: RenderedPage[] = [];\n\n for (let i = 1; i <= numPages; i++) {\n if (cancelled) return;\n const page = await pdf.getPage(i);\n const viewport = page.getViewport({ scale: baseWidth / page.getViewport({ scale: 1 }).width });\n const canvas = document.createElement('canvas');\n const dpr = Math.min(window.devicePixelRatio * 2, 4);\n canvas.width = viewport.width * dpr;\n canvas.height = viewport.height * dpr;\n const ctx = canvas.getContext('2d')!;\n ctx.scale(dpr, dpr);\n await page.render({ canvasContext: ctx, canvas, viewport }).promise;\n rendered.push({ pageNum: i, dataUrl: canvas.toDataURL(), width: viewport.width, height: viewport.height });\n page.cleanup();\n if (!cancelled) setPages([...rendered]);\n }\n\n if (!cancelled) setStatus('done');\n } catch {\n if (!cancelled) setStatus('error');\n }\n })();\n\n return () => {\n cancelled = true;\n pdfRef.current?.destroy();\n pdfRef.current = null;\n };\n }, [url, baseWidth]);\n\n useEffect(() => {\n onPageInfo?.({ current, total: pages.length });\n }, [current, pages.length, onPageInfo]);\n\n useEffect(() => {\n const el = pageRefs.current[scrollToPage - 1];\n if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });\n }, [scrollToPage]);\n\n useEffect(() => {\n const root = containerRef.current;\n if (!root || pages.length === 0) return;\n const observer = new IntersectionObserver(\n (entries) => {\n const visible = entries\n .filter((e) => e.isIntersecting)\n .sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top)[0];\n if (visible) {\n const idx = Number((visible.target as HTMLElement).dataset.pageIndex);\n if (!Number.isNaN(idx)) setCurrent(idx + 1);\n }\n },\n { root, rootMargin: '0px 0px -70% 0px', threshold: 0 },\n );\n pageRefs.current.forEach((el) => el && observer.observe(el));\n return () => observer.disconnect();\n }, [pages.length]);\n\n const setPageRef = useCallback((i: number) => (el: HTMLDivElement | null) => {\n pageRefs.current[i] = el;\n }, []);\n\n if (status === 'error') {\n return <div className=\"mr-doc-viewer__status\">Couldn't load this document.</div>;\n }\n\n const totalWidth = (pages[0]?.width ?? baseWidth) * scale;\n const totalHeight = pages.reduce((sum, p) => sum + p.height, 0) * scale;\n\n return (\n <div ref={containerRef} className=\"mr-doc-viewer__pdf-scroll\">\n <div\n className=\"mr-doc-viewer__pdf-sizer\"\n style={{ width: totalWidth, height: totalHeight || undefined }}\n >\n <div\n className=\"mr-doc-viewer__pdf-stack\"\n style={{ transform: `scale(${scale})`, transformOrigin: 'top left' }}\n >\n {status === 'loading' && pages.length === 0 && (\n <div className=\"mr-doc-viewer__status\">Loading document…</div>\n )}\n {pages.map((p, i) => (\n <div\n key={p.pageNum}\n data-page-index={i}\n ref={setPageRef(i)}\n >\n <img\n src={p.dataUrl}\n width={p.width}\n height={p.height}\n alt={`Page ${p.pageNum}`}\n className=\"mr-doc-viewer__page\"\n draggable={false}\n />\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n","/**\n * Loads pdf.js from the CDN at runtime and exposes it as `window.pdfjsLib`.\n *\n * pdfjs-dist v5 is externalized from the webpack bundle (see next.config.ts) to\n * avoid the \"Object.defineProperty called on non-object\" ESM error. So the\n * library must exist as a global *before* react-pdf (which imports `pdfjs-dist`,\n * resolved to `window.pdfjsLib`) evaluates. Call and await this before lazily\n * importing the PDF viewer. The legacy ESM build assigns `globalThis.pdfjsLib`\n * on load and also configures the matching worker.\n */\nconst PDFJS_VERSION = '5.4.296'; // must match the installed pdfjs-dist\n// jsDelivr mirrors the npm package exactly (cdnjs doesn't host the .mjs build).\n// The legacy build is the most browser-compatible.\nconst CDN_BASE = `https://cdn.jsdelivr.net/npm/pdfjs-dist@${PDFJS_VERSION}/legacy/build`;\n\nlet loadPromise: Promise<void> | null = null;\n\nexport function ensurePdfjs(): Promise<void> {\n if (typeof window === 'undefined') return Promise.resolve();\n // Already loaded.\n if ((window as unknown as { pdfjsLib?: unknown }).pdfjsLib) return Promise.resolve();\n if (loadPromise) return loadPromise;\n\n loadPromise = new Promise<void>((resolve, reject) => {\n const script = document.createElement('script');\n script.type = 'module';\n // Import the legacy ESM build (sets globalThis.pdfjsLib), then point its\n // worker at the matching CDN worker.\n script.textContent = `\n import * as pdfjsLib from '${CDN_BASE}/pdf.min.mjs';\n pdfjsLib.GlobalWorkerOptions.workerSrc = '${CDN_BASE}/pdf.worker.min.mjs';\n window.pdfjsLib = pdfjsLib;\n window.dispatchEvent(new Event('pdfjs-ready'));\n `;\n script.onerror = () => reject(new Error('Failed to load pdf.js from CDN'));\n window.addEventListener('pdfjs-ready', () => resolve(), { once: true });\n document.head.appendChild(script);\n });\n return loadPromise;\n}\n"],"mappings":"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","names":[]}
@@ -33,6 +33,7 @@ function PdfViewer({ url, baseWidth, scale, scrollToPage, onPageInfo }) {
33
33
  const [current, setCurrent] = _react.useState.call(void 0, 1);
34
34
  const containerRef = _react.useRef.call(void 0, null);
35
35
  const pageRefs = _react.useRef.call(void 0, []);
36
+ const pdfRef = _react.useRef.call(void 0, null);
36
37
  _react.useEffect.call(void 0, () => {
37
38
  let cancelled = false;
38
39
  setStatus("loading");
@@ -42,6 +43,11 @@ function PdfViewer({ url, baseWidth, scale, scrollToPage, onPageInfo }) {
42
43
  await ensurePdfjs();
43
44
  const pdfjs = window.pdfjsLib;
44
45
  const pdf = await pdfjs.getDocument(url).promise;
46
+ if (cancelled) {
47
+ pdf.destroy();
48
+ return;
49
+ }
50
+ pdfRef.current = pdf;
45
51
  const numPages = pdf.numPages;
46
52
  const rendered = [];
47
53
  for (let i = 1; i <= numPages; i++) {
@@ -56,6 +62,7 @@ function PdfViewer({ url, baseWidth, scale, scrollToPage, onPageInfo }) {
56
62
  ctx.scale(dpr, dpr);
57
63
  await page.render({ canvasContext: ctx, canvas, viewport }).promise;
58
64
  rendered.push({ pageNum: i, dataUrl: canvas.toDataURL(), width: viewport.width, height: viewport.height });
65
+ page.cleanup();
59
66
  if (!cancelled) setPages([...rendered]);
60
67
  }
61
68
  if (!cancelled) setStatus("done");
@@ -65,10 +72,12 @@ function PdfViewer({ url, baseWidth, scale, scrollToPage, onPageInfo }) {
65
72
  })();
66
73
  return () => {
67
74
  cancelled = true;
75
+ _optionalChain([pdfRef, 'access', _ => _.current, 'optionalAccess', _2 => _2.destroy, 'call', _3 => _3()]);
76
+ pdfRef.current = null;
68
77
  };
69
78
  }, [url, baseWidth]);
70
79
  _react.useEffect.call(void 0, () => {
71
- _optionalChain([onPageInfo, 'optionalCall', _ => _({ current, total: pages.length })]);
80
+ _optionalChain([onPageInfo, 'optionalCall', _4 => _4({ current, total: pages.length })]);
72
81
  }, [current, pages.length, onPageInfo]);
73
82
  _react.useEffect.call(void 0, () => {
74
83
  const el = pageRefs.current[scrollToPage - 1];
@@ -96,7 +105,7 @@ function PdfViewer({ url, baseWidth, scale, scrollToPage, onPageInfo }) {
96
105
  if (status === "error") {
97
106
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "mr-doc-viewer__status", children: "Couldn't load this document." });
98
107
  }
99
- const totalWidth = (_nullishCoalesce(_optionalChain([pages, 'access', _2 => _2[0], 'optionalAccess', _3 => _3.width]), () => ( baseWidth))) * scale;
108
+ const totalWidth = (_nullishCoalesce(_optionalChain([pages, 'access', _5 => _5[0], 'optionalAccess', _6 => _6.width]), () => ( baseWidth))) * scale;
100
109
  const totalHeight = pages.reduce((sum, p) => sum + p.height, 0) * scale;
101
110
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { ref: containerRef, className: "mr-doc-viewer__pdf-scroll", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
102
111
  "div",
@@ -138,4 +147,4 @@ function PdfViewer({ url, baseWidth, scale, scrollToPage, onPageInfo }) {
138
147
 
139
148
 
140
149
  exports.PdfViewer = PdfViewer;
141
- //# sourceMappingURL=PdfViewer-EAN6EQAB.js.map
150
+ //# sourceMappingURL=PdfViewer-GIWRL6FQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/medical-records-ui/medical-records-ui/dist/PdfViewer-GIWRL6FQ.js","../src/views/RecordPreview/PdfViewer.tsx","../src/views/RecordPreview/ensurePdfjs.ts"],"names":[],"mappings":"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","file":"/home/runner/work/medical-records-ui/medical-records-ui/dist/PdfViewer-GIWRL6FQ.js","sourcesContent":[null,"import { useState, useEffect, useRef, useCallback } from 'react';\nimport { ensurePdfjs } from './ensurePdfjs';\n\ndeclare global {\n interface Window {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n pdfjsLib: any;\n }\n}\n\nexport interface PdfViewerProps {\n url: string;\n baseWidth: number;\n scale: number;\n scrollToPage: number;\n onPageInfo?: (info: { current: number; total: number }) => void;\n}\n\ninterface RenderedPage {\n pageNum: number;\n dataUrl: string;\n width: number;\n height: number;\n}\n\nexport function PdfViewer({ url, baseWidth, scale, scrollToPage, onPageInfo }: PdfViewerProps) {\n const [pages, setPages] = useState<RenderedPage[]>([]);\n const [status, setStatus] = useState<'loading' | 'error' | 'done'>('loading');\n const [current, setCurrent] = useState(1);\n const containerRef = useRef<HTMLDivElement>(null);\n const pageRefs = useRef<(HTMLDivElement | null)[]>([]);\n const pdfRef = useRef<any>(null);\n\n useEffect(() => {\n let cancelled = false;\n setStatus('loading');\n setPages([]);\n\n (async () => {\n try {\n await ensurePdfjs();\n const pdfjs = window.pdfjsLib;\n const pdf = await pdfjs.getDocument(url).promise;\n if (cancelled) {\n pdf.destroy();\n return;\n }\n pdfRef.current = pdf;\n const numPages = pdf.numPages;\n const rendered: RenderedPage[] = [];\n\n for (let i = 1; i <= numPages; i++) {\n if (cancelled) return;\n const page = await pdf.getPage(i);\n const viewport = page.getViewport({ scale: baseWidth / page.getViewport({ scale: 1 }).width });\n const canvas = document.createElement('canvas');\n const dpr = Math.min(window.devicePixelRatio * 2, 4);\n canvas.width = viewport.width * dpr;\n canvas.height = viewport.height * dpr;\n const ctx = canvas.getContext('2d')!;\n ctx.scale(dpr, dpr);\n await page.render({ canvasContext: ctx, canvas, viewport }).promise;\n rendered.push({ pageNum: i, dataUrl: canvas.toDataURL(), width: viewport.width, height: viewport.height });\n page.cleanup();\n if (!cancelled) setPages([...rendered]);\n }\n\n if (!cancelled) setStatus('done');\n } catch {\n if (!cancelled) setStatus('error');\n }\n })();\n\n return () => {\n cancelled = true;\n pdfRef.current?.destroy();\n pdfRef.current = null;\n };\n }, [url, baseWidth]);\n\n useEffect(() => {\n onPageInfo?.({ current, total: pages.length });\n }, [current, pages.length, onPageInfo]);\n\n useEffect(() => {\n const el = pageRefs.current[scrollToPage - 1];\n if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });\n }, [scrollToPage]);\n\n useEffect(() => {\n const root = containerRef.current;\n if (!root || pages.length === 0) return;\n const observer = new IntersectionObserver(\n (entries) => {\n const visible = entries\n .filter((e) => e.isIntersecting)\n .sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top)[0];\n if (visible) {\n const idx = Number((visible.target as HTMLElement).dataset.pageIndex);\n if (!Number.isNaN(idx)) setCurrent(idx + 1);\n }\n },\n { root, rootMargin: '0px 0px -70% 0px', threshold: 0 },\n );\n pageRefs.current.forEach((el) => el && observer.observe(el));\n return () => observer.disconnect();\n }, [pages.length]);\n\n const setPageRef = useCallback((i: number) => (el: HTMLDivElement | null) => {\n pageRefs.current[i] = el;\n }, []);\n\n if (status === 'error') {\n return <div className=\"mr-doc-viewer__status\">Couldn't load this document.</div>;\n }\n\n const totalWidth = (pages[0]?.width ?? baseWidth) * scale;\n const totalHeight = pages.reduce((sum, p) => sum + p.height, 0) * scale;\n\n return (\n <div ref={containerRef} className=\"mr-doc-viewer__pdf-scroll\">\n <div\n className=\"mr-doc-viewer__pdf-sizer\"\n style={{ width: totalWidth, height: totalHeight || undefined }}\n >\n <div\n className=\"mr-doc-viewer__pdf-stack\"\n style={{ transform: `scale(${scale})`, transformOrigin: 'top left' }}\n >\n {status === 'loading' && pages.length === 0 && (\n <div className=\"mr-doc-viewer__status\">Loading document…</div>\n )}\n {pages.map((p, i) => (\n <div\n key={p.pageNum}\n data-page-index={i}\n ref={setPageRef(i)}\n >\n <img\n src={p.dataUrl}\n width={p.width}\n height={p.height}\n alt={`Page ${p.pageNum}`}\n className=\"mr-doc-viewer__page\"\n draggable={false}\n />\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n","/**\n * Loads pdf.js from the CDN at runtime and exposes it as `window.pdfjsLib`.\n *\n * pdfjs-dist v5 is externalized from the webpack bundle (see next.config.ts) to\n * avoid the \"Object.defineProperty called on non-object\" ESM error. So the\n * library must exist as a global *before* react-pdf (which imports `pdfjs-dist`,\n * resolved to `window.pdfjsLib`) evaluates. Call and await this before lazily\n * importing the PDF viewer. The legacy ESM build assigns `globalThis.pdfjsLib`\n * on load and also configures the matching worker.\n */\nconst PDFJS_VERSION = '5.4.296'; // must match the installed pdfjs-dist\n// jsDelivr mirrors the npm package exactly (cdnjs doesn't host the .mjs build).\n// The legacy build is the most browser-compatible.\nconst CDN_BASE = `https://cdn.jsdelivr.net/npm/pdfjs-dist@${PDFJS_VERSION}/legacy/build`;\n\nlet loadPromise: Promise<void> | null = null;\n\nexport function ensurePdfjs(): Promise<void> {\n if (typeof window === 'undefined') return Promise.resolve();\n // Already loaded.\n if ((window as unknown as { pdfjsLib?: unknown }).pdfjsLib) return Promise.resolve();\n if (loadPromise) return loadPromise;\n\n loadPromise = new Promise<void>((resolve, reject) => {\n const script = document.createElement('script');\n script.type = 'module';\n // Import the legacy ESM build (sets globalThis.pdfjsLib), then point its\n // worker at the matching CDN worker.\n script.textContent = `\n import * as pdfjsLib from '${CDN_BASE}/pdf.min.mjs';\n pdfjsLib.GlobalWorkerOptions.workerSrc = '${CDN_BASE}/pdf.worker.min.mjs';\n window.pdfjsLib = pdfjsLib;\n window.dispatchEvent(new Event('pdfjs-ready'));\n `;\n script.onerror = () => reject(new Error('Failed to load pdf.js from CDN'));\n window.addEventListener('pdfjs-ready', () => resolve(), { once: true });\n document.head.appendChild(script);\n });\n return loadPromise;\n}\n"]}
package/dist/index.js CHANGED
@@ -1674,7 +1674,7 @@ function CareContextPanel({ caseId, showFiles: showFilesProp, onBack }) {
1674
1674
 
1675
1675
 
1676
1676
  var DocumentViewer = _react.lazy.call(void 0,
1677
- () => Promise.resolve().then(() => _interopRequireWildcard(require("./DocumentViewer-D7OJVVZM.js"))).then((m) => ({
1677
+ () => Promise.resolve().then(() => _interopRequireWildcard(require("./DocumentViewer-Y3SQ2BMS.js"))).then((m) => ({
1678
1678
  default: m.DocumentViewer
1679
1679
  }))
1680
1680
  );
@@ -1846,10 +1846,12 @@ function SmartRecordSmartPanel({
1846
1846
 
1847
1847
  var HIDDEN = { visible: false, direction: "down" };
1848
1848
  function useJumpToSelected(activeId, listKey) {
1849
- const scrollBodyRef = _react.useRef.call(void 0, null);
1849
+ const [body, setBody] = _react.useState.call(void 0, null);
1850
+ const scrollBodyRef = _react.useCallback.call(void 0, (node) => {
1851
+ setBody(node);
1852
+ }, []);
1850
1853
  const [state, setState] = _react.useState.call(void 0, HIDDEN);
1851
1854
  const evaluate = _react.useCallback.call(void 0, () => {
1852
- const body = scrollBodyRef.current;
1853
1855
  const active = _nullishCoalesce(_optionalChain([body, 'optionalAccess', _44 => _44.querySelector, 'call', _45 => _45(".mr-sv__record-item--active")]), () => ( null));
1854
1856
  if (!body || !active) {
1855
1857
  setState((s) => s.visible ? HIDDEN : s);
@@ -1864,9 +1866,8 @@ function useJumpToSelected(activeId, listKey) {
1864
1866
  } else {
1865
1867
  setState((s) => s.visible ? HIDDEN : s);
1866
1868
  }
1867
- }, [activeId]);
1869
+ }, [body, activeId]);
1868
1870
  _react.useEffect.call(void 0, () => {
1869
- const body = scrollBodyRef.current;
1870
1871
  if (!body) return;
1871
1872
  body.addEventListener("scroll", evaluate, { passive: true });
1872
1873
  window.addEventListener("resize", evaluate);
@@ -1874,13 +1875,13 @@ function useJumpToSelected(activeId, listKey) {
1874
1875
  body.removeEventListener("scroll", evaluate);
1875
1876
  window.removeEventListener("resize", evaluate);
1876
1877
  };
1877
- }, [evaluate]);
1878
+ }, [body, evaluate]);
1878
1879
  _react.useEffect.call(void 0, () => {
1879
1880
  evaluate();
1880
1881
  }, [evaluate, listKey]);
1881
1882
  const jumpToSelected = _react.useCallback.call(void 0, () => {
1882
- _optionalChain([scrollBodyRef, 'access', _46 => _46.current, 'optionalAccess', _47 => _47.querySelector, 'call', _48 => _48(".mr-sv__record-item--active"), 'optionalAccess', _49 => _49.scrollIntoView, 'call', _50 => _50({ behavior: "smooth", block: "center" })]);
1883
- }, []);
1883
+ _optionalChain([body, 'optionalAccess', _46 => _46.querySelector, 'call', _47 => _47(".mr-sv__record-item--active"), 'optionalAccess', _48 => _48.scrollIntoView, 'call', _49 => _49({ behavior: "smooth", block: "center" })]);
1884
+ }, [body]);
1884
1885
  return {
1885
1886
  scrollBodyRef,
1886
1887
  showChip: state.visible,
@@ -1990,7 +1991,7 @@ function resolveContentType(file) {
1990
1991
  if (t === "image/jpeg" || t === "image/jpg" || t === "image/png" || t === "application/pdf") {
1991
1992
  return t;
1992
1993
  }
1993
- const ext = _optionalChain([file, 'access', _51 => _51.name, 'access', _52 => _52.split, 'call', _53 => _53("."), 'access', _54 => _54.pop, 'call', _55 => _55(), 'optionalAccess', _56 => _56.toLowerCase, 'call', _57 => _57()]);
1994
+ const ext = _optionalChain([file, 'access', _50 => _50.name, 'access', _51 => _51.split, 'call', _52 => _52("."), 'access', _53 => _53.pop, 'call', _54 => _54(), 'optionalAccess', _55 => _55.toLowerCase, 'call', _56 => _56()]);
1994
1995
  if (ext === "pdf") return "application/pdf";
1995
1996
  if (ext === "png") return "image/png";
1996
1997
  if (ext === "jpg" || ext === "jpeg") return "image/jpeg";
@@ -2029,12 +2030,12 @@ function useUploadConnection() {
2029
2030
  patientId
2030
2031
  });
2031
2032
  const queue = store.getState().uploadQueue;
2032
- if (res.message === "queued_offline" || !_optionalChain([res, 'access', _58 => _58.batch_response, 'optionalAccess', _59 => _59.length])) {
2033
+ if (res.message === "queued_offline" || !_optionalChain([res, 'access', _57 => _57.batch_response, 'optionalAccess', _58 => _58.length])) {
2033
2034
  throw new Error("No internet connection. Your upload will retry automatically when you're back online.");
2034
2035
  }
2035
2036
  localIds.forEach((id, i) => {
2036
- const documentId = _nullishCoalesce(_optionalChain([res, 'access', _60 => _60.batch_response, 'optionalAccess', _61 => _61[i], 'optionalAccess', _62 => _62.document_id]), () => ( ""));
2037
- const errorDetails = _optionalChain([res, 'access', _63 => _63.batch_response, 'optionalAccess', _64 => _64[i], 'optionalAccess', _65 => _65.error_details]);
2037
+ const documentId = _nullishCoalesce(_optionalChain([res, 'access', _59 => _59.batch_response, 'optionalAccess', _60 => _60[i], 'optionalAccess', _61 => _61.document_id]), () => ( ""));
2038
+ const errorDetails = _optionalChain([res, 'access', _62 => _62.batch_response, 'optionalAccess', _63 => _63[i], 'optionalAccess', _64 => _64.error_details]);
2038
2039
  if (errorDetails) {
2039
2040
  queue.markFailed(id);
2040
2041
  } else {
@@ -2275,7 +2276,7 @@ var SmartRecordsSidebar = _react.memo.call(void 0, function SmartRecordsSidebar2
2275
2276
  if (!c) return false;
2276
2277
  if (c.name.toLowerCase().includes(q)) return true;
2277
2278
  return Object.values(recordsById).some(
2278
- (r) => _optionalChain([r, 'optionalAccess', _66 => _66.cases, 'access', _67 => _67.includes, 'call', _68 => _68(caseId)]) && r.title.toLowerCase().includes(q)
2279
+ (r) => _optionalChain([r, 'optionalAccess', _65 => _65.cases, 'access', _66 => _66.includes, 'call', _67 => _67(caseId)]) && r.title.toLowerCase().includes(q)
2279
2280
  );
2280
2281
  });
2281
2282
  }, [caseIds, casesById, recordsById, search]);
@@ -2456,7 +2457,7 @@ var VitalHistoryPanel2 = _react.lazy.call(void 0,
2456
2457
  }))
2457
2458
  );
2458
2459
  var OriginalRecordPanel = _react.lazy.call(void 0,
2459
- () => Promise.resolve().then(() => _interopRequireWildcard(require("./OriginalRecordPanel-UHNIWIT3.js"))).then((m) => ({
2460
+ () => Promise.resolve().then(() => _interopRequireWildcard(require("./OriginalRecordPanel-SJIBGHYZ.js"))).then((m) => ({
2460
2461
  default: m.OriginalRecordPanel
2461
2462
  }))
2462
2463
  );
@@ -2564,19 +2565,19 @@ function SmartRecordsViewInner({
2564
2565
  const [selectedCaseId, setSelectedCaseId] = _react.useState.call(void 0, null);
2565
2566
  const [uploadOpen, setUploadOpen] = _react.useState.call(void 0, false);
2566
2567
  const stableCopyToNote = _react.useCallback.call(void 0,
2567
- (text, anchor) => _optionalChain([onCopyToNoteRef, 'access', _69 => _69.current, 'optionalCall', _70 => _70(text, anchor)]),
2568
+ (text, anchor) => _optionalChain([onCopyToNoteRef, 'access', _68 => _68.current, 'optionalCall', _69 => _69(text, anchor)]),
2568
2569
  []
2569
2570
  );
2570
2571
  const stableCopyVitalsToNote = _react.useCallback.call(void 0,
2571
- (text) => _optionalChain([onCopyVitalsToNoteRef, 'access', _71 => _71.current, 'optionalCall', _72 => _72(text)]),
2572
+ (text) => _optionalChain([onCopyVitalsToNoteRef, 'access', _70 => _70.current, 'optionalCall', _71 => _71(text)]),
2572
2573
  []
2573
2574
  );
2574
2575
  const stableToast = _react.useCallback.call(void 0,
2575
- (message, type) => _optionalChain([onToastRef, 'access', _73 => _73.current, 'optionalCall', _74 => _74(message, type)]),
2576
+ (message, type) => _optionalChain([onToastRef, 'access', _72 => _72.current, 'optionalCall', _73 => _73(message, type)]),
2576
2577
  []
2577
2578
  );
2578
2579
  const attachToContext = _react.useCallback.call(void 0, (documentId, name) => {
2579
- void _optionalChain([onAddToContextRef, 'access', _75 => _75.current, 'optionalCall', _76 => _76([{ documentId, name }])]);
2580
+ void _optionalChain([onAddToContextRef, 'access', _74 => _74.current, 'optionalCall', _75 => _75([{ documentId, name }])]);
2580
2581
  }, []);
2581
2582
  const handleSelectCase = _react.useCallback.call(void 0,
2582
2583
  (caseId) => {