diff --git a/src/Components/Common/components/CollapseV2.tsx b/src/Components/Common/components/CollapseV2.tsx index 6552868b6ac..2e1a4bd4c78 100644 --- a/src/Components/Common/components/CollapseV2.tsx +++ b/src/Components/Common/components/CollapseV2.tsx @@ -35,7 +35,7 @@ export default function CollapseV2(props: { } >
import("../Common/Loading")); const PageTitle = loadable(() => import("../Common/PageTitle")); @@ -102,13 +100,10 @@ type FormDetails = { cause_of_death: string; death_datetime: string; death_confirmed_doctor: string; + InvestigationAdvice: InvestigationType[]; + procedures: ProcedureType[]; }; -type Action = - | { type: "set_form"; form: FormDetails } - | { type: "set_error"; errors: FormDetails } - | { type: "set_form_field"; field: keyof FormDetails; value: any }; - const initForm: FormDetails = { symptoms: [], other_symptoms: "", @@ -150,6 +145,8 @@ const initForm: FormDetails = { cause_of_death: "", death_datetime: "", death_confirmed_doctor: "", + InvestigationAdvice: [], + procedures: [], }; const initError = Object.assign( @@ -175,7 +172,10 @@ const fieldRef = formErrorKeys.reduce( {} ); -const consultationFormReducer = (state = initialState, action: Action) => { +const consultationFormReducer = ( + state = initialState, + action: FormReducerAction +) => { switch (action.type) { case "set_form": { return { @@ -183,20 +183,15 @@ const consultationFormReducer = (state = initialState, action: Action) => { form: { ...state.form, ...action.form }, }; } - case "set_error": { + case "set_errors": { return { ...state, errors: action.errors, }; } - case "set_form_field": { - return { - ...state, - form: { - ...state.form, - [action.field]: action.value, - }, - }; + case "set_state": { + if (action.state) return action.state; + return state; } } }; @@ -211,12 +206,11 @@ export const ConsultationForm = (props: any) => { const { kasp_enabled, kasp_string } = useConfig(); const dispatchAction: any = useDispatch(); const { facilityId, patientId, id } = props; - const [state, dispatch] = useReducer(consultationFormReducer, initialState); + const [state, dispatch] = useAutoSaveReducer( + consultationFormReducer, + initialState + ); const [bed, setBed] = useState(null); - const [InvestigationAdvice, setInvestigationAdvice] = useState< - InvestigationType[] - >([]); - const [procedures, setProcedures] = useState([]); const [selectedFacility, setSelectedFacility] = useState(null); @@ -267,12 +261,11 @@ export const ConsultationForm = (props: any) => { setPatientName(res.data.name); setFacilityName(res.data.facility_object.name); if (isUpdate) { - dispatch({ - type: "set_form_field", - field: "action", - value: TELEMEDICINE_ACTIONS.find((a) => a.id === res.data.action) - ?.text, - }); + const form = { ...state.form }; + form.action = TELEMEDICINE_ACTIONS.find( + (a) => a.id === res.data.action + )?.text; + dispatch({ type: "set_form", form }); } } } else { @@ -291,12 +284,16 @@ export const ConsultationForm = (props: any) => { async (status: statusType) => { setIsLoading(true); const res = await dispatchAction(getConsultation(id)); - setInvestigationAdvice( - !Array.isArray(res.data.investigation) ? [] : res.data.investigation - ); - setProcedures( - !Array.isArray(res.data.procedure) ? [] : res.data.procedure - ); + 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) setSelectedFacility({ id: -1, name: res.data.referred_to_external }); @@ -471,7 +468,7 @@ export const ConsultationForm = (props: any) => { } return; case "procedure": { - for (const p of procedures) { + for (const p of state.form.procedures) { if (!p.procedure?.replace(/\s/g, "").length) { errors[field] = "Procedure field can not be empty"; invalidForm = true; @@ -492,7 +489,7 @@ export const ConsultationForm = (props: any) => { } case "investigation": { - for (const i of InvestigationAdvice) { + for (const i of state.form.InvestigationAdvice) { if (!i.type?.length) { errors[field] = "Investigation field can not be empty"; invalidForm = true; @@ -535,7 +532,7 @@ export const ConsultationForm = (props: any) => { } }); if (invalidForm) { - dispatch({ type: "set_error", errors }); + dispatch({ type: "set_errors", errors }); const firstError = Object.keys(errors).find((key) => errors[key]); if (firstError) { fieldRef[firstError].current?.scrollIntoView({ @@ -545,7 +542,7 @@ export const ConsultationForm = (props: any) => { } return false; } - dispatch({ type: "set_error", errors }); + dispatch({ type: "set_errors", errors }); return true; }; @@ -602,12 +599,16 @@ export const ConsultationForm = (props: any) => { discharge_date: state.form.discharge_date, ip_no: state.form.ip_no, op_no: state.form.op_no, - icd11_diagnoses: state.form.icd11_diagnoses_object.map((o) => o.id), + icd11_diagnoses: state.form.icd11_diagnoses_object.map( + (o: ICD11DiagnosisModel) => o.id + ), icd11_provisional_diagnoses: - state.form.icd11_provisional_diagnoses_object.map((o) => o.id), + state.form.icd11_provisional_diagnoses_object.map( + (o: ICD11DiagnosisModel) => o.id + ), verified_by: state.form.verified_by, - investigation: InvestigationAdvice, - procedure: procedures, + investigation: state.form.InvestigationAdvice, + procedure: state.form.procedures, patient: patientId, facility: facilityId, referred_to: @@ -768,7 +769,7 @@ export const ConsultationForm = (props: any) => { id: name, name, value: (state.form as any)[name], - error: state.errors[name], + error: (state.errors as any)[name], onChange: handleFormFieldChange, }; }; @@ -831,6 +832,12 @@ export const ConsultationForm = (props: any) => { onSubmit={handleSubmit} className="rounded sm:rounded-xl bg-white p-6 sm:p-12 transition-all" > + { + dispatch({ type: "set_state", state: newState }); + }} + formData={state.form} + />
{sectionTitle("Consultation Details")} @@ -1120,8 +1127,13 @@ export const ConsultationForm = (props: any) => { > Investigations Suggestions { + handleFormFieldChange({ + name: "InvestigationAdvice", + value: investigations, + }); + }} /> { > Procedures { + handleFormFieldChange({ + name: "procedures", + value: procedures, + }); + }} /> import("../Common/Loading")); const PageTitle = loadable(() => import("../Common/PageTitle")); @@ -128,22 +130,16 @@ const initialState = { errors: { ...initError }, }; -type SetFormAction = { type: "set_form"; form: FacilityForm }; -type SetErrorAction = { - type: "set_error"; - errors: Record; -}; -type FacilityCreateFormAction = SetFormAction | SetErrorAction; - -const facilityCreateReducer = ( - state = initialState, - action: FacilityCreateFormAction -) => { +const facilityCreateReducer = (state = initialState, action: FormAction) => { switch (action.type) { case "set_form": return { ...state, form: action.form }; - case "set_error": + case "set_errors": return { ...state, errors: action.errors }; + case "set_state": { + if (action.state) return action.state; + return state; + } } }; @@ -153,7 +149,10 @@ export const FacilityCreate = (props: FacilityProps) => { const dispatchAction: any = useDispatch(); const { facilityId } = props; - const [state, dispatch] = useReducer(facilityCreateReducer, initialState); + const [state, dispatch] = useAutoSaveReducer( + facilityCreateReducer, + initialState + ); const [isLoading, setIsLoading] = useState(false); const [isStateLoading, setIsStateLoading] = useState(false); const [isDistrictLoading, setIsDistrictLoading] = useState(false); @@ -472,10 +471,10 @@ export const FacilityCreate = (props: FacilityProps) => { } }); if (invalidForm) { - dispatch({ type: "set_error", errors }); + dispatch({ type: "set_errors", errors }); return false; } - dispatch({ type: "set_error", errors }); + dispatch({ type: "set_errors", errors }); return true; }; @@ -767,6 +766,17 @@ export const FacilityCreate = (props: FacilityProps) => {
handleSubmit(e)}> + { + dispatch({ type: "set_state", state: newState }); + Promise.all([ + fetchDistricts(newState.form.state), + fetchLocalBody(newState.form.district), + fetchWards(newState.form.local_body), + ]); + }} + formData={state.form} + />
= { className?: string; @@ -29,7 +30,7 @@ const Form = ({ }: Props) => { const initial = { form: props.defaults, errors: {} }; const [isLoading, setIsLoading] = useState(!!asyncGetDefaults); - const [state, dispatch] = useReducer>(formReducer, initial); + const [state, dispatch] = useAutoSaveReducer(formReducer, initial); useEffect(() => { if (!asyncGetDefaults) return; @@ -75,6 +76,12 @@ const Form = ({ )} noValidate > + { + dispatch({ type: "set_state", state: newState }); + }} + formData={state.form} + /> ) => { return { diff --git a/src/Components/Form/FormContext.ts b/src/Components/Form/FormContext.ts index b77eb5c96fa..25d8410d0b2 100644 --- a/src/Components/Form/FormContext.ts +++ b/src/Components/Form/FormContext.ts @@ -4,7 +4,8 @@ import { FormDetails } from "./Utils"; export type FormContextValue = ( name: keyof T, - validate?: FieldValidator + validate?: FieldValidator, + excludeFromDraft?: boolean ) => { id: keyof T; name: keyof T; diff --git a/src/Components/Form/FormFields/DateFormField.tsx b/src/Components/Form/FormFields/DateFormField.tsx index 85b6ebbe131..6d0bf48a5bd 100644 --- a/src/Components/Form/FormFields/DateFormField.tsx +++ b/src/Components/Form/FormFields/DateFormField.tsx @@ -35,7 +35,11 @@ const DateFormField = (props: Props) => { className={classNames(field.error && "border-red-500")} id={field.id} name={field.name} - value={field.value} + value={ + field.value && typeof field.value === "string" + ? new Date(field.value) + : field.value + } onChange={field.handleChange} disabled={field.disabled} max={props.max || (props.disableFuture ? new Date() : undefined)} diff --git a/src/Components/Form/Utils.ts b/src/Components/Form/Utils.ts index c6bf46e6d68..2ec5d4b60e5 100644 --- a/src/Components/Form/Utils.ts +++ b/src/Components/Form/Utils.ts @@ -6,11 +6,13 @@ export type FormState = { form: T; errors: FormErrors }; export type FormAction = | { type: "set_form"; form: T } | { type: "set_errors"; errors: FormErrors } - | { type: "set_field"; name: keyof T; value: any; error: FieldError }; + | { type: "set_field"; name: keyof T; value: any; error: FieldError } + | { type: "set_state"; state: FormState }; export type FormReducer = ( prevState: FormState, action: FormAction ) => FormState; +export type FormDraft = { timestamp: number; form: FormDetails }; export const formReducer = ( state: FormState, @@ -26,5 +28,7 @@ export const formReducer = ( form: { ...state.form, [action.name]: action.value }, errors: { ...state.errors, [action.name]: action.error }, }; + case "set_state": + return action.state; } }; diff --git a/src/Components/Patient/DailyRounds.tsx b/src/Components/Patient/DailyRounds.tsx index 0ba60f4cf5c..fb4783f85a4 100644 --- a/src/Components/Patient/DailyRounds.tsx +++ b/src/Components/Patient/DailyRounds.tsx @@ -8,7 +8,7 @@ import { import { navigate } from "raviger"; import moment from "moment"; import loadable from "@loadable/component"; -import { useCallback, useReducer, useState, useEffect } from "react"; +import { useCallback, useState, useEffect } from "react"; import { useDispatch } from "react-redux"; import { SYMPTOM_CHOICES, @@ -40,6 +40,7 @@ import { FieldLabel } from "../Form/FormFields/FormField"; import TextAreaFormField from "../Form/FormFields/TextAreaFormField"; import { Cancel, Submit } from "../Common/components/ButtonV2"; import useAppHistory from "../../Common/hooks/useAppHistory"; +import { DraftSection, useAutoSaveReducer } from "../../Utils/AutoSave"; const Loading = loadable(() => import("../Common/Loading")); const PageTitle = loadable(() => import("../Common/PageTitle")); @@ -90,12 +91,16 @@ const DailyRoundsFormReducer = (state = initialState, action: any) => { form: action.form, }; } - case "set_error": { + case "set_errors": { return { ...state, errors: action.errors, }; } + case "set_state": { + if (action.state) return action.state; + return state; + } default: return state; } @@ -105,7 +110,10 @@ export const DailyRounds = (props: any) => { const { goBack } = useAppHistory(); const dispatchAction: any = useDispatch(); const { facilityId, patientId, consultationId, id } = props; - const [state, dispatch] = useReducer(DailyRoundsFormReducer, initialState); + const [state, dispatch] = useAutoSaveReducer( + DailyRoundsFormReducer, + initialState + ); const [isLoading, setIsLoading] = useState(false); const [facilityName, setFacilityName] = useState(""); const [patientName, setPatientName] = useState(""); @@ -225,7 +233,7 @@ export const DailyRounds = (props: any) => { return; } }); - dispatch({ type: "set_error", errors }); + dispatch({ type: "set_errors", errors }); return !invalidForm; }; @@ -514,6 +522,12 @@ export const DailyRounds = (props: any) => {
handleSubmit(e)}> + { + dispatch({ type: "set_state", state: newState }); + }} + formData={state.form} + />
{ disableFuture={true} showTodayButton={true} onChange={(date) => handleDateChange(date, "taken_at")} - errors={state.errors.taken_at} + errors={state.errors.taken_at as string} />
@@ -653,7 +667,7 @@ export const DailyRounds = (props: any) => { optionKey="text" optionValue="desc" options={TELEMEDICINE_ACTIONS} - onChange={(e) => setPreviousAction(e.target.value)} + onChange={(e: any) => setPreviousAction(e.target.value)} />
diff --git a/src/Components/Patient/PatientRegister.tsx b/src/Components/Patient/PatientRegister.tsx index da32db60872..7f9685479a2 100644 --- a/src/Components/Patient/PatientRegister.tsx +++ b/src/Components/Patient/PatientRegister.tsx @@ -12,7 +12,7 @@ import { navigate, useQueryParams } from "raviger"; import { parsePhoneNumberFromString } from "libphonenumber-js"; import moment from "moment"; import loadable from "@loadable/component"; -import { useCallback, useReducer, useState, useEffect } from "react"; +import { useCallback, useState, useEffect } from "react"; import { useDispatch } from "react-redux"; import { BLOOD_GROUPS, @@ -73,9 +73,10 @@ import { MaterialUiPickersDate } from "@material-ui/pickers/typings/date"; import InsuranceDetailsBuilder from "../HCX/InsuranceDetailsBuilder"; import { HCXPolicyModel } from "../HCX/models"; import HCXPolicyValidator from "../HCX/validators"; -import { FieldError } from "../Form/FieldValidators"; +import { FieldError } from "../Form/Fi1eldValidators"; import useAppHistory from "../../Common/hooks/useAppHistory"; import DialogModal from "../Common/Dialog"; +import { DraftSection, useAutoSaveReducer } from "../../Utils/AutoSave"; // const debounce = require("lodash.debounce"); interface PatientRegisterProps extends PatientModel { @@ -121,6 +122,7 @@ const initForm: any = { ward: "", address: "", permanent_address: "", + sameAddress: true, village: "", allergies: "", pincode: "", @@ -170,12 +172,16 @@ const patientFormReducer = (state = initialState, action: any) => { form: action.form, }; } - case "set_error": { + case "set_errors": { return { ...state, errors: action.errors, }; } + case "set_state": { + if (action.state) return action.state; + return state; + } default: return state; } @@ -194,7 +200,10 @@ export const PatientRegister = (props: PatientRegisterProps) => { const { gov_data_api_key, enable_hcx } = useConfig(); const dispatchAction: any = useDispatch(); const { facilityId, id } = props; - const [state, dispatch] = useReducer(patientFormReducer, initialState); + const [state, dispatch] = useAutoSaveReducer( + patientFormReducer, + initialState + ); const [showAlertMessage, setAlertMessage] = useState({ show: false, message: "", @@ -216,7 +225,6 @@ export const PatientRegister = (props: PatientRegisterProps) => { transfer?: boolean; patientList: Array; }>({ patientList: [] }); - const [sameAddress, setSameAddress] = useState(true); const [facilityName, setFacilityName] = useState(""); const [patientName, setPatientName] = useState(""); const [{ extId }, setQuery] = useQueryParams(); @@ -437,7 +445,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { : null, }; if (res.data.address !== res.data.permanent_address) { - setSameAddress(false); + formData["sameAddress"] = false; } res.data.medical_history.forEach((i: any) => { const medicalHistory = MEDICAL_HISTORY_CHOICES.find( @@ -548,7 +556,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { } return; case "permanent_address": - if (!sameAddress) { + if (!state.form.sameAddress) { if (!state.form[field]) { errors[field] = "Field is required"; if (!error_div) error_div = field; @@ -722,7 +730,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { } }); - dispatch({ type: "set_error", errors }); + dispatch({ type: "set_errors", errors }); return [!invalidForm, error_div]; }; @@ -844,7 +852,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { ward: state.form.ward, village: state.form.village, address: state.form.address ? state.form.address : undefined, - permanent_address: sameAddress + permanent_address: state.form.sameAddress ? state.form.address : state.form.permanent_address ? state.form.permanent_address @@ -1159,7 +1167,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { required value={careExtId} onChange={(e) => setCareExtId(e.target.value)} - errors={state.errors.name} + errors={state.errors.name as string} />
@@ -1401,7 +1428,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { value={state.form.nationality} options={countryList} onChange={handleChange} - errors={state.errors.nationality} + errors={state.errors.nationality as string} />
{state.form.nationality === "India" ? ( @@ -1533,7 +1560,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { margin="dense" value={state.form.passport_no} onChange={handleChange} - errors={state.errors.passport_no} + errors={state.errors.passport_no as string} />
)} @@ -1603,7 +1630,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { type="text" value={state.form.covin_id} onChange={handleChange} - errors={state.errors.covin_id} + errors={state.errors.covin_id as string} />
@@ -1681,7 +1708,10 @@ export const PatientRegister = (props: PatientRegisterProps) => { "last_vaccinated_date" ) } - errors={state.errors.last_vaccinated_date} + errors={ + state.errors + .last_vaccinated_date as string + } inputVariant="outlined" margin="dense" openTo="year" @@ -1772,7 +1802,10 @@ export const PatientRegister = (props: PatientRegisterProps) => { "estimated_contact_date" ) } - errors={state.errors.estimated_contact_date} + errors={ + state.errors + .estimated_contact_date as string + } inputVariant="outlined" margin="dense" disableFuture={true} @@ -1796,7 +1829,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { placeholder="Name / Cluster of Contact" value={state.form.cluster_name} onChange={handleChange} - errors={state.errors.cluster_name} + errors={state.errors.cluster_name as string} />
@@ -1821,7 +1854,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { value={state.form.disease_status} options={diseaseStatus} onChange={handleChange} - errors={state.errors.disease_status} + errors={state.errors.disease_status as string} />
@@ -1856,7 +1889,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { type="text" value={state.form.srf_id} onChange={handleChange} - errors={state.errors.srf_id} + errors={state.errors.srf_id as string} />
@@ -1907,7 +1940,10 @@ export const PatientRegister = (props: PatientRegisterProps) => { "date_declared_positive" ) } - errors={state.errors.date_declared_positive} + errors={ + state.errors + .date_declared_positive as string + } inputVariant="outlined" margin="dense" disableFuture={true} @@ -1927,7 +1963,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { type="number" value={state.form.test_id} onChange={handleChange} - errors={state.errors.test_id} + errors={state.errors.test_id as string} />
@@ -1945,7 +1981,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { onChange={(date) => handleDateChange(date, "date_of_test") } - errors={state.errors.date_of_test} + errors={state.errors.date_of_test as string} inputVariant="outlined" margin="dense" disableFuture={true} @@ -1965,7 +2001,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { onChange={(date) => handleDateChange(date, "date_of_result") } - errors={state.errors.date_of_result} + errors={state.errors.date_of_result as string} inputVariant="outlined" margin="dense" disableFuture={true} @@ -1987,7 +2023,10 @@ export const PatientRegister = (props: PatientRegisterProps) => { type="number" value={state.form.number_of_primary_contacts} onChange={handleChange} - errors={state.errors.number_of_primary_contacts} + errors={ + state.errors + .number_of_primary_contacts as string + } />
@@ -2005,7 +2044,10 @@ export const PatientRegister = (props: PatientRegisterProps) => { type="number" value={state.form.number_of_secondary_contacts} onChange={handleChange} - errors={state.errors.number_of_secondary_contacts} + errors={ + state.errors + .number_of_secondary_contacts as string + } />
@@ -2032,7 +2074,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { placeholder="Optional Information" value={state.form.present_health} onChange={handleFormFieldChange} - error={state.errors.present_health} + error={state.errors.present_health as string} /> diff --git a/src/Components/Patient/models.tsx b/src/Components/Patient/models.tsx index 3da59b9ba4a..effc3e3dcc9 100644 --- a/src/Components/Patient/models.tsx +++ b/src/Components/Patient/models.tsx @@ -56,6 +56,7 @@ export interface PatientModel { tele_consultation_history?: Array; last_consultation?: ConsultationModel; address?: string; + permanent_address?: string; village?: string; pincode?: number; contact_with_confirmed_carrier?: boolean; diff --git a/src/Components/Users/UserAdd.tsx b/src/Components/Users/UserAdd.tsx index f857cede4d9..dfa0c0e8f93 100644 --- a/src/Components/Users/UserAdd.tsx +++ b/src/Components/Users/UserAdd.tsx @@ -2,7 +2,7 @@ import loadable from "@loadable/component"; import { Link, navigate } from "raviger"; import { parsePhoneNumberFromString } from "libphonenumber-js/max"; import moment from "moment"; -import { useCallback, useEffect, useReducer, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { GENDER_TYPES, @@ -41,6 +41,7 @@ import useAppHistory from "../../Common/hooks/useAppHistory"; import Page from "../Common/components/Page"; import Card from "../../CAREUI/display/Card"; import CircularProgress from "../Common/components/CircularProgress"; +import { DraftSection, useAutoSaveReducer } from "../../Utils/AutoSave"; const Loading = loadable(() => import("../Common/Loading")); @@ -66,6 +67,7 @@ type UserForm = { email: string; phone_number: string; alt_phone_number: string; + phone_number_is_whatsapp: boolean; age: number; date_of_birth: Date | null; state: number; @@ -89,6 +91,7 @@ const initForm: UserForm = { email: "", phone_number: "+91", alt_phone_number: "+91", + phone_number_is_whatsapp: true, age: 0, date_of_birth: null, state: 0, @@ -123,6 +126,10 @@ const user_create_reducer = (state = initialState, action: any) => { errors: action.errors, }; } + case "set_state": { + if (action.state) return action.state; + return state; + } default: return state; } @@ -156,7 +163,10 @@ export const UserAdd = (props: UserProps) => { const dispatchAction: any = useDispatch(); const { userId } = props; - const [state, dispatch] = useReducer(user_create_reducer, initialState); + const [state, dispatch] = useAutoSaveReducer( + user_create_reducer, + initialState + ); const [isLoading, setIsLoading] = useState(false); const [isStateLoading, setIsStateLoading] = useState(false); const [isDistrictLoading, setIsDistrictLoading] = useState(false); @@ -168,7 +178,6 @@ export const UserAdd = (props: UserProps) => { const [districts, setDistricts] = useState([]); const [localBodies, setLocalBodies] = useState([]); const [selectedFacility, setSelectedFacility] = useState([]); - const [phoneIsWhatsApp, setPhoneIsWhatsApp] = useState(true); const [usernameInputInFocus, setUsernameInputInFocus] = useState(false); const [passwordInputInFocus, setPasswordInputInFocus] = useState(false); const [confirmPasswordInputInFocus, setConfirmPasswordInputInFocus] = @@ -363,18 +372,13 @@ export const UserAdd = (props: UserProps) => { }; useAbortableEffect(() => { - if (phoneIsWhatsApp) { + if (state.form.phone_number_is_whatsapp) { handleFieldChange({ name: "alt_phone_number", value: state.form.phone_number, }); - } else { - handleFieldChange({ - name: "alt_phone_number", - value: "+91", - }); } - }, [phoneIsWhatsApp, state.form.phone_number]); + }, [state.form.phone_number_is_whatsapp, state.form.phone_number]); const setFacility = (selected: FacilityModel | FacilityModel[] | null) => { setSelectedFacility(selected as FacilityModel[]); @@ -573,9 +577,11 @@ export const UserAdd = (props: UserProps) => { state.form.phone_number )?.format("E.164"), alt_phone_number: - parsePhoneNumberFromString(state.form.alt_phone_number)?.format( - "E.164" - ) || "", + parsePhoneNumberFromString( + state.form.phone_number_is_whatsapp + ? state.form.phone_number + : state.form.alt_phone_number + )?.format("E.164") || "", date_of_birth: moment(state.form.date_of_birth).format("YYYY-MM-DD"), age: Number(moment().diff(state.form.date_of_birth, "years", false)), doctor_qualification: @@ -649,6 +655,12 @@ export const UserAdd = (props: UserProps) => { > handleSubmit(e)}> + { + dispatch({ type: "set_state", state: newState }); + }} + formData={state.form} + />
Facilities @@ -717,8 +729,13 @@ export const UserAdd = (props: UserProps) => { disableCountry /> { + handleFieldChange({ + name: "phone_number_is_whatsapp", + value: checked, + }); + }} label="Is the phone number a WhatsApp number?" />
@@ -727,7 +744,7 @@ export const UserAdd = (props: UserProps) => { {...field("alt_phone_number")} placeholder="WhatsApp Phone Number" label="Whatsapp Number" - disabled={phoneIsWhatsApp} + disabled={state.form.phone_number_is_whatsapp} disableCountry /> diff --git a/src/Utils/AutoSave.tsx b/src/Utils/AutoSave.tsx new file mode 100644 index 00000000000..4d5345e8fac --- /dev/null +++ b/src/Utils/AutoSave.tsx @@ -0,0 +1,139 @@ +import moment from "moment"; +import { useReducer, useEffect, useRef, useState, Dispatch } from "react"; +import ButtonV2 from "../Components/Common/components/ButtonV2"; +import { FormAction, FormReducer, FormState } from "../Components/Form/Utils"; + +type Draft = { + timestamp: number; + draft: { + [key: string]: any; + }; +}; + +export function useAutoSaveReducer( + reducer: any, + initialState: any +): [FormState, Dispatch>] { + const saveInterval = 1000; + const saveKey = useRef(`form_draft_${window.location.pathname}`); + const sessionStartTime = useRef(Date.now()); + const [canStartDraft, setCanStartDraft] = useState(false); + const [draftStarted, setDraftStarted] = useState(false); + const [state, dispatch] = useReducer>(reducer, initialState); + + useEffect(() => { + if (!canStartDraft) return; + setDraftStarted(true); + }, [canStartDraft, state]); + + useEffect(() => { + const timeoutId = setTimeout(() => { + setCanStartDraft(true); + }, 3000); + + return () => { + clearTimeout(timeoutId); + }; + }, []); + + useEffect(() => { + const intervalId = setInterval(() => { + if (!draftStarted) return; + const savedDrafts = localStorage.getItem(saveKey.current); + const drafts = savedDrafts ? JSON.parse(savedDrafts) : []; + const existingIndex = drafts.findIndex( + (draft: Draft) => draft.timestamp === sessionStartTime.current + ); + const currentDraft = { + timestamp: sessionStartTime.current, + draft: state, + }; + if (existingIndex !== -1) { + drafts[existingIndex] = currentDraft; + } else { + drafts.push(currentDraft); + } + if (drafts.length > 2) drafts.shift(); + localStorage.setItem(saveKey.current, JSON.stringify(drafts)); + }, saveInterval); + + return () => { + clearInterval(intervalId); + }; + }, [state, draftStarted]); + + return [state, dispatch]; +} + +export function useAutoSaveState(initialState: any) { + const [state, dispatch] = useAutoSaveReducer((state: any, action: any) => { + if (action.type === "set_state") { + return action.state; + } + return state; + }, initialState); + + const setState = (newState: any) => { + dispatch({ type: "set_state", state: newState }); + }; + + return [state, setState]; +} + +export function DraftSection(props: { + handleDraftSelect: (formState: any) => void; + formData: any; +}) { + const { handleDraftSelect } = props; + const [drafts, setDrafts] = useState([]); + const saveKey = useRef(`form_draft_${window.location.pathname}`); + const draftStarted = + drafts.length > 0 + ? drafts[drafts.length - 1].draft == props.formData + : false; + + useEffect(() => { + const timeoutId = setTimeout(() => { + const savedDrafts = localStorage.getItem(saveKey.current); + const drafts = savedDrafts ? JSON.parse(savedDrafts) : []; + setDrafts(drafts); + }, 1000); + + return () => { + clearTimeout(timeoutId); + }; + }, []); + + return ( + <> + {drafts && ( +
+ {drafts?.length > 0 && ( +
+

+ Last saved draft:{" "} + {moment( + draftStarted + ? drafts[0].timestamp + : drafts[drafts.length - 1].timestamp + ).fromNow()} +

+ + handleDraftSelect( + (draftStarted ? drafts[0] : drafts[drafts.length - 1]).draft + ) + } + className="ml-2" + > + Restore + +
+ )} +
+ )} + + ); +}