Skip to content

Commit

Permalink
Merge branch 'develop' into issue/9683/userResetPassword_form_switch
Browse files Browse the repository at this point in the history
  • Loading branch information
AdityaJ2305 authored Jan 7, 2025
2 parents f8fe6a2 + 9b33e90 commit 576e356
Show file tree
Hide file tree
Showing 20 changed files with 259 additions and 272 deletions.
16 changes: 8 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,10 @@
"events": "^3.3.0",
"hi-profiles": "^1.1.0",
"html2canvas": "^1.4.1",
"i18next": "^24.2.0",
"i18next": "^24.2.1",
"i18next-browser-languagedetector": "^8.0.2",
"i18next-http-backend": "^3.0.1",
"input-otp": "^1.4.1",
"input-otp": "^1.4.2",
"lodash-es": "^4.17.21",
"lucide-react": "^0.469.0",
"markdown-it": "^14.1.0",
Expand Down
1 change: 1 addition & 0 deletions public/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,7 @@
"domestic_international_travel": "Domestic/international Travel (within last 28 days)",
"done": "Done",
"dosage": "Dosage",
"dosage_instructions": "Dosage Instructions",
"down": "Down",
"download": "Download",
"download_discharge_summary": "Download discharge summary",
Expand Down
47 changes: 32 additions & 15 deletions src/Providers/AuthUserProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { LocalStorageKeys } from "@/common/constants";
import routes from "@/Utils/request/api";
import query from "@/Utils/request/query";
import request from "@/Utils/request/request";
import { TokenData } from "@/types/auth/otpToken";

interface Props {
children: React.ReactNode;
Expand All @@ -27,29 +26,29 @@ export default function AuthUserProvider({
otpAuthorized,
}: Props) {
const queryClient = useQueryClient();
const [accessToken, setAccessToken] = useState(
localStorage.getItem(LocalStorageKeys.accessToken),
);
const [patientToken, setPatientToken] = useState(
JSON.parse(localStorage.getItem(LocalStorageKeys.patientTokenKey) || "{}"),
);

const { data: user, isLoading } = useQuery({
queryKey: ["currentUser"],
queryKey: ["currentUser", accessToken],
queryFn: query(routes.currentUser, { silent: true }),
retry: false,
enabled: !!localStorage.getItem(LocalStorageKeys.accessToken),
});

const [isOTPAuthorized, setIsOTPAuthorized] = useState(false);

const tokenData: TokenData = JSON.parse(
localStorage.getItem(LocalStorageKeys.patientTokenKey) || "{}",
);

useEffect(() => {
if (
tokenData.token &&
Object.keys(tokenData).length > 0 &&
dayjs(tokenData.createdAt).isAfter(dayjs().subtract(14, "minutes"))
patientToken.token &&
Object.keys(patientToken).length > 0 &&
dayjs(patientToken.createdAt).isAfter(dayjs().subtract(14, "minutes"))
) {
setIsOTPAuthorized(true);
navigate("/patient/home");
}
}, [tokenData]);
}, [patientToken]);

useEffect(() => {
if (!user) {
Expand All @@ -68,6 +67,7 @@ export default function AuthUserProvider({
const query = await request(routes.login, { body: creds });

if (query.res?.ok && query.data) {
setAccessToken(query.data.access);
localStorage.setItem(LocalStorageKeys.accessToken, query.data.access);
localStorage.setItem(LocalStorageKeys.refreshToken, query.data.refresh);

Expand All @@ -83,10 +83,20 @@ export default function AuthUserProvider({
[queryClient],
);

const patientLogin = useCallback(() => {
setPatientToken(
JSON.parse(
localStorage.getItem(LocalStorageKeys.patientTokenKey) || "{}",
),
);
navigate("/patient/home");
}, []);

const signOut = useCallback(async () => {
localStorage.removeItem(LocalStorageKeys.accessToken);
localStorage.removeItem(LocalStorageKeys.refreshToken);
localStorage.removeItem(LocalStorageKeys.patientTokenKey);
setPatientToken({});

await queryClient.resetQueries({ queryKey: ["currentUser"] });

Expand Down Expand Up @@ -124,15 +134,22 @@ export default function AuthUserProvider({
const SelectedRouter = () => {
if (user) {
return children;
} else if (isOTPAuthorized) {
} else if (patientToken.token) {
return otpAuthorized;
} else {
return unauthorized;
}
};

return (
<AuthUserContext.Provider value={{ signIn, signOut, user }}>
<AuthUserContext.Provider
value={{
signIn,
signOut,
user,
patientLogin,
}}
>
<SelectedRouter />
</AuthUserContext.Provider>
);
Expand Down
97 changes: 50 additions & 47 deletions src/components/Auth/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import careConfig from "@careConfig";
import { useMutation } from "@tanstack/react-query";
import { Link, useQueryParams } from "raviger";
import { useEffect, useState } from "react";
import { useState } from "react";
import ReCaptcha from "react-google-recaptcha";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";
Expand Down Expand Up @@ -29,25 +29,23 @@ import BrowserWarning from "@/components/ErrorPages/BrowserWarning";

import { useAuthContext } from "@/hooks/useAuthUser";

import { CarePatientTokenKey } from "@/common/constants";
import { LocalStorageKeys } from "@/common/constants";

import FiltersCache from "@/Utils/FiltersCache";
import * as Notification from "@/Utils/Notifications";
import routes from "@/Utils/request/api";
import mutate from "@/Utils/request/mutate";
import request from "@/Utils/request/request";
import { HTTPError } from "@/Utils/request/types";
import { TokenData } from "@/types/auth/otpToken";

interface LoginFormData {
username: string;
password: string;
}

type LoginMode = "staff" | "patient";

interface LoginProps {
forgot?: boolean;
interface OtpLoginData {
phone_number: string;
otp: string;
}

interface OtpError {
Expand All @@ -61,14 +59,19 @@ interface OtpError {
url: string;
}

// Update interface for OTP data
interface OtpLoginData {
phone_number: string;
otp: string;
interface OtpValidationError {
otp?: string;
[key: string]: string | undefined;
}

type LoginMode = "staff" | "patient";

interface LoginProps {
forgot?: boolean;
}

const Login = (props: LoginProps) => {
const { signIn } = useAuthContext();
const { signIn, patientLogin } = useAuthContext();
const { reCaptchaSiteKey, urls, stateLogo, customLogo, customLogoAlt } =
careConfig;
const customDescriptionHtml = __CUSTOM_DESCRIPTION_HTML__;
Expand All @@ -84,8 +87,6 @@ const Login = (props: LoginProps) => {
const [errors, setErrors] = useState(initErr);
const [isCaptchaEnabled, setCaptcha] = useState(false);
const { t } = useTranslation();
// display spinner while login is under progress
const [loading, setLoading] = useState(false);
const [forgotPassword, setForgotPassword] = useState(forgot);
const [loginMode, setLoginMode] = useState<LoginMode>(
mode === "patient" ? "patient" : "staff",
Expand All @@ -104,15 +105,6 @@ const Login = (props: LoginProps) => {
},
onSuccess: ({ res }) => {
setCaptcha(res?.status === 429);
window.location.href = "/";
},
});

// Forgot Password Mutation
const { mutate: submitForgetPassword } = useMutation({
mutationFn: mutate(routes.forgotPassword),
onSuccess: () => {
toast.success(t("password_sent"));
},
});

Expand All @@ -128,7 +120,7 @@ const Login = (props: LoginProps) => {
onSuccess: () => {
setIsOtpSent(true);
setOtpError("");
Notification.Success({ msg: t("send_otp_success") });
toast.success(t("send_otp_success"));
},
onError: (error: any) => {
const errors = error?.data || [];
Expand Down Expand Up @@ -161,20 +153,23 @@ const Login = (props: LoginProps) => {
phoneNumber: `+91${phone}`,
createdAt: new Date().toISOString(),
};
localStorage.setItem(CarePatientTokenKey, JSON.stringify(tokenData));
window.location.href = "/patient/home";
localStorage.setItem(
LocalStorageKeys.patientTokenKey,
JSON.stringify(tokenData),
);
patientLogin();
}
},

//Invalid OTP error handling
onError: (error: HTTPError) => {
onError: (error: any) => {
let errorMessage = t("invalid_otp");
if (
error.cause &&
Array.isArray(error.cause.errors) &&
error.cause.errors.length > 0
) {
const otpError = error.cause.errors.find((e) => e.otp);
const otpError = error.cause.errors.find(
(e: OtpValidationError) => e.otp,
);
if (otpError && otpError.otp) {
errorMessage = otpError.otp;
}
Expand All @@ -186,14 +181,20 @@ const Login = (props: LoginProps) => {
},
});

// Forgot Password Mutation
const { mutate: submitForgetPassword } = useMutation({
mutationFn: mutate(routes.forgotPassword),
onSuccess: () => {
toast.success(t("password_sent"));
},
});

// Format phone number to include +91
const formatPhoneNumber = (value: string) => {
// Remove any non-digit characters
const digits = value.replace(/\D/g, "");

// Limit to 10 digits
const truncated = digits.slice(0, 10);

return truncated;
};

Expand All @@ -205,7 +206,6 @@ const Login = (props: LoginProps) => {
};

// Login form validation

const handleChange = (e: any) => {
const { value, name } = e.target;
const fieldValue = Object.assign({}, form);
Expand Down Expand Up @@ -244,17 +244,9 @@ const Login = (props: LoginProps) => {
setErrors(err);
return false;
}

return form;
};

// set loading to false when component is unmounted
useEffect(() => {
return () => {
setLoading(false);
};
}, []);

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const validated = validateData();
Expand Down Expand Up @@ -305,17 +297,28 @@ const Login = (props: LoginProps) => {
const handlePatientLogin = async (e: React.FormEvent) => {
e.preventDefault();

if (!isOtpSent) {
sendOtp(phone);
} else {
verifyOtp({ phone_number: `+91${phone}`, otp });
try {
if (!isOtpSent) {
await sendOtp(phone);
setIsOtpSent(true);
} else {
await verifyOtp({ phone_number: `+91${phone}`, otp });
}
} catch (error: any) {
if (!isOtpSent) {
setOtpError(error.message);
} else {
setOtpValidationError(error.message);
}
}
};

const resetPatientLogin = () => {
setIsOtpSent(false);
setPhone("");
setOtp("");
setOtpError("");
setOtpValidationError("");
};

// Loading state derived from mutations
Expand Down Expand Up @@ -695,13 +698,13 @@ const Login = (props: LoginProps) => {
className="w-full"
variant="primary"
disabled={
loading ||
isLoading ||
!phone ||
phone.length !== 10 ||
(isOtpSent && otp.length !== 5)
}
>
{loading ? (
{isLoading ? (
<CircularProgress className="text-white" />
) : isOtpSent ? (
t("verify_otp")
Expand Down
Loading

0 comments on commit 576e356

Please sign in to comment.