diff --git a/cypress/e2e/patient_spec/patient_registration.cy.ts b/cypress/e2e/patient_spec/patient_registration.cy.ts index 05f0fb7a35e..28b92340654 100644 --- a/cypress/e2e/patient_spec/patient_registration.cy.ts +++ b/cypress/e2e/patient_spec/patient_registration.cy.ts @@ -247,7 +247,7 @@ describe("Patient Creation with consultation", () => { patientTransfer.clickAdmitPatientRecordButton(); patientTransfer.clickTransferPopupContinueButton(); patientTransfer.clickTransferPatientNameList(patientTransferName); - patientTransfer.clickTransferPatientDob(patientDateOfBirth); + patientTransfer.clickTransferPatientYOB(yearOfBirth); patientTransfer.clickTransferSubmitButton(); patientTransfer.verifyFacilitySuccessfullMessage(); patientTransfer.clickConsultationCancelButton(); @@ -263,7 +263,7 @@ describe("Patient Creation with consultation", () => { patientTransfer.clickAdmitPatientRecordButton(); patientTransfer.clickTransferPopupContinueButton(); patientTransfer.clickTransferPatientNameList(patientTransferName); - patientTransfer.clickTransferPatientDob(patientDateOfBirth); + patientTransfer.clickTransferPatientYOB(yearOfBirth); patientTransfer.clickTransferSubmitButton(); patientTransfer.verifyFacilityErrorMessage(); }); diff --git a/cypress/pageobject/Patient/PatientTransfer.ts b/cypress/pageobject/Patient/PatientTransfer.ts index 293490a8bdd..726b888a6ee 100644 --- a/cypress/pageobject/Patient/PatientTransfer.ts +++ b/cypress/pageobject/Patient/PatientTransfer.ts @@ -12,10 +12,9 @@ class PatientTransfer { cy.get("li[role=option]").contains(facilityName).click(); } - clickTransferPatientDob(dateOfBirth: string) { - cy.get("#dateofbirth-transferform").scrollIntoView(); - cy.get("#dateofbirth-transferform").should("be.visible").click(); - cy.get("#date-input").click().type(dateOfBirth); + clickTransferPatientYOB(yearOfBirth: string) { + cy.get("#year_of_birth").scrollIntoView(); + cy.get("#year_of_birth").should("be.visible").click().type(yearOfBirth); } clickTransferSubmitButton() { diff --git a/src/Components/Facility/ConsultationForm.tsx b/src/Components/Facility/ConsultationForm.tsx index 09d843c502e..6d62fbdff0b 100644 --- a/src/Components/Facility/ConsultationForm.tsx +++ b/src/Components/Facility/ConsultationForm.tsx @@ -17,26 +17,10 @@ import { FieldErrorText, FieldLabel } from "../Form/FormFields/FormField"; import InvestigationBuilder, { InvestigationType, } from "../Common/prescription-builder/InvestigationBuilder"; -import { - LegacyRef, - createRef, - lazy, - useCallback, - useEffect, - useState, -} from "react"; +import { LegacyRef, createRef, lazy, useEffect, useState } from "react"; import ProcedureBuilder, { ProcedureType, } from "../Common/prescription-builder/ProcedureBuilder"; -import { - createConsultation, - getConsultation, - getPatient, - partialUpdateConsultation, - updateConsultation, -} from "../../Redux/actions"; -import { statusType, useAbortableEffect } from "../../Common/utils"; - import { BedSelect } from "../Common/BedSelect"; import Beds from "./Consultations/Beds"; import CareIcon from "../../CAREUI/icons/CareIcon"; @@ -55,12 +39,10 @@ import TextAreaFormField from "../Form/FormFields/TextAreaFormField"; import TextFormField from "../Form/FormFields/TextFormField"; import UserAutocompleteFormField from "../Common/UserAutocompleteFormField"; import { UserModel } from "../Users/models"; -import { dischargePatient } from "../../Redux/actions"; import { navigate } from "raviger"; import useAppHistory from "../../Common/hooks/useAppHistory"; import useConfig from "../../Common/hooks/useConfig"; -import { useDispatch } from "react-redux"; import useVisibility from "../../Utils/useVisibility"; import dayjs from "../../Utils/dayjs"; import RouteToFacilitySelect, { @@ -79,6 +61,9 @@ import { } from "../Diagnosis/ConsultationDiagnosisBuilder/ConsultationDiagnosisBuilder.js"; import { FileUpload } from "../Patient/FileUpload.js"; import ConfirmDialog from "../Common/ConfirmDialog.js"; +import request from "../../Utils/request/request.js"; +import routes from "../../Redux/api.js"; +import useQuery from "../../Utils/request/useQuery.js"; const Loading = lazy(() => import("../Common/Loading")); const PageTitle = lazy(() => import("../Common/PageTitle")); @@ -254,7 +239,6 @@ type Props = { export const ConsultationForm = ({ facilityId, patientId, id }: Props) => { const { goBack } = useAppHistory(); const { kasp_enabled, kasp_string } = useConfig(); - const dispatchAction: any = useDispatch(); const [state, dispatch] = useAutoSaveReducer( consultationFormReducer, initialState @@ -332,29 +316,21 @@ export const ConsultationForm = ({ facilityId, patientId, id }: Props) => { bedStatusVisible, ]); - useEffect(() => { - async function fetchPatientName() { - if (patientId) { - setIsLoading(true); - const res = await dispatchAction(getPatient({ id: patientId })); - if (res.data) { - if (isUpdate) { - dispatch({ - type: "set_form", - form: { ...state.form, action: res.data.action }, - }); - } - setPatientName(res.data.name); - setFacilityName(res.data.facility_object.name); - } - } else { - setPatientName(""); - setFacilityName(""); + const { loading: loadingPatient } = useQuery(routes.getPatient, { + pathParams: { id: patientId }, + onResponse: ({ data }) => { + if (!data) return; + if (isUpdate) { + dispatch({ + type: "set_form", + form: { ...state.form, action: data.action }, + }); } - if (!id) setIsLoading(false); - } - fetchPatientName(); - }, [dispatchAction, patientId]); + setPatientName(data.name ?? ""); + setFacilityName(data.facility_object?.name ?? ""); + }, + prefetch: !!patientId, + }); useEffect(() => { dispatch({ @@ -385,96 +361,97 @@ export const ConsultationForm = ({ facilityId, patientId, id }: Props) => { } }; - const fetchData = useCallback( - async (status: statusType) => { - if (!patientId) setIsLoading(true); - const res = await dispatchAction(getConsultation(id!)); - handleFormFieldChange({ - name: "InvestigationAdvice", - value: !Array.isArray(res.data.investigation) - ? [] - : res.data.investigation, - }); - handleFormFieldChange({ - name: "procedures", - value: !Array.isArray(res.data.procedure) ? [] : res.data.procedure, - }); - if (res.data.suggestion === "R") { - if (res.data.referred_to_external) - setReferredToFacility({ - name: res.data.referred_to_external, - }); - else setReferredToFacility(res.data.referred_to_object); - } - if (res.data.route_to_facility === 20) { - if (res.data.referred_from_facility_external) - setReferredFromFacility({ - name: res.data.referred_from_facility_external, - }); - else setReferredFromFacility(res.data.referred_from_facility_object); - } - if (!status.aborted) { - if (res?.data) { + const { loading: consultationLoading, refetch } = useQuery( + routes.getConsultation, + { + pathParams: { id: id! }, + prefetch: !!(id && ((patientId && patientName) || !patientId)), + onResponse: ({ data }) => { + if (!data) return; + handleFormFieldChange({ + name: "InvestigationAdvice", + value: + (Array.isArray(data.investigation) && data.investigation) || [], + }); + handleFormFieldChange({ + name: "procedures", + value: (Array.isArray(data.procedure) && data.procedure) || [], + }); + if (data.suggestion === "R") { + if (data.referred_to_external) + setReferredToFacility({ + name: data.referred_to_external, + }); + else setReferredToFacility(data.referred_to_object ?? null); + } + if (data.route_to_facility === 20) { + if (data.referred_from_facility_external) + setReferredFromFacility({ + name: data.referred_from_facility_external, + }); + else + setReferredFromFacility(data.referred_from_facility_object ?? null); + } + + if (data) { const formData = { - ...res.data, - symptoms_onset_date: isoStringToDate(res.data.symptoms_onset_date), - encounter_date: isoStringToDate(res.data.encounter_date), - icu_admission_date: isoStringToDate(res.data.icu_admission_date), - admitted: res.data.admitted ? String(res.data.admitted) : "false", - admitted_to: res.data.admitted_to ? res.data.admitted_to : "", - category: res.data.category - ? PATIENT_CATEGORIES.find((i) => i.text === res.data.category) - ?.id ?? "" + ...data, + symptoms_onset_date: + data.symptoms_onset_date && + isoStringToDate(data.symptoms_onset_date), + encounter_date: isoStringToDate(data.encounter_date), + icu_admission_date: + data.icu_admission_date && + isoStringToDate(data.icu_admission_date), + admitted: data.admitted ? String(data.admitted) : "false", + admitted_to: data.admitted_to ? data.admitted_to : "", + category: data.category + ? PATIENT_CATEGORIES.find((i) => i.text === data.category)?.id ?? + "" : "", - patient_no: res.data.patient_no ?? "", - OPconsultation: res.data.consultation_notes, - is_telemedicine: `${res.data.is_telemedicine}`, - is_kasp: `${res.data.is_kasp}`, - assigned_to: res.data.assigned_to || "", - assigned_to_object: res.data.assigned_to_object, - treating_physician: res.data.treating_physician || "", - treating_physician_object: res.data.treating_physician_object, - ett_tt: res.data.ett_tt ? Number(res.data.ett_tt) : 3, - special_instruction: res.data.special_instruction || "", - weight: res.data.weight ? res.data.weight : "", - height: res.data.height ? res.data.height : "", - bed: res.data?.current_bed?.bed_object || null, - new_discharge_reason: res.data?.new_discharge_reason || null, - cause_of_death: res.data?.discharge_notes || "", - death_datetime: res.data?.death_datetime || "", - death_confirmed_doctor: res.data?.death_confirmed_doctor || "", - InvestigationAdvice: Array.isArray(res.data.investigation) - ? res.data.investigation + patient_no: data.patient_no ?? "", + OPconsultation: data.consultation_notes, + is_telemedicine: `${data.is_telemedicine}`, + is_kasp: `${data.is_kasp}`, + assigned_to: data.assigned_to || "", + assigned_to_object: data.assigned_to_object, + treating_physician: data.treating_physician || "", + treating_physician_object: data.treating_physician_object, + ett_tt: data.ett_tt ? Number(data.ett_tt) : 3, + special_instruction: data.special_instruction || "", + weight: data.weight ? data.weight : "", + height: data.height ? data.height : "", + bed: data?.current_bed?.bed_object || null, + new_discharge_reason: data?.new_discharge_reason || null, + cause_of_death: data?.discharge_notes || "", + death_datetime: data?.death_datetime || "", + death_confirmed_doctor: data?.death_confirmed_doctor || "", + InvestigationAdvice: Array.isArray(data.investigation) + ? data.investigation : [], - diagnoses: res.data.diagnoses.sort( + diagnoses: data.diagnoses?.sort( (a: ConsultationDiagnosis, b: ConsultationDiagnosis) => ConditionVerificationStatuses.indexOf(a.verification_status) - ConditionVerificationStatuses.indexOf(b.verification_status) ), }; - dispatch({ type: "set_form", form: { ...state.form, ...formData } }); + dispatch({ + type: "set_form", + form: { ...state.form, ...(formData as unknown as FormDetails) }, + }); setBed(formData.bed); - if (res.data.last_daily_round && state.form.category) { + if (data.last_daily_round && state.form.category) { setDisabledFields((fields) => [...fields, "category"]); } } else { goBack(); } - setIsLoading(false); - } - }, - [dispatchAction, id, patientName, patientId] - ); - - useAbortableEffect( - (status: statusType) => { - if (id && ((patientId && patientName) || !patientId)) fetchData(status); - }, - [fetchData, id, patientId, patientName] + }, + } ); - if (isLoading) return ; + if (isLoading || loadingPatient || consultationLoading) return ; const validateForm = () => { const errors = { ...initError }; @@ -692,24 +669,18 @@ export const ConsultationForm = ({ facilityId, patientId, id }: Props) => { death_datetime: string, death_confirmed_doctor: string ) => { - const dischargeResponse = await dispatchAction( - dischargePatient( - { - new_discharge_reason: DISCHARGE_REASONS.find( - (i) => i.text === "Expired" - )?.id, - discharge_notes: cause_of_death, - death_datetime: death_datetime, - death_confirmed_doctor: death_confirmed_doctor, - discharge_date: dayjs().toISOString(), - }, - { id } - ) - ); - - if (dischargeResponse?.status === 200) { - return dischargeResponse; - } + await request(routes.dischargePatient, { + pathParams: { id }, + body: { + new_discharge_reason: DISCHARGE_REASONS.find( + (i) => i.text === "Expired" + )?.id, + discharge_notes: cause_of_death, + death_datetime: death_datetime, + death_confirmed_doctor: death_confirmed_doctor, + discharge_date: dayjs().toISOString(), + }, + }); }; const handleSubmit = async ( @@ -792,16 +763,21 @@ export const ConsultationForm = ({ facilityId, patientId, id }: Props) => { consent_records: state.form.consent_records || [], }; - const res = await dispatchAction( - id ? updateConsultation(id!, data) : createConsultation(data) + const { data: obj } = await request( + id ? routes.updateConsultation : routes.createConsultation, + { + pathParams: id ? { id } : undefined, + body: data, + } ); + setIsLoading(false); - if (res?.data && res.status !== 400) { + if (obj) { dispatch({ type: "set_form", form: initForm }); if (data.suggestion === "DD") { await declareThePatientDead( - res.data.id, + obj.id, state.form.cause_of_death, state.form.death_datetime, state.form.death_confirmed_doctor @@ -809,13 +785,13 @@ export const ConsultationForm = ({ facilityId, patientId, id }: Props) => { } Notification.Success({ - msg: res.data.discharge_date + msg: obj.discharge_date ? "Patient discharged successfully" : `Consultation ${id ? "updated" : "created"} successfully`, }); navigate( - `/facility/${facilityId}/patient/${patientId}/consultation/${res.data.id}` + `/facility/${facilityId}/patient/${patientId}/consultation/${obj.id}` ); if (data.suggestion === "R") { @@ -823,7 +799,7 @@ export const ConsultationForm = ({ facilityId, patientId, id }: Props) => { return; } else if (!id && data.suggestion === "A") { navigate( - `/facility/${facilityId}/patient/${patientId}/consultation/${res.data.id}/prescriptions` + `/facility/${facilityId}/patient/${patientId}/consultation/${obj.id}/prescriptions` ); } } @@ -950,9 +926,10 @@ export const ConsultationForm = ({ facilityId, patientId, id }: Props) => { ...consentRecords, { id: randomId, type: event.value }, ]; - await dispatchAction( - partialUpdateConsultation(id, { consent_records: newRecords }) - ); + await request(routes.partialUpdateConsultation, { + pathParams: { id }, + body: { consent_records: newRecords }, + }); dispatch({ type: "set_form", form: { ...state.form, consent_records: newRecords }, @@ -978,9 +955,10 @@ export const ConsultationForm = ({ facilityId, patientId, id }: Props) => { const newRecords = state.form.consent_records.map((cr) => cr.id === consent_id ? { ...cr, deleted: true } : cr ); - await dispatchAction( - partialUpdateConsultation(id, { consent_records: newRecords }) - ); + await request(routes.partialUpdateConsultation, { + pathParams: { id }, + body: { consent_records: newRecords }, + }); dispatch({ type: "set_form", form: { ...state.form, consent_records: newRecords }, @@ -1699,7 +1677,7 @@ export const ConsultationForm = ({ facilityId, patientId, id }: Props) => { facilityId={facilityId} patientId={patientId} consultationId={id} - fetchPatientData={fetchData} + fetchPatientData={() => refetch()} /> diff --git a/src/Components/Facility/TransferPatientDialog.tsx b/src/Components/Facility/TransferPatientDialog.tsx index 4c38965c235..012f58d4e53 100644 --- a/src/Components/Facility/TransferPatientDialog.tsx +++ b/src/Components/Facility/TransferPatientDialog.tsx @@ -1,18 +1,14 @@ import * as Notification from "../../Utils/Notifications.js"; - import { Cancel, Submit } from "../Common/components/ButtonV2"; import { useReducer, useState } from "react"; - -import DateFormField from "../Form/FormFields/DateFormField"; import { DupPatientModel } from "./models"; -import { FieldLabel } from "../Form/FormFields/FormField"; import { OptionsType } from "../../Common/constants"; import { SelectFormField } from "../Form/FormFields/SelectFormField"; import { navigate } from "raviger"; -import { dateQueryString } from "../../Utils/utils.js"; -import dayjs from "dayjs"; import request from "../../Utils/request/request.js"; import routes from "../../Redux/api.js"; +import TextFormField from "../Form/FormFields/TextFormField.js"; +import { FieldChangeEvent } from "../Form/FormFields/Utils.js"; interface Props { patientList: Array; @@ -21,9 +17,9 @@ interface Props { facilityId: string; } -const initForm: any = { +const initForm = { patient: "", - date_of_birth: null, + year_of_birth: null, }; const initError = Object.assign( @@ -36,9 +32,6 @@ const initialState = { errors: { ...initError }, }; -const getDate = (value: any) => - value && dayjs(value).isValid() && dayjs(value).toDate(); - const patientFormReducer = (state = initialState, action: any) => { switch (action.type) { case "set_form": { @@ -69,18 +62,26 @@ const TransferPatientDialog = (props: Props) => { }; }); - const handleChange = (e: any) => { - const form = { ...state.form }; - form[e.name] = e.value; - dispatch({ type: "set_form", form }); - }; - - const handleDateChange = (e: any) => { - if (dayjs(e.value).isValid()) { - const form = { ...state.form }; - form[e.name] = dateQueryString(e.value); - dispatch({ type: "set_form", form }); + const maxYear = new Date().getFullYear(); + + const handleChange = (e: FieldChangeEvent) => { + if ( + e.name === "year_of_birth" && + parseInt((e.value as string) || "0") > maxYear + ) { + dispatch({ + type: "set_error", + errors: { + ...state.errors, + [e.name]: `Cannot be greater than ${maxYear}`, + }, + }); + return; } + dispatch({ + type: "set_form", + form: { ...state.form, [e.name]: e.value }, + }); }; const validateForm = () => { @@ -94,9 +95,14 @@ const TransferPatientDialog = (props: Props) => { invalidForm = true; } return; - case "date_of_birth": + case "year_of_birth": if (!state.form[field]) { - errors[field] = "Please enter date in YYYY/MM/DD format"; + errors[field] = "This field is required"; + invalidForm = true; + } + + if (parseInt(state.form[field] || "0") > maxYear) { + errors[field] = `Cannot be greater than ${maxYear}`; invalidForm = true; } return; @@ -108,15 +114,14 @@ const TransferPatientDialog = (props: Props) => { return !invalidForm; }; - const handleSubmit = async (e: any) => { - e.preventDefault(); + const handleSubmit = async () => { const validForm = validateForm(); if (validForm) { setIsLoading(true); const { res, data } = await request(routes.transferPatient, { body: { facility: facilityId, - date_of_birth: dateQueryString(state.form.date_of_birth), + year_of_birth: state.form.year_of_birth, }, pathParams: { id: state.form.patient, @@ -156,37 +161,34 @@ const TransferPatientDialog = (props: Props) => {

-
- - Patient - - patient.text} - optionValue={(patient) => patient.id} - value={state.form.patient} - onChange={handleChange} - error={state.errors.patient} - /> -
-
- -
+ patient.text} + optionValue={(patient) => patient.id} + value={state.form.patient} + onChange={handleChange} + error={state.errors.patient} + /> +
@@ -195,7 +197,10 @@ const TransferPatientDialog = (props: Props) => { { + e.preventDefault(); + handleSubmit(); + }} label="Transfer Suspect / Patient" /> diff --git a/src/Components/Facility/models.tsx b/src/Components/Facility/models.tsx index 73e027421a7..12cce6c7b7c 100644 --- a/src/Components/Facility/models.tsx +++ b/src/Components/Facility/models.tsx @@ -148,6 +148,7 @@ export interface ConsultationModel { consultation_notes?: string; is_telemedicine?: boolean; procedure?: ProcedureType[]; + assigned_to?: string; assigned_to_object?: AssignedToObjectModel; created_by?: any; last_edited_by?: any; @@ -593,6 +594,11 @@ export type InventoryLogResponse = InventorySummaryResponse & { created_by: number; }; +export type PatientTransferRequest = { + facility: string; + year_of_birth: string; +}; + export type PatientTransferResponse = { id: string; patient: string; diff --git a/src/Components/Patient/DailyRounds.tsx b/src/Components/Patient/DailyRounds.tsx index 288d46f7359..f285007da0a 100644 --- a/src/Components/Patient/DailyRounds.tsx +++ b/src/Components/Patient/DailyRounds.tsx @@ -1,8 +1,7 @@ import { navigate } from "raviger"; import dayjs from "dayjs"; -import { lazy, useCallback, useState } from "react"; -import { useDispatch } from "react-redux"; +import { lazy, useCallback, useEffect, useState } from "react"; import { CONSCIOUSNESS_LEVEL, PATIENT_CATEGORIES, @@ -11,14 +10,6 @@ import { TELEMEDICINE_ACTIONS, } from "../../Common/constants"; import useAppHistory from "../../Common/hooks/useAppHistory"; -import { statusType, useAbortableEffect } from "../../Common/utils"; -import { - createDailyReport, - getConsultationDailyRoundsDetails, - getDailyReport, - getPatient, - updateDailyReport, -} from "../../Redux/actions"; import { DraftSection, useAutoSaveReducer } from "../../Utils/AutoSave"; import * as Notification from "../../Utils/Notifications"; import { formatDateTime } from "../../Utils/utils"; @@ -37,6 +28,8 @@ import TextFormField from "../Form/FormFields/TextFormField"; import { FieldChangeEvent } from "../Form/FormFields/Utils"; import PatientCategorySelect from "./PatientCategorySelect"; import RadioFormField from "../Form/FormFields/RadioFormField"; +import request from "../../Utils/request/request"; +import routes from "../../Redux/api"; const Loading = lazy(() => import("../Common/Loading")); const initForm: any = { @@ -105,7 +98,6 @@ const DailyRoundsFormReducer = (state = initialState, action: any) => { export const DailyRounds = (props: any) => { const { goBack } = useAppHistory(); - const dispatchAction: any = useDispatch(); const { facilityId, patientId, consultationId, id } = props; const [state, dispatch] = useAutoSaveReducer( DailyRoundsFormReducer, @@ -140,92 +132,70 @@ export const DailyRounds = (props: any) => { "consciousness_level", ]; - const fetchRoundDetails = useCallback( - async (status: statusType) => { - setIsLoading(true); - let formData: any = initialData; - if (id) { - const res = await dispatchAction( - getConsultationDailyRoundsDetails({ consultationId, id }) - ); + const fetchRoundDetails = useCallback(async () => { + setIsLoading(true); + let formData: any = initialData; + if (id) { + const { data } = await request(routes.getDailyReport, { + pathParams: { consultationId, id }, + }); - if (!status.aborted) { - if (res?.data) { - const data = { - ...res.data, - patient_category: res.data.patient_category - ? PATIENT_CATEGORIES.find( - (i) => i.text === res.data.patient_category - )?.id ?? "" - : "", - rhythm: - (res.data.rhythm && - RHYTHM_CHOICES.find((i) => i.text === res.data.rhythm)?.id) || - "0", - admitted_to: res.data.admitted_to - ? res.data.admitted_to - : "Select", - }; - formData = { ...formData, ...data }; - } - } - } - setIsLoading(false); - if (patientId) { - const res = await dispatchAction(getPatient({ id: patientId })); - if (res.data) { - setPatientName(res.data.name); - setFacilityName(res.data.facility_object.name); - setConsultationSuggestion(res.data.last_consultation?.suggestion); - setPreviousReviewInterval( - Number(res.data.last_consultation.review_interval) - ); - const getAction = - TELEMEDICINE_ACTIONS.find((action) => action.id === res.data.action) - ?.text || "NO_ACTION"; - setPreviousAction(getAction); - setInitialData({ - ...initialData, - action: getAction, - }); - formData = { ...formData, ...{ action: getAction } }; - } - } else { - setPatientName(""); - setFacilityName(""); - } - if (consultationId && !id) { - const res = await dispatchAction( - getDailyReport({ limit: 1, offset: 0 }, { consultationId }) - ); - setHasPreviousLog(res.data.count > 0); + if (data) { formData = { ...formData, - ...{ - patient_category: res.data.patient_category - ? PATIENT_CATEGORIES.find( - (i) => i.text === res.data.patient_category - )?.id ?? "" - : "", - rhythm: - (res.data.rhythm && - RHYTHM_CHOICES.find((i) => i.text === res.data.rhythm)?.id) || - "0", - temperature: parseFloat(res.data.temperature), - }, + ...data, + patient_category: data.patient_category + ? PATIENT_CATEGORIES.find((i) => i.text === data.patient_category) + ?.id ?? "" + : "", + rhythm: + (data.rhythm && + RHYTHM_CHOICES.find((i) => i.text === data.rhythm)?.id) || + "0", + admitted_to: data.admitted_to ? data.admitted_to : "Select", }; } - dispatch({ type: "set_form", form: formData }); - setInitialData(formData); - }, - [consultationId, id, dispatchAction, patientId] - ); - useAbortableEffect( - (status: statusType) => { - fetchRoundDetails(status); - }, - [dispatchAction, fetchRoundDetails] - ); + } + setIsLoading(false); + if (patientId) { + const { data } = await request(routes.getPatient, { + pathParams: { id: patientId }, + }); + if (data) { + setPatientName(data.name!); + setFacilityName(data.facility_object!.name); + setConsultationSuggestion(data.last_consultation?.suggestion); + setPreviousReviewInterval( + Number(data.last_consultation?.review_interval) + ); + const getAction = + TELEMEDICINE_ACTIONS.find((action) => action.id === data.action) + ?.text || "NO_ACTION"; + setPreviousAction(getAction); + setInitialData({ + ...initialData, + action: getAction, + }); + formData = { ...formData, ...{ action: getAction } }; + } + } else { + setPatientName(""); + setFacilityName(""); + } + if (consultationId && !id) { + const { data } = await request(routes.getDailyReports, { + pathParams: { consultationId }, + query: { limit: 1, offset: 0 }, + }); + setHasPreviousLog(!!data?.count); + } + dispatch({ type: "set_form", form: formData }); + setInitialData(formData); + }, [consultationId, id, patientId]); + + useEffect(() => { + fetchRoundDetails(); + }, [fetchRoundDetails]); const validateForm = () => { const errors = { ...initError }; @@ -313,20 +283,16 @@ export const DailyRounds = (props: any) => { data = baseData; } - let res; if (id) { - res = await dispatchAction( - updateDailyReport(data, { consultationId, id }) - ); - } else { - res = await dispatchAction(createDailyReport(data, { consultationId })); - } + const { data: obj } = await request(routes.updateDailyReport, { + body: data, + pathParams: { consultationId, id }, + }); - setIsLoading(false); - if (res && res.data && (res.status === 201 || res.status === 200)) { - dispatch({ type: "set_form", form: initForm }); + setIsLoading(false); - if (id) { + if (obj) { + dispatch({ type: "set_form", form: initForm }); Notification.Success({ msg: "Consultation Updates details updated successfully", }); @@ -336,17 +302,26 @@ export const DailyRounds = (props: any) => { ); } else { navigate( - `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/daily_rounds/${res.data.external_id}/update` + `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/daily_rounds/${obj.id}/update` ); } - } else { + } + } else { + const { data: obj } = await request(routes.createDailyRounds, { + pathParams: { consultationId }, + body: data, + }); + setIsLoading(false); + if (obj) { + dispatch({ type: "set_form", form: initForm }); + Notification.Success({ msg: "Consultation Updates details created successfully", }); if (["NORMAL", "TELEMEDICINE"].includes(state.form.rounds_type)) { if (data.clone_last) { navigate( - `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/daily-rounds/${res.data.external_id}/update` + `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/daily-rounds/${obj.id}/update` ); } else { navigate( @@ -356,17 +331,15 @@ export const DailyRounds = (props: any) => { } else { if (data.clone_last) { navigate( - `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/daily-rounds/${res.data.external_id}/update` + `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/daily-rounds/${obj.id}/update` ); } else { navigate( - `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/daily_rounds/${res.data.external_id}/update` + `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/daily_rounds/${obj.id}/update` ); } } } - } else { - setIsLoading(false); } } }; diff --git a/src/Components/Patient/FileUpload.tsx b/src/Components/Patient/FileUpload.tsx index 4f74d9f62f4..40776853ec8 100644 --- a/src/Components/Patient/FileUpload.tsx +++ b/src/Components/Patient/FileUpload.tsx @@ -232,6 +232,26 @@ export const FileUpload = (props: FileUploadProps) => { { name: "Unarchived Files", value: "UNARCHIVED" }, { name: "Archived Files", value: "ARCHIVED" }, ]); + const [isMicPermission, setIsMicPermission] = useState(true); + + useEffect(() => { + const checkMicPermission = async () => { + try { + const permissions = await navigator.permissions.query({ + name: "microphone" as PermissionName, + }); + setIsMicPermission(permissions.state === "granted"); + } catch (error) { + setIsMicPermission(false); + } + }; + + checkMicPermission(); + + return () => { + setIsMicPermission(true); + }; + }, []); const { data: patient } = useQuery(routes.getPatient, { pathParams: { id: patientId }, @@ -1510,8 +1530,9 @@ export const FileUpload = (props: FileUploadProps) => { confirmAudioBlobExists={confirmAudioBlobExists} reset={resetRecording} setResetRecording={setResetRecording} + handleSetMicPermission={setIsMicPermission} /> - {!audioBlobExists && ( + {!audioBlobExists && !isMicPermission && ( { return fireRequest("getTestSampleList", [], { ...params, csv: 1 }); }; -// Daily Rounds - -export const createDailyReport = (params: object, pathParam: object) => { - return fireRequest("createDailyRounds", [], params, pathParam); -}; -export const updateDailyReport = (params: object, pathParam: object) => { - return fireRequest("updateDailyReport", [], params, pathParam); -}; -export const getDailyReport = (params: object, pathParam: object) => { - return fireRequest("getDailyReports", [], params, pathParam); -}; -export const getConsultationDailyRoundsDetails = (pathParam: object) => { - return fireRequest("getDailyReport", [], {}, pathParam); -}; - // Consultation -export const createConsultation = (params: object) => { - return fireRequest("createConsultation", [], params); -}; export const getConsultation = (id: string) => { return fireRequest("getConsultation", [], {}, { id: id }); }; -export const updateConsultation = (id: string, params: object) => { - return fireRequest("updateConsultation", [], params, { id: id }); -}; -export const partialUpdateConsultation = (id: string, params: object) => { - return fireRequest("partialUpdateConsultation", [], params, { id: id }); -}; export const generateDischargeSummary = (pathParams: object) => { return fireRequest("dischargeSummaryGenerate", [], {}, pathParams); diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx index e1ed6161cc3..5d2d70df2b3 100644 --- a/src/Redux/api.tsx +++ b/src/Redux/api.tsx @@ -64,6 +64,7 @@ import { PatientNotesEditModel, PatientNotesModel, PatientStatsModel, + PatientTransferRequest, PatientTransferResponse, StateModel, WardModel, @@ -528,6 +529,8 @@ const routes = { createConsultation: { path: "/api/v1/consultation/", method: "POST", + TBody: Type(), + TRes: Type(), }, getConsultation: { path: "/api/v1/consultation/{id}/", @@ -537,6 +540,8 @@ const routes = { updateConsultation: { path: "/api/v1/consultation/{id}/", method: "PUT", + TBody: Type(), + TRes: Type(), }, partialUpdateConsultation: { path: "/api/v1/consultation/{id}/", @@ -550,10 +555,14 @@ const routes = { }, createDailyRounds: { path: "/api/v1/consultation/{consultationId}/daily_rounds/", + TBody: Type(), + TRes: Type(), method: "POST", }, updateDailyReport: { path: "/api/v1/consultation/{consultationId}/daily_rounds/{id}/", + TBody: Type(), + TRes: Type(), method: "PUT", }, updateDailyRound: { @@ -700,6 +709,7 @@ const routes = { transferPatient: { path: "/api/v1/patient/{id}/transfer/", method: "POST", + TBody: Type(), TRes: Type(), }, getPatientNotes: { @@ -929,6 +939,8 @@ const routes = { dischargePatient: { path: "/api/v1/consultation/{id}/discharge_patient/", method: "POST", + TBody: Type(), + TRes: Type(), }, listFacilityDischargedPatients: { path: "/api/v1/facility/{facility_external_id}/discharged_patients/", diff --git a/src/Utils/VoiceRecorder.tsx b/src/Utils/VoiceRecorder.tsx index 471c267e47d..f087291ad9b 100644 --- a/src/Utils/VoiceRecorder.tsx +++ b/src/Utils/VoiceRecorder.tsx @@ -7,8 +7,13 @@ import { useTranslation } from "react-i18next"; export const VoiceRecorder = (props: any) => { const { t } = useTranslation(); - const { createAudioBlob, confirmAudioBlobExists, reset, setResetRecording } = - props; + const { + createAudioBlob, + confirmAudioBlobExists, + reset, + setResetRecording, + handleSetMicPermission, + } = props; const [ audioURL, isRecording, @@ -16,7 +21,7 @@ export const VoiceRecorder = (props: any) => { stopRecording, newBlob, resetRecording, - ] = useRecorder(); + ] = useRecorder(handleSetMicPermission); const [time, setTime] = useState(0); createAudioBlob(newBlob); useEffect(() => { diff --git a/src/Utils/useRecorder.js b/src/Utils/useRecorder.js index 27a8b4d834e..b601a08f06c 100644 --- a/src/Utils/useRecorder.js +++ b/src/Utils/useRecorder.js @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; import { Error } from "./Notifications"; -const useRecorder = () => { +const useRecorder = (handleMicPermission) => { const [audioURL, setAudioURL] = useState(""); const [isRecording, setIsRecording] = useState(false); const [recorder, setRecorder] = useState(null); @@ -11,10 +11,19 @@ const useRecorder = () => { // Lazily obtain recorder first time we're recording. if (recorder === null) { if (isRecording) { - requestRecorder().then(setRecorder, () => { - Error({ msg: "Please grant microphone permission to record audio." }); - setIsRecording(false); - }); + requestRecorder().then( + (fetchedRecorder) => { + setRecorder(fetchedRecorder); + handleMicPermission(true); + }, + () => { + Error({ + msg: "Please grant microphone permission to record audio.", + }); + setIsRecording(false); + handleMicPermission(false); + } + ); } return; } diff --git a/src/Utils/useVisibility.ts b/src/Utils/useVisibility.ts index ef12310cc9e..0475c2cd8e3 100644 --- a/src/Utils/useVisibility.ts +++ b/src/Utils/useVisibility.ts @@ -35,9 +35,10 @@ export default function useVisibility( }; useEffect(() => { + onScroll(); document.addEventListener("scroll", onScroll, true); return () => document.removeEventListener("scroll", onScroll, true); - }); + }, [currentElement.current]); return [isVisible, currentElement]; }