diff --git a/.example.env b/.example.env index 0ce9043b9a3..b4b6fdbd051 100644 --- a/.example.env +++ b/.example.env @@ -13,7 +13,7 @@ REACT_OHCN_URL= # Plausible site domain (default: care.ohc.network) REACT_PLAUSIBLE_SITE_DOMAIN= -# Plausible server URL (default: https://plausible.10bedicu.in) +# Plausible server URL (default: https://plausible.ohc.network) REACT_PLAUSIBLE_SERVER_URL= # Care Apps. repo@branch seperated by commas diff --git a/care.config.ts b/care.config.ts index ed82d69554a..6dd2c1e4bfd 100644 --- a/care.config.ts +++ b/care.config.ts @@ -85,7 +85,7 @@ const careConfig = { // Plugins related configs... plausible: { - server: env.REACT_PLAUSIBLE_SERVER_URL || "https://plausible.10bedicu.in", + server: env.REACT_PLAUSIBLE_SERVER_URL || "https://plausible.ohc.network", domain: env.REACT_PLAUSIBLE_SITE_DOMAIN || "care.ohc.network", }, diff --git a/cypress/pageobject/Facility/FacilityManage.ts b/cypress/pageobject/Facility/FacilityManage.ts index 52a432abe59..9a32a5be36f 100644 --- a/cypress/pageobject/Facility/FacilityManage.ts +++ b/cypress/pageobject/Facility/FacilityManage.ts @@ -1,6 +1,6 @@ class FacilityManage { clickCoverImage() { - cy.get("#facility-coverimage").click({ force: true }); + cy.get("#facility-coverimage").click(); } verifyUploadButtonVisible() { @@ -13,6 +13,11 @@ class FacilityManage { .wait(100); // Adjust the wait time as needed } + clickSaveCoverImage() { + cy.get("#save-cover-image").scrollIntoView(); + cy.get("#save-cover-image").click(); + } + verifyTotalDoctorCapacity(expectedCapacity: string) { cy.get("#facility-doctor-totalcapacity").contains(expectedCapacity); } @@ -37,11 +42,6 @@ class FacilityManage { cy.get("#delete-facility-bedcapacity").click(); } - clickSaveCoverImage() { - cy.get("#save-cover-image").scrollIntoView(); - cy.get("#save-cover-image").click(); - } - clickFacilityConfigureButton() { cy.get("#configure-facility").should("be.visible"); cy.get("#configure-facility").click(); diff --git a/netlify.toml b/netlify.toml index 59cbe33e2d4..7aa95ee51a8 100644 --- a/netlify.toml +++ b/netlify.toml @@ -20,9 +20,9 @@ X-Frame-Options = "DENY" X-Content-Type-Options = "nosniff" Content-Security-Policy-Report-Only = ''' default-src 'self'; - script-src 'self' 'nonce-f51b9742' https://plausible.10bedicu.in; + script-src 'self' 'nonce-f51b9742' https://plausible.ohc.network; style-src 'self' 'unsafe-inline'; - connect-src 'self' https://plausible.10bedicu.in; + connect-src 'self' https://plausible.ohc.network; img-src 'self' https://cdn.ohc.network https://egov-s3-facility-10bedicu.s3.amazonaws.com https://egov-s3-patient-data-10bedicu.s3.amazonaws.com; object-src 'self' https://egov-s3-facility-10bedicu.s3.amazonaws.com https://egov-s3-patient-data-10bedicu.s3.amazonaws.com; report-uri https://csp-logger.ohc.network/ diff --git a/src/Common/hooks/useAuthUser.ts b/src/Common/hooks/useAuthUser.ts index 78bf93fa8f8..ae181f62ca8 100644 --- a/src/Common/hooks/useAuthUser.ts +++ b/src/Common/hooks/useAuthUser.ts @@ -9,6 +9,7 @@ type AuthContextType = { user: UserModel | undefined; signIn: (creds: LoginCredentials) => Promise; signOut: () => Promise; + refetchUser: () => Promise>; }; export const AuthUserContext = createContext(null); diff --git a/src/Components/CameraFeed/CameraFeed.tsx b/src/Components/CameraFeed/CameraFeed.tsx index 3c9b25b6fc9..93f87ee20aa 100644 --- a/src/Components/CameraFeed/CameraFeed.tsx +++ b/src/Components/CameraFeed/CameraFeed.tsx @@ -12,7 +12,7 @@ import useFullscreen from "../../Common/hooks/useFullscreen"; import useBreakpoints from "../../Common/hooks/useBreakpoints"; import { GetPresetsResponse } from "./routes"; import VideoPlayer from "./videoPlayer"; -import MonitorAssetPopover from "../Common/MonitorAssetPopover"; +import AssetInfoPopover from "../Common/AssetInfoPopover"; interface Props { children?: React.ReactNode; @@ -28,6 +28,7 @@ interface Props { shortcutsDisabled?: boolean; onMove?: () => void; operate: ReturnType["operate"]; + hideAssetInfo?: boolean; } export default function CameraFeed(props: Props) { @@ -183,15 +184,18 @@ export default function CameraFeed(props: Props) { {props.children}
-
- - {props.asset.name} - - -
+ {!props.hideAssetInfo && ( +
+ + {props.asset.name} + + +
+ )} + {!isIOS && (
- +
); } @@ -246,6 +251,7 @@ export default function ConfigureCamera(props: Props) { ); } }} + hideAssetInfo >
{ +const AssetInfoPopover = ({ asset, className }: AssetInfoPopoverProps) => { const { t } = useTranslation(); return ( @@ -87,4 +84,4 @@ const MonitorAssetPopover = ({ ); }; -export default MonitorAssetPopover; +export default AssetInfoPopover; diff --git a/src/Components/Common/Avatar.tsx b/src/Components/Common/Avatar.tsx index e4d42dd72ae..1d20c3ad18f 100644 --- a/src/Components/Common/Avatar.tsx +++ b/src/Components/Common/Avatar.tsx @@ -1,5 +1,5 @@ import { cn } from "@/lib/utils"; -import React, { useEffect, useRef, useState } from "react"; +import React from "react"; const colors: string[] = [ "#E6F3FF", // Light Blue @@ -56,44 +56,47 @@ const Avatar: React.FC = ({ className, }) => { const [bgColor] = propColors || toColor(name); - const [width, setWidth] = useState(0); - const avatarRef = useRef(null); - - useEffect(() => { - const updateWidth = () => { - const avatarRect = avatarRef.current?.getBoundingClientRect(); - const width = avatarRect?.width || 0; - setWidth(width); - }; - updateWidth(); - document.addEventListener("resize", updateWidth); - return () => document.removeEventListener("resize", updateWidth); - }, []); - return (
{imageUrl ? ( {name} ) : ( -
{initials(name)}
+ + + {initials(name)} + + )}
); }; export { Avatar }; +export type { AvatarProps }; diff --git a/src/Components/Common/AvatarEditModal.tsx b/src/Components/Common/AvatarEditModal.tsx new file mode 100644 index 00000000000..f83b76e4a2e --- /dev/null +++ b/src/Components/Common/AvatarEditModal.tsx @@ -0,0 +1,391 @@ +import React, { + useState, + ChangeEventHandler, + useCallback, + useEffect, + useRef, +} from "react"; +import { Warn } from "@/Utils/Notifications"; +import useDragAndDrop from "@/Utils/useDragAndDrop"; +import ButtonV2, { Cancel, Submit } from "../Common/components/ButtonV2"; +import Webcam from "react-webcam"; +import CareIcon from "@/CAREUI/icons/CareIcon"; +import { useTranslation } from "react-i18next"; +import DialogModal from "../Common/Dialog"; + +interface Props { + title: string; + open: boolean; + imageUrl?: string; + handleUpload: (file: File, onError: () => void) => Promise; + handleDelete: (onError: () => void) => Promise; + onClose?: () => void; +} + +const VideoConstraints = { + user: { + width: 1280, + height: 720, + facingMode: "user", + }, + environment: { + width: 1280, + height: 720, + facingMode: { exact: "environment" }, + }, +} as const; + +type IVideoConstraint = + (typeof VideoConstraints)[keyof typeof VideoConstraints]; + +const AvatarEditModal = ({ + title, + open, + imageUrl, + handleUpload, + handleDelete, + onClose, +}: Props) => { + const [isProcessing, setIsProcessing] = useState(false); + const [selectedFile, setSelectedFile] = useState(); + const [preview, setPreview] = useState(); + const [isCameraOpen, setIsCameraOpen] = useState(false); + const webRef = useRef(null); + const [previewImage, setPreviewImage] = useState(null); + const [isCaptureImgBeingUploaded, setIsCaptureImgBeingUploaded] = + useState(false); + const [constraint, setConstraint] = useState( + VideoConstraints.user, + ); + const { t } = useTranslation(); + const [isDragging, setIsDragging] = useState(false); + + const handleSwitchCamera = useCallback(() => { + setConstraint( + constraint.facingMode === "user" + ? VideoConstraints.environment + : VideoConstraints.user, + ); + }, []); + + const captureImage = () => { + setPreviewImage(webRef.current.getScreenshot()); + const canvas = webRef.current.getCanvas(); + canvas?.toBlob((blob: Blob) => { + const myFile = new File([blob], "image.png", { + type: blob.type, + }); + setSelectedFile(myFile); + }); + }; + + const closeModal = () => { + setPreview(undefined); + setIsProcessing(false); + setSelectedFile(undefined); + onClose?.(); + }; + + useEffect(() => { + if (selectedFile) { + const objectUrl = URL.createObjectURL(selectedFile); + setPreview(objectUrl); + return () => URL.revokeObjectURL(objectUrl); + } + }, [selectedFile]); + + const onSelectFile: ChangeEventHandler = (e) => { + if (!e.target.files || e.target.files.length === 0) { + setSelectedFile(undefined); + return; + } + if (e.target.files[0]?.type.split("/")[0] !== "image") { + Warn({ msg: "Please upload an image file!" }); + return; + } + setSelectedFile(e.target.files[0]); + }; + + const uploadAvatar = async () => { + if (!selectedFile) { + closeModal(); + return; + } + + setIsProcessing(true); + setIsCaptureImgBeingUploaded(true); + + await handleUpload(selectedFile, () => { + setIsCaptureImgBeingUploaded(false); + setIsProcessing(false); + }); + }; + + const deleteAvatar = async () => { + setIsProcessing(true); + await handleDelete(() => { + setIsProcessing(false); + }); + }; + + const dragProps = useDragAndDrop(); + const onDrop = (e: React.DragEvent) => { + e.preventDefault(); + dragProps.setDragOver(false); + setIsDragging(false); + const droppedFile = e?.dataTransfer?.files[0]; + if (droppedFile.type.split("/")[0] !== "image") + return dragProps.setFileDropError("Please drop an image file to upload!"); + setSelectedFile(droppedFile); + }; + + const onDragOver = (e: React.DragEvent) => { + e.preventDefault(); + dragProps.onDragOver(e); + setIsDragging(true); + }; + + const onDragLeave = (e: React.DragEvent) => { + e.preventDefault(); + dragProps.onDragLeave(); + setIsDragging(false); + }; + + const commonHint = ( + <> + {t("max_size_for_image_uploaded_should_be")} 1mb. +
+ {t("allowed_formats_are")} jpg,png,jpeg.{" "} + {t("recommended_aspect_ratio_for")} the image is 1:1 + + ); + + return ( + +
+
+ {!isCameraOpen ? ( + <> + {preview || imageUrl ? ( + <> +
+ cover-photo +
+

+ {commonHint} +

+ + ) : ( +
+ +

+ {dragProps.fileDropError !== "" + ? dragProps.fileDropError + : `${t("drag_drop_image_to_upload")}`} +

+

+ {t("no_image_found")}. {commonHint} +

+
+ )} + +
+
+ +
+ { + setConstraint(() => VideoConstraints.user); + setIsCameraOpen(true); + }} + > + {`${t("open")} ${t("camera")}`} + +
+ { + e.stopPropagation(); + closeModal(); + dragProps.setFileDropError(""); + }} + disabled={isProcessing} + /> + {imageUrl && ( + + {t("delete")} + + )} + + {isProcessing ? ( + + ) : ( + + )} + + {isProcessing ? `${t("uploading")}...` : `${t("save")}`} + + +
+ + ) : ( + <> +
+ + {t("capture_cover_photo")} + +
+
+ {!previewImage ? ( + <> + { + setIsCameraOpen(false); + Warn({ msg: t("camera_permission_denied") }); + }} + /> + + ) : ( + <> + + + )} +
+ {/* buttons for mobile screens */} +
+ {!previewImage ? ( + <> + + + {`${t("switch")} ${t("camera")}`} + + { + captureImage(); + }} + > + + {t("capture")} + + + ) : ( + <> + { + setPreviewImage(null); + }} + > + {t("retake")} + + + {isCaptureImgBeingUploaded ? ( + <> + + {`${t("submitting")}...`} + + ) : ( + <> {t("submit")} + )} + + + )} +
+ { + setPreviewImage(null); + setIsCameraOpen(false); + webRef.current.stopCamera(); + }} + label={t("close")} + disabled={isProcessing} + /> +
+ + )} +
+
+ + ); +}; + +export default AvatarEditModal; diff --git a/src/Components/Common/AvatarEditable.tsx b/src/Components/Common/AvatarEditable.tsx new file mode 100644 index 00000000000..ce3112f2107 --- /dev/null +++ b/src/Components/Common/AvatarEditable.tsx @@ -0,0 +1,54 @@ +import CareIcon from "@/CAREUI/icons/CareIcon"; +import { cn } from "@/lib/utils"; +import { useTranslation } from "react-i18next"; +import { AvatarProps, Avatar } from "@/Components/Common/Avatar"; + +interface AvatarEditableProps extends AvatarProps { + id?: string; + editable?: boolean; + onClick?: () => void; +} + +const AvatarEditable: React.FC = ({ + id, + colors: propColors, + name, + imageUrl, + className, + editable = true, + onClick, +}) => { + const { t } = useTranslation(); + return ( +
+ + + {editable && ( +
+ + {t(imageUrl ? "edit" : "upload")} +
+ )} +
+ ); +}; + +export default AvatarEditable; diff --git a/src/Components/Common/LanguageSelector.tsx b/src/Components/Common/LanguageSelector.tsx index 38fc8024a01..a015d6fd609 100644 --- a/src/Components/Common/LanguageSelector.tsx +++ b/src/Components/Common/LanguageSelector.tsx @@ -29,7 +29,7 @@ export const LanguageSelector = (props: any) => { - -
-
- { - setConstraint(() => VideoConstraints.user); - setIsCameraOpen(true); - }} - > - {`${t("open")} ${t("camera")}`} - - { - e.stopPropagation(); - closeModal(); - dragProps.setFileDropError(""); - }} - disabled={isProcessing} - /> - {facility.read_cover_image_url && ( - - {t("delete")} - - )} - - {isProcessing ? ( - - ) : ( - - )} - - {isProcessing ? `${t("uploading")}...` : `${t("save")}`} - - -
- - ) : ( -
-
- - {t("capture_cover_photo")} - -
-
- {!previewImage ? ( - <> - { - setIsCameraOpen(false); - Warn({ msg: t("camera_permission_denied") }); - }} - /> - - ) : ( - <> - - - )} -
- {/* buttons for mobile screens */} -
-
- {!previewImage ? ( - - {t("switch")} - - ) : ( - <> - )} -
-
- {!previewImage ? ( - <> -
- { - captureImage(); - }} - className="my-2 w-full" - > - {t("capture")} - -
- - ) : ( - <> -
- { - setPreviewImage(null); - }} - className="my-2 w-full" - disabled={isProcessing} - > - {t("retake")} - - - {isCaptureImgBeingUploaded && ( - - )} - {t("submit")} - -
- - )} -
-
- { - setPreviewImage(null); - setIsCameraOpen(false); - webRef.current.stopCamera(); - }} - className="my-2 w-full" - > - {t("close")} - -
-
- {/* buttons for laptop screens */} -
-
- - - {`${t("switch")} ${t("camera")}`} - -
- -
-
- {!previewImage ? ( - <> -
- { - captureImage(); - }} - > - - {t("capture")} - -
- - ) : ( - <> -
- { - setPreviewImage(null); - }} - > - {t("retake")} - - - {isCaptureImgBeingUploaded ? ( - <> - - {`${t("submitting")}...`} - - ) : ( - <> {t("submit")} - )} - -
- - )} -
-
- { - setPreviewImage(null); - setIsCameraOpen(false); - webRef.current.stopCamera(); - }} - > - {`${t("close")} ${t("camera")}`} - -
-
-
- )} -
-
- ); -}; - -export default CoverImageEditModal; diff --git a/src/Components/Facility/FacilityHome.tsx b/src/Components/Facility/FacilityHome.tsx index 56919e6fde4..1a6b79611ad 100644 --- a/src/Components/Facility/FacilityHome.tsx +++ b/src/Components/Facility/FacilityHome.tsx @@ -1,8 +1,11 @@ import * as Notification from "../../Utils/Notifications.js"; import { NonReadOnlyUsers } from "../../Utils/AuthorizeFor"; -import { FacilityModel } from "./models"; -import { FACILITY_FEATURE_TYPES, USER_TYPES } from "../../Common/constants"; +import { + FACILITY_FEATURE_TYPES, + LocalStorageKeys, + USER_TYPES, +} from "../../Common/constants"; import DropdownMenu, { DropdownItem } from "../Common/components/Menu"; import { useState } from "react"; @@ -11,7 +14,6 @@ import CareIcon from "../../CAREUI/icons/CareIcon"; import Chip from "../../CAREUI/display/Chip"; import ConfirmDialog from "../Common/ConfirmDialog"; import ContactLink from "../Common/components/ContactLink"; -import CoverImageEditModal from "./CoverImageEditModal"; import Page from "../Common/components/Page"; import RecordMeta from "../../CAREUI/display/RecordMeta"; @@ -37,13 +39,16 @@ import { LocationSelect } from "../Common/LocationSelect.js"; import { CameraFeedPermittedUserTypes } from "../../Utils/permissions.js"; import { FacilityStaffList } from "./FacilityStaffList.js"; import FacilityBlock from "./FacilityBlock.js"; +import Loading from "@/Components/Common/Loading"; +import AvatarEditable from "@/Components/Common/AvatarEditable"; +import AvatarEditModal from "@/Components/Common/AvatarEditModal"; +import careConfig from "@careConfig"; +import uploadFile from "@/Utils/request/uploadFile"; +import { sleep } from "@/Utils/utils"; type Props = { facilityId: string; }; - -import Loading from "@/Components/Common/Loading"; -import { Avatar } from "@/Components/Common/Avatar.js"; export const getFacilityFeatureIcon = (featureId: number) => { const feature = FACILITY_FEATURE_TYPES.find((f) => f.id === featureId); if (!feature?.icon) return null; @@ -107,12 +112,51 @@ export const FacilityHome = ({ facilityId }: Props) => { }); }; + const handleCoverImageUpload = async (file: File, onError: () => void) => { + const formData = new FormData(); + formData.append("cover_image", file); + const url = `${careConfig.apiUrl}/api/v1/facility/${facilityId}/cover_image/`; + + uploadFile( + url, + formData, + "POST", + { + Authorization: + "Bearer " + localStorage.getItem(LocalStorageKeys.accessToken), + }, + async (xhr: XMLHttpRequest) => { + if (xhr.status === 200) { + await sleep(1000); + facilityFetch(); + Notification.Success({ msg: "Cover image updated." }); + setEditCoverImage(false); + } + }, + null, + () => { + onError(); + }, + ); + }; + + const handleCoverImageDelete = async (onError: () => void) => { + const { res } = await request(routes.deleteFacilityCoverImage, { + pathParams: { id: facilityId }, + }); + if (res?.ok) { + Notification.Success({ msg: "Cover image deleted" }); + facilityFetch(); + setEditCoverImage(false); + } else { + onError(); + } + }; + if (isLoading) { return ; } - const hasCoverImage = !!facilityData?.read_cover_image_url; - const StaffUserTypeIndex = USER_TYPES.findIndex((type) => type === "Staff"); const hasPermissionToEditCoverImage = !(authUser.user_type as string).includes("ReadOnly") && @@ -123,19 +167,6 @@ export const FacilityHome = ({ facilityId }: Props) => { authUser.user_type === "DistrictAdmin" || authUser.user_type === "StateAdmin"; - const editCoverImageTooltip = hasPermissionToEditCoverImage && ( -
setEditCoverImage(true)} - > - - {t(hasCoverImage ? "edit" : "upload")} -
- ); - return ( { onClose={handleDeleteClose} onConfirm={handleDeleteSubmit} /> - facilityFetch()} + imageUrl={facilityData?.read_cover_image_url} + handleUpload={handleCoverImageUpload} + handleDelete={handleCoverImageDelete} onClose={() => setEditCoverImage(false)} - onDelete={() => facilityFetch()} - facility={facilityData ?? ({} as FacilityModel)} /> -
hasPermissionToEditCoverImage && setEditCoverImage(true)} - > - - {editCoverImageTooltip} -
-
+
-
- +
+ setEditCoverImage(true)} + className="md:mr-2 lg:mr-6 lg:h-80 lg:w-80" + />
diff --git a/src/Components/Facility/Investigations/ViewInvestigations.tsx b/src/Components/Facility/Investigations/ViewInvestigations.tsx index e889fac4c01..6ea846ecbd0 100644 --- a/src/Components/Facility/Investigations/ViewInvestigations.tsx +++ b/src/Components/Facility/Investigations/ViewInvestigations.tsx @@ -58,7 +58,7 @@ export default function ViewInvestigations(props: {
{formatDateTime(investigationSession.session_created_date)}
-
+
navigate( diff --git a/src/Components/Facility/Investigations/index.tsx b/src/Components/Facility/Investigations/index.tsx index df93c7945c6..5c40766b6b9 100644 --- a/src/Components/Facility/Investigations/index.tsx +++ b/src/Components/Facility/Investigations/index.tsx @@ -232,7 +232,7 @@ const Investigation = (props: { msg: "Investigation created successfully!", }); navigate( - `/facility/${props.facilityId}/patient/${props.patientId}/consultation/${props.consultationId}`, + `/facility/${props.facilityId}/patient/${props.patientId}/consultation/${props.consultationId}/investigations`, ); } else { setSaving(false); diff --git a/src/Components/Facility/PatientNoteCard.tsx b/src/Components/Facility/PatientNoteCard.tsx index 9ddfbc009e6..80443ad02ae 100644 --- a/src/Components/Facility/PatientNoteCard.tsx +++ b/src/Components/Facility/PatientNoteCard.tsx @@ -135,21 +135,32 @@ const PatientNoteCard = ({ note.created_by_object.id === authUser.id && !isEditing && ( { setIsEditing(true); }} > + + {t("edit")} + )} { setReplyTo && setReplyTo(note); }} > - + + + {t("reply")} +
diff --git a/src/Components/Facility/PatientNotesSlideover.tsx b/src/Components/Facility/PatientNotesSlideover.tsx index 52f99aee763..fe716950aa9 100644 --- a/src/Components/Facility/PatientNotesSlideover.tsx +++ b/src/Components/Facility/PatientNotesSlideover.tsx @@ -137,36 +137,45 @@ export default function PatientNotesSlideover(props: PatientNotesProps) {
{show && ( + + {t("full_screen")} + )}
setShow(!show)} > + + {t("minimize")} +
setShowPatientNotesPopup(false)} > + + {t("close")} +
); @@ -248,13 +257,16 @@ export default function PatientNotesSlideover(props: PatientNotesProps) { id="add_doctor_note_button" onClick={onAddNote} border={false} - className="absolute right-2" + className="tooltip absolute right-2" ghost size="small" disabled={!patientActive} authorizeFor={NonReadOnlyUsers} > - + + + {t("send")} +
diff --git a/src/Components/HCX/validators.ts b/src/Components/HCX/validators.ts index cc83b39b6af..5d5c979a405 100644 --- a/src/Components/HCX/validators.ts +++ b/src/Components/HCX/validators.ts @@ -1,3 +1,4 @@ +import { t } from "i18next"; import { FieldValidator } from "../Form/FieldValidators"; import { HCXPolicyModel } from "./models"; @@ -5,12 +6,16 @@ const HCXPolicyValidator: FieldValidator = ( value, enable_hcx, ) => { - if ( - !value.policy_id.trim() || - !value.subscriber_id.trim() || - (enable_hcx && (!value.insurer_id?.trim() || !value.insurer_name?.trim())) - ) - return "All fields are mandatory"; + if (!value.subscriber_id.trim()) { + return t("member_id_required"); + } else if (!value.policy_id.trim()) { + return t("policy_id_required"); + } + if (enable_hcx) { + if (!value.insurer_id?.trim() || !value.insurer_name?.trim()) { + return t("insurer_name_required"); + } + } }; export default HCXPolicyValidator; diff --git a/src/Components/Patient/PatientHome.tsx b/src/Components/Patient/PatientHome.tsx index 75f060c28ed..ba2f6e33a3f 100644 --- a/src/Components/Patient/PatientHome.tsx +++ b/src/Components/Patient/PatientHome.tsx @@ -1,4 +1,4 @@ -import { Link, navigate } from "raviger"; +import { navigate } from "raviger"; import { useEffect, useState } from "react"; import * as Notification from "../../Utils/Notifications"; @@ -18,7 +18,7 @@ import { isAntenatal, isPostPartum, } from "../../Utils/utils"; -import ButtonV2, { buttonStyles } from "../Common/components/ButtonV2"; +import ButtonV2 from "../Common/components/ButtonV2"; import CareIcon from "../../CAREUI/icons/CareIcon"; import Chip from "../../CAREUI/display/Chip"; @@ -39,7 +39,6 @@ import routes from "../../Redux/api"; import { InsuranceDetialsCard } from "./InsuranceDetailsCard"; import request from "../../Utils/request/request"; import PaginatedList from "../../CAREUI/misc/PaginatedList"; -import { isPatientMandatoryDataFilled } from "./Utils"; import { useTranslation } from "react-i18next"; import { Alert, AlertDescription, AlertTitle } from "@/Components/ui/alert"; import { Button } from "@/Components/ui/button"; @@ -343,39 +342,6 @@ export const PatientHome = (props: any) => { )} - {isPatientMandatoryDataFilled(patientData) && - (patientData?.facility != patientData?.last_consultation?.facility || - (patientData.is_active && - patientData?.last_consultation?.discharge_date)) && ( -
-
-
-

- - - {t("consultation_missing_warning")}{" "} - - {patientData.facility_object?.name || "-"}{" "} - - -

-
-
-
- - {t("create_consultation")} - -
-
- )}
diff --git a/src/Components/Patient/PatientRegister.tsx b/src/Components/Patient/PatientRegister.tsx index 9f3b03c929e..4b136ffa0f1 100644 --- a/src/Components/Patient/PatientRegister.tsx +++ b/src/Components/Patient/PatientRegister.tsx @@ -412,6 +412,8 @@ export const PatientRegister = (props: PatientRegisterProps) => { .find((error) => !!error); setInsuranceDetailsError(insuranceDetailsError); + errors["insurance_details"] = insuranceDetailsError; + Object.keys(form).forEach((field) => { let phoneNumber, emergency_phone_number; switch (field) { diff --git a/src/Components/Resource/CommentSection.tsx b/src/Components/Resource/CommentSection.tsx index 5a3d94687b3..2a82fa064c3 100644 --- a/src/Components/Resource/CommentSection.tsx +++ b/src/Components/Resource/CommentSection.tsx @@ -85,8 +85,8 @@ export const Comment = ({ modified_date, }: IComment) => (
-
-

{comment}

+
+

{comment}

diff --git a/src/Components/Shifting/CommentsSection.tsx b/src/Components/Shifting/CommentsSection.tsx index ff826bec63f..1a83f91d3f4 100644 --- a/src/Components/Shifting/CommentsSection.tsx +++ b/src/Components/Shifting/CommentsSection.tsx @@ -94,8 +94,8 @@ export const Comment = ({ key={id} className="mt-4 flex w-full flex-col rounded-lg border border-secondary-300 bg-white p-4 text-secondary-800" > -
-

{comment}

+
+

{comment}

diff --git a/src/Components/Shifting/ListView.tsx b/src/Components/Shifting/ListView.tsx index 8d9f2f929f0..1c1d63991f6 100644 --- a/src/Components/Shifting/ListView.tsx +++ b/src/Components/Shifting/ListView.tsx @@ -19,8 +19,9 @@ import request from "../../Utils/request/request"; import routes from "../../Redux/api"; import useQuery from "../../Utils/request/useQuery"; import careConfig from "@careConfig"; - import Loading from "@/Components/Common/Loading"; +import { IShift } from "./models"; + export default function ListView() { const { qParams, @@ -31,14 +32,17 @@ export default function ListView() { resultsPerPage, } = useFilters({ cacheBlacklist: ["patient_name"] }); - const [modalFor, setModalFor] = useState({ - externalId: undefined, + const [modalFor, setModalFor] = useState<{ + external_id: string | undefined; + loading: boolean; + }>({ + external_id: undefined, loading: false, }); const authUser = useAuthUser(); const { t } = useTranslation(); - const handleTransferComplete = async (shift: any) => { + const handleTransferComplete = async (shift: IShift) => { setModalFor({ ...modalFor, loading: true }); await request(routes.completeTransfer, { pathParams: { externalId: shift.external_id }, @@ -59,171 +63,190 @@ export default function ListView() { }), }); - const showShiftingCardList = (data: any) => { - if (data && !data.length) { + const showShiftingCardList = (data: IShift[]) => { + if (loading) { + return ; + } + + if (!data || data.length === 0) { return ( -
- {t("no_patients_to_show")} +
+
+ {t("no_patients_to_show")} +
); } - return data.map((shift: any) => ( -
-
-
-
-
-
- {shift.patient_object.name} - {shift.patient_object.age} -
+ return ( +
+ {data.map((shift: IShift) => ( +
+
+
- {shift.emergency && ( - - {t("emergency")} - - )} -
-
-
-
-
- -
- {shift.status} -
- -
-
-
- -
- {shift.patient_object.phone_number || ""} -
- -
-
-
- -
- {(shift.origin_facility_object || {}).name} -
- -
- {careConfig.wartimeShifting && ( -
-
- -
- {(shift.shifting_approving_facility_object || {}).name} -
- +
+
+ {shift.patient_object.name} - {shift.patient_object.age} +
+
+ {shift.emergency && ( + + {t("emergency")} + + )} +
- )} -
-
- +
+
+
+ +
+ {shift.status} +
+
+
+
+
+ +
+ {shift.patient_object.phone_number || ""} +
+ +
+
+
+ +
+ {(shift.origin_facility_object || {}).name} +
+ +
+ {careConfig.wartimeShifting && ( +
+
+ +
+ { + (shift.shifting_approving_facility_object || {}) + .name + } +
+ +
+ )} +
+
+ -
- {shift.assigned_facility_external || - shift.assigned_facility_object?.name || - t("yet_to_be_decided")} -
- -
+
+ {shift.assigned_facility_external || + shift.assigned_facility_object?.name || + t("yet_to_be_decided")} +
+ +
-
-
- -
- {formatDateTime(shift.modified_date) || "--"} -
- +
+
+ +
+ {formatDateTime(shift.modified_date) || "--"} +
+ +
+ +
+
+ +
+ {shift.patient_object.address || "--"} +
+ +
+
-
-
+ navigate(`/shifting/${shift.external_id}`)} + variant="secondary" + border + className="w-full" > - -
- {shift.patient_object.address || "--"} -
- + {" "} + {t("all_details")} +
- -
- -
- navigate(`/shifting/${shift.external_id}`)} - variant="secondary" - border - className="w-full" - > - {t("all_details")} - -
- {shift.status === "COMPLETED" && shift.assigned_facility && ( -
- setModalFor(shift.external_id)} - > - {t("transfer_to_receiving_facility")} - - - setModalFor({ externalId: undefined, loading: false }) - } - onConfirm={() => handleTransferComplete(shift)} - /> + {shift.status === "COMPLETED" && shift.assigned_facility && ( +
+ + setModalFor({ + external_id: shift.external_id, + loading: false, + }) + } + > + {t("transfer_to_receiving_facility")} + + + setModalFor({ external_id: undefined, loading: false }) + } + onConfirm={() => handleTransferComplete(shift)} + /> +
+ )}
- )} +
-
+ ))}
- )); + ); }; return ( @@ -291,9 +314,8 @@ export default function ListView() {
-
- {showShiftingCardList(shiftData?.results || [])} -
+ {showShiftingCardList(shiftData?.results || [])} +
diff --git a/src/Components/Users/UserProfile.tsx b/src/Components/Users/UserProfile.tsx index cebdfe3b792..8f3e7569a42 100644 --- a/src/Components/Users/UserProfile.tsx +++ b/src/Components/Users/UserProfile.tsx @@ -1,5 +1,5 @@ import { useState, useReducer, FormEvent } from "react"; -import { GENDER_TYPES } from "../../Common/constants"; +import { GENDER_TYPES, LocalStorageKeys } from "../../Common/constants"; import { validateEmailAddress } from "../../Common/validation"; import * as Notification from "../../Utils/Notifications.js"; import LanguageSelector from "../../Components/Common/LanguageSelector"; @@ -9,9 +9,11 @@ import { classNames, dateQueryString, formatDate, + formatDisplayName, isValidUrl, parsePhoneNumber, -} from "../../Utils/utils"; + sleep, +} from "@/Utils/utils"; import CareIcon from "../../CAREUI/icons/CareIcon"; import PhoneNumberFormField from "../Form/FormFields/PhoneNumberFormField"; import { FieldChangeEvent } from "../Form/FormFields/Utils"; @@ -27,7 +29,13 @@ import request from "../../Utils/request/request"; import DateFormField from "../Form/FormFields/DateFormField"; import { validateRule } from "./UserAdd"; import { useTranslation } from "react-i18next"; +import AvatarEditable from "@/Components/Common/AvatarEditable"; +import Page from "@/Components/Common/components/Page"; import Loading from "@/Components/Common/Loading"; +import AvatarEditModal from "@/Components/Common/AvatarEditModal"; +import uploadFile from "@/Utils/request/uploadFile"; +import careConfig from "@careConfig"; + type EditForm = { firstName: string; lastName: string; @@ -111,8 +119,9 @@ const editFormReducer = (state: State, action: Action) => { export default function UserProfile() { const { t } = useTranslation(); - const { signOut } = useAuthContext(); + const { signOut, refetchUser } = useAuthContext(); const [states, dispatch] = useReducer(editFormReducer, initialState); + const [editAvatar, setEditAvatar] = useState(false); const [updateStatus, setUpdateStatus] = useState({ isChecking: false, isUpdateAvailable: false, @@ -465,494 +474,550 @@ export default function UserProfile() { }); } }; + + const handleAvatarUpload = async (file: File, onError: () => void) => { + const formData = new FormData(); + formData.append("profile_picture", file); + const url = `${careConfig.apiUrl}/api/v1/users/${authUser.username}/profile_picture/`; + + uploadFile( + url, + formData, + "POST", + { + Authorization: + "Bearer " + localStorage.getItem(LocalStorageKeys.accessToken), + }, + async (xhr: XMLHttpRequest) => { + if (xhr.status === 200) { + await sleep(1000); + refetchUser(); + Notification.Success({ msg: "Profile picture updated." }); + setEditAvatar(false); + } + }, + null, + () => { + onError(); + }, + ); + }; + + const handleAvatarDelete = async (onError: () => void) => { + const { res } = await request(routes.deleteProfilePicture, { + pathParams: { username: authUser.username }, + }); + if (res?.ok) { + Notification.Success({ msg: "Profile picture deleted" }); + await refetchUser(); + setEditAvatar(false); + } else { + onError(); + } + }; + return ( -
-
-
-
-
-

- {t("personal_information")} + + setEditAvatar(false)} + /> +
+
+

+ {t("local_body")}, {t("district")}, {t("state")}{" "} + {t("are_non_editable_fields")}. +

+
+ setEditAvatar(!editAvatar)} + className="h-20 w-20" + /> +
+

+ {authUser?.first_name} {authUser?.last_name}

-

- {t("local_body")}, {t("district")}, {t("state")}{" "} - {t("are_non_editable_fields")}. +

+ @{authUser?.username}

-
- setShowEdit(!showEdit)} - type="button" - id="edit-cancel-profile-button" - > - {showEdit ? t("cancel") : t("edit_user_profile")} - - - - {t("sign_out")} - -
-
- {!showEdit && !isLoading && ( -
-
-
-
- {t("username")} -
-
- {userData?.username || "-"} -
-
-
-
- {t("phone_number")} -
-
- {userData?.phone_number || "-"} -
-
+
+ setShowEdit(!showEdit)} + type="button" + id="edit-cancel-profile-button" + > + {showEdit ? t("cancel") : t("edit_user_profile")} + + + + {t("sign_out")} + +
+
+
+ {!showEdit && !isLoading && ( +
+
+
+
+ {t("username")} +
+
+ {userData?.username || "-"} +
+
+
+
+ {t("phone_number")} +
+
+ {userData?.phone_number || "-"} +
+
-
-
- {t("whatsapp_number")} -
-
- {userData?.alt_phone_number || "-"} -
-
-
-
- {t("email")} -
-
- {userData?.email || "-"} -
-
-
-
- {t("first_name")} -
-
- {userData?.first_name || "-"} -
-
-
-
- {t("last_name")} -
-
- {userData?.last_name || "-"} -
-
-
-
- {t("date_of_birth")} -
-
- {userData?.date_of_birth - ? formatDate(userData?.date_of_birth) +
+
+ {t("whatsapp_number")} +
+
+ {userData?.alt_phone_number || "-"} +
+
+
+
+ {t("email")} +
+
+ {userData?.email || "-"} +
+
+
+
+ {t("first_name")} +
+
+ {userData?.first_name || "-"} +
+
+
+
+ {t("last_name")} +
+
+ {userData?.last_name || "-"} +
+
+
+
+ {t("date_of_birth")} +
+
+ {userData?.date_of_birth + ? formatDate(userData?.date_of_birth) + : "-"} +
+
+
+
+ {t("access_level")} +
+
+ + {userData?.user_type || "-"} +
+
+
+
+ {t("gender")} +
+
+ {userData?.gender || "-"} +
+
+
+
+ {t("local_body")} +
+
+ {userData?.local_body_object?.name || "-"} +
+
+
+
+ {t("district")} +
+
+ {userData?.district_object?.name || "-"} +
+
+
+
+ {t("state")} +
+
+ {userData?.state_object?.name || "-"} +
+
+
+
+ {t("skills")} +
+
+
+ {skillsView?.results?.length + ? skillsView.results?.map((skill: SkillModel) => { + return ( + +

+ {skill.skill_object.name} +

+
+ ); + }) : "-"} -
-
-
-
- {t("access_level")} -
-
- {" "} - {userData?.user_type || "-"} -
-
-
-
- {t("gender")} -
-
- {userData?.gender || "-"} -
-
-
-
- {t("local_body")} -
-
- {userData?.local_body_object?.name || "-"} -
-
-
-
- {t("district")} -
-
- {userData?.district_object?.name || "-"} -
-
-
-
- {t("state")} -
-
- {userData?.state_object?.name || "-"} -
-
-
-
- {t("skills")} -
-
-
+
+
+
+
+ {t("average_weekly_working_hours")} +
+
+ {userData?.weekly_working_hours ?? "-"} +
+
+ - -
-
-
- {t("average_weekly_working_hours")} -
-
- {userData?.weekly_working_hours ?? "-"} -
-
-
- -
- )} - {showEdit && ( -
-
-
-
-
- + {userData?.video_connect_link} + + ) : ( + "-" + )} + +
+ +
+ )} + {showEdit && ( +
+ +
+
+
+ + + + o.text} + optionValue={(o) => o.text} + options={GENDER_TYPES} + /> + + + + {(states.form.user_type === "Doctor" || + states.form.user_type === "Nurse") && ( - - o.text} - optionValue={(o) => o.text} - options={GENDER_TYPES} - /> - - - - {(states.form.user_type === "Doctor" || - states.form.user_type === "Nurse") && ( + )} + {states.form.user_type === "Doctor" && ( + <> - )} - {states.form.user_type === "Doctor" && ( - <> - - - - )} - - -
-
-
- + + )} + +
- -
-
-
-
+
+ +
+
+ +
+
+
+
+ + setChangePasswordForm({ + ...changePasswordForm, + old_password: e.value, + }) + } + error={changePasswordErrors.old_password} + required + /> +
+ value={changePasswordForm.new_password_1} + className="peer col-span-6 sm:col-span-3" + onChange={(e) => { setChangePasswordForm({ ...changePasswordForm, - old_password: e.value, - }) - } - error={changePasswordErrors.old_password} + new_password_1: e.value, + }); + }} required /> -
- { - setChangePasswordForm({ - ...changePasswordForm, - new_password_1: e.value, - }); - }} - required - /> +
+ {validateRule( + changePasswordForm.new_password_1?.length >= 8, + "Password should be atleast 8 characters long", + )} + {validateRule( + changePasswordForm.new_password_1 !== + changePasswordForm.new_password_1.toUpperCase(), + "Password should contain at least 1 lowercase letter", + )} + {validateRule( + changePasswordForm.new_password_1 !== + changePasswordForm.new_password_1.toLowerCase(), + "Password should contain at least 1 uppercase letter", + )} + {validateRule( + /\d/.test(changePasswordForm.new_password_1), + "Password should contain at least 1 number", + )} +
+
+
+ { + setChangePasswordForm({ + ...changePasswordForm, + new_password_2: e.value, + }); + }} + /> + {changePasswordForm.new_password_2.length > 0 && (
{validateRule( - changePasswordForm.new_password_1?.length >= 8, - "Password should be atleast 8 characters long", - )} - {validateRule( - changePasswordForm.new_password_1 !== - changePasswordForm.new_password_1.toUpperCase(), - "Password should contain at least 1 lowercase letter", - )} - {validateRule( - changePasswordForm.new_password_1 !== - changePasswordForm.new_password_1.toLowerCase(), - "Password should contain at least 1 uppercase letter", - )} - {validateRule( - /\d/.test(changePasswordForm.new_password_1), - "Password should contain at least 1 number", + changePasswordForm.new_password_1 === + changePasswordForm.new_password_2, + "Confirm password should match the new password", )}
-
-
- { - setChangePasswordForm({ - ...changePasswordForm, - new_password_2: e.value, - }); - }} - /> - {changePasswordForm.new_password_2.length > 0 && ( -
- {validateRule( - changePasswordForm.new_password_1 === - changePasswordForm.new_password_2, - "Confirm password should match the new password", - )} -
- )} -
+ )}
-
- -
- -
- )} -
+
+ +
+
+ +
+ )}
+
-
-
-
-

- {t("language_selection")} -

-

- {t("set_your_local_language")} -

-
-
-
- +
+
+
+

+ {t("language_selection")} +

+

+ {t("set_your_local_language")} +

-
-
-
-

- {t("software_update")} -

-

- {t("check_for_available_update")} -

-
+
+ +
+
+
+
+
+

+ {t("software_update")} +

+

+ {t("check_for_available_update")} +

- {updateStatus.isUpdateAvailable && ( - - -
- - {t("update_available")} -
-
-
+
+ {updateStatus.isUpdateAvailable && ( + + +
+ + {t("update_available")} +
+
+
+ )} +
+ {!updateStatus.isUpdateAvailable && ( + + {" "} +
+ + {updateStatus.isChecking + ? t("checking_for_update") + : t("check_for_update")} +
+
)} -
- {!updateStatus.isUpdateAvailable && ( - - {" "} -
- - {updateStatus.isChecking - ? t("checking_for_update") - : t("check_for_update")} -
-
- )} -
-
+ ); } diff --git a/src/Components/Users/models.tsx b/src/Components/Users/models.tsx index 5d5c092dd02..cb41779c7ed 100644 --- a/src/Components/Users/models.tsx +++ b/src/Components/Users/models.tsx @@ -33,6 +33,7 @@ export type UserModel = UserBareMinimum & { phone_number?: string; alt_phone_number?: string; gender?: GenderType; + read_profile_picture_url?: string; date_of_birth: Date | null | string; is_superuser?: boolean; verified?: boolean; diff --git a/src/Components/VitalsMonitor/VitalsMonitorFooter.tsx b/src/Components/VitalsMonitor/VitalsMonitorFooter.tsx index 1c669cd5bbf..1a45b2cd000 100644 --- a/src/Components/VitalsMonitor/VitalsMonitorFooter.tsx +++ b/src/Components/VitalsMonitor/VitalsMonitorFooter.tsx @@ -1,5 +1,5 @@ import { AssetData } from "../Assets/AssetTypes"; -import MonitorAssetPopover from "../Common/MonitorAssetPopover"; +import AssetInfoPopover from "../Common/AssetInfoPopover"; interface IVitalsMonitorFooterProps { asset?: AssetData; @@ -9,7 +9,7 @@ const VitalsMonitorFooter = ({ asset }: IVitalsMonitorFooterProps) => { return (

{asset?.name}

- diff --git a/src/Locale/en.json b/src/Locale/en.json index 024c782d146..50093251818 100644 --- a/src/Locale/en.json +++ b/src/Locale/en.json @@ -569,6 +569,7 @@ "duplicate_patient_record_confirmation": "Admit the patient record to your facility by adding the year of birth", "duplicate_patient_record_rejection": "I confirm that the suspect / patient I want to create is not on the list.", "edit": "Edit", + "edit_avatar": "Edit Avatar", "edit_caution_note": "A new prescription will be added to the consultation with the edited details and the current prescription will be discontinued.", "edit_cover_photo": "Edit Cover Photo", "edit_history": "Edit History", @@ -715,6 +716,7 @@ "indicator": "Indicator", "inidcator_event": "Indicator Event", "instruction_on_titration": "Instruction on titration", + "insurer_name_required": "Insurer Name is required", "international_mobile": "International Mobile", "invalid_asset_id_msg": "Oops! The asset ID you entered does not appear to be valid.", "invalid_email": "Please Enter a Valid Email Address", @@ -808,11 +810,13 @@ "medicine_administration_history": "Medicine Administration History", "medicines_administered": "Medicine(s) administered", "medicines_administered_error": "Error administering medicine(s)", + "member_id_required": "Member Id is required", "middleware_hostname": "Middleware Hostname", "middleware_hostname_example": "e.g. example.ohc.network", "middleware_hostname_sourced_from": "Middleware hostname sourced from {{ source }}", "min_password_len_8": "Minimum password length 8", "min_time_bw_doses": "Min. time b/w doses", + "minimize": "Minimize", "mobile": "Mobile", "mobile_number": "Mobile Number", "mobile_number_different_from_aadhaar_mobile_number": "We have noticed that you have entered a mobile number that is different from the one linked to your Aadhaar. We will send an OTP to this number to link it with your Abha number.", @@ -843,12 +847,12 @@ "no_changes": "No changes", "no_changes_made": "No changes made", "no_consultation_updates": "No consultation updates", - "no_cover_photo_uploaded_for_this_facility": "No cover photo uploaded for this facility", "no_data_found": "No data found", "no_duplicate_facility": "You should not create duplicate facilities", "no_facilities": "No Facilities found", "no_files_found": "No {{type}} files found", "no_home_facility": "No home facility assigned", + "no_image_found": "No image found", "no_investigation": "No investigation Reports found", "no_investigation_suggestions": "No Investigation Suggestions", "no_linked_facilities": "No Linked Facilities", @@ -967,6 +971,7 @@ "policy__policy_id__example": "POL001", "policy__subscriber_id": "Member ID", "policy__subscriber_id__example": "SUB001", + "policy_id_required": "Policy Id or Policy Name is required", "position": "Position", "post_your_comment": "Post Your Comment", "powered_by": "Powered By", @@ -1024,6 +1029,7 @@ "reload": "Reload", "remove": "Remove", "rename": "Rename", + "reply": "Reply", "report": "Report", "req_atleast_one_digit": "Require at least one digit", "req_atleast_one_lowercase": "Require at least one lower case letter", @@ -1086,6 +1092,7 @@ "select_policy_to_add_items": "Select a Policy to Add Items", "select_skills": "Select and add some skills", "select_wards": "Select wards", + "send": "Send", "send_email": "Send Email", "send_message": "Send Message", "send_otp": "Send OTP", diff --git a/src/Locale/hi.json b/src/Locale/hi.json index e235c558978..20a822323bf 100644 --- a/src/Locale/hi.json +++ b/src/Locale/hi.json @@ -506,7 +506,6 @@ "no_changes": "कोई परिवर्तन नहीं", "no_changes_made": "कोई परिवर्तन नहीं किया गया", "no_consultation_updates": "कोई परामर्श अपडेट नहीं", - "no_cover_photo_uploaded_for_this_facility": "इस सुविधा के लिए कोई कवर फ़ोटो अपलोड नहीं किया गया", "no_data_found": "डाटा प्राप्त नहीं हुआ", "no_duplicate_facility": "आपको डुप्लिकेट सुविधाएं नहीं बनानी चाहिए", "no_facilities": "कोई सुविधा नहीं मिली", diff --git a/src/Locale/kn.json b/src/Locale/kn.json index dc46e49394f..0fa96427b85 100644 --- a/src/Locale/kn.json +++ b/src/Locale/kn.json @@ -507,7 +507,6 @@ "no_changes": "ಯಾವುದೇ ಬದಲಾವಣೆಗಳಿಲ್ಲ", "no_changes_made": "ಯಾವುದೇ ಬದಲಾವಣೆಗಳನ್ನು ಮಾಡಲಾಗಿಲ್ಲ", "no_consultation_updates": "ಸಮಾಲೋಚನೆಯ ನವೀಕರಣಗಳಿಲ್ಲ", - "no_cover_photo_uploaded_for_this_facility": "ಈ ಸೌಲಭ್ಯಕ್ಕಾಗಿ ಯಾವುದೇ ಕವರ್ ಫೋಟೋ ಅಪ್‌ಲೋಡ್ ಮಾಡಲಾಗಿಲ್ಲ", "no_data_found": "ಯಾವುದೇ ಡೇಟಾ ಕಂಡುಬಂದಿಲ್ಲ", "no_duplicate_facility": "ನೀವು ನಕಲಿ ಸೌಲಭ್ಯಗಳನ್ನು ರಚಿಸಬಾರದು", "no_facilities": "ಯಾವುದೇ ಸೌಲಭ್ಯಗಳು ಕಂಡುಬಂದಿಲ್ಲ", diff --git a/src/Locale/ml.json b/src/Locale/ml.json index d830d3a46ec..b9a45ca73fb 100644 --- a/src/Locale/ml.json +++ b/src/Locale/ml.json @@ -506,7 +506,6 @@ "no_changes": "മാറ്റങ്ങളൊന്നുമില്ല", "no_changes_made": "മാറ്റങ്ങളൊന്നും വരുത്തിയിട്ടില്ല", "no_consultation_updates": "കൺസൾട്ടേഷൻ അപ്‌ഡേറ്റുകളൊന്നുമില്ല", - "no_cover_photo_uploaded_for_this_facility": "ഈ സൗകര്യത്തിനായി കവർ ഫോട്ടോ അപ്‌ലോഡ് ചെയ്‌തിട്ടില്ല", "no_data_found": "വിവരങ്ങളൊന്നും കണ്ടെത്തിയില്ല", "no_duplicate_facility": "അനധികൃതമായി ഫെസിലിറ്റികള്‍ സൃഷ്ടിക്കരുത്", "no_facilities": "ഫെസിലിറ്റികളൊന്നും കണ്ടെത്തുവാനായില്ല", diff --git a/src/Locale/ta.json b/src/Locale/ta.json index 042f5a068c4..fb5dd0a72c8 100644 --- a/src/Locale/ta.json +++ b/src/Locale/ta.json @@ -506,7 +506,6 @@ "no_changes": "மாற்றங்கள் இல்லை", "no_changes_made": "எந்த மாற்றமும் செய்யப்படவில்லை", "no_consultation_updates": "ஆலோசனை அறிவிப்புகள் இல்லை", - "no_cover_photo_uploaded_for_this_facility": "இந்த வசதிக்காக அட்டைப் புகைப்படம் பதிவேற்றப்படவில்லை", "no_data_found": "தரவு எதுவும் கிடைக்கவில்லை", "no_duplicate_facility": "நீங்கள் நகல் வசதிகளை உருவாக்கக்கூடாது", "no_facilities": "வசதிகள் எதுவும் கிடைக்கவில்லை", diff --git a/src/Providers/AuthUserProvider.tsx b/src/Providers/AuthUserProvider.tsx index df2997abe2e..cda5fbfe671 100644 --- a/src/Providers/AuthUserProvider.tsx +++ b/src/Providers/AuthUserProvider.tsx @@ -87,7 +87,14 @@ export default function AuthUserProvider({ children, unauthorized }: Props) { } return ( - + {!res.ok || !user ? unauthorized : children} ); diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx index 6c169d355d9..953a1967306 100644 --- a/src/Redux/api.tsx +++ b/src/Redux/api.tsx @@ -288,6 +288,19 @@ const routes = { TBody: Type>(), }, + updateProfilePicture: { + path: "/api/v1/users/{username}/profile_picture/", + method: "PATCH", + TRes: Type(), + TBody: Type<{ profile_picture_url: string }>(), + }, + + deleteProfilePicture: { + path: "/api/v1/users/{username}/profile_picture/", + method: "DELETE", + TRes: Type(), + }, + deleteUser: { path: "/api/v1/users/{username}/", method: "DELETE",