From 899e787b7fb7cb97f5c73ab7b8112298c7fe446e Mon Sep 17 00:00:00 2001 From: Nikhil Golla <155874950+NikhilGolla72@users.noreply.github.com> Date: Wed, 9 Oct 2024 01:43:07 +0530 Subject: [PATCH] Facility image is Fixed Users can now upload facility cover image in the facility registration form itself. --- src/Components/Facility/FacilityCreate.tsx | 458 +++++++++++---------- 1 file changed, 238 insertions(+), 220 deletions(-) diff --git a/src/Components/Facility/FacilityCreate.tsx b/src/Components/Facility/FacilityCreate.tsx index 81bc6f48125..4440b920f50 100644 --- a/src/Components/Facility/FacilityCreate.tsx +++ b/src/Components/Facility/FacilityCreate.tsx @@ -1,71 +1,70 @@ -import * as Notification from "../../Utils/Notifications.js"; - -import ButtonV2, { Cancel, Submit } from "../Common/components/ButtonV2"; +import * as Notification from '../../Utils/Notifications.js'; +import ButtonV2, { Cancel, Submit } from '../Common/components/ButtonV2'; import { CapacityModal, DistrictModel, DoctorModal, FacilityRequest, -} from "./models"; -import { DraftSection, useAutoSaveReducer } from "../../Utils/AutoSave.js"; +} from './models'; +import { DraftSection, useAutoSaveReducer } from '../../Utils/AutoSave.js'; import { BED_TYPES, FACILITY_FEATURE_TYPES, FACILITY_TYPES, -} from "../../Common/constants"; +} from '../../Common/constants'; import { MultiSelectFormField, SelectFormField, -} from "../Form/FormFields/SelectFormField"; +} from '../Form/FormFields/SelectFormField'; import { Popover, PopoverButton, PopoverPanel, Transition, -} from "@headlessui/react"; -import { useEffect, useState } from "react"; -import Steps, { Step } from "../Common/Steps"; +} from '@headlessui/react'; +import {useEffect, useState } from 'react'; +import Steps, { Step } from '../Common/Steps'; import { getPincodeDetails, includesIgnoreCase, parsePhoneNumber, compareBy, -} from "../../Utils/utils"; +} from '../../Utils/utils'; import { phonePreg, validateLatitude, validateLongitude, validatePincode, -} from "../../Common/validation"; - -import { BedCapacity } from "./BedCapacity"; -import BedTypeCard from "./BedTypeCard"; -import Card from "../../CAREUI/display/Card.js"; -import CareIcon from "../../CAREUI/icons/CareIcon"; -import { StaffCapacity } from "./StaffCapacity.js"; -import StaffCountCard from "./StaffCountCard.js"; -import { FieldChangeEvent } from "../Form/FormFields/Utils"; -import { FormAction } from "../Form/Utils.js"; -import GLocationPicker from "../Common/GLocationPicker"; -import Page from "../Common/components/Page.js"; -import PhoneNumberFormField from "../Form/FormFields/PhoneNumberFormField"; -import RadioFormField from "../Form/FormFields/RadioFormField"; -import TextAreaFormField from "../Form/FormFields/TextAreaFormField"; -import TextFormField from "../Form/FormFields/TextFormField"; - -import { navigate } from "raviger"; -import useAppHistory from "../../Common/hooks/useAppHistory"; -import { useTranslation } from "react-i18next"; -import { PhoneNumberValidator } from "../Form/FieldValidators.js"; -import request from "../../Utils/request/request.js"; -import routes from "../../Redux/api.js"; -import useQuery from "../../Utils/request/useQuery.js"; -import { RequestResult } from "../../Utils/request/types.js"; -import useAuthUser from "../../Common/hooks/useAuthUser"; -import SpokeFacilityEditor from "./SpokeFacilityEditor.js"; -import careConfig from "@careConfig"; - -import Loading from "@/Components/Common/Loading"; +} from '../../Common/validation'; + +import { BedCapacity } from './BedCapacity'; +import BedTypeCard from './BedTypeCard'; +import Card from '../../CAREUI/display/Card.js'; +import CareIcon from '../../CAREUI/icons/CareIcon'; +import { StaffCapacity } from './StaffCapacity.js'; +import StaffCountCard from './StaffCountCard.js'; +import { FieldChangeEvent } from '../Form/FormFields/Utils'; +import { FormAction } from '../Form/Utils.js'; +import GLocationPicker from '../Common/GLocationPicker'; +import Page from '../Common/components/Page.js'; +import PhoneNumberFormField from '../Form/FormFields/PhoneNumberFormField'; +import RadioFormField from '../Form/FormFields/RadioFormField'; +import TextAreaFormField from '../Form/FormFields/TextAreaFormField'; +import TextFormField from '../Form/FormFields/TextFormField'; +import { navigate } from 'raviger'; +import useAppHistory from '../../Common/hooks/useAppHistory'; +import { useTranslation } from 'react-i18next'; +import { PhoneNumberValidator } from '../Form/FieldValidators.js'; +import request from '../../Utils/request/request.js'; +import routes from '../../Redux/api.js'; +import useQuery from '../../Utils/request/useQuery.js'; +import { RequestResult } from '../../Utils/request/types.js'; +import useAuthUser from '../../Common/hooks/useAuthUser'; +import SpokeFacilityEditor from './SpokeFacilityEditor.js'; +import careConfig from '@careConfig'; +import CoverImageEditModal from './CoverImageEditModal'; + +import Loading from "../Common/Loading"; interface FacilityProps { facilityId?: string; } @@ -96,18 +95,18 @@ type FacilityForm = { const initForm: FacilityForm = { facility_type: undefined, - name: "", + name: '', state: 0, district: 0, local_body: 0, ward: 0, - kasp_empanelled: "false", + kasp_empanelled: 'false', features: [], - address: "", - phone_number: "", - latitude: "", - longitude: "", - pincode: "", + address: '', + phone_number: '', + latitude: '', + longitude: '', + pincode: '', oxygen_capacity: undefined, type_b_cylinders: undefined, type_c_cylinders: undefined, @@ -120,7 +119,7 @@ const initForm: FacilityForm = { const initError: Record = Object.assign( {}, - ...Object.keys(initForm).map((k) => ({ [k]: "" })), + ...Object.keys(initForm).map((k) => ({ [k]: '' })) ); const initialState = { @@ -130,11 +129,11 @@ const initialState = { const facilityCreateReducer = (state = initialState, action: FormAction) => { switch (action.type) { - case "set_form": + case 'set_form': return { ...state, form: action.form }; - case "set_errors": + case 'set_errors': return { ...state, errors: action.errors }; - case "set_state": { + case 'set_state': { if (action.state) return action.state; return state; } @@ -147,11 +146,11 @@ export const FacilityCreate = (props: FacilityProps) => { const [state, dispatch] = useAutoSaveReducer( facilityCreateReducer, - initialState, + initialState ); const [isLoading, setIsLoading] = useState(false); const [currentStep, setCurrentStep] = useState(1); - const [createdFacilityId, setCreatedFacilityId] = useState(""); + const [createdFacilityId, setCreatedFacilityId] = useState(''); const [showAutoFilledPincode, setShowAutoFilledPincode] = useState(false); const [capacityData, setCapacityData] = useState>([]); const [doctorData, setDoctorData] = useState>([]); @@ -161,18 +160,20 @@ export const FacilityCreate = (props: FacilityProps) => { const [districtId, setDistrictId] = useState(); const [localBodyId, setLocalBodyId] = useState(); const { goBack } = useAppHistory(); - const headerText = !facilityId ? "Create Facility" : "Update Facility"; - const buttonText = !facilityId ? "Save Facility" : "Update Facility"; + const headerText = !facilityId ? 'Create Facility' : 'Update Facility'; + const buttonText = !facilityId ? 'Save Facility' : 'Update Facility'; + const [isImageUploadChecked, setIsImageUploadChecked] = useState(false); // Track if the user wants to upload a cover image + const authUser = useAuthUser(); useEffect(() => { if ( authUser && - authUser.user_type !== "StateAdmin" && - authUser.user_type !== "DistrictAdmin" && - authUser.user_type !== "DistrictLabAdmin" + authUser.user_type !== 'StateAdmin' && + authUser.user_type !== 'DistrictAdmin' && + authUser.user_type !== 'DistrictLabAdmin' ) { - navigate("/facility"); + navigate('/facility'); Notification.Error({ msg: "You don't have permission to perform this action. Contact the admin", }); @@ -197,42 +198,42 @@ export const FacilityCreate = (props: FacilityProps) => { id: String(districtId), }, prefetch: !!districtId, - }, + } ); const getSteps = (): Step[] => { return [ { id: 1, - name: "Facility details", + name: 'Facility details', onClick: () => { setCurrentStep(1); }, - status: currentStep === 1 ? "current" : "complete", + status: currentStep === 1 ? 'current' : 'complete', disabled: currentStep > 1, }, { id: 2, - name: "Bed Capacity", + name: 'Bed Capacity', onClick: () => { setCurrentStep(2); }, status: currentStep === 2 - ? "current" + ? 'current' : currentStep > 2 - ? "complete" - : "upcoming", - disabled: createdFacilityId == "", + ? 'complete' + : 'upcoming', + disabled: createdFacilityId == '', }, { id: 3, - name: "Staff Capacity", + name: 'Staff Capacity', onClick: () => { setCurrentStep(3); }, - disabled: createdFacilityId == "", - status: currentStep === 3 ? "current" : "upcoming", + disabled: createdFacilityId == '', + status: currentStep === 3 ? 'current' : 'upcoming', }, ]; }; @@ -244,7 +245,7 @@ export const FacilityCreate = (props: FacilityProps) => { id: String(localBodyId), }, prefetch: !!localBodyId, - }, + } ); const facilityQuery = useQuery(routes.getPermittedFacility, { @@ -257,25 +258,25 @@ export const FacilityCreate = (props: FacilityProps) => { setIsLoading(true); if (res?.ok && data) { const formData = { - facility_type: data.facility_type ? data.facility_type : "", - name: data.name ? data.name : "", + facility_type: data.facility_type ? data.facility_type : '', + name: data.name ? data.name : '', state: data.state ? data.state : 0, district: data.district ? data.district : 0, local_body: data.local_body ? data.local_body : 0, features: data.features || [], ward: data.ward_object ? data.ward_object.id : 0, - kasp_empanelled: "", - address: data.address ? data.address : "", - pincode: data.pincode ? data.pincode : "", + kasp_empanelled: '', + address: data.address ? data.address : '', + pincode: data.pincode ? data.pincode : '', phone_number: data.phone_number ? data.phone_number.length == 10 - ? "+91" + data.phone_number + ? '+91' + data.phone_number : data.phone_number - : "", - latitude: data.latitude ? parseFloat(data.latitude).toFixed(7) : "", + : '', + latitude: data.latitude ? parseFloat(data.latitude).toFixed(7) : '', longitude: data.longitude ? parseFloat(data.longitude).toFixed(7) - : "", + : '', type_b_cylinders: data.type_b_cylinders, type_c_cylinders: data.type_c_cylinders, type_d_cylinders: data.type_d_cylinders, @@ -285,7 +286,7 @@ export const FacilityCreate = (props: FacilityProps) => { expected_oxygen_requirement: data.expected_oxygen_requirement, oxygen_capacity: data.oxygen_capacity, }; - dispatch({ type: "set_form", form: formData }); + dispatch({ type: 'set_form', form: formData }); setStateId(data.state); setDistrictId(data.district); setLocalBodyId(data.local_body); @@ -298,12 +299,12 @@ export const FacilityCreate = (props: FacilityProps) => { }); const { data: stateData, loading: isStateLoading } = useQuery( - routes.statesList, + routes.statesList ); const handleChange = (e: FieldChangeEvent) => { dispatch({ - type: "set_form", + type: 'set_form', form: { ...state.form, [e.name]: e.value }, }); }; @@ -311,7 +312,7 @@ export const FacilityCreate = (props: FacilityProps) => { const handleLocationChange = (location: google.maps.LatLng | undefined) => { if (location) { dispatch({ - type: "set_form", + type: 'set_form', form: { ...state.form, latitude: location.lat().toFixed(7), @@ -328,7 +329,7 @@ export const FacilityCreate = (props: FacilityProps) => { const pincodeDetails = await getPincodeDetails( e.value, - careConfig.govDataApiKey, + careConfig.govDataApiKey ); if (!pincodeDetails) return; @@ -349,7 +350,7 @@ export const FacilityCreate = (props: FacilityProps) => { if (!matchedDistrict) return; dispatch({ - type: "set_form", + type: 'set_form', form: { ...state.form, state: matchedState.id, @@ -366,12 +367,12 @@ export const FacilityCreate = (props: FacilityProps) => { }; const handleSelectCurrentLocation = ( - setCenter: (lat: number, lng: number) => void, + setCenter: (lat: number, lng: number) => void ) => { if (navigator.geolocation) { navigator.geolocation.getCurrentPosition((position) => { dispatch({ - type: "set_form", + type: 'set_form', form: { ...state.form, latitude: String(position.coords.latitude), @@ -389,32 +390,32 @@ export const FacilityCreate = (props: FacilityProps) => { let invalidForm = false; Object.keys(state.form).forEach((field) => { switch (field) { - case "facility_type": - case "name": - case "address": + case 'facility_type': + case 'name': + case 'address': if (!state.form[field]) { - errors[field] = t("required"); + errors[field] = t('required'); invalidForm = true; } return; - case "district": - case "state": - case "local_body": - case "ward": + case 'district': + case 'state': + case 'local_body': + case 'ward': if (!Number(state.form[field])) { - errors[field] = t("required"); + errors[field] = t('required'); invalidForm = true; } return; - case "pincode": + case 'pincode': if (!validatePincode(state.form[field])) { - errors[field] = t("invalid_pincode"); + errors[field] = t('invalid_pincode'); invalidForm = true; } return; - case "phone_number": + case 'phone_number': // eslint-disable-next-line no-case-declarations const phoneNumber = state.form[field]; if ( @@ -422,19 +423,19 @@ export const FacilityCreate = (props: FacilityProps) => { !PhoneNumberValidator()(phoneNumber) === undefined || !phonePreg(phoneNumber) ) { - errors[field] = t("invalid_phone_number"); + errors[field] = t('invalid_phone_number'); invalidForm = true; } return; - case "latitude": + case 'latitude': if (!!state.form.latitude && !validateLatitude(state.form[field])) { - errors[field] = t("latitude_invalid"); + errors[field] = t('latitude_invalid'); invalidForm = true; } return; - case "longitude": + case 'longitude': if (!!state.form.longitude && !validateLongitude(state.form[field])) { - errors[field] = t("longitude_invalid"); + errors[field] = t('longitude_invalid'); invalidForm = true; } return; @@ -444,18 +445,28 @@ export const FacilityCreate = (props: FacilityProps) => { } }); if (invalidForm) { - dispatch({ type: "set_errors", errors }); + dispatch({ type: 'set_errors', errors }); return false; } - dispatch({ type: "set_errors", errors }); + dispatch({ type: 'set_errors', errors }); return true; }; + const [isCoverImageModalOpen, setIsCoverImageModalOpen] = useState(false); const handleSubmit = async (e: any) => { e.preventDefault(); const validated = validateForm(); + const formData = new FormData(); + + // Append form fields to FormData + Object.keys(state.form).forEach((key) => { + formData.append(key, (state.form as any)[key]); + }); + if (validated) { setIsLoading(true); + + // Facility data for creating/updating const data: FacilityRequest = { facility_type: state.form.facility_type, name: state.form.name, @@ -469,61 +480,36 @@ export const FacilityCreate = (props: FacilityProps) => { latitude: state.form.latitude, longitude: state.form.longitude, phone_number: parsePhoneNumber(state.form.phone_number), - oxygen_capacity: state.form.oxygen_capacity - ? state.form.oxygen_capacity - : 0, - type_b_cylinders: state.form.type_b_cylinders - ? state.form.type_b_cylinders - : 0, - type_c_cylinders: state.form.type_c_cylinders - ? state.form.type_c_cylinders - : 0, - type_d_cylinders: state.form.type_d_cylinders - ? state.form.type_d_cylinders - : 0, - expected_oxygen_requirement: state.form.expected_oxygen_requirement - ? state.form.expected_oxygen_requirement - : 0, - expected_type_b_cylinders: state.form.expected_type_b_cylinders - ? state.form.expected_type_b_cylinders - : 0, - - expected_type_c_cylinders: state.form.expected_type_c_cylinders - ? state.form.expected_type_c_cylinders - : 0, - - expected_type_d_cylinders: state.form.expected_type_d_cylinders - ? state.form.expected_type_d_cylinders - : 0, + oxygen_capacity: state.form.oxygen_capacity ?? 0, + type_b_cylinders: state.form.type_b_cylinders ?? 0, + type_c_cylinders: state.form.type_c_cylinders ?? 0, + type_d_cylinders: state.form.type_d_cylinders ?? 0, + expected_oxygen_requirement: + state.form.expected_oxygen_requirement ?? 0, + expected_type_b_cylinders: state.form.expected_type_b_cylinders ?? 0, + expected_type_c_cylinders: state.form.expected_type_c_cylinders ?? 0, + expected_type_d_cylinders: state.form.expected_type_d_cylinders ?? 0, }; + // Create or update facility const { res, data: requestData } = facilityId ? await request(routes.updateFacility, { body: data, - pathParams: { - id: facilityId, - }, + pathParams: { id: facilityId }, }) - : await request(routes.createFacility, { - body: data, - }); + : await request(routes.createFacility, { body: data }); if (res?.ok && requestData) { const id = requestData.id; - dispatch({ type: "set_form", form: initForm }); - if (!facilityId) { - Notification.Success({ - msg: "Facility added successfully", - }); - setCreatedFacilityId(String(id)); - setCurrentStep(2); - } else { - Notification.Success({ - msg: "Facility updated successfully", - }); - navigate(`/facility/${facilityId}`); + dispatch({ type: 'set_form', form: initForm }); + Notification.Success({ msg: 'Facility added successfully' }); + setCreatedFacilityId(String(id)); + setCurrentStep(2); + if (isImageUploadChecked) { + setIsCoverImageModalOpen(true); } } + setIsLoading(false); } }; @@ -531,7 +517,6 @@ export const FacilityCreate = (props: FacilityProps) => { if (isLoading) { return ; } - let capacityList: any = null; let totalBedCount = 0; let totalOccupiedBedCount = 0; @@ -539,7 +524,7 @@ export const FacilityCreate = (props: FacilityProps) => { if (!capacityData || !capacityData.length) { capacityList = (
- {t("no_bed_types_found")} + {t('no_bed_types_found')}
); } else { @@ -554,7 +539,7 @@ export const FacilityCreate = (props: FacilityProps) => { id="total-bed-capacity" > { if (res) { const removeCurrentBedType = (bedTypeId: number | undefined) => { setCapacityData((state) => - state.filter((i) => i.id !== bedTypeId), + state.filter((i) => i.id !== bedTypeId) ); setBedCapacityKey((bedCapacityKey) => bedCapacityKey + 1); }; @@ -603,7 +588,7 @@ export const FacilityCreate = (props: FacilityProps) => { if (!doctorData || !doctorData.length) { doctorList = (
- {t("no_staff")} + {t('no_staff')}
); } else { @@ -612,14 +597,14 @@ export const FacilityCreate = (props: FacilityProps) => { {doctorData.map((data: DoctorModal) => { const removeCurrentDoctorData = (doctorId: number | undefined) => { setDoctorData((state) => - state.filter((i: DoctorModal) => i.id !== doctorId), + state.filter((i: DoctorModal) => i.id !== doctorId) ); setDocCapacityKey((docCapacityKey) => docCapacityKey + 1); }; return ( { const { res, data } = await request(routes.listDoctor, { @@ -655,15 +640,46 @@ export const FacilityCreate = (props: FacilityProps) => { +
+ +
+ +
+ setIsCoverImageModalOpen(true)} + className="mt-4" + disabled={!isImageUploadChecked} + > + {t('Upload Facility Image')} + + + setIsCoverImageModalOpen(false)} + onSave={() => setIsCoverImageModalOpen(false)} + facility={{ + id: createdFacilityId, + name: state.form.name, + read_cover_image_url: '', + }} + /> +
{ navigate(`/facility/${createdFacilityId}`); }} @@ -679,7 +695,7 @@ export const FacilityCreate = (props: FacilityProps) => {
-
{t("staff_list")}
+
{t('staff_list')}
{doctorList} @@ -692,7 +708,7 @@ export const FacilityCreate = (props: FacilityProps) => { @@ -700,7 +716,7 @@ export const FacilityCreate = (props: FacilityProps) => { { setCurrentStep(3); }} @@ -717,11 +733,12 @@ export const FacilityCreate = (props: FacilityProps) => {
- {t("bed_capacity")} + {t('bed_capacity')}
{capacityList}
+
); case 1: @@ -730,7 +747,7 @@ export const FacilityCreate = (props: FacilityProps) => { {!facilityId && } @@ -739,7 +756,7 @@ export const FacilityCreate = (props: FacilityProps) => {
handleSubmit(e)}> { - dispatch({ type: "set_state", state: newState }); + dispatch({ type: 'set_state', state: newState }); setStateId(newState.form.state); setDistrictId(newState.form.district); setLocalBodyId(newState.form.local_body); @@ -748,27 +765,28 @@ export const FacilityCreate = (props: FacilityProps) => { />
o.text} optionValue={(o) => o.text} /> + o.name} optionValue={(o) => o.id} />
@@ -776,16 +794,16 @@ export const FacilityCreate = (props: FacilityProps) => {
- State and district auto-filled from pincode + {t('State and district auto-filled from pincode')}
)}
o.name} @@ -797,10 +815,10 @@ export const FacilityCreate = (props: FacilityProps) => { }} /> o.name} @@ -812,11 +830,11 @@ export const FacilityCreate = (props: FacilityProps) => { }} /> o.name} optionValue={(o) => o.id} @@ -827,32 +845,32 @@ export const FacilityCreate = (props: FacilityProps) => { }} /> { return { id: e.id, - name: e.number + ": " + e.name, + name: e.number + ': ' + e.name, }; })} optionLabel={(o) => o.name} optionValue={(o) => o.id} /> - + {facilityId && (
-

{t("spokes")}

+

{t('spokes')}

@@ -860,83 +878,83 @@ export const FacilityCreate = (props: FacilityProps) => { )}
} + trailing={} min={0} /> } - label={t("expected_burn_rate")} + trailing={} + label={t('expected_burn_rate')} min={0} /> } + trailing={} min={0} /> } + trailing={} min={0} /> } + trailing={} min={0} /> } - label={t("expected_burn_rate")} + trailing={} + label={t('expected_burn_rate')} min={0} /> } + trailing={} min={0} /> } + trailing={} min={0} />
{careConfig.kasp.enabled && ( (o ? "Yes" : "No")} + optionLabel={(o) => (o ? 'Yes' : 'No')} optionValue={(o) => String(o)} /> )} @@ -945,8 +963,8 @@ export const FacilityCreate = (props: FacilityProps) => {
@@ -962,7 +980,7 @@ export const FacilityCreate = (props: FacilityProps) => { > - Select location from map + {t('Select location from map')} @@ -992,7 +1010,7 @@ export const FacilityCreate = (props: FacilityProps) => {
} placeholder="Longitude" />