diff --git a/src/Components/ExternalResult/ExternalResultUpload.tsx b/src/Components/ExternalResult/ExternalResultUpload.tsx index 5f39cb213a4..a9d5c29bfe8 100644 --- a/src/Components/ExternalResult/ExternalResultUpload.tsx +++ b/src/Components/ExternalResult/ExternalResultUpload.tsx @@ -2,21 +2,21 @@ import _ from "lodash"; import { navigate } from "raviger"; import { lazy, useState } from "react"; import CSVReader from "react-csv-reader"; -import { useDispatch } from "react-redux"; import useConfig from "../../Common/hooks/useConfig"; -import { externalResultUploadCsv } from "../../Redux/actions"; import * as Notification from "../../Utils/Notifications.js"; const PageTitle = lazy(() => import("../Common/PageTitle")); import { useTranslation } from "react-i18next"; import { Cancel, Submit } from "../Common/components/ButtonV2"; import useAppHistory from "../../Common/hooks/useAppHistory"; +import request from "../../Utils/request/request"; +import routes from "../../Redux/api"; +import { IExternalResult } from "./models"; export default function ExternalResultUpload() { const { sample_format_external_result_import } = useConfig(); - const dispatch: any = useDispatch(); // for disabling save button once clicked const [loading, setLoading] = useState(false); - const [csvData, setCsvData] = useState(new Array()); + const [csvData, setCsvData] = useState(new Array()); const [errors, setErrors] = useState([]); const handleForce = (data: any) => { setCsvData(data); @@ -32,26 +32,35 @@ export default function ExternalResultUpload() { header.toLowerCase().replace(/\W/g, "_"), }; - const handleSubmit = (e: any) => { + const handleSubmit = async (e: any) => { e.preventDefault(); setLoading(true); const valid = true; - if (csvData.length !== 0) { - const data = { - sample_tests: csvData, - }; + if (csvData.length !== 0) { if (valid) { setErrors([]); - dispatch(externalResultUploadCsv(data)).then((resp: any) => { - if (resp && resp.status === 202) { + + try { + const { res, data } = await request(routes.externalResultUploadCsv, { + body: { + sample_tests: csvData, + }, + }); + + if (res && res.status === 202) { setLoading(false); navigate("/external_results"); } else { - setErrors(resp.data.map((err: any) => Object.entries(err))); + if (data) { + setErrors(data.map((err: any) => Object.entries(err))); + } setLoading(false); } - }); + } catch (error) { + console.error("An error occurred:", error); + setLoading(false); + } } else { setLoading(false); } diff --git a/src/Components/ExternalResult/ListFilter.tsx b/src/Components/ExternalResult/ListFilter.tsx index 91b75e535d2..75358196ffd 100644 --- a/src/Components/ExternalResult/ListFilter.tsx +++ b/src/Components/ExternalResult/ListFilter.tsx @@ -1,6 +1,4 @@ -import { useEffect, useState } from "react"; -import { getAllLocalBodyByDistrict } from "../../Redux/actions"; -import { useDispatch } from "react-redux"; +import { useState } from "react"; import useMergeState from "../../Common/hooks/useMergeState"; import { navigate } from "raviger"; import { useTranslation } from "react-i18next"; @@ -11,6 +9,9 @@ import DateRangeFormField from "../Form/FormFields/DateRangeFormField"; import dayjs from "dayjs"; import { dateQueryString } from "../../Utils/utils"; import useAuthUser from "../../Common/hooks/useAuthUser"; +import useQuery from "../../Utils/request/useQuery"; +import routes from "../../Redux/api"; +import Loading from "../Common/Loading"; const clearFilterState = { created_date_before: "", @@ -29,10 +30,8 @@ export default function ListFilter(props: any) { const { filter, onChange, closeFilter, dataList } = props; const [wardList, setWardList] = useState([]); const [lsgList, setLsgList] = useState([]); - const [wards, setWards] = useState([]); const [selectedLsgs, setSelectedLsgs] = useState([]); - const dispatch: any = useDispatch(); const authUser = useAuthUser(); const [filterState, setFilterState] = useMergeState({ created_date_before: filter.created_date_before || null, @@ -45,6 +44,57 @@ export default function ListFilter(props: any) { }); const { t } = useTranslation(); + const { loading } = useQuery(routes.getAllLocalBodyByDistrict, { + pathParams: { id: String(authUser.district) }, + onResponse: ({ res, data }) => { + if (res && data) { + let allWards: any[] = []; + let allLsgs: any[] = []; + if (res && data) { + data.forEach((local: any) => { + allLsgs = [...allLsgs, { id: local.id, name: local.name }]; + if (local.wards) { + local.wards.forEach((ward: any) => { + allWards = [ + ...allWards, + { + id: ward.id, + name: ward.number + ": " + ward.name, + panchayath: local.name, + number: ward.number, + local_body_id: local.id, + }, + ]; + }); + } + }); + } + + sortByName(allWards); + sortByName(allLsgs); + setWardList(allWards || []); + setLsgList(allLsgs || []); + const filteredWard = filter?.wards?.split(",").map(Number); + const selectedWards: any = + filteredWard && allWards + ? allWards.filter(({ id }: { id: number }) => { + return filteredWard.includes(id); + }) + : []; + setWards(selectedWards); + + const filteredLsgs = filter?.local_bodies?.split(",").map(Number); + const selectedLsgs: any = + filteredLsgs && allLsgs + ? allLsgs.filter(({ id }: { id: number }) => { + return filteredLsgs.includes(id); + }) + : []; + setSelectedLsgs(selectedLsgs); + } + }, + }); + const handleDateRangeChange = ( startDateId: string, endDateId: string, @@ -118,59 +168,10 @@ export default function ListFilter(props: any) { }); }; - useEffect(() => { - async function getWardList() { - const id = authUser.district; - const res = await dispatch(getAllLocalBodyByDistrict({ id })); - let allWards: any[] = []; - let allLsgs: any[] = []; - res?.data?.forEach((local: any) => { - allLsgs = [...allLsgs, { id: local.id, name: local.name }]; - if (local.wards) { - local.wards.forEach((ward: any) => { - allWards = [ - ...allWards, - { - id: ward.id, - name: ward.number + ": " + ward.name, - panchayath: local.name, - number: ward.number, - local_body_id: local.id, - }, - ]; - }); - } - }); - sortByName(allWards); - sortByName(allLsgs); - setWardList(allWards || []); - setLsgList(allLsgs || []); - const filteredWard = filter?.wards?.split(",").map(Number); - const selectedWards: any = - filteredWard && allWards - ? allWards.filter(({ id }: { id: number }) => { - return filteredWard.includes(id); - }) - : []; - setWards(selectedWards); - - const filteredLsgs = filter?.local_bodies?.split(",").map(Number); - const selectedLsgs: any = - filteredLsgs && allLsgs - ? allLsgs.filter(({ id }: { id: number }) => { - return filteredLsgs.includes(id); - }) - : []; - setSelectedLsgs(selectedLsgs); - } - getWardList(); - }, []); - const filterWards = () => { const selectedLsgIds: any = selectedLsgs.map((e) => { return e.id; }); - const selectedwards: any = selectedLsgIds.length === 0 ? wardList @@ -183,13 +184,15 @@ export default function ListFilter(props: any) { const handleChange = (event: any) => { const { name, value } = event.target; - const filterData: any = { ...filterState }; filterData[name] = value; - setFilterState(filterData); }; + if (loading) { + ; + } + return ( import("../Common/Loading")); export default function ResultItem(props: any) { - const dispatch: any = useDispatch(); - const initialData: any = {}; - const [data, setData] = useState(initialData); - const [isLoading, setIsLoading] = useState(true); const [showDeleteAlert, setShowDeleteAlert] = useState(false); const { t } = useTranslation(); - const fetchData = useCallback( - async (status: statusType) => { - setIsLoading(true); - const res = await dispatch(externalResult({ id: props.id })); - if (!status.aborted) { - if (res && res.data) { - setData(res.data); - } - setIsLoading(false); - } - }, - [props.id, dispatch] - ); + const { data: resultItemData, loading } = useQuery(routes.externalResult, { + pathParams: { id: props.id }, + }); const handleDelete = async () => { - const res = await dispatch(deleteExternalResult(props.id)); - if (res?.status === 204) { - Notification.Success({ - msg: t("record_has_been_deleted_successfully"), + if (showDeleteAlert) { + const { res, data } = await request(routes.deleteExternalResult, { + pathParams: { id: props.id }, }); - } else { - Notification.Error({ - msg: - t("error_while_deleting_record") + ": " + (res?.data?.detail || ""), - }); - } - setShowDeleteAlert(false); - navigate("/external_results"); + if (res?.status === 204) { + Notification.Success({ + msg: t("record_has_been_deleted_successfully"), + }); + } else { + Notification.Error({ + msg: t("error_while_deleting_record") + ": " + (data?.detail || ""), + }); + } + setShowDeleteAlert(false); + navigate("/external_results"); + } }; - useAbortableEffect( - (status: statusType) => { - fetchData(status); - }, - [fetchData] - ); - - if (isLoading) { + if (loading || !resultItemData) { return ; } @@ -69,14 +50,18 @@ export default function ResultItem(props: any) { variant="danger" action={t("delete")} show={showDeleteAlert} - onConfirm={() => handleDelete()} + onConfirm={() => { + handleDelete(); + }} onClose={() => setShowDeleteAlert(false)} />
diff --git a/src/Components/ExternalResult/ResultList.tsx b/src/Components/ExternalResult/ResultList.tsx index 74fbf8430b0..d148b26e3f5 100644 --- a/src/Components/ExternalResult/ResultList.tsx +++ b/src/Components/ExternalResult/ResultList.tsx @@ -1,12 +1,10 @@ import ButtonV2 from "../Common/components/ButtonV2"; import { navigate } from "raviger"; -import { lazy, useEffect, useState } from "react"; -import { useDispatch } from "react-redux"; +import { lazy, useState } from "react"; import { externalResultList } from "../../Redux/actions"; import ListFilter from "./ListFilter"; import FacilitiesSelectDialogue from "./FacilitiesSelectDialogue"; import { FacilityModel } from "../Facility/models"; -import { parsePhoneNumber } from "../../Utils/utils"; import SearchInput from "../Form/SearchInput"; import useFilters from "../../Common/hooks/useFilters"; import CareIcon from "../../CAREUI/icons/CareIcon"; @@ -15,14 +13,12 @@ import PhoneNumberFormField from "../Form/FormFields/PhoneNumberFormField"; import CountBlock from "../../CAREUI/display/Count"; import { AdvancedFilterButton } from "../../CAREUI/interactive/FiltersSlideover"; import Page from "../Common/components/Page"; - +import routes from "../../Redux/api"; +import useQuery from "../../Utils/request/useQuery"; +import { parsePhoneNumber } from "../../Utils/utils"; const Loading = lazy(() => import("../Common/Loading")); export default function ResultList() { - const dispatch: any = useDispatch(); - const [data, setData] = useState([]); - const [isLoading, setIsLoading] = useState(false); - const [totalCount, setTotalCount] = useState(0); const { qParams, updateQuery, @@ -57,61 +53,31 @@ export default function ResultList() { setPhoneNumberError("Enter a valid number"); }; + const params = { + page: qParams.page || 1, + name: qParams.name || "", + mobile_number: qParams.mobile_number + ? parsePhoneNumber(qParams.mobile_number) ?? "" + : "", + wards: qParams.wards || undefined, + local_bodies: qParams.local_bodies || undefined, + created_date_before: qParams.created_date_before || undefined, + created_date_after: qParams.created_date_after || undefined, + result_date_before: qParams.result_date_before || undefined, + result_date_after: qParams.result_date_after || undefined, + sample_collection_date_after: + qParams.sample_collection_date_after || undefined, + sample_collection_date_before: + qParams.sample_collection_date_before || undefined, + offset: (qParams.page ? qParams.page - 1 : 0) * resultsPerPage, + srf_id: qParams.srf_id || undefined, + }; - let manageResults: any = null; - useEffect(() => { - setIsLoading(true); - const params = { - page: qParams.page || 1, - name: qParams.name || "", - mobile_number: qParams.mobile_number - ? parsePhoneNumber(qParams.mobile_number) ?? "" - : "", - wards: qParams.wards || undefined, - local_bodies: qParams.local_bodies || undefined, - created_date_before: qParams.created_date_before || undefined, - created_date_after: qParams.created_date_after || undefined, - result_date_before: qParams.result_date_before || undefined, - result_date_after: qParams.result_date_after || undefined, - sample_collection_date_after: - qParams.sample_collection_date_after || undefined, - sample_collection_date_before: - qParams.sample_collection_date_before || undefined, - offset: (qParams.page ? qParams.page - 1 : 0) * resultsPerPage, - srf_id: qParams.srf_id || undefined, - }; - - dispatch(externalResultList(params, "externalResultList")) - .then((res: any) => { - if (res && res.data) { - setData(res.data.results); - setTotalCount(res.data.count); - setIsLoading(false); - } - }) - .catch(() => { - setIsLoading(false); - }); + const { data, loading } = useQuery(routes.externalResultList, { + query: params, + }); - if (!params.mobile_number) { - setPhoneNum("+91"); - } - }, [ - dispatch, - qParams.name, - qParams.page, - qParams.mobile_number, - qParams.wards, - qParams.created_date_before, - qParams.created_date_after, - qParams.result_date_before, - qParams.result_date_after, - qParams.sample_collection_date_after, - qParams.sample_collection_date_before, - qParams.local_bodies, - qParams.srf_id, - dataList, - ]); + let manageResults: any = null; const removeLSGFilter = (paramKey: any, id: any) => { const updatedLsgList = dataList.lsgList.filter((x: any) => x.id !== id); @@ -158,8 +124,8 @@ export default function ResultList() { }; let resultList: any[] = []; - if (data && data.length) { - resultList = data.map((result: any) => { + if (data?.results.length) { + resultList = data.results.map((result: any) => { const resultUrl = `/external_results/${result.id}`; return ( @@ -173,7 +139,7 @@ export default function ResultList() { className="group inline-flex space-x-2 text-sm leading-5" >

- {result.name} - {result.age} {result.age_in} + {`${result.name}`} - {result.age} {result.age_in}

@@ -214,7 +180,7 @@ export default function ResultList() { }); } - if (isLoading || !data) { + if (loading) { manageResults = ( @@ -222,9 +188,9 @@ export default function ResultList() { ); - } else if (data && data.length) { + } else if (data?.results.length) { manageResults = <>{resultList}; - } else if (data && data.length === 0) { + } else if (data?.results.length === 0) { manageResults = ( @@ -286,8 +252,8 @@ export default function ResultList() {
@@ -358,7 +324,7 @@ export default function ResultList() {
- + import("../Common/Loading")); @@ -63,79 +57,70 @@ export default function UpdateResult(props: any) { const { id } = props; const { goBack } = useAppHistory(); - const dispatchAction: any = useDispatch(); const [state, dispatch] = useReducer(FormReducer, initialState); - const [isLoading, setIsLoading] = useState(false); + const [isLoading, setIsLoading] = useState(true); const [isLocalbodyLoading, setIsLocalbodyLoading] = useState(false); const [isWardLoading, setIsWardLoading] = useState(false); const [localBody, setLocalBody] = useState(initialLocalbodies); const [ward, setWard] = useState(initialLocalbodies); - const fetchData = useCallback( - async (status: statusType) => { - setIsLoading(true); - const res = await dispatchAction(externalResult({ id: id })); - if (!status.aborted) { - if (res && res.data) { - const form = { ...state.form }; - form["name"] = res.data.name; - form["age"] = res.data.age; - form["age_in"] = res.data.age_in; - form["srf_id"] = res.data.srf_id; - form["address"] = res.data.address; - form["district"] = res.data.district_object.name; - form["local_body"] = String(res.data.local_body); - form["ward"] = String(res.data.ward); - form["patient_created"] = String(res.data.patient_created); + const { loading } = useQuery(routes.externalResult, { + pathParams: { id }, + onResponse: async ({ res, data }) => { + if (res && data) { + const form = { ...state.form }; + form["name"] = data.name; + form["age"] = data.age; + form["age_in"] = data.age_in; + form["srf_id"] = data.srf_id; + form["address"] = data.address; + form["district"] = data.district_object.name; + form["local_body"] = String(data.local_body); + form["ward"] = String(data.ward); + form["patient_created"] = String(data.patient_created); - dispatch({ type: "set_form", form }); + dispatch({ type: "set_form", form }); - Promise.all([ - fetchLocalBody(res.data.district), - fetchWards(res.data.local_body), - ]); - } + Promise.all([ + fetchLocalBody(data.district), + fetchWards(data.local_body), + ]); setIsLoading(false); } }, - [props.id, dispatchAction] - ); + }); - const fetchLocalBody = useCallback( - async (id: string) => { - if (Number(id) > 0) { - setIsLocalbodyLoading(true); - const localBodyList = await dispatchAction( - getLocalbodyByDistrict({ id }) - ); + const fetchLocalBody = async (id: number) => { + if (Number(id) > 0) { + setIsLocalbodyLoading(true); + const { res, data } = await request(routes.getLocalbodyByDistrict, { + pathParams: { id: String(id) }, + }); + if (res && data) { setIsLocalbodyLoading(false); - setLocalBody([...initialLocalbodies, ...localBodyList.data]); - } else { - setLocalBody(initialLocalbodies); + setLocalBody([...initialLocalbodies, ...data]); } - }, - [dispatchAction] - ); + } else { + setLocalBody(initialLocalbodies); + } + }; const fetchWards = useCallback( - async (id: string) => { + async (id: number) => { if (Number(id) > 0) { setIsWardLoading(true); - const wardList = await dispatchAction(getWardByLocalBody({ id })); + const { res, data } = await request(routes.getWardByLocalBody, { + pathParams: { id: String(id) }, + }); + if (res && data) { + setWard([...initialWard, ...data.results]); + } setIsWardLoading(false); - setWard([...initialWard, ...wardList.data.results]); } else { setWard(initialLocalbodies); } }, - [dispatchAction] - ); - - useAbortableEffect( - (status: statusType) => { - fetchData(status); - }, - [fetchData] + [props.id] ); const validateForm = () => { @@ -195,15 +180,20 @@ export default function UpdateResult(props: any) { const validForm = validateForm(); if (validForm) { setIsLoading(true); - const data = { + const rdata = { address: state.form.address ? state.form.address : undefined, local_body: state.form.local_body ? state.form.local_body : undefined, ward: state.form.ward, patient_created: state.form.patient_created === "true", }; - const res = await dispatchAction(partialUpdateExternalResult(id, data)); + + const { res, data } = await request(routes.partialUpdateExternalResult, { + pathParams: { id }, + body: rdata, + }); + setIsLoading(false); - if (res && res.data) { + if (res && data) { dispatch({ type: "set_form", form: initForm }); Notification.Success({ msg: "External Result updated successfully", @@ -213,7 +203,7 @@ export default function UpdateResult(props: any) { } }; - if (isLoading) { + if (isLoading || loading) { return ; } @@ -262,10 +252,7 @@ export default function UpdateResult(props: any) { options={localBody} optionLabel={(localBody) => localBody.name} optionValue={(localBody) => localBody.id} - onChange={(e) => [ - handleChange(e), - fetchWards(String(e.value)), - ]} + onChange={(e) => [handleChange(e), fetchWards(e.value)]} error={state.errors.local_body} /> )} diff --git a/src/Components/ExternalResult/models.ts b/src/Components/ExternalResult/models.ts new file mode 100644 index 00000000000..8ccaba04d05 --- /dev/null +++ b/src/Components/ExternalResult/models.ts @@ -0,0 +1,72 @@ +export interface IExternalResultUploadCsv { + sample_tests: any[]; +} + +export interface IExternalResult { + id: number; + name: string; + age: number; + age_in: string; + test_type: string; + result: string; + result_date: string; + patient_created: boolean; + gender: string; + source: string; + is_repeat: boolean; + mobile_number: string; + patient_status: string; + sample_type: string; + sample_collection_date: string; + patient_category: string; + srf_id: string; + district_object: { + id: number; + name: string; + state: number; + }; + district: number; + ward: number; + local_body: number; + address: string; + ward_object: { + id: number; + number: number; + name: string; + }; + local_body_object: { + id: number; + name: string; + }; +} + +export interface ILocalBodies { + id: number; + name: string; + state: number; + number: number; + body_type: number; + localbody_code: string; + district: number; +} + +export interface IDeleteExternalResult { + detail: string; +} + +export interface IPartialUpdateExternalResult { + address: string; + ward: number; + local_body: number; + patient_created: boolean; +} + +export interface ILocalBodyByDistrict { + id: number; + name: string; + state: number; +} + +export interface IExternalResultCsv { + sample_tests: Partial[]; +} diff --git a/src/Redux/actions.tsx b/src/Redux/actions.tsx index 26e006f4cea..06a12796737 100644 --- a/src/Redux/actions.tsx +++ b/src/Redux/actions.tsx @@ -668,13 +668,6 @@ export const externalResultList = (params: object, altKey: string) => { export const externalResult = (pathParam: object) => { return fireRequest("externalResult", [], {}, pathParam); }; -export const externalResultUploadCsv = (params: object) => { - return fireRequest("externalResultUploadCsv", [], params); -}; - -export const deleteExternalResult = (id: string) => { - return fireRequest("deleteExternalResult", [id], {}); -}; export const updateExternalResult = (id: number, params: object) => { return fireRequest("updateExternalResult", [], params, { id }); diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx index 8a4ca5cf1df..02c38afcc5a 100644 --- a/src/Redux/api.tsx +++ b/src/Redux/api.tsx @@ -1,6 +1,14 @@ import { IConfig } from "../Common/hooks/useConfig"; import { AssetData } from "../Components/Assets/AssetTypes"; -import { LocationModel } from "../Components/Facility/models"; +import { + IDeleteExternalResult, + IExternalResult, + IExternalResultCsv, + ILocalBodies, + ILocalBodyByDistrict, + IPartialUpdateExternalResult, +} from "../Components/ExternalResult/models"; +import { LocationModel, WardModel } from "../Components/Facility/models"; import { Prescription } from "../Components/Medicine/models"; import { UserModel } from "../Components/Users/models"; import { PaginatedResponse } from "../Utils/request/types"; @@ -501,18 +509,25 @@ const routes = { // External Results externalResultList: { path: "/api/v1/external_result/", + method: "GET", + TRes: Type>(), }, externalResult: { path: "/api/v1/external_result/{id}/", + method: "GET", + TRes: Type(), }, externalResultUploadCsv: { path: "/api/v1/external_result/bulk_upsert/", method: "POST", + TBody: Type(), + TRes: Type(), }, deleteExternalResult: { - path: "/api/v1/external_result", + path: "/api/v1/external_result/{id}/", method: "DELETE", + TRes: Type(), }, updateExternalResult: { @@ -523,6 +538,8 @@ const routes = { partialUpdateExternalResult: { path: "/api/v1/external_result/{id}/", method: "PATCH", + TRes: Type(), + TBody: Type(), }, // States @@ -547,9 +564,13 @@ const routes = { }, getAllLocalBodyByDistrict: { path: "/api/v1/district/{id}/get_all_local_body/", + method: "GET", + TRes: Type(), }, getLocalbodyByDistrict: { path: "/api/v1/district/{id}/local_bodies/", + method: "GET", + TRes: Type(), }, // Local Body @@ -572,6 +593,8 @@ const routes = { }, getWardByLocalBody: { path: "/api/v1/ward/?local_body={id}", + method: "GET", + TRes: Type>(), }, // Sample Test diff --git a/src/Utils/request/request.ts b/src/Utils/request/request.ts index 2d735734aae..d428107b64c 100644 --- a/src/Utils/request/request.ts +++ b/src/Utils/request/request.ts @@ -38,7 +38,7 @@ export default async function request( try { const res = await fetch(url, options); - const data: TData = await res.json(); + const data = await getResponseBody(res); result = { res, @@ -61,3 +61,21 @@ export default async function request( ); return result; } + +async function getResponseBody(res: Response): Promise { + if (!(res.headers.get("content-length") !== "0")) { + return null as TData; + } + + const isJson = res.headers.get("content-type")?.includes("application/json"); + + if (!isJson) { + return (await res.text()) as TData; + } + + try { + return await res.json(); + } catch { + return (await res.text()) as TData; + } +}