From e68a637fd49cd9635ff4b651a3159aadb13c28e5 Mon Sep 17 00:00:00 2001 From: rithviknishad Date: Mon, 7 Oct 2024 11:15:49 +0530 Subject: [PATCH] Drops all remaining usages of `useDispatch` --- .../Facility/ConsultationDetails/index.tsx | 163 +++++++--------- src/Components/Facility/DischargeModal.tsx | 25 +-- .../FacilityFilter/DistrictSelect.tsx | 38 ++-- src/Redux/Reducer.tsx | 42 ----- src/Redux/actions.tsx | 19 -- src/Redux/api.tsx | 2 + src/Redux/fireRequest.tsx | 175 ------------------ src/index.tsx | 11 +- 8 files changed, 97 insertions(+), 378 deletions(-) delete mode 100644 src/Redux/Reducer.tsx delete mode 100644 src/Redux/actions.tsx delete mode 100644 src/Redux/fireRequest.tsx diff --git a/src/Components/Facility/ConsultationDetails/index.tsx b/src/Components/Facility/ConsultationDetails/index.tsx index 65cb1331291..3b39a274dc6 100644 --- a/src/Components/Facility/ConsultationDetails/index.tsx +++ b/src/Components/Facility/ConsultationDetails/index.tsx @@ -1,8 +1,6 @@ import { GENDER_TYPES } from "../../../Common/constants"; import { ConsultationModel } from "../models"; -import { getConsultation, getPatient } from "../../../Redux/actions"; -import { statusType, useAbortableEffect } from "../../../Common/utils"; -import { lazy, useCallback, useState } from "react"; +import { lazy, useCallback, useEffect, useState } from "react"; import DoctorVideoSlideover from "../DoctorVideoSlideover"; import { PatientModel } from "../../Patient/models"; import { @@ -13,7 +11,6 @@ import { } from "../../../Utils/utils"; import { Link, navigate, useQueryParams } from "raviger"; -import { useDispatch } from "react-redux"; import { triggerGoal } from "../../../Integrations/Plausible"; import useAuthUser from "../../../Common/hooks/useAuthUser"; import { ConsultationUpdatesTab } from "./ConsultationUpdatesTab"; @@ -40,6 +37,7 @@ import request from "../../../Utils/request/request"; import { CameraFeedPermittedUserTypes } from "../../../Utils/permissions"; import Error404 from "../../ErrorPages/404"; import { useTranslation } from "react-i18next"; +import useQuery from "../../../Utils/request/useQuery"; const Loading = lazy(() => import("../../Common/Loading")); const PageTitle = lazy(() => import("../../Common/PageTitle")); @@ -76,18 +74,11 @@ export const ConsultationDetails = (props: any) => { if (Object.keys(TABS).includes(props.tab.toUpperCase())) { tab = props.tab.toUpperCase() as keyof typeof TABS; } - const dispatch: any = useDispatch(); - const [isLoading, setIsLoading] = useState(false); const [showDoctors, setShowDoctors] = useState(false); const [qParams, _] = useQueryParams(); - - const [consultationData, setConsultationData] = useState( - {} as ConsultationModel, - ); const [patientData, setPatientData] = useState({}); const [abhaNumberData, setAbhaNumberData] = useState(); const [activeShiftingData, setActiveShiftingData] = useState>([]); - const [isCameraAttached, setIsCameraAttached] = useState(false); const getPatientGender = (patientData: any) => GENDER_TYPES.find((i) => i.id === patientData.gender)?.text; @@ -108,98 +99,86 @@ export const ConsultationDetails = (props: any) => { const authUser = useAuthUser(); - const fetchData = useCallback( - async (status: statusType) => { - setIsLoading(true); - const res = await dispatch(getConsultation(consultationId)); - if (!status.aborted) { - if (res?.data) { - const data: ConsultationModel = { - ...res.data, - symptoms_text: "", - }; - if (facilityId != data.facility || patientId != data.patient) { - navigate( - `/facility/${data.facility}/patient/${data.patient}/consultation/${data?.id}`, - ); - } - setConsultationData(data); + const consultationQuery = useQuery(routes.getConsultation, { + pathParams: { id: consultationId }, + onResponse: ({ data }) => { + if (!data) { + navigate("/not-found"); + return; + } + if (facilityId != data.facility || patientId != data.patient) { + navigate( + `/facility/${data.facility}/patient/${data.patient}/consultation/${data?.id}`, + ); + } + }, + }); - setIsCameraAttached( - await (async () => { - const bedId = data?.current_bed?.bed_object?.id; - if (!bedId) { - return false; - } - const { data: assetBeds } = await request(routes.listAssetBeds, { - query: { bed: bedId }, - }); - if (!assetBeds) { - return false; - } - return assetBeds.results.some( - (a) => a.asset_object.asset_class === "ONVIF", - ); - })(), - ); + const consultationData = consultationQuery.data; + const bedId = consultationData?.current_bed?.bed_object?.id; - // Get patient data - const id = res.data.patient; - const patientRes = await dispatch(getPatient({ id })); - if (patientRes?.data) { - const patientGender = getPatientGender(patientRes.data); - const patientAddress = getPatientAddress(patientRes.data); - const patientComorbidities = getPatientComorbidities( - patientRes.data, - ); - const data = { - ...patientRes.data, - gender: patientGender, - address: patientAddress, - comorbidities: patientComorbidities, - is_declared_positive: patientRes.data.is_declared_positive - ? "Yes" - : "No", - is_vaccinated: patientData.is_vaccinated ? "Yes" : "No", - }; + const isCameraAttached = useQuery(routes.listAssetBeds, { + prefetch: !!bedId, + query: { bed: bedId }, + }).data?.results.some((a) => a.asset_object.asset_class === "ONVIF"); - setPatientData(data); - } + const patientDataQuery = useQuery(routes.getPatient, { + pathParams: { id: consultationQuery.data?.patient ?? "" }, + prefetch: !!consultationQuery.data?.patient, + onResponse: ({ data }) => { + if (!data) { + return; + } + setPatientData({ + ...data, + gender: getPatientGender(data), + address: getPatientAddress(data), + comorbidities: getPatientComorbidities(data), + is_declared_positive: data.is_declared_positive ? "Yes" : "No", + is_vaccinated: patientData.is_vaccinated ? "Yes" : "No", + } as any); + }, + }); - // Get abha number data - const { data: abhaNumberData } = await request( - routes.abha.getAbhaNumber, - { - pathParams: { abhaNumberId: id ?? "" }, - silent: true, - }, - ); - setAbhaNumberData(abhaNumberData); + const fetchData = useCallback( + async (id: string) => { + // Get abha number data + const { data: abhaNumberData } = await request( + routes.abha.getAbhaNumber, + { + pathParams: { abhaNumberId: id ?? "" }, + silent: true, + }, + ); + setAbhaNumberData(abhaNumberData); - // Get shifting data - const shiftRequestsQuery = await request(routes.listShiftRequests, { - query: { patient: id }, - }); - if (shiftRequestsQuery.data?.results) { - setActiveShiftingData(shiftRequestsQuery.data.results); - } - } else { - navigate("/not-found"); - } - setIsLoading(false); + // Get shifting data + const shiftRequestsQuery = await request(routes.listShiftRequests, { + query: { patient: id }, + }); + if (shiftRequestsQuery.data?.results) { + setActiveShiftingData(shiftRequestsQuery.data.results); } }, - [consultationId, dispatch, patientData.is_vaccinated], + [consultationId, patientData.is_vaccinated], ); - useAbortableEffect((status: statusType) => { - fetchData(status); + useEffect(() => { + const id = patientDataQuery.data?.id; + if (!id) { + return; + } + fetchData(id); triggerGoal("Patient Consultation Viewed", { facilityId: facilityId, consultationId: consultationId, userId: authUser.id, }); - }, []); + }, [patientDataQuery.data?.id]); + + if (!consultationData || patientDataQuery.loading) { + return ; + } const consultationTabProps: ConsultationTabProps = { consultationId, @@ -215,10 +194,6 @@ export const ConsultationDetails = (props: any) => { const SelectedTab = TABS[tab]; - if (isLoading) { - return ; - } - const tabButtonClasses = (selected: boolean) => `capitalize min-w-max-content cursor-pointer font-bold whitespace-nowrap ${ selected === true @@ -308,7 +283,7 @@ export const ConsultationDetails = (props: any) => { patient={patientData} abhaNumber={abhaNumberData} consultation={consultationData} - fetchPatientData={fetchData} + fetchPatientData={() => patientDataQuery.refetch()} consultationId={consultationId} activeShiftingData={activeShiftingData} showAbhaProfile={qParams["show-abha-profile"] === "true"} diff --git a/src/Components/Facility/DischargeModal.tsx b/src/Components/Facility/DischargeModal.tsx index 5573fe883d0..786e23ec8a3 100644 --- a/src/Components/Facility/DischargeModal.tsx +++ b/src/Components/Facility/DischargeModal.tsx @@ -20,8 +20,6 @@ import { SelectFormField } from "../Form/FormFields/SelectFormField"; import TextAreaFormField from "../Form/FormFields/TextAreaFormField"; import TextFormField from "../Form/FormFields/TextFormField"; import dayjs from "../../Utils/dayjs"; -import { dischargePatient } from "../../Redux/actions"; -import { useDispatch } from "react-redux"; import { useMessageListener } from "../../Common/hooks/useMessageListener"; import useQuery from "../../Utils/request/useQuery"; import { useTranslation } from "react-i18next"; @@ -31,6 +29,7 @@ import routes from "../../Redux/api"; import { EditDiagnosesBuilder } from "../Diagnosis/ConsultationDiagnosisBuilder/ConsultationDiagnosisBuilder"; import Loading from "../Common/Loading"; import careConfig from "@careConfig"; +import request from "../../Utils/request/request"; interface PreDischargeFormInterface { new_discharge_reason: number | null; @@ -64,8 +63,6 @@ const DischargeModal = ({ death_datetime = dayjs().format("YYYY-MM-DDTHH:mm"), }: IProps) => { const { t } = useTranslation(); - - const dispatch: any = useDispatch(); const [preDischargeForm, setPreDischargeForm] = useState({ new_discharge_reason, @@ -175,19 +172,17 @@ const DischargeModal = ({ const submitAction = useConfirmedAction(async () => { setIsSendingDischargeApi(true); - const dischargeResponse = await dispatch( - dischargePatient( - { - ...preDischargeForm, - new_discharge_reason: discharge_reason, - discharge_date: dayjs(preDischargeForm.discharge_date).toISOString(), - }, - { id: consultationData.id }, - ), - ); + const { res } = await request(routes.dischargePatient, { + pathParams: { id: consultationData.id }, + body: { + ...preDischargeForm, + new_discharge_reason: discharge_reason, + discharge_date: dayjs(preDischargeForm.discharge_date).toISOString(), + }, + }); setIsSendingDischargeApi(false); - if (dischargeResponse?.status === 200) { + if (res?.ok) { Notification.Success({ msg: "Patient Discharged Successfully" }); afterSubmit?.(); } diff --git a/src/Components/Facility/FacilityFilter/DistrictSelect.tsx b/src/Components/Facility/FacilityFilter/DistrictSelect.tsx index 2d9c0701f13..633927efdb8 100644 --- a/src/Components/Facility/FacilityFilter/DistrictSelect.tsx +++ b/src/Components/Facility/FacilityFilter/DistrictSelect.tsx @@ -1,6 +1,5 @@ -import { useCallback } from "react"; -import { useDispatch } from "react-redux"; -import { getDistrictByName } from "../../../Redux/actions"; +import routes from "../../../Redux/api"; +import request from "../../../Utils/request/request"; import AutoCompleteAsync from "../../Form/AutoCompleteAsync"; interface DistrictSelectProps { @@ -13,29 +12,22 @@ interface DistrictSelectProps { } function DistrictSelect(props: DistrictSelectProps) { - const { name, errors, className, multiple, selected, setSelected } = props; - const dispatchAction: any = useDispatch(); - - const districtSearch = useCallback( - async (text: string) => { - const params = { limit: 50, offset: 0, district_name: text }; - const res = await dispatchAction(getDistrictByName(params)); - return res?.data?.results; - }, - [dispatchAction], - ); - return ( option.name} + name={props.name} + multiple={props.multiple} + selected={props.selected} + fetchData={async (search) => { + const { data } = await request(routes.getDistrictByName, { + query: { limit: 50, offset: 0, district_name: search }, + }); + return data?.results; + }} + onChange={props.setSelected} + optionLabel={(option) => option.name} compareBy="id" - error={errors} - className={className} + error={props.errors} + className={props.className} /> ); } diff --git a/src/Redux/Reducer.tsx b/src/Redux/Reducer.tsx deleted file mode 100644 index c7d4698b694..00000000000 --- a/src/Redux/Reducer.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { actions } from "./fireRequest"; - -const reducer = (state = {}, changeAction: any) => { - switch (changeAction.type) { - case actions.FETCH_REQUEST: { - const obj: any = Object.assign({}, state); - obj[changeAction.key] = { - isFetching: true, - error: false, - }; - return obj; - } - case actions.FETCH_REQUEST_SUCCESS: { - const obj: any = Object.assign({}, state); - obj[changeAction.key] = { - isFetching: false, - error: false, - data: changeAction.data, - }; - return obj; - } - case actions.FETCH_REQUEST_ERROR: { - const obj: any = Object.assign({}, state); - obj[changeAction.key] = { - isFetching: false, - error: true, - errorMessage: changeAction.error, - }; - return obj; - } - - case actions.SET_DATA: { - const obj: any = Object.assign({}, state); - obj[changeAction.key] = changeAction.value; - return obj; - } - - default: - return state; - } -}; -export default reducer; diff --git a/src/Redux/actions.tsx b/src/Redux/actions.tsx deleted file mode 100644 index 29fe2fed2ef..00000000000 --- a/src/Redux/actions.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { fireRequest } from "./fireRequest"; - -export const getPatient = (pathParam: object) => { - return fireRequest("getPatient", [], {}, pathParam); -}; - -// District/State/Local body/ward -export const getDistrictByName = (params: object) => { - return fireRequest("getDistrictByName", [], params, null); -}; - -// Consultation -export const getConsultation = (id: string) => { - return fireRequest("getConsultation", [], {}, { id: id }); -}; - -export const dischargePatient = (params: object, pathParams: object) => { - return fireRequest("dischargePatient", [], params, pathParams); -}; diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx index a0c3271386d..cd1a019e221 100644 --- a/src/Redux/api.tsx +++ b/src/Redux/api.tsx @@ -908,6 +908,8 @@ const routes = { }, getDistrictByName: { path: "/api/v1/district/", + method: "GET", + TRes: Type>(), }, getAllLocalBodyByDistrict: { path: "/api/v1/district/{id}/get_all_local_body/", diff --git a/src/Redux/fireRequest.tsx b/src/Redux/fireRequest.tsx deleted file mode 100644 index fe72b2af0d2..00000000000 --- a/src/Redux/fireRequest.tsx +++ /dev/null @@ -1,175 +0,0 @@ -import * as Notification from "../Utils/Notifications.js"; - -import { LocalStorageKeys } from "../Common/constants"; -import api from "./api"; -import axios from "axios"; -import careConfig from "@careConfig"; - -const requestMap: any = api; -export const actions = { - FETCH_REQUEST: "FETCH_REQUEST", - FETCH_REQUEST_SUCCESS: "FETCH_REQUEST_SUCCESS", - FETCH_REQUEST_ERROR: "FETCH_REQUEST_ERROR", - SET_DATA: "SET_DATA", -}; - -const isRunning: any = {}; - -export const setStoreData = (key: string, value: any) => { - return { - type: actions.SET_DATA, - key, - value, - }; -}; - -export const fetchDataRequest = (key: string) => { - return { - type: actions.FETCH_REQUEST, - key, - }; -}; - -export const fetchDataRequestError = (key: string, error: any) => { - return { - type: actions.FETCH_REQUEST_ERROR, - key, - error, - }; -}; - -export const fetchResponseSuccess = (key: string, data: any) => { - return { - type: actions.FETCH_REQUEST_SUCCESS, - key, - data, - }; -}; - -export const fireRequest = ( - key: string, - path: any = [], - params: any = {}, - pathParam?: any, - altKey?: string, - suppressNotif?: boolean, -) => { - return (dispatch: any) => { - // cancel previous api call - if (isRunning[altKey ? altKey : key]) { - isRunning[altKey ? altKey : key].cancel(); - } - isRunning[altKey ? altKey : key] = axios.CancelToken.source(); - // get api url / method - const request = Object.assign({}, requestMap[key]); - if (path.length > 0) { - request.path += "/" + path.join("/"); - } - // add trailing slash to path before query paramaters - if (request.path.slice(-1) !== "/" && request.path.indexOf("?") === -1) { - request.path += "/"; - } - if (request.method === undefined || request.method === "GET") { - request.method = "GET"; - let qString = ""; - Object.keys(params).forEach((param: any) => { - if (params[param] !== undefined && params[param] !== "") { - qString += `${param}=${encodeURIComponent(params[param])}&`; - } - }); - if (qString !== "") { - request.path += `?${qString}`; - } - } - // set dynamic params in the URL - if (pathParam) { - Object.keys(pathParam).forEach((param: any) => { - request.path = request.path.replace(`{${param}}`, pathParam[param]); - }); - } - - // set authorization header in the request header - const config: any = { - headers: {}, - baseURL: careConfig.apiUrl, - }; - if (!request.noAuth) { - const access_token = localStorage.getItem(LocalStorageKeys.accessToken); - if (access_token) { - config.headers["Authorization"] = "Bearer " + access_token; - } else { - // The access token is missing from the local storage. Redirect to login page. - window.location.href = "/"; - return; - } - } - const axiosApiCall: any = axios.create(config); - - dispatch(fetchDataRequest(key)); - return axiosApiCall[request.method.toLowerCase()](request.path, { - ...params, - cancelToken: isRunning[altKey ? altKey : key].token, - }) - .then((response: any) => { - dispatch(fetchResponseSuccess(key, response.data)); - return response; - }) - .catch((error: any) => { - dispatch(fetchDataRequestError(key, error)); - - if (!(suppressNotif ?? false) && error.response) { - // temporarily don't show invalid phone number error on duplicate patient check - if (error.response.status === 400 && key === "searchPatient") { - return; - } - - // deleteUser: 404 is for permission denied - if (error.response.status === 404 && key === "deleteUser") { - Notification.Error({ - msg: "Permission denied!", - }); - return; - } - - // currentUser is ignored because on the first page load - // 403 error is displayed for invalid credential. - if (error.response.status === 403 && key === "currentUser") { - if (localStorage.getItem(LocalStorageKeys.accessToken)) { - localStorage.removeItem(LocalStorageKeys.accessToken); - } - return; - } - - // 400 Bad Request Error - if (error.response.status === 400 || error.response.status === 406) { - Notification.BadRequest({ - errs: error.response.data, - }); - return error.response; - } - - // 4xx Errors - if (error.response.status > 400 && error.response.status < 600) { - if (error.response.data && error.response.data.detail) { - if (error.response.data.code === "token_not_valid") { - window.location.href = `/session-expired?redirect=${window.location.href}`; - } - Notification.Error({ - msg: error.response.data.detail, - }); - } else { - Notification.Error({ - msg: "Something went wrong...!", - }); - } - if (error.response.status === 429) { - return error.response; - } - return; - } - } else { - return error.response; - } - }); - }; -}; diff --git a/src/index.tsx b/src/index.tsx index 24a8aa8cff3..355d985724a 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,10 +1,6 @@ import { createRoot } from "react-dom/client"; -import reducer from "../src/Redux/Reducer"; import App from "./App"; import "./i18n"; -import { applyMiddleware, createStore } from "redux"; -import thunk from "redux-thunk"; -import { Provider } from "react-redux"; import * as Sentry from "@sentry/browser"; import "./style/index.css"; import { registerSW } from "virtual:pwa-register"; @@ -13,7 +9,6 @@ if ("serviceWorker" in navigator) { registerSW({ immediate: false }); } -const store = createStore(reducer, applyMiddleware(thunk)); if (import.meta.env.PROD) { Sentry.init({ environment: import.meta.env.MODE, @@ -22,8 +17,4 @@ if (import.meta.env.PROD) { } const root = createRoot(document.getElementById("root") as HTMLElement); -root.render( - - - , -); +root.render();