diff --git a/public/locale/en.json b/public/locale/en.json index 121710b..0585099 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -145,5 +145,13 @@ "verify_bio": "Verify Biometrics", "capture_fingerprint": "Capture Fingerprint", "initiate_face_auth": "Initiate Face Authentication", - "retry_face_auth": "Retry Face Authentication" + "retry_face_auth": "Retry Face Authentication", + "error_verifying_aadhaar_demographics": "Error verifying Aadhaar demographics.", + "error_verifying_aadhaar_face": "Error verifying Aadhaar face.", + "error_capturing_pid_via_face": "Error capturing PID via face.", + "error_linking_abha_number": "Error linking ABHA number.", + "otp_sent_successfully": "OTP has been sent successfully.", + "otp_verified_successfully": "OTP has been verified successfully.", + "otp_resend_successfully": "OTP has been resend successfully.", + "abha_address_created_successfully": "ABHA Address has been created successfully." } diff --git a/src/components/ConfigureHealthFacilityForm.tsx b/src/components/ConfigureHealthFacilityForm.tsx index dd436af..e58bedb 100644 --- a/src/components/ConfigureHealthFacilityForm.tsx +++ b/src/components/ConfigureHealthFacilityForm.tsx @@ -26,6 +26,7 @@ import { GenerateScanAndShareQR } from "./GenerateScanAndShareQR"; import { HealthFacility } from "@/types/healthFacility"; import { I18NNAMESPACE } from "@/lib/constants"; import { Input } from "@/components/ui/input"; +import { Meta } from "@/types/meta"; import { apis } from "@/apis"; import { toast } from "@/lib/utils"; import { useForm } from "react-hook-form"; @@ -34,6 +35,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; type ConfigureHealthFacilityFormProps = { facilityId: string; onSuccess?: (data: HealthFacility) => void; + meta?: Meta; }; const configureHealthFacilityFormSchema = z.object({ @@ -44,9 +46,11 @@ type ConfigureHealthFacilityFormValues = z.infer< typeof configureHealthFacilityFormSchema >; -export const ConfigureHealthFacilityForm: FC< - ConfigureHealthFacilityFormProps -> = ({ facilityId, onSuccess }) => { +export const ConfigureHealthFacilityForm: FC = ({ + facilityId, + onSuccess, + meta, +}) => { const { t } = useTranslation(I18NNAMESPACE); const { data: healthFacility, refetch } = useQuery({ @@ -221,7 +225,10 @@ export const ConfigureHealthFacilityForm: FC< {healthFacility && ( - + )} ); diff --git a/src/components/GenerateScanAndShareQR.tsx b/src/components/GenerateScanAndShareQR.tsx index 9040ce7..8042b28 100644 --- a/src/components/GenerateScanAndShareQR.tsx +++ b/src/components/GenerateScanAndShareQR.tsx @@ -13,7 +13,6 @@ import { Button } from "./ui/button"; import { I18NNAMESPACE } from "@/lib/constants"; import { Input } from "@/components/ui/input"; import { QRCodeSVG } from "qrcode.react"; -import { scanAndShareUrl } from "@/config"; import { useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { z } from "zod"; @@ -21,6 +20,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; type GenerateScanAndShareQRProps = { healthFacilityId: string; + scanAndShareUrl?: string; }; const scanAndShareQrFormSchema = z.object({ @@ -29,6 +29,7 @@ const scanAndShareQrFormSchema = z.object({ export const GenerateScanAndShareQR: FC = ({ healthFacilityId, + scanAndShareUrl, }) => { const { t } = useTranslation(I18NNAMESPACE); @@ -84,7 +85,7 @@ export const GenerateScanAndShareQR: FC = ({ document.body.removeChild(downloadLink); }; - function onSubmit(_values: z.infer) { + function onSubmit() { downloadQR(); } @@ -135,7 +136,7 @@ export const GenerateScanAndShareQR: FC = ({ /> - {!!scanAndShareQrForm.watch("counterName") && ( + {!!scanAndShareQrForm.watch("counterName") && scanAndShareQrValue && (
= ({ setMemory, goTo }) => { const { t } = useTranslation(I18NNAMESPACE); const { healthFacility, currentUser } = useLinkAbhaNumberContext(); + const faceAuthUrl = window.__CARE_PLUGIN_RUNTIME__?.meta?.care_abdm_fe?.config?.faceAuthUrl; + const form = useForm({ resolver: zodResolver(enterAadhaarFormSchema), defaultValues: { @@ -182,11 +183,18 @@ const EnterAadhaar: FC = ({ setMemory, goTo }) => { }, }); + const handleCheckAllDisclaimers = () => { + Array.from({ length: 6 }).forEach((_, index) => { + const fieldName = `disclaimer_${index + 1}` as keyof EnterAadhaarFormValues; + form.setValue(fieldName, true, { shouldValidate: true }); + }); + }; + const sendAadhaarOtpMutation = useMutation({ mutationFn: apis.healthId.abhaCreateSendAadhaarOtp, onSuccess: (data) => { if (data) { - toast.success(data.detail); + toast.success(data.detail || t("otp_sent_successfully")); setMemory((prev) => ({ ...prev, transactionId: data.transaction_id, @@ -293,6 +301,20 @@ const EnterAadhaar: FC = ({ setMemory, goTo }) => { )} /> ))} + +
+ +
- + }
@@ -405,7 +427,7 @@ const VerifyAadhaarWithOtp: FC = ({ mutationFn: apis.healthId.abhaCreateVerifyAadhaarOtp, onSuccess: (data) => { if (data) { - toast.success(data.detail); + toast.success(data.detail || t("otp_verified_successfully")); setMemory((prev) => ({ ...prev, transactionId: data.transaction_id, @@ -421,7 +443,7 @@ const VerifyAadhaarWithOtp: FC = ({ mutationFn: apis.healthId.abhaCreateSendAadhaarOtp, onSuccess: (data) => { if (data) { - toast.success(data.detail); + toast.success(data.detail || t("otp_resend_successfully")); form.setValue("otp", ""); setMemory((prev) => ({ ...prev, @@ -620,7 +642,7 @@ const VerifyAadhaarWithDemographics: FC = ({ form.setError("_aadhaar", { message: error.message, }); - toast.error(error.message); + toast.error(error.message || t("error_verifying_aadhaar_demographics")); }, }); @@ -894,6 +916,7 @@ const VerifyAadhaarWithFace: FC = ({ }) => { const { t } = useTranslation(I18NNAMESPACE); const [isPolling, setIsPolling] = useState(false); + const faceAuthUrl = window?.__CARE_PLUGIN_RUNTIME__?.meta?.care_abdm_fe?.config?.faceAuthUrl; const form = useForm({ resolver: zodResolver(verifyAadhaarWithFaceFormSchema), @@ -923,7 +946,7 @@ const VerifyAadhaarWithFace: FC = ({ } }, onError: (error) => { - toast.error(error.message); + toast.error(error.message || t("error_verifying_aadhaar_face")); setMemory((prev) => ({ ...prev, transactionId: "", @@ -963,7 +986,7 @@ const VerifyAadhaarWithFace: FC = ({ ...prev, error: error.message, })); - toast.error(error.message); + toast.error(error.message || t("error_capturing_pid_via_face")); }, }); @@ -990,6 +1013,7 @@ const VerifyAadhaarWithFace: FC = ({ } }, [isPolling]); + // eslint-disable-next-line @typescript-eslint/no-unused-vars function onSubmit(_values: VerifyAadhaarWithFaceFormValues) { authInitViaFaceMutation.mutate(); } @@ -1423,7 +1447,7 @@ const LinkMobile: FC = ({ memory, setMemory, goTo }) => { mutationFn: apis.healthId.abhaCreateLinkMobileNumber, onSuccess: (data) => { if (data) { - toast.success(data.detail); + toast.success(data.detail || t("otp_sent_successfully")); setMemory((prev) => ({ ...prev, transactionId: data.transaction_id, @@ -1511,7 +1535,7 @@ const VerifyMobile: FC = ({ memory, setMemory, goTo }) => { mutationFn: apis.healthId.abhaCreateVerifyMobileNumber, onSuccess: (data) => { if (data) { - toast.success(data.detail); + toast.success(data.detail || t("otp_verified_successfully")); setMemory((prev) => ({ ...prev, transactionId: data.transaction_id, @@ -1525,7 +1549,7 @@ const VerifyMobile: FC = ({ memory, setMemory, goTo }) => { mutationFn: apis.healthId.abhaCreateLinkMobileNumber, onSuccess: (data) => { if (data) { - toast.success(data.detail); + toast.success(data.detail || t("otp_resend_successfully")); form.setValue("otp", ""); setMemory((prev) => ({ ...prev, @@ -1714,7 +1738,7 @@ export const ChooseAbhaAddress: FC = ({ transactionId: data.transaction_id, abhaNumber: data.abha_number, })); - toast.success("ABHA Address created successfully"); + toast.success(t("abha_address_created_successfully")); goTo("show-abha-profile"); } }, diff --git a/src/components/LinkAbhaNumber/LinkWithOtp.tsx b/src/components/LinkAbhaNumber/LinkWithOtp.tsx index d8a4937..75786a7 100644 --- a/src/components/LinkAbhaNumber/LinkWithOtp.tsx +++ b/src/components/LinkAbhaNumber/LinkWithOtp.tsx @@ -33,10 +33,10 @@ import { Input } from "@/components/ui/input"; import { apis } from "@/apis"; import { toast } from "@/lib/utils"; import { useForm } from "react-hook-form"; +import { useLinkAbhaNumberContext } from "."; import { useMutation } from "@tanstack/react-query"; import { z } from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; -import { useLinkAbhaNumberContext } from "."; type LinkWithOtpProps = { onSuccess: (abhaNumber: AbhaNumber) => void; @@ -113,7 +113,7 @@ const enterIdFormSchema = z.object({ type EnterIdFormValues = z.infer; -const EnterId: FC = ({ memory, setMemory, goTo }) => { +const EnterId: FC = ({ setMemory, goTo }) => { const { t } = useTranslation(I18NNAMESPACE); const { currentUser } = useLinkAbhaNumberContext(); @@ -134,6 +134,13 @@ const EnterId: FC = ({ memory, setMemory, goTo }) => { }, }); + const handleCheckAllDisclaimers = () => { + Array.from({ length: 5 }).forEach((_, index) => { + const fieldName = `disclaimer_${index + 1}` as keyof EnterIdFormValues; + form.setValue(fieldName, true, { shouldValidate: true }); + }); + }; + const checkAuthMethodsMutation = useMutation({ mutationFn: apis.healthId.abhaLoginCheckAuthMethods, onSuccess: (data) => { @@ -170,7 +177,7 @@ const EnterId: FC = ({ memory, setMemory, goTo }) => { mutationFn: apis.healthId.abhaLoginSendOtp, onSuccess: (data) => { if (data) { - toast.success(data.detail); + toast.success(data.detail || t("otp_sent_successfully")); setMemory((prev) => ({ ...prev, transactionId: data.transaction_id, @@ -249,6 +256,20 @@ const EnterId: FC = ({ memory, setMemory, goTo }) => { /> ))} +
+ +
+ !open && setShowAuthMethods(false)} @@ -339,7 +360,7 @@ const VerifyId: FC = ({ memory, setMemory, onSuccess }) => { mutationFn: apis.healthId.abhaLoginVerifyOtp, onSuccess: (data) => { if (data) { - toast.success(t("verify_otp_success")); + toast.success(t("otp_verified_successfully")); onSuccess(data.abha_number); } }, @@ -349,7 +370,7 @@ const VerifyId: FC = ({ memory, setMemory, onSuccess }) => { mutationFn: apis.healthId.abhaLoginSendOtp, onSuccess: (data) => { if (data) { - toast.success(data.detail); + toast.success(data.detail || t("otp_resend_successfully")); form.setValue("otp", ""); setMemory((prev) => ({ ...prev, diff --git a/src/components/TokenSearchDialog.tsx b/src/components/TokenSearchDialog.tsx index 7c40b54..197dce9 100644 --- a/src/components/TokenSearchDialog.tsx +++ b/src/components/TokenSearchDialog.tsx @@ -8,7 +8,6 @@ import { } from "@/components/ui/dialog"; import { FC, useMemo, useState } from "react"; -import { ArrowRightIcon } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Patient } from "@/types/patient"; @@ -124,14 +123,14 @@ const PatientCard: FC<{ patient: Patient }> = ({ patient }) => { return (
{ - navigate("patients/verify", { + navigate("patients/home", { query: { phone_number: patient.phone_number, year_of_birth: yearOfBirth, partial_id: patient.partial_id || patient.id.slice(0, 5), }, }); - }} + }} className="border rounded-md p-4 cursor-pointer" >
diff --git a/src/components/pluggables/EncounterActions.tsx b/src/components/pluggables/EncounterActions.tsx index 8018c3a..5d5048c 100644 --- a/src/components/pluggables/EncounterActions.tsx +++ b/src/components/pluggables/EncounterActions.tsx @@ -13,6 +13,7 @@ import { Button } from "@/components/ui/button"; import CreateConsentRequestForm from "../CreateConsentRequestForm"; import { Encounter } from "@/types/encounter"; import { I18NNAMESPACE } from "@/lib/constants"; +import { WithMeta } from "@/types/meta"; import { apis } from "@/apis"; import { cn } from "@/lib/utils"; import { useTranslation } from "react-i18next"; @@ -22,7 +23,7 @@ type EncounterActionsProps = { className?: string; }; -const EncounterActions: FC = ({ +const EncounterActions: FC> = ({ encounter, className, }) => { diff --git a/src/components/pluggables/FacilityHomeActions.tsx b/src/components/pluggables/FacilityHomeActions.tsx index 35e2acb..54928ac 100644 --- a/src/components/pluggables/FacilityHomeActions.tsx +++ b/src/components/pluggables/FacilityHomeActions.tsx @@ -1,9 +1,4 @@ import { FC, useState } from "react"; -import { useTranslation } from "react-i18next"; -import { Button } from "@/components/ui/button"; -import { Facility } from "@/types/facility"; -import { SettingsIcon } from "lucide-react"; -import { ConfigureHealthFacilityForm } from "../ConfigureHealthFacilityForm"; import { Sheet, SheetContent, @@ -12,15 +7,25 @@ import { SheetTitle, SheetTrigger, } from "@/components/ui/sheet"; -import { useQueryClient } from "@tanstack/react-query"; + +import { Button } from "@/components/ui/button"; +import { ConfigureHealthFacilityForm } from "../ConfigureHealthFacilityForm"; +import { Facility } from "@/types/facility"; import { I18NNAMESPACE } from "@/lib/constants"; +import { SettingsIcon } from "lucide-react"; +import { WithMeta } from "@/types/meta"; +import { useQueryClient } from "@tanstack/react-query"; +import { useTranslation } from "react-i18next"; type FacilityHomeActionsProps = { facility: Facility; className?: string; }; -const FacilityHomeActions: FC = ({ facility }) => { +const FacilityHomeActions: FC> = ({ + facility, + __meta, +}) => { const { t } = useTranslation(I18NNAMESPACE); const queryClient = useQueryClient(); @@ -59,6 +64,7 @@ const FacilityHomeActions: FC = ({ facility }) => { }); setOpen(false); }} + meta={__meta} />
diff --git a/src/components/pluggables/PatientDetailsTabDemographyGeneralInfo.tsx b/src/components/pluggables/PatientDetailsTabDemographyGeneralInfo.tsx index 712a30e..0709803 100644 --- a/src/components/pluggables/PatientDetailsTabDemographyGeneralInfo.tsx +++ b/src/components/pluggables/PatientDetailsTabDemographyGeneralInfo.tsx @@ -1,11 +1,13 @@ +import { FileDownIcon, ImageDownIcon } from "lucide-react"; +import { useMutation, useQuery } from "@tanstack/react-query"; + +import { Button } from "@/components/ui/button"; import { FC } from "react"; import { I18NNAMESPACE } from "@/lib/constants"; import { Patient } from "@/types/patient"; +import { WithMeta } from "@/types/meta"; import { apis } from "@/apis"; -import { useMutation, useQuery } from "@tanstack/react-query"; import { useTranslation } from "react-i18next"; -import { Button } from "@/components/ui/button"; -import { ImageDownIcon, FileDownIcon } from "lucide-react"; type PatientDetailsTabDemographyGeneralInfoProps = { patientData: Patient; @@ -14,7 +16,7 @@ type PatientDetailsTabDemographyGeneralInfoProps = { }; const PatientDetailsTabDemographyGeneralInfo: FC< - PatientDetailsTabDemographyGeneralInfoProps + WithMeta > = ({ patientId }) => { const { t } = useTranslation(I18NNAMESPACE); diff --git a/src/components/pluggables/PatientHomeActions.tsx b/src/components/pluggables/PatientHomeActions.tsx index 51092eb..637a9fb 100644 --- a/src/components/pluggables/PatientHomeActions.tsx +++ b/src/components/pluggables/PatientHomeActions.tsx @@ -1,10 +1,13 @@ import { useMutation, useQuery } from "@tanstack/react-query"; import { FC } from "react"; +import { I18NNAMESPACE } from "@/lib/constants"; import { LinkAbhaNumber } from "@/components/LinkAbhaNumber"; import { Patient } from "@/types/patient"; +import { WithMeta } from "@/types/meta"; import { apis } from "@/apis"; import { toast } from "@/lib/utils"; +import { useTranslation } from "react-i18next"; type PatientHomeActionsProps = { patient: Patient; @@ -12,11 +15,12 @@ type PatientHomeActionsProps = { className?: string; }; -const PatientHomeActions: FC = ({ +const PatientHomeActions: FC> = ({ patient, facilityId, className, }) => { + const { t } = useTranslation(I18NNAMESPACE); const { data: abhaNumber, refetch } = useQuery({ queryKey: ["abhaNumber", patient.id], queryFn: () => apis.abhaNumber.get(patient.id), @@ -27,12 +31,12 @@ const PatientHomeActions: FC = ({ mutationFn: apis.healthId.linkAbhaNumberAndPatient, onSuccess: (data) => { if (data) { - toast.success(data.detail); + toast.success(data.detail || t("abha_number_linked_successfully")); refetch(); } }, onError: (error) => { - toast.error(error.message); + toast.error(error.message || t("error_linking_abha_number")); }, }); diff --git a/src/components/pluggables/PatientRegistrationForm.tsx b/src/components/pluggables/PatientRegistrationForm.tsx index 8bb707c..0c499ca 100644 --- a/src/components/pluggables/PatientRegistrationForm.tsx +++ b/src/components/pluggables/PatientRegistrationForm.tsx @@ -1,14 +1,16 @@ import { FC, useEffect } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { I18NNAMESPACE } from "@/lib/constants"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { LinkAbhaNumber } from "../LinkAbhaNumber"; import { ShowAbhaProfile } from "../LinkAbhaNumber/ShowAbhaProfile"; import { UseFormReturn } from "react-hook-form"; +import { WithMeta } from "@/types/meta"; import { apis } from "@/apis"; -import { enforceAbhaNumberLinking } from "@/config"; import { toast } from "@/lib/utils"; +import { useTranslation } from "react-i18next"; type PatientRegistrationFormProps = { form: UseFormReturn; @@ -16,12 +18,14 @@ type PatientRegistrationFormProps = { patientId?: string; }; -const PatientRegistrationForm: FC = ({ +const PatientRegistrationForm: FC> = ({ form, facilityId, patientId, + __meta, }) => { const queryClient = useQueryClient(); + const { t } = useTranslation(I18NNAMESPACE); const { data: abhaNumber, refetch } = useQuery({ queryKey: ["abhaNumber", patientId], @@ -41,12 +45,12 @@ const PatientRegistrationForm: FC = ({ mutationFn: apis.healthId.linkAbhaNumberAndPatient, onSuccess: (data) => { if (data) { - toast.success(data.detail); + toast.success(data.detail || t("abha_number_linked_successfully")); refetch(); } }, onError: (error) => { - toast.error(error.message); + toast.error(error.message || t("error_linking_abha_number")); }, }); @@ -89,9 +93,9 @@ const PatientRegistrationForm: FC = ({ return (
= ({ +const PatientSearchActions: FC> = ({ facilityId, className, }) => { diff --git a/src/config.ts b/src/config.ts index f0405c2..e69de29 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,7 +0,0 @@ -export const scanAndShareUrl = import.meta.env.REACT_SCAN_AND_SHARE_URL; - -export const faceAuthUrl = - import.meta.env.REACT_FACE_AUTH_URL || "https://phrsbx.abdm.gov.in/face-auth"; - -export const enforceAbhaNumberLinking = - import.meta.env.REACT_ENFORCE_ABHA_NUMBER_LINKING === "true"; \ No newline at end of file diff --git a/src/types/meta.ts b/src/types/meta.ts new file mode 100644 index 0000000..9429b2b --- /dev/null +++ b/src/types/meta.ts @@ -0,0 +1,14 @@ +export type MetaConfig = { + enforceAbhaNumberLinking?: boolean; + scanAndShareUrl?: string; +}; + +export type Meta = { + name?: string; + url?: string; + config?: MetaConfig; +}; + +export type WithMeta = T & { + __meta?: Meta; +}; diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 5af4fa2..a765633 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -1,14 +1,33 @@ /// /// -interface ImportMetaEnv { - readonly REACT_SCAN_AND_SHARE_URL: string; - readonly REACT_FACE_AUTH_URL: string; - readonly REACT_ENFORCE_ABHA_NUMBER_LINKING: string; +// eslint-disable-next-line @typescript-eslint/no-empty-object-type +interface ImportMetaEnv {} + +interface CareAbdmFePluginConfig { + faceAuthUrl?: string; + enforceAbhaNumberLinking?: boolean; + scanAndShareUrl?: string; +} + +interface CarePluginRuntimeMeta { + care_abdm_fe?: { + config?: CareAbdmFePluginConfig; + }; +} + +interface CarePluginRuntime { + meta?: CarePluginRuntimeMeta; } declare global { - var __CORE_ENV__: { + const __CORE_ENV__: { readonly apiUrl: string; }; + + interface Window { + __CARE_PLUGIN_RUNTIME__?: CarePluginRuntime; + } } + +export {};