diff --git a/.env b/.env index 2e32ac97bbf..3c4b26fdad4 100644 --- a/.env +++ b/.env @@ -6,6 +6,7 @@ REACT_APP_COVER_IMAGE=https://cdn.ohc.network/care_logo.svg REACT_APP_COVER_IMAGE_ALT=https://cdn.ohc.network/care_logo.svg REACT_PUBLIC_URL=https://care.ohc.network HEADERS="/*\n Strict-Transport-Security: max-age=63072000; includeSubDomains; preload\n X-XSS-Protection: 1; mode=block\n X-Frame-Options: SAMEORIGIN\n X-Content-Type-Options: nosniff\n Referrer-Policy: strict-origin-when-cross-origin\n Permissions-Policy: geolocation=(self), microphone=()" +REACT_RECAPTCHA_SITE_KEY=6LcedK8qAAAAAM2PpuqlqhZUxQpmIqHqluL74dDs # Care API URL without the /api prefix REACT_CARE_API_URL=https://careapi.ohc.network diff --git a/care.config.ts b/care.config.ts index 6813a60b331..78547b66d74 100644 --- a/care.config.ts +++ b/care.config.ts @@ -61,8 +61,7 @@ const careConfig = { govDataApiKey: env.REACT_GOV_DATA_API_KEY || "579b464db66ec23bdd000001cdd3946e44ce4aad7209ff7b23ac571b", - reCaptchaSiteKey: - env.REACT_RECAPTCHA_SITE_KEY || "6LdvxuQUAAAAADDWVflgBqyHGfq-xmvNJaToM0pN", + reCaptchaSiteKey: env.REACT_RECAPTCHA_SITE_KEY, wartimeShifting: boolean("REACT_WARTIME_SHIFTING"), diff --git a/package.json b/package.json index ce9575a1b39..b8f4ccd9a02 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "care_fe", - "version": "2.13.0", + "version": "3.0.0", "description": "Care is a Digital Public Good enabling TeleICU & Decentralised Administration of Healthcare Capacity across States.", "private": true, "repository": { diff --git a/public/locale/en.json b/public/locale/en.json index ad07727eec9..21fc0d34843 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -506,7 +506,7 @@ "care_backend": "Care Backend", "care_frontend": "Care Frontend", "category": "Category", - "category_description": "Choose the category that best describes the resource needed.", + "category_description": "Choose the category ", "caution": "Caution", "central_nursing_station": "Central Nursing Station", "change_avatar": "Change Avatar", @@ -1847,6 +1847,7 @@ "resource_status__transportation_to_be_arranged": "Transportation to be arranged", "resource_title": "Resource Title", "resource_type": "Request Type", + "resource_updated_successfully": "Resource updated successfully", "respiratory_status": "Respiratory Status", "result": "Result", "result_date": "Result Date", @@ -1940,6 +1941,7 @@ "search_tags": "Search tags...", "search_user": "Search User", "search_user_description": "Search for a user and assign a role to add them to the patient.", + "search_users": "Search users...", "searching": "Searching...", "see_attachments": "See Attachments", "see_details": "See Details", @@ -2244,6 +2246,7 @@ "update_record": "Update Record", "update_record_for_asset": "Update record for asset", "update_request": "Update Request", + "update_resource_request": "Update Resource Request", "update_role": "Update Role", "update_shift_request": "Update Shift Request", "update_status": "Update Status", diff --git a/src/CAREUI/display/Card.tsx b/src/CAREUI/display/Card.tsx deleted file mode 100644 index 3772fa0b75b..00000000000 --- a/src/CAREUI/display/Card.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { HTMLAttributes, ReactNode } from "react"; - -export default function Card( - props: { - children?: ReactNode; - } & HTMLAttributes, -) { - const { children, ...rest } = props; - return ( -
- {children} -
- ); -} diff --git a/src/Routers/routes/ConsultationRoutes.tsx b/src/Routers/routes/ConsultationRoutes.tsx index 00d052ba366..2aaa8c115e8 100644 --- a/src/Routers/routes/ConsultationRoutes.tsx +++ b/src/Routers/routes/ConsultationRoutes.tsx @@ -1,6 +1,5 @@ import QuestionnaireResponseView from "@/components/Facility/ConsultationDetails/QuestionnaireResponseView"; import EncounterQuestionnaire from "@/components/Patient/EncounterQuestionnaire"; -import FileUploadPage from "@/components/Patient/FileUploadPage"; import TreatmentSummary from "@/components/Patient/TreatmentSummary"; import { AppRoutes } from "@/Routers/AppRouter"; @@ -72,18 +71,6 @@ const consultationRoutes: AppRoutes = { ({ patientId, id }) => ( ), - "/facility/:facilityId/patient/:patientId/encounterId/:id/files/": ({ - facilityId, - patientId, - id, - }) => ( - - ), }; export default consultationRoutes; diff --git a/src/Routers/routes/FacilityRoutes.tsx b/src/Routers/routes/FacilityRoutes.tsx index bf716284545..875d3e32ebd 100644 --- a/src/Routers/routes/FacilityRoutes.tsx +++ b/src/Routers/routes/FacilityRoutes.tsx @@ -1,7 +1,7 @@ import { Redirect } from "raviger"; import FacilityUsers from "@/components/Facility/FacilityUsers"; -import ResourceCreate from "@/components/Resource/ResourceCreate"; +import ResourceCreate from "@/components/Resource/ResourceForm"; import { AppRoutes } from "@/Routers/AppRouter"; import { FacilityOverview } from "@/pages/Facility/overview"; diff --git a/src/Routers/routes/PatientRoutes.tsx b/src/Routers/routes/PatientRoutes.tsx index 77230f3ee76..26b2ff4aa38 100644 --- a/src/Routers/routes/PatientRoutes.tsx +++ b/src/Routers/routes/PatientRoutes.tsx @@ -1,4 +1,3 @@ -import FileUploadPage from "@/components/Patient/FileUploadPage"; import { facilityPatientTabs, patientTabs, @@ -45,16 +44,6 @@ const PatientRoutes: AppRoutes = { "/facility/:facilityId/patient/:id/update": ({ facilityId, id }) => ( ), - "/facility/:facilityId/patient/:patientId/files": ({ - facilityId, - patientId, - }) => ( - - ), }; export default PatientRoutes; diff --git a/src/Routers/routes/ResourceRoutes.tsx b/src/Routers/routes/ResourceRoutes.tsx index 390e566325b..d82b17d1672 100644 --- a/src/Routers/routes/ResourceRoutes.tsx +++ b/src/Routers/routes/ResourceRoutes.tsx @@ -1,6 +1,6 @@ import PrintResourceLetter from "@/components/Resource/PrintResourceLetter"; import ResourceDetails from "@/components/Resource/ResourceDetails"; -import { ResourceDetailsUpdate } from "@/components/Resource/ResourceDetailsUpdate"; +import ResourceForm from "@/components/Resource/ResourceForm"; import ResourceList from "@/components/Resource/ResourceList"; import { AppRoutes } from "@/Routers/AppRouter"; @@ -13,7 +13,7 @@ const ResourceRoutes: AppRoutes = { ), "/facility/:facilityId/resource/:id/update": ({ facilityId, id }) => ( - + ), "/facility/:facilityId/resource/:id/print": ({ id }) => ( diff --git a/src/Utils/request/api.tsx b/src/Utils/request/api.tsx index c01425c3d74..8e33d33b816 100644 --- a/src/Utils/request/api.tsx +++ b/src/Utils/request/api.tsx @@ -164,12 +164,6 @@ const routes = { TRes: Type(), }, - userList: { - path: "/api/v1/users/", - method: "GET", - TRes: Type>(), - }, - deleteProfilePicture: { path: "/api/v1/users/{username}/profile_picture/", method: "DELETE", diff --git a/src/Utils/request/useQuery.ts b/src/Utils/request/useQuery.ts deleted file mode 100644 index 422e5f96868..00000000000 --- a/src/Utils/request/useQuery.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { useQuery } from "@tanstack/react-query"; -import { useMemo, useRef } from "react"; - -import request from "@/Utils/request/request"; -import { ApiRoute, RequestOptions } from "@/Utils/request/types"; - -import { mergeRequestOptions } from "./utils"; - -export interface QueryOptions extends RequestOptions { - prefetch?: boolean; - key?: string; -} - -/** - * @deprecated use `useQuery` from `@tanstack/react-query` instead. - */ -export default function useTanStackQueryInstead( - route: ApiRoute, - options?: QueryOptions, -) { - const overridesRef = useRef>(); - - // Ensure unique key for each usage of the hook unless explicitly provided - // (hack to opt-out of tanstack query's caching between usages) - const key = useMemo(() => options?.key ?? Math.random(), [options?.key]); - - const { - data: response, - refetch, - isFetching: isLoading, - } = useQuery({ - queryKey: [route.path, options?.pathParams, options?.query, key], - queryFn: async ({ signal }) => { - const resolvedOptions = overridesRef.current - ? mergeRequestOptions(options || {}, overridesRef.current) - : options; - - return await request(route, { ...resolvedOptions, signal }); - }, - enabled: options?.prefetch ?? true, - refetchOnWindowFocus: false, - }); - - return { - data: response?.data, - loading: isLoading, - error: response?.error, - res: response?.res, - /** - * Refetch function that applies new options and fetches fresh data. - */ - refetch: async (overrides?: QueryOptions) => { - overridesRef.current = overrides; - await refetch(); - return response!; - }, - }; -} diff --git a/src/components/Auth/Login.tsx b/src/components/Auth/Login.tsx index c79eacaa7ba..dfe4aaac4de 100644 --- a/src/components/Auth/Login.tsx +++ b/src/components/Auth/Login.tsx @@ -504,7 +504,7 @@ const Login = (props: LoginProps) => { )} - {isCaptchaEnabled && ( + {isCaptchaEnabled && reCaptchaSiteKey && (
import("@/components/Common/PDFViewer")); @@ -40,6 +39,19 @@ export const zoom_values = [ "scale-200", ]; +export interface StateInterface { + open: boolean; + isImage: boolean; + name: string; + extension: string; + zoom: number; + isZoomInDisabled: boolean; + isZoomOutDisabled: boolean; + rotation: number; + id?: string; + associating_id?: string; +} + type FilePreviewProps = { title?: ReactNode; description?: ReactNode; diff --git a/src/components/Common/HelperComponents.tsx b/src/components/Common/HelperComponents.tsx deleted file mode 100644 index 5100476284e..00000000000 --- a/src/components/Common/HelperComponents.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { Transition, TransitionEvents } from "@headlessui/react"; -import { ReactNode } from "react"; - -type DropdownMenuTransitionProps = { - show?: boolean | undefined; - children: ReactNode; -} & TransitionEvents; - -export const DropdownTransition = ({ - show, - children, - ...transitionEvents -}: DropdownMenuTransitionProps) => ( - - {children} - -); diff --git a/src/components/Common/Tabs.tsx b/src/components/Common/Tabs.tsx deleted file mode 100644 index 9c375db618e..00000000000 --- a/src/components/Common/Tabs.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { type ReactNode, useEffect, useRef } from "react"; - -import useWindowDimensions from "@/hooks/useWindowDimensions"; - -import { classNames } from "@/Utils/utils"; - -export default function Tabs(props: { - className?: string; - currentTab: string | number; - onTabChange: (value: string | number) => void; - tabs: { text: ReactNode; value: string | number; id?: string }[]; -}) { - const { className, currentTab, onTabChange, tabs } = props; - const ref = useRef(null); - const tabSwitcherRef = useRef(null); - - const dimensions = useWindowDimensions(); - - useEffect(() => { - const currentTabIndex = tabs.findIndex((t) => t.value === currentTab); - if ( - typeof currentTabIndex != "number" || - !ref.current || - !tabSwitcherRef.current - ) - return; - const tabButton = ref.current.querySelectorAll("button")[currentTabIndex]; - if (!tabButton) return; - tabSwitcherRef.current.style.width = tabButton.clientWidth + "px"; - tabSwitcherRef.current.style.left = - tabButton.getBoundingClientRect().left - - ref.current.getBoundingClientRect().left + - ref.current.scrollLeft + - "px"; - }, [currentTab, tabSwitcherRef.current, ref.current, dimensions]); - - return ( -
-
- {/* There has to be a better way of handling this... */} - {tabs.map((tab, i) => ( -
- {tab.text} -
- ))} -
- {tabs.map((tab, i) => ( - - ))} -
-
- ); -} diff --git a/src/components/Common/UserAutocompleteFormField.tsx b/src/components/Common/UserAutocompleteFormField.tsx deleted file mode 100644 index 90addafb702..00000000000 --- a/src/components/Common/UserAutocompleteFormField.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import { useEffect, useState } from "react"; - -import { Autocomplete } from "@/components/Form/FormFields/Autocomplete"; -import FormField from "@/components/Form/FormFields/FormField"; -import { - FormFieldBaseProps, - useFormFieldPropsResolver, -} from "@/components/Form/FormFields/Utils"; -import { UserType } from "@/components/Users/UserFormValidations"; -import { UserBareMinimum } from "@/components/Users/models"; - -import routes from "@/Utils/request/api"; -import useTanStackQueryInstead from "@/Utils/request/useQuery"; -import { - classNames, - formatName, - isUserOnline, - mergeQueryOptions, -} from "@/Utils/utils"; - -import { Avatar } from "./Avatar"; - -type BaseProps = FormFieldBaseProps & { - placeholder?: string; - userType?: UserType; - noResultsError?: string; -}; - -type UserSearchProps = BaseProps & { - facilityId?: undefined; - homeFacility?: string; -}; - -export default function UserAutocomplete(props: UserSearchProps) { - const field = useFormFieldPropsResolver(props); - const [query, setQuery] = useState(""); - const [disabled, setDisabled] = useState(false); - - const { data, loading } = useTanStackQueryInstead(routes.userList, { - query: { - home_facility: props.homeFacility, - user_type: props.userType, - search_text: query, - limit: 50, - offset: 0, - }, - }); - - useEffect(() => { - if ( - loading || - query || - !field.required || - !props.noResultsError || - !data?.results - ) { - return; - } - - if (data.results.length === 0) { - setDisabled(true); - field.handleChange(undefined as unknown as UserBareMinimum); - } - }, [loading, field.required, data?.results, props.noResultsError]); - - const getAvatar = (option: UserBareMinimum) => { - return ( - - ); - }; - - return ( - - obj.username, - )} - optionLabel={formatName} - optionIcon={userOnlineDot} - optionImage={getAvatar} - optionDescription={(option) => - `${option.user_type} - ${option.username}` - } - optionValue={(option) => option} - onQuery={setQuery} - isLoading={loading} - /> - - ); -} - -const userOnlineDot = (user: UserBareMinimum) => ( -
-); diff --git a/src/components/Files/FileBlock.tsx b/src/components/Files/FileBlock.tsx deleted file mode 100644 index 1b9cb9f8959..00000000000 --- a/src/components/Files/FileBlock.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import { useQuery } from "@tanstack/react-query"; -import dayjs from "dayjs"; -import { t } from "i18next"; - -import CareIcon, { IconName } from "@/CAREUI/icons/CareIcon"; - -import { Button } from "@/components/ui/button"; - -import { FileUploadModel } from "@/components/Patient/models"; - -import { FileManagerResult } from "@/hooks/useFileManager"; - -import { FILE_EXTENSIONS } from "@/common/constants"; - -import routes from "@/Utils/request/api"; -import query from "@/Utils/request/query"; - -export interface FileBlockProps { - file: FileUploadModel; - fileManager: FileManagerResult; - associating_id: string; - editable: boolean; - archivable?: boolean; -} - -export default function FileBlock(props: FileBlockProps) { - const { - file, - fileManager, - associating_id, - editable = false, - archivable = false, - } = props; - - const filetype = fileManager.getFileType(file); - - const { data: fileData } = useQuery({ - queryKey: ["file", { id: file.id, type: fileManager.type, associating_id }], - queryFn: query(routes.retrieveUpload, { - queryParams: { file_type: fileManager.type, associating_id }, - pathParams: { id: file.id || "" }, - }), - enabled: filetype === "AUDIO" && !file.is_archived, - }); - - const icons: Record = { - AUDIO: "l-volume", - IMAGE: "l-image", - PRESENTATION: "l-presentation-play", - VIDEO: "l-video", - UNKNOWN: "l-file-medical", - DOCUMENT: "l-file-medical", - }; - - const archived = file.is_archived; - - return ( -
-
-
- -
-
-
- {file.name} - {file.extension} {file.is_archived && "(Archived)"} -
-
- {dayjs( - file.is_archived ? file.archived_datetime : file.created_date, - ).format("DD MMM YYYY, hh:mm A")}{" "} - by{" "} - {file.is_archived - ? file.archived_by?.username - : file.uploaded_by?.username} -
-
-
-
- {filetype === "AUDIO" && !file.is_archived && ( -
-
- )} - {!file.is_archived && - (fileManager.isPreviewable(file) ? ( - - ) : ( - - ))} -
- {!file.is_archived && editable && ( - - )} - {(file.is_archived || editable) && archivable && ( - - )} -
-
-
- ); -} diff --git a/src/components/Files/FileUpload.tsx b/src/components/Files/FileUpload.tsx deleted file mode 100644 index 54881c80c0a..00000000000 --- a/src/components/Files/FileUpload.tsx +++ /dev/null @@ -1,413 +0,0 @@ -import { useQuery, useQueryClient } from "@tanstack/react-query"; -import { Loader2 } from "lucide-react"; -import { ReactNode, useState } from "react"; -import { useTranslation } from "react-i18next"; - -import CareIcon, { IconName } from "@/CAREUI/icons/CareIcon"; - -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; - -import Pagination from "@/components/Common/Pagination"; -import Tabs from "@/components/Common/Tabs"; -import FileBlock from "@/components/Files/FileBlock"; -import { FileUploadModel } from "@/components/Patient/models"; - -import useAuthUser from "@/hooks/useAuthUser"; -import useFileManager from "@/hooks/useFileManager"; -import useFileUpload from "@/hooks/useFileUpload"; - -import { RESULTS_PER_PAGE_LIMIT } from "@/common/constants"; - -import routes from "@/Utils/request/api"; -import query from "@/Utils/request/query"; - -export const LinearProgressWithLabel = (props: { value: number }) => { - return ( -
-
-
-
-
-
-
-

{`${Math.round(props.value)}%`}

-
-
- ); -}; - -interface FileUploadProps { - type: string; - patientId?: string; - encounterId?: string; - consentId?: string; - allowAudio?: boolean; - sampleId?: string; - claimId?: string; - className?: string; - hideUpload?: boolean; -} - -export interface ModalDetails { - name?: string; - id?: string; - reason?: string; - userArchived?: string; - archiveTime?: string; - associatedId?: string; -} - -export interface StateInterface { - open: boolean; - isImage: boolean; - name: string; - extension: string; - zoom: number; - isZoomInDisabled: boolean; - isZoomOutDisabled: boolean; - rotation: number; - id?: string; - associating_id?: string; -} - -export const FileUpload = (props: FileUploadProps) => { - const { t } = useTranslation(); - const { - encounterId, - patientId, - consentId, - type, - sampleId, - claimId, - allowAudio, - hideUpload, - } = props; - const [currentPage, setCurrentPage] = useState(1); - const [offset, setOffset] = useState(0); - const [tab, setTab] = useState("UNARCHIVED"); - const authUser = useAuthUser(); - const queryClient = useQueryClient(); - - const handlePagination = (page: number, limit: number) => { - const offset = (page - 1) * limit; - setCurrentPage(page); - setOffset(offset); - }; - - const UPLOAD_HEADING: { [index: string]: string } = { - PATIENT: t("upload_headings__patient"), - CONSULTATION: t("upload_headings__consultation"), - SAMPLE_MANAGEMENT: t("upload_headings__sample_report"), - CLAIM: t("upload_headings__supporting_info"), - }; - const VIEW_HEADING: { [index: string]: string } = { - PATIENT: t("file_list_headings__patient"), - CONSULTATION: t("file_list_headings__consultation"), - SAMPLE_MANAGEMENT: t("file_list_headings__sample_report"), - CLAIM: t("file_list_headings__supporting_info"), - }; - - const associatedId = - { - PATIENT: patientId, - CONSENT_RECORD: consentId, - ENCOUNTER: encounterId, - SAMPLE_MANAGEMENT: sampleId, - CLAIM: claimId, - }[type] || ""; - - const refetchAll = () => { - queryClient.invalidateQueries({ - queryKey: ["viewUpload", "active", type, associatedId], - }); - queryClient.invalidateQueries({ - queryKey: ["viewUpload", "archived", type, associatedId], - }); - if (type === "consultation") { - queryClient.invalidateQueries({ - queryKey: ["viewUpload", "discharge_summary", associatedId], - }); - } - }; - - const { data: activeFiles, isLoading: activeFilesLoading } = useQuery({ - queryKey: ["viewUpload", "active", type, associatedId, offset], - queryFn: query(routes.viewUpload, { - queryParams: { - file_type: type, - associating_id: associatedId, - is_archived: false, - limit: RESULTS_PER_PAGE_LIMIT, - offset: offset, - }, - }), - }); - - const { data: archivedFiles, isLoading: archivedFilesLoading } = useQuery({ - queryKey: ["viewUpload", "archived", type, associatedId, offset], - queryFn: query(routes.viewUpload, { - queryParams: { - file_type: type, - associating_id: associatedId, - is_archived: true, - limit: RESULTS_PER_PAGE_LIMIT, - offset: offset, - }, - }), - }); - - const { data: dischargeSummary, isLoading: dischargeSummaryLoading } = - useQuery({ - queryKey: ["viewUpload", "discharge_summary", associatedId, offset], - queryFn: query(routes.viewUpload, { - queryParams: { - file_type: "discharge_summary", - associating_id: associatedId, - is_archived: false, - limit: RESULTS_PER_PAGE_LIMIT, - offset: offset, - silent: true, - }, - }), - enabled: type === "consultation", - }); - - const queries = { - UNARCHIVED: { data: activeFiles, isLoading: activeFilesLoading }, - ARCHIVED: { data: archivedFiles, isLoading: archivedFilesLoading }, - DISCHARGE_SUMMARY: { - data: dischargeSummary, - isLoading: dischargeSummaryLoading, - }, - }; - - const loading = Object.values(queries).some((q) => q.isLoading); - const fileQuery = queries[tab as keyof typeof queries]; - - const tabs = [ - { text: "Active Files", value: "UNARCHIVED" }, - { text: "Archived Files", value: "ARCHIVED" }, - ...(dischargeSummary?.results?.length - ? [ - { - text: "Discharge Summary", - value: "DISCHARGE_SUMMARY", - }, - ] - : []), - ]; - - const fileUpload = useFileUpload({ - type, - allowedExtensions: [ - "jpg", - "jpeg", - "png", - "gif", - "bmp", - "tiff", - "mp4", - "mov", - "avi", - "wmv", - "mp3", - "wav", - "ogg", - "txt", - "csv", - "rtf", - "doc", - "odt", - "pdf", - "xls", - "xlsx", - "ods", - "pdf", - ], - allowNameFallback: false, - onUpload: refetchAll, - }); - - const fileManager = useFileManager({ - type, - onArchive: refetchAll, - onEdit: refetchAll, - uploadedFiles: - fileQuery?.data?.results - .slice() - .reverse() - .map((file) => ({ - ...file, - associating_id: associatedId, - })) || [], - }); - const dischargeSummaryFileManager = useFileManager({ - type: "DISCHARGE_SUMMARY", - onArchive: refetchAll, - onEdit: refetchAll, - }); - - const uploadButtons: { - name: string; - icon: IconName; - onClick?: () => void; - children?: ReactNode; - show?: boolean; - id: string; - }[] = [ - { - name: t("choose_file"), - icon: "l-file-upload-alt", - children: , - id: "upload-file", - }, - { - name: t("open_camera"), - icon: "l-camera", - onClick: fileUpload.handleCameraCapture, - id: "open-webcam", - }, - { - name: t("record"), - icon: "l-microphone", - onClick: fileUpload.handleAudioCapture, - show: allowAudio, - id: "record-audio", - }, - ]; - return ( -
- {fileUpload.Dialogues} - {fileManager.Dialogues} - {dischargeSummaryFileManager.Dialogues} - {!hideUpload && ( - <> -

{UPLOAD_HEADING[type]}

- {fileUpload.files[0] ? ( -
-
- - - {fileUpload.files[0].name} - - -
- - fileUpload.setFileName(e.target.value)} - /> - {fileUpload.error && ( -

{fileUpload.error}

- )} -
- - -
- {!!fileUpload.progress && ( - - )} -
- ) : ( -
- {uploadButtons - .filter((b) => b.show !== false) - .map((button, i) => ( - - ))} -
- )} - - )} -
-

{VIEW_HEADING[type]}

- setTab(v.toString())} - currentTab={tab} - /> -
-
- {!(fileQuery?.data?.results || []).length && loading && ( -
- )} - {fileQuery?.data?.results.map((item: FileUploadModel) => ( - - ))} - {!(fileQuery?.data?.results || []).length && ( -
-
- {t("no_files_found", { type: tab.toLowerCase() })} -
-
- )} -
- {(fileQuery?.data?.count ?? 0) > RESULTS_PER_PAGE_LIMIT && ( -
- -
- )} -
- ); -}; diff --git a/src/components/Form/FieldValidators.tsx b/src/components/Form/FieldValidators.tsx deleted file mode 100644 index 989a7aa6688..00000000000 --- a/src/components/Form/FieldValidators.tsx +++ /dev/null @@ -1 +0,0 @@ -export type FieldError = string | undefined; diff --git a/src/components/Form/FormFields/Autocomplete.tsx b/src/components/Form/FormFields/Autocomplete.tsx deleted file mode 100644 index 7e00c89fc7c..00000000000 --- a/src/components/Form/FormFields/Autocomplete.tsx +++ /dev/null @@ -1,225 +0,0 @@ -import { - Combobox, - ComboboxButton, - ComboboxInput, - ComboboxOption, - ComboboxOptions, -} from "@headlessui/react"; -import { ReactNode, useEffect, useState } from "react"; -import { useTranslation } from "react-i18next"; - -import CareIcon from "@/CAREUI/icons/CareIcon"; - -import { DropdownTransition } from "@/components/Common/HelperComponents"; -import { dropdownOptionClassNames } from "@/components/Form/MultiSelectMenuV2"; - -import { classNames } from "@/Utils/utils"; - -type OptionCallback = (option: T) => R; - -type AutocompleteProps = { - id?: string; - options: readonly T[]; - disabled?: boolean | undefined; - value: V | undefined; - placeholder?: string; - optionLabel: OptionCallback; - optionIcon?: OptionCallback; - optionImage?: OptionCallback; - optionValue?: OptionCallback; - optionDescription?: OptionCallback; - optionDisabled?: OptionCallback; - className?: string; - minQueryLength?: number; - onQuery?: (query: string) => void; - requiredError?: boolean; - isLoading?: boolean; - allowRawInput?: boolean; - error?: string; -} & ( - | { - required?: false; - onChange: OptionCallback; - } - | { - required: true; - onChange: OptionCallback; - } -); - -/** - * Avoid using this component directly. Use `AutocompleteFormField` instead as - * its API is easier to use and compliant with `FormField` based components. - * - * Use this only when you want to hack into the design and get more - * customizability. - */ -export const Autocomplete = (props: AutocompleteProps) => { - const { t } = useTranslation(); - const [query, setQuery] = useState(""); // Ensure lower case - - useEffect(() => { - props.onQuery?.(query); - }, [query]); - - const mappedOptions = props.options.map((option) => { - const label = props.optionLabel(option); - const description = props.optionDescription?.(option); - return { - label, - description, - search: label.toLowerCase(), - icon: props.optionIcon?.(option), - image: props.optionImage?.(option), - value: props.optionValue ? props.optionValue(option) : option, - disabled: props.optionDisabled?.(option), - }; - }); - - const getOptions = () => { - if (!query) return mappedOptions; - - const knownOption = mappedOptions.find( - (o) => o.value == props.value || o.label == props.value, - ); - - if (knownOption) return mappedOptions; - return [ - { - label: query, - description: undefined, - search: query.toLowerCase(), - icon: , - image: undefined, - value: query, - disabled: undefined, - }, - ...mappedOptions, - ]; - }; - - const options = props.allowRawInput ? getOptions() : mappedOptions; - - const value = options.find((o) => props.value == o.value); - - const filteredOptions = - props.onQuery === undefined - ? options.filter((o) => o.search.includes(query)) - : options; - - return ( -
- props.onChange(selection?.value)} - > -
-
- value?.label || ""} - onChange={(event) => setQuery(event.target.value.toLowerCase())} - onBlur={() => value && setQuery("")} - autoComplete="off" - /> - {!props.disabled && ( - -
- {value?.icon} - - {value && !props.isLoading && !props.required && ( -
- { - e.preventDefault(); - props.onChange(undefined); - }} - /> - - {t("clear_selection")} - -
- )} - - {props.isLoading ? ( - - ) : ( - - )} -
-
- )} -
- - - - {props.minQueryLength && query.length < props.minQueryLength ? ( -
- {`Please enter at least ${props.minQueryLength} characters to search`} -
- ) : filteredOptions.length === 0 ? ( -
- No options found -
- ) : ( - filteredOptions.map((option, index) => ( - - {({ focus }) => ( -
- {option?.image} -
-
- {option.label} - {option.icon} -
- {option.description && ( -
- {option.description} -
- )} -
-
- )} -
- )) - )} -
-
-
-
-
- ); -}; diff --git a/src/components/Form/FormFields/FormField.tsx b/src/components/Form/FormFields/FormField.tsx deleted file mode 100644 index f3ad0559e44..00000000000 --- a/src/components/Form/FormFields/FormField.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { FieldError } from "@/components/Form/FieldValidators"; -import { FormFieldBaseProps } from "@/components/Form/FormFields/Utils"; - -import { classNames } from "@/Utils/utils"; - -type LabelProps = { - id?: string | undefined; - required?: boolean; - htmlFor?: string; - children: React.ReactNode; - className?: string | undefined; - noPadding?: boolean; -}; - -export const FieldLabel = (props: LabelProps) => { - return ( - - ); -}; - -type ErrorProps = { - error: FieldError; - className?: string | undefined; -}; - -export const FieldErrorText = (props: ErrorProps) => { - return ( - - {props.error} - - ); -}; - -/** - * @deprecated use shadcn/ui's solution for form fields instead along with react-hook-form - */ -const FormField = ({ - field, - ...props -}: { - field?: FormFieldBaseProps; - children: React.ReactNode; -}) => { - return ( -
-
- {field?.label && ( - - {field?.label} - - )} - {field?.labelSuffix && ( - {field?.labelSuffix} - )} -
-
{props.children}
- -
- ); -}; - -export default FormField; diff --git a/src/components/Form/FormFields/RadioFormField.tsx b/src/components/Form/FormFields/RadioFormField.tsx deleted file mode 100644 index ca205fcccad..00000000000 --- a/src/components/Form/FormFields/RadioFormField.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { ChangeEventHandler, ReactNode } from "react"; - -import FormField from "@/components/Form/FormFields/FormField"; -import { - FormFieldBaseProps, - useFormFieldPropsResolver, -} from "@/components/Form/FormFields/Utils"; - -import { classNames } from "@/Utils/utils"; - -type Props = FormFieldBaseProps & { - options: readonly T[]; - optionLabel: (option: T) => React.ReactNode; - optionValue: (option: T) => V; - containerClassName?: string; - unselectLabel?: string; - layout?: "vertical" | "horizontal" | "grid" | "auto"; -}; - -/** - * @deprecated use shadcn/ui's radio-group instead - */ -const RadioFormField = (props: Props) => { - const field = useFormFieldPropsResolver(props); - return ( - -
- {props.unselectLabel && ( -
- field.handleChange(null)} - /> - -
- )} - {props.options.map((option) => { - const value = props.optionValue(option); - return ( - field.handleChange(e.target.value as V)} - /> - ); - })} -
-
- ); -}; - -export default RadioFormField; - -export const RadioInput = (props: { - label?: ReactNode; - id?: string; - name?: string; - value?: string; - checked?: boolean; - onChange?: ChangeEventHandler; -}) => { - return ( -
- props.onChange?.(e)} - /> - -
- ); -}; diff --git a/src/components/Form/FormFields/SelectFormField.tsx b/src/components/Form/FormFields/SelectFormField.tsx deleted file mode 100644 index 6b95550299f..00000000000 --- a/src/components/Form/FormFields/SelectFormField.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import FormField from "@/components/Form/FormFields/FormField"; -import { - FormFieldBaseProps, - useFormFieldPropsResolver, -} from "@/components/Form/FormFields/Utils"; -import SelectMenuV2 from "@/components/Form/SelectMenuV2"; - -type OptionCallback = (option: T) => R; - -type SelectFormFieldProps = FormFieldBaseProps & { - placeholder?: React.ReactNode; - options: readonly T[]; - position?: "above" | "below"; - optionLabel: OptionCallback; - optionSelectedLabel?: OptionCallback; - optionDescription?: OptionCallback; - optionIcon?: OptionCallback; - optionValue?: OptionCallback; - optionDisabled?: OptionCallback; - inputClassName?: string; -}; - -/** - * @deprecated use shadcn/ui's select instead - */ -export const SelectFormField = (props: SelectFormFieldProps) => { - const field = useFormFieldPropsResolver(props); - return ( - - field.handleChange(value)} - position={props.position} - placeholder={props.placeholder} - optionLabel={props.optionLabel} - inputClassName={props.inputClassName} - optionSelectedLabel={props.optionSelectedLabel} - optionDescription={props.optionDescription} - optionIcon={props.optionIcon} - optionValue={props.optionValue} - optionDisabled={props.optionDisabled} - requiredError={field.error ? props.required : false} - /> - - ); -}; diff --git a/src/components/Form/FormFields/TextFormField.tsx b/src/components/Form/FormFields/TextFormField.tsx deleted file mode 100644 index 18a4de18341..00000000000 --- a/src/components/Form/FormFields/TextFormField.tsx +++ /dev/null @@ -1,196 +0,0 @@ -import { - DetailedHTMLProps, - InputHTMLAttributes, - forwardRef, - useState, -} from "react"; - -import CareIcon from "@/CAREUI/icons/CareIcon"; - -import FormField from "@/components/Form/FormFields/FormField"; -import { - FormFieldBaseProps, - useFormFieldPropsResolver, -} from "@/components/Form/FormFields/Utils"; - -import { classNames } from "@/Utils/utils"; - -export type TextFormFieldProps = FormFieldBaseProps & - Omit< - DetailedHTMLProps, HTMLInputElement>, - "onChange" - > & { - inputClassName?: string | undefined; - removeDefaultClasses?: true | undefined; - leading?: React.ReactNode | undefined; - trailing?: React.ReactNode | undefined; - leadingFocused?: React.ReactNode | undefined; - trailingFocused?: React.ReactNode | undefined; - trailingPadding?: string | undefined; - leadingPadding?: string | undefined; - suggestions?: string[]; - clearable?: boolean | undefined; - }; - -/** - * @deprecated use shadcn/ui's Input instead - */ -const TextFormField = forwardRef((props: TextFormFieldProps, ref) => { - const field = useFormFieldPropsResolver(props); - const { leading, trailing } = props; - const leadingFocused = props.leadingFocused || props.leading; - const trailingFocused = props.trailingFocused || props.trailing; - const hasLeading = !!(leading || leadingFocused); - const hasTrailing = !!(trailing || trailingFocused); - const hasIcon = hasLeading || hasTrailing; - const [showPassword, setShowPassword] = useState(false); - - const getPasswordFieldType = () => { - return showPassword ? "text" : "password"; - }; - - const minError = - typeof props.min !== "undefined" && - typeof field.value !== "undefined" && - parseFloat(`${props.min}`) > parseFloat(`${field.value}`) - ? `Value can not be smaller than ${props.min}` - : undefined; - const maxError = - typeof props.max !== "undefined" && - typeof field.value !== "undefined" && - parseFloat(`${props.max}`) < parseFloat(`${field.value}`) - ? `Value can not be greater than ${props.max}` - : undefined; - - const labelSuffixWithThreshold = ( -
- {field.labelSuffix} -
- ); - - let child = ( -
- } - id={field.id} - className={classNames( - "cui-input-base peer", - hasLeading && (props.leadingPadding || "pl-10"), - hasTrailing && (props.trailingPadding || "pr-10"), - field.error && "border-danger-500", - props.inputClassName, - )} - disabled={field.disabled} - type={props.type === "password" ? getPasswordFieldType() : props.type} - name={field.name} - value={field.value} - required={field.required} - onChange={(e) => field.handleChange(e.target.value)} - /> - {props.clearable && field.value && ( - - )} -
- ); - - if (props.type === "password") { - child = ( -
- {child} - -
- ); - } - - if (hasIcon) { - const _leading = - leading === leadingFocused ? ( -
- {leading} -
- ) : ( - <> -
- {leading} -
-
- {leadingFocused} -
- - ); - const _trailing = - trailing === trailingFocused ? ( -
- {trailing} -
- ) : ( - <> -
- {trailing} -
-
- {trailingFocused} -
- - ); - - child = ( -
- {(leading || leadingFocused) && _leading} - {child} - {(trailing || trailingFocused) && _trailing} -
- ); - } - - if ( - props.suggestions?.length && - !props.suggestions.includes(`${field.value}`) - ) { - child = ( -
- {child} -
    - {props.suggestions.map((suggestion) => ( -
  • field.handleChange(suggestion)} - > - {suggestion} -
  • - ))} -
-
- ); - } - - return ( - - {child} - - ); -}); -TextFormField.displayName = "TextFormField"; - -export default TextFormField; diff --git a/src/components/Form/FormFields/Utils.ts b/src/components/Form/FormFields/Utils.ts deleted file mode 100644 index 1e88bcbd6a0..00000000000 --- a/src/components/Form/FormFields/Utils.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { FocusEvent } from "react"; - -import { FieldError } from "@/components/Form/FieldValidators"; - -export type FieldChangeEvent = { name: string; value: T }; -export type FieldChangeEventHandler = (event: FieldChangeEvent) => void; - -/** - * The base props for a form field. - * - * If a form context is provided, the field will be registered with the form - * and the onChange, value, and error props will be ignored. - * - * If a form context is not provided, the field will be treated as a standalone - * field. - * - * @template T The type of the field value. - * @template Form The type of the form details. - */ -export type FormFieldBaseProps = { - label?: React.ReactNode; - labelSuffix?: React.ReactNode; - disabled?: boolean; - className?: string; - required?: boolean; - labelClassName?: string; - errorClassName?: string; - name: string; - validate?: undefined; - id?: string; - onChange: FieldChangeEventHandler; - value?: T; - error?: FieldError; - onFocus?: (event: FocusEvent) => void; - onBlur?: (event: FocusEvent) => void; -}; - -/** - * Resolves the props for a form field. - * If a form context is provided, the field will be registered with the form. - * Otherwise, the field will be treated as a standalone field. - * - * @param props The props for the field. - * @returns The resolved props along with a handleChange function. - */ -export const useFormFieldPropsResolver = (props: FormFieldBaseProps) => { - const handleChange = (value: T) => - props.onChange({ name: props.name, value }); - - return { - ...props, - id: props.id ?? props.name, - name: props.name, - onChange: props.onChange, - value: props.value, - error: props.error, - handleChange, - }; -}; diff --git a/src/components/Form/MultiSelectMenuV2.tsx b/src/components/Form/MultiSelectMenuV2.tsx deleted file mode 100644 index b67da3656f4..00000000000 --- a/src/components/Form/MultiSelectMenuV2.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { ReactNode } from "react"; - -import CareIcon from "@/CAREUI/icons/CareIcon"; - -import { classNames } from "@/Utils/utils"; - -interface MultiSelectOptionChipProps { - label: ReactNode; - onRemove?: () => void; -} - -export const MultiSelectOptionChip = ({ - label, - onRemove, -}: MultiSelectOptionChipProps) => { - return ( - -

{label}

- {onRemove && ( -

{ - e.stopPropagation(); - onRemove(); - }} - > - -

- )} -
- ); -}; - -interface OptionRenderPropArg { - focus: boolean; - selected: boolean; - disabled: boolean; -} - -export const dropdownOptionClassNames = ({ - focus, - selected, - disabled, -}: OptionRenderPropArg) => { - return classNames( - "group/option relative w-full cursor-default select-none p-4 text-sm transition-colors duration-75 ease-in-out", - !disabled && focus && "bg-primary-500 text-white", - !disabled && !focus && selected && "text-primary-500", - !disabled && !focus && !selected && "text-secondary-900", - disabled && "cursor-not-allowed text-secondary-600", - selected ? "font-semibold" : "font-normal", - ); -}; diff --git a/src/components/Form/SelectMenuV2.tsx b/src/components/Form/SelectMenuV2.tsx deleted file mode 100644 index a13d03607c6..00000000000 --- a/src/components/Form/SelectMenuV2.tsx +++ /dev/null @@ -1,188 +0,0 @@ -import { - Label, - Listbox, - ListboxButton, - ListboxOption, - ListboxOptions, -} from "@headlessui/react"; -import { ReactNode } from "react"; - -import CareIcon from "@/CAREUI/icons/CareIcon"; - -import { dropdownOptionClassNames } from "@/components/Form/MultiSelectMenuV2"; - -import { classNames } from "@/Utils/utils"; - -type OptionCallback = (option: T) => R; - -type SelectMenuProps = { - id?: string; - options: readonly T[]; - disabled?: boolean | undefined; - value: V | undefined; - placeholder?: ReactNode; - position?: "above" | "below"; - optionLabel: OptionCallback; - optionSelectedLabel?: OptionCallback; - optionDescription?: OptionCallback; - optionIcon?: OptionCallback; - optionValue?: OptionCallback; - optionDisabled?: OptionCallback; - showIconWhenSelected?: boolean; - showChevronIcon?: boolean; - className?: string; - inputClassName?: string; - requiredError?: boolean; - onFocus?: () => void; - onBlur?: () => void; -} & ( - | { - required?: false; - onChange: OptionCallback; - } - | { - required: true; - onChange: OptionCallback; - } -); - -/** - * Avoid using this component directly. Use `SelectFormField` instead as its API - * is easier to use and compliant with `FormField` based components. - * - * Use this only when you want to hack into the design and get more - * customizability. - */ -const SelectMenuV2 = (props: SelectMenuProps) => { - const valueOptions = props.options.map((option) => { - const label = props.optionLabel(option); - return { - label, - selectedLabel: props.optionSelectedLabel - ? props.optionSelectedLabel(option) - : label, - description: props.optionDescription?.(option), - icon: props.optionIcon?.(option), - value: props.optionValue ? props.optionValue(option) : option, - disabled: props.optionDisabled?.(option), - }; - }); - - const showChevronIcon = props.showChevronIcon ?? true; - - const placeholder = - valueOptions?.length > 0 ? (props.placeholder ?? "Select") : "No options"; - const defaultOption = { - label: placeholder, - selectedLabel: ( - {placeholder} - ), - description: undefined, - icon: undefined, - value: undefined, - disabled: undefined, - }; - - const options = props.required - ? valueOptions - : [defaultOption, ...valueOptions]; - - const value = options.find((o) => props.value == o.value) ?? defaultOption; - - return ( -
- props.onChange(selection.value)} - > - <> - -
- -
-
- {props.showIconWhenSelected && value?.icon && ( -
- {value.icon} -
- )} -

- {value.selectedLabel} -

-
- {showChevronIcon && ( - - )} -
-
-
- - {options.map((option, index) => ( - - {({ focus, selected }) => ( -
-
- {option.label} - {props.optionIcon - ? option.icon - : selected && ( - - )} -
- {option.description && ( - - {option.description} - - )} -
- )} -
- ))} -
-
-
- -
-
- ); -}; - -export default SelectMenuV2; diff --git a/src/components/Patient/FileUploadPage.tsx b/src/components/Patient/FileUploadPage.tsx deleted file mode 100644 index 83bce32954c..00000000000 --- a/src/components/Patient/FileUploadPage.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { useTranslation } from "react-i18next"; - -import Page from "@/components/Common/Page"; -import { FileUpload } from "@/components/Files/FileUpload"; - -export default function FileUploadPage(props: { - facilityId: string; - patientId: string; - encounterId?: string; - type: "encounter" | "patient"; -}) { - const { patientId, encounterId, type } = props; - const { t } = useTranslation(); - - return ( - - - - ); -} diff --git a/src/components/Resource/ResourceCreate.tsx b/src/components/Resource/ResourceCreate.tsx deleted file mode 100644 index 49b1d3b98a7..00000000000 --- a/src/components/Resource/ResourceCreate.tsx +++ /dev/null @@ -1,445 +0,0 @@ -import { zodResolver } from "@hookform/resolvers/zod"; -import { useMutation, useQuery } from "@tanstack/react-query"; -import { Link, navigate, useQueryParams } from "raviger"; -import { useState } from "react"; -import { useForm } from "react-hook-form"; -import { useTranslation } from "react-i18next"; -import { toast } from "sonner"; -import * as z from "zod"; - -import Card from "@/CAREUI/display/Card"; -import CareIcon from "@/CAREUI/icons/CareIcon"; - -import { Alert, AlertDescription } from "@/components/ui/alert"; -import Autocomplete from "@/components/ui/autocomplete"; -import { Button } from "@/components/ui/button"; -import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form"; -import { Input } from "@/components/ui/input"; -import { PhoneInput } from "@/components/ui/phone-input"; -import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { Separator } from "@/components/ui/separator"; -import { Textarea } from "@/components/ui/textarea"; - -import Loading from "@/components/Common/Loading"; -import Page from "@/components/Common/Page"; - -import useAppHistory from "@/hooks/useAppHistory"; -import useAuthUser from "@/hooks/useAuthUser"; - -import { RESOURCE_CATEGORY_CHOICES } from "@/common/constants"; - -import routes from "@/Utils/request/api"; -import mutate from "@/Utils/request/mutate"; -import query from "@/Utils/request/query"; -import validators from "@/Utils/validators"; -import facilityApi from "@/types/facility/facilityApi"; -import { ResourceRequest } from "@/types/resourceRequest/resourceRequest"; - -interface ResourceProps { - facilityId: number; -} - -export default function ResourceCreate(props: ResourceProps) { - const [facilitySearch, setFacilitySearch] = useState(""); - const { goBack } = useAppHistory(); - const { facilityId } = props; - const { t } = useTranslation(); - const [{ related_patient }] = useQueryParams(); - const authUser = useAuthUser(); - - const resourceFormSchema = z.object({ - category: z.string().min(1, { message: t("field_required") }), - assigned_facility: z - .object({ - id: z.string(), - name: z.string(), - }) - .nullable(), - emergency: z.enum(["true", "false"]), - title: z.string().min(1, { message: t("field_required") }), - reason: z.string().min(1, { message: t("field_required") }), - referring_facility_contact_name: z - .string() - .min(1, { message: t("field_required") }), - referring_facility_contact_number: validators().phoneNumber.required, - priority: z.number().default(1), - }); - - type ResourceFormValues = z.infer; - - const { data: patientData } = useQuery({ - queryKey: ["patient", related_patient], - queryFn: query(routes.patient.getPatient, { - pathParams: { id: String(related_patient) }, - }), - enabled: !!related_patient, - }); - - const form = useForm({ - resolver: zodResolver(resourceFormSchema), - defaultValues: { - category: "", - assigned_facility: null, - emergency: "false" as const, - title: "", - reason: "", - referring_facility_contact_name: "", - referring_facility_contact_number: "", - priority: 1, - }, - }); - - const { mutate: createResource, isPending } = useMutation({ - mutationFn: mutate(routes.createResource), - onSuccess: (data: ResourceRequest) => { - toast.success(t("resource_created_successfully")); - navigate(`/facility/${facilityId}/resource/${data.id}`); - }, - }); - - const onSubmit = (data: ResourceFormValues) => { - createResource({ - status: "PENDING", - category: data.category, - origin_facility: String(props.facilityId), - assigned_facility: data.assigned_facility?.id || null, - approving_facility: null, - emergency: data.emergency === "true", - title: data.title, - reason: data.reason, - referring_facility_contact_name: data.referring_facility_contact_name, - referring_facility_contact_number: data.referring_facility_contact_number, - related_patient: related_patient, - priority: data.priority, - }); - }; - - const { data: facilities } = useQuery({ - queryKey: ["facilities", facilitySearch], - queryFn: query.debounced(facilityApi.getAllFacilities, { - queryParams: { - search_text: facilitySearch, - limit: 50, - }, - }), - }); - - const facilityOptions = facilities?.results.map((facility) => ({ - label: facility.name, - value: facility.id, - })); - - const fillMyDetails = () => { - form.setValue( - "referring_facility_contact_name", - `${authUser.first_name} ${authUser.last_name}`.trim(), - ); - if (authUser.phone_number) { - form.setValue("referring_facility_contact_number", authUser.phone_number); - } - }; - - if (isPending) { - return ; - } - - return ( - -
- -
- - {patientData && ( - -
- - - - {t("linked_patient")}:{" "} - {patientData.name} - - -
-
- )} - -
-
-

- {t("basic_information")} -

-

- {t("resource_request_basic_info_description")} -

-
- -
- ( - - - {t("facility_for_care_support")} - - - { - const facility = - facilities?.results.find( - (f) => f.id === value, - ) ?? null; - form.setValue("assigned_facility", facility); - }} - /> - - - {t("select_facility_description")} - - - - )} - /> - - ( - - {t("is_this_an_emergency")} - - - - - - - - {t("yes")} - - - - - - - - {t("no")} - - - - - - {t("emergency_description")} - - - - )} - /> -
- - ( - - {t("category")} - - - {t("category_description")} - - - - )} - /> -
- - - -
-
-

- {t("request_details")} -

-

- {t("resource_request_details_description")} -

-
- - ( - - {t("request_title")} - - field.onChange(value)} - /> - - - {t("request_title_description")} - - - - )} - /> - - ( - - {t("request_reason")} - -