diff --git a/package-lock.json b/package-lock.json index 604d6bb7e61..97073c88ecb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5509,15 +5509,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dev": true, - "dependencies": { - "@types/unist": "*" - } - }, "node_modules/@types/is-empty": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@types/is-empty/-/is-empty-1.2.3.tgz", @@ -12614,6 +12605,16 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdast-util-mdx-expression/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/mdast-util-mdx-expression/node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", @@ -12699,6 +12700,45 @@ "micromark-util-types": "^2.0.0" } }, + "node_modules/mdast-util-mdx-expression/node_modules/micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-mdx-expression/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, "node_modules/mdast-util-mdx-expression/node_modules/unist-util-stringify-position": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", @@ -12738,6 +12778,16 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdast-util-mdx-jsx/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/mdast-util-mdx-jsx/node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", @@ -12904,6 +12954,45 @@ "micromark-util-types": "^2.0.0" } }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, "node_modules/mdast-util-mdx-jsx/node_modules/parse-entities": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz", @@ -13031,6 +13120,45 @@ "micromark-util-types": "^2.0.0" } }, + "node_modules/mdast-util-mdx/node_modules/micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-mdx/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, "node_modules/mdast-util-mdx/node_modules/unist-util-stringify-position": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", @@ -13064,6 +13192,16 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdast-util-mdxjs-esm/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/mdast-util-mdxjs-esm/node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", @@ -13149,6 +13287,45 @@ "micromark-util-types": "^2.0.0" } }, + "node_modules/mdast-util-mdxjs-esm/node_modules/micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, "node_modules/mdast-util-mdxjs-esm/node_modules/unist-util-stringify-position": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", @@ -13713,22 +13890,6 @@ "micromark-util-symbol": "^2.0.0" } }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, "node_modules/micromark-util-events-to-acorn": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.2.tgz", @@ -13813,27 +13974,6 @@ "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, "node_modules/micromark-util-subtokenize": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.1.tgz", @@ -16040,6 +16180,45 @@ "micromark-util-types": "^2.0.0" } }, + "node_modules/remark-parse/node_modules/micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/remark-parse/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, "node_modules/remark-parse/node_modules/unist-util-stringify-position": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", diff --git a/src/components/Common/DiscussionNotesEditor.tsx b/src/components/Common/DiscussionNotesEditor.tsx index c89ea3f295b..ab1eadbf8d1 100644 --- a/src/components/Common/DiscussionNotesEditor.tsx +++ b/src/components/Common/DiscussionNotesEditor.tsx @@ -9,40 +9,10 @@ import useFileUpload from "@/hooks/useFileUpload"; import { getCaretCoordinates, getCaretInfo } from "@/Utils/textEditor"; import { classNames } from "@/Utils/utils"; +import { FilePreviewCard } from "./FilePreviewCard"; import MentionsDropdown from "./MentionDropdown"; import NotePreview from "./NotePreview"; -interface FilePreviewProps { - file: File; - index: number; - onRemove: (index: number) => void; -} - -const FilePreview: React.FC = ({ file, index, onRemove }) => { - return ( -
- -
- - - {file?.name || "file"} - -
-
- ); -}; - interface DiscussionNotesEditorProps { initialNote?: string; onChange: (text: string) => void; @@ -156,7 +126,7 @@ const DiscussionNotesEditor: React.FC = ({ }; return ( -
+
= ({ {fileUpload.files.length > 0 && (
{fileUpload.files.map((file, index) => ( - void; + readonly?: boolean; + isPlaying?: boolean; + onPlay?: () => void; + onClick?: () => void; +} + +const icons: Record = { + AUDIO: "l-volume", + IMAGE: "l-image", + PRESENTATION: "l-presentation-play", + VIDEO: "l-video", + UNKNOWN: "l-file-medical", + DOCUMENT: "l-file-medical", +}; + +export function FilePreviewCard({ + file, + index, + onRemove, + readonly = false, + isPlaying, + onPlay, + onClick, +}: FilePreviewCardProps) { + const getFileType = (file: FileUploadModel) => { + if (!file.extension) return "UNKNOWN"; + const ftype = ( + Object.keys(FILE_EXTENSIONS) as (keyof typeof FILE_EXTENSIONS)[] + ).find((type) => + FILE_EXTENSIONS[type].includes((file.extension?.slice(1) || "") as never), + ); + return ftype || "UNKNOWN"; + }; + + const fileName = file instanceof File ? file.name : file.name; + const fileType = getFileType(file); + + return ( +
+ {!readonly && onRemove && typeof index === "number" && ( + + )} + + {fileType === "AUDIO" && onPlay ? ( +
{ + e.stopPropagation(); + onPlay(); + }} + > + + + {fileName} + +
+ ) : ( +
+ + + {fileName} + +
+ )} +
+ ); +} diff --git a/src/components/Common/FilePreviewDialog.tsx b/src/components/Common/FilePreviewDialog.tsx index eb3a32cf078..fa8a333515d 100644 --- a/src/components/Common/FilePreviewDialog.tsx +++ b/src/components/Common/FilePreviewDialog.tsx @@ -49,17 +49,16 @@ type FilePreviewProps = { }; const previewExtensions = [ - "html", - "htm", - "pdf", - "mp4", - "mp3", - "webm", - "jpg", - "jpeg", - "png", - "gif", - "webp", + ".html", + ".htm", + ".pdf", + ".mp4", + ".webm", + ".jpg", + ".jpeg", + ".png", + ".gif", + ".webp", ]; const FilePreviewDialog = (props: FilePreviewProps) => { diff --git a/src/components/Facility/DoctorNote.tsx b/src/components/Facility/DoctorNote.tsx index 5b6fbcd538f..f3d13c975f8 100644 --- a/src/components/Facility/DoctorNote.tsx +++ b/src/components/Facility/DoctorNote.tsx @@ -29,12 +29,17 @@ const DoctorNote = (props: DoctorNoteProps) => { setThreadViewNote, } = props; + const notes = + mode === "thread-view" + ? state.notes.filter((note) => !note.root_note_object) + : state.notes; + return (
- {state.notes.length ? ( + {notes.length ? ( { } className="flex h-full flex-col-reverse overflow-hidden" inverse={true} - dataLength={state.notes.length} + dataLength={notes.length} scrollableTarget="patient-notes-list" > - {state.notes.map((note) => { + {notes.map((note) => { const noteCard = ( { setThreadViewNote={setThreadViewNote} /> ); - if (mode === "thread-view" && !note.root_note_object) { + if (mode === "thread-view") { return (
{noteCard} diff --git a/src/components/Facility/DoctorNoteReplyPreviewCard.tsx b/src/components/Facility/DoctorNoteReplyPreviewCard.tsx index 299071a65cc..df70a13ee1f 100644 --- a/src/components/Facility/DoctorNoteReplyPreviewCard.tsx +++ b/src/components/Facility/DoctorNoteReplyPreviewCard.tsx @@ -4,9 +4,10 @@ import { PatientNotesReplyModel } from "@/components/Facility/models"; import { USER_TYPES_MAP } from "@/common/constants"; -import { formatDateTime, relativeDate } from "@/Utils/utils"; +import { formatDateTime, formatDisplayName, relativeDate } from "@/Utils/utils"; import CareIcon from "../../CAREUI/icons/CareIcon"; +import { Avatar } from "../Common/Avatar"; import NotePreview from "../Common/NotePreview"; interface Props { @@ -28,9 +29,11 @@ const DoctorNoteReplyPreviewCard = ({
-
- {parentNote.created_by_object?.first_name[0]} -
+
diff --git a/src/components/Facility/PatientNoteCard.tsx b/src/components/Facility/PatientNoteCard.tsx index 0b445d5d111..329365e547d 100644 --- a/src/components/Facility/PatientNoteCard.tsx +++ b/src/components/Facility/PatientNoteCard.tsx @@ -1,23 +1,22 @@ import dayjs from "dayjs"; import { t } from "i18next"; -import { useState } from "react"; +import { useRef, useState } from "react"; import CareIcon from "@/CAREUI/icons/CareIcon"; import ButtonV2 from "@/components/Common/ButtonV2"; import DialogModal from "@/components/Common/Dialog"; -import FilePreviewDialog from "@/components/Common/FilePreviewDialog"; +import { FilePreviewCard } from "@/components/Common/FilePreviewCard"; import NotePreview from "@/components/Common/NotePreview"; import Spinner from "@/components/Common/Spinner"; import { PatientNotesEditModel, PatientNotesModel, } from "@/components/Facility/models"; -import { StateInterface } from "@/components/Files/FileUpload"; import { FileUploadModel } from "@/components/Patient/models"; import useAuthUser from "@/hooks/useAuthUser"; -import { ExtImage } from "@/hooks/useFileUpload"; +import useFileManager from "@/hooks/useFileManager"; import useSlug from "@/hooks/useSlug"; import { USER_TYPES_MAP } from "@/common/constants"; @@ -28,10 +27,13 @@ import request from "@/Utils/request/request"; import { classNames, formatDateTime, + formatDisplayName, formatName, relativeDate, } from "@/Utils/utils"; +import { Avatar } from "../Common/Avatar"; + const PatientNoteCard = ({ note, setReload, @@ -55,73 +57,11 @@ const PatientNoteCard = ({ const [noteField, setNoteField] = useState(note.note); const [showEditHistory, setShowEditHistory] = useState(false); const [editHistory, setEditHistory] = useState([]); + const [isPlaying, setIsPlaying] = useState(null); + const audioRef = useRef(null); const authUser = useAuthUser(); const patientId = useSlug("patient"); - const file_type = "PATIENT_NOTES"; - const [file_state, setFileState] = useState({ - open: false, - isImage: false, - name: "", - extension: "", - zoom: 4, - isZoomInDisabled: false, - isZoomOutDisabled: false, - rotation: 0, - }); - const [fileUrl, setFileUrl] = useState(""); - const [downloadURL, setDownloadURL] = useState(""); - - const getExtension = (url: string) => { - const extension = url.split("?")[0].split(".").pop(); - return extension ?? ""; - }; - const downloadFileUrl = (url: string) => { - fetch(url) - .then((res) => res.blob()) - .then((blob) => { - setDownloadURL(URL.createObjectURL(blob)); - }); - }; - - const loadFile = async (id: string, noteId: string) => { - setFileUrl(""); - setFileState({ ...file_state, open: true }); - const { data } = await request(routes.retrieveUpload, { - query: { - file_type: file_type, - associating_id: noteId, - }, - pathParams: { id }, - }); - - if (!data) return; - - const signedUrl = data.read_signed_url as string; - const extension = getExtension(signedUrl); - - setFileState({ - ...file_state, - open: true, - name: data.name?.split(".")[0] ?? "file", - extension, - isImage: ExtImage.includes(extension), - }); - downloadFileUrl(signedUrl); - setFileUrl(signedUrl); - }; - - const handleClose = () => { - setDownloadURL(""); - setFileState({ - ...file_state, - open: false, - zoom: 4, - isZoomInDisabled: false, - isZoomOutDisabled: false, - }); - }; - const fetchEditHistory = async () => { const { res, data } = await request(routes.getPatientNoteEditHistory, { pathParams: { patientId, noteId: note.id }, @@ -157,29 +97,47 @@ const PatientNoteCard = ({ } }; + const fileManager = useFileManager({ + type: "PATIENT_NOTES", + uploadedFiles: note?.files, + }); + + const handleAudioPlay = (file: FileUploadModel) => { + if (isPlaying === file.id) { + audioRef.current?.pause(); + setIsPlaying(null); + } else { + if (audioRef.current) { + fileManager.getSignedUrl(file).then((url) => { + if (audioRef.current) { + audioRef.current.src = url; + audioRef.current.play(); + setIsPlaying(file.id!); + } + }); + } + } + }; + + const isAudioFile = (file: FileUploadModel) => { + return fileManager.getFileType(file) === "AUDIO"; + }; + return ( <> - {" "} +