freshyo/apps/web-ui/.output/server/_ssr/login-QH2hpwxi.mjs
2026-05-10 16:45:39 +05:30

259 lines
9 KiB
JavaScript

import { o as __toESM } from "../_runtime.mjs";
import { h as require_react, m as require_jsx_runtime } from "../_libs/react+tanstack__react-query.mjs";
import { a as MyText, i as MyButton, o as MyTextInput, s as MyTouchableOpacity } from "./src-u_N1opJl.mjs";
import { n as trpc } from "./trpc-client-CQOIB5UU.mjs";
import { l as useNavigate } from "../_libs/@tanstack/react-router+[...].mjs";
import { n as useAuth } from "./auth-context-DzjwonUC.mjs";
import { n as useForm, t as Controller } from "../_libs/react-hook-form.mjs";
//#region node_modules/.nitro/vite/services/ssr/assets/login-QH2hpwxi.js
var import_react = /* @__PURE__ */ __toESM(require_react());
var import_jsx_runtime = require_jsx_runtime();
function LoginPage() {
const { loginWithToken } = useAuth();
const navigate = useNavigate();
const [step, setStep] = (0, import_react.useState)("mobile");
const [selectedMobile, setSelectedMobile] = (0, import_react.useState)("");
const [canResend, setCanResend] = (0, import_react.useState)(false);
const [resendCountdown, setResendCountdown] = (0, import_react.useState)(0);
const intervalRef = (0, import_react.useRef)();
const [otpCells, setOtpCells] = (0, import_react.useState)([
"",
"",
"",
""
]);
const inputRefs = (0, import_react.useRef)([
null,
null,
null,
null
]);
const loginMutation = trpc.user.auth.login.useMutation();
const sendOtpMutation = trpc.user.auth.sendOtp.useMutation({ onSuccess: (data) => {
if (data.success) {
setResendCountdown(120);
setCanResend(false);
setStep("otp");
}
} });
const verifyOtpMutation = trpc.user.auth.verifyOtp.useMutation({ onSuccess: (data) => {
if (data.success && data.token && data.user) {
loginWithToken(data.token, data.user);
navigate({ to: "/home" });
}
} });
(0, import_react.useEffect)(() => {
return () => {
if (intervalRef.current) clearInterval(intervalRef.current);
};
}, []);
(0, import_react.useEffect)(() => {
if (intervalRef.current) clearInterval(intervalRef.current);
if (resendCountdown > 0) intervalRef.current = setInterval(() => {
setResendCountdown((prev) => {
if (prev <= 1) {
setCanResend(true);
return 0;
}
return prev - 1;
});
}, 1e3);
return () => {
if (intervalRef.current) clearInterval(intervalRef.current);
};
}, [resendCountdown]);
const { control, handleSubmit, setValue, clearErrors, setError } = useForm({ defaultValues: {
mobile: "",
otp: "",
password: ""
} });
const validateMobile = (mobile) => {
const clean = mobile.replace(/\D/g, "");
return clean.length === 10 && /^[6-9]/.test(clean);
};
const handleOtpChange = (index, text) => {
if (text.length > 1) {
const digits = text.replace(/\D/g, "").slice(0, 4);
const newCells = digits.split("").concat([
"",
"",
"",
""
]).slice(0, 4);
setOtpCells(newCells);
setValue("otp", newCells.join(""));
const lastIndex = Math.min(digits.length - 1, 3);
inputRefs.current[lastIndex]?.focus();
return;
}
const newCells = [...otpCells];
newCells[index] = text;
setOtpCells(newCells);
setValue("otp", newCells.join(""));
if (text && index < 3) inputRefs.current[index + 1]?.focus();
else if (!text && index > 0) inputRefs.current[index - 1]?.focus();
};
const onSubmit = (data) => {
if (step === "mobile") {
const mobile = data.mobile.trim();
if (!validateMobile(mobile)) {
setError("mobile", { message: "Enter a valid 10-digit mobile number" });
return;
}
setSelectedMobile(mobile.replace(/\D/g, ""));
sendOtpMutation.mutate({ mobile });
} else if (step === "otp") {
if (!data.otp || data.otp.length < 4) {
setError("otp", { message: "Enter a valid OTP" });
return;
}
verifyOtpMutation.mutate({
mobile: selectedMobile,
otp: data.otp
});
} else if (step === "password") loginMutation.mutate({
identifier: selectedMobile,
password: data.password
}, { onSuccess: (res) => {
loginWithToken(res.data.token, res.data.user);
navigate({ to: "/home" });
} });
};
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
className: "flex min-h-screen items-center justify-center bg-gradient-to-b from-brand-400 to-brand-700 p-4",
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
className: "w-full max-w-md",
children: [
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(MyText, {
weight: "bold",
className: "mb-2 text-center text-4xl text-white",
children: "Welcome"
}),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(MyText, {
className: "mb-8 text-center text-lg text-blue-100",
children: "Sign in to continue your journey"
}),
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
className: "rounded-2xl bg-white p-8 shadow-xl",
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", {
onSubmit: handleSubmit(onSubmit),
children: [
step === "mobile" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Controller, {
control,
name: "mobile",
render: ({ field: { onChange, value } }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MyTextInput, {
placeholder: "Enter your mobile number",
value,
onChange: (e) => {
const clean = e.target.value.replace(/\D/g, "");
if (clean.length <= 10) onChange(clean);
},
className: "bg-gray-50"
})
}),
step === "otp" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
className: "mb-6",
children: [
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(MyText, {
weight: "semibold",
className: "mb-3 text-center text-base text-gray-800",
children: "Enter 4-digit OTP"
}),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
className: "flex justify-center gap-2",
children: [
0,
1,
2,
3
].map((i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
ref: (el) => {
inputRefs.current[i] = el;
},
className: "h-14 w-14 rounded-xl border-2 text-center text-2xl font-bold",
style: {
borderColor: otpCells[i] ? "#E63946" : "#E5E7EB",
backgroundColor: otpCells[i] ? "#FFF5F6" : "#F9FAFB"
},
type: "text",
inputMode: "numeric",
maxLength: 1,
value: otpCells[i],
onChange: (e) => handleOtpChange(i, e.target.value)
}, i))
}),
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
className: "mt-4 flex items-center justify-between border-t border-gray-100 pt-4",
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(MyTouchableOpacity, {
onClick: () => {
setStep("choice");
setOtpCells([
"",
"",
"",
""
]);
},
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MyText, {
weight: "medium",
className: "text-gray-500",
children: "Back"
})
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MyTouchableOpacity, {
onClick: () => sendOtpMutation.mutate({ mobile: selectedMobile }),
disabled: !canResend,
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MyText, {
weight: "semibold",
className: canResend ? "text-brand-600" : "text-gray-400",
children: canResend ? "Resend OTP" : `Resend in ${resendCountdown}s`
})
})]
})
]
}),
step === "password" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Controller, {
control,
name: "password",
render: ({ field: { onChange, value } }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MyTextInput, {
placeholder: "Enter your password",
value,
onChange: (e) => onChange(e.target.value),
type: "password",
className: "bg-gray-50"
})
}),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
className: "mt-6",
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MyButton, {
type: "submit",
fullWidth: true,
className: "h-12 rounded-xl bg-brand-600 text-white shadow-lg",
disabled: sendOtpMutation.isPending || verifyOtpMutation.isPending || loginMutation.isPending,
textContent: step === "otp" ? "Verify & Login" : step === "password" ? "Login" : "Continue"
})
})
]
}), step === "otp" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MyTouchableOpacity, {
onClick: () => {
setStep("password");
setOtpCells([
"",
"",
"",
""
]);
},
className: "mt-4 block text-center",
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MyText, {
weight: "semibold",
className: "text-brand-600",
children: "Or login with Password"
})
})]
})
]
})
});
}
//#endregion
export { LoginPage as component };