Login: 6-digit OTP + desktop layout
- OTP input now 6 digits (matches backend 6-digit codes): maxLength 6, six slots with responsive widths, verify enabled at length === 6 - login page: centered desktop card on a neutral canvas with brand mark - MyLayout: auth pages (/login, /logout) get full-width desktop canvas and no mobile bottom nav at lg (no shopping header/footer) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
232f7d1b3c
commit
d0571f8253
@ -36,6 +36,10 @@ const MyLayout = React.memo(({ children }: { children: React.ReactNode }) => {
|
|||||||
p.startsWith("/search/") ||
|
p.startsWith("/search/") ||
|
||||||
p.startsWith("/seller/") ||
|
p.startsWith("/seller/") ||
|
||||||
p === "/cart";
|
p === "/cart";
|
||||||
|
// Auth pages get a full-width desktop canvas (so they can center their own
|
||||||
|
// card) but no shopping header/footer.
|
||||||
|
const isAuth = p === "/login" || p === "/logout";
|
||||||
|
const expandOnDesktop = isDesktopReady || isAuth;
|
||||||
const isThread = !!location.pathname.split("threads")[1];
|
const isThread = !!location.pathname.split("threads")[1];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -43,18 +47,22 @@ const MyLayout = React.memo(({ children }: { children: React.ReactNode }) => {
|
|||||||
{isDesktopReady && <DesktopHeader />}
|
{isDesktopReady && <DesktopHeader />}
|
||||||
<div
|
<div
|
||||||
className={`max-w-md mx-auto bg-WHITE min-h-screen shadow-lg lg:shadow-xl ${
|
className={`max-w-md mx-auto bg-WHITE min-h-screen shadow-lg lg:shadow-xl ${
|
||||||
isDesktopReady
|
expandOnDesktop
|
||||||
? "lg:max-w-none lg:mx-0 lg:min-h-0 lg:shadow-none"
|
? "lg:max-w-none lg:mx-0 lg:min-h-0 lg:shadow-none"
|
||||||
: ""
|
: ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`pb-[calc(50px+env(safe-area-inset-bottom))] ${
|
className={`pb-[calc(50px+env(safe-area-inset-bottom))] ${
|
||||||
isDesktopReady ? "lg:pb-0" : ""
|
expandOnDesktop ? "lg:pb-0" : ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
{isThread ? <></> : <MobileBottomNavigation hideOnDesktop={isDesktopReady} />}
|
{isThread ? (
|
||||||
|
<></>
|
||||||
|
) : (
|
||||||
|
<MobileBottomNavigation hideOnDesktop={expandOnDesktop} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{isDesktopReady && <DesktopFooter />}
|
{isDesktopReady && <DesktopFooter />}
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import AppInput from "~/components/AppInput";
|
|||||||
import { useToast } from "~/hooks/use-toast";
|
import { useToast } from "~/hooks/use-toast";
|
||||||
import { useSubmit, useLoaderData } from "@remix-run/react";
|
import { useSubmit, useLoaderData } from "@remix-run/react";
|
||||||
import { User } from "~/utils/token-manager.server";
|
import { User } from "~/utils/token-manager.server";
|
||||||
|
import logo from "~/assets/logo/SVG-07.svg";
|
||||||
|
|
||||||
// Only allow same-site, path-relative redirects. Rejects absolute URLs
|
// Only allow same-site, path-relative redirects. Rejects absolute URLs
|
||||||
// (https://evil.com) and protocol-relative URLs (//evil.com) to prevent
|
// (https://evil.com) and protocol-relative URLs (//evil.com) to prevent
|
||||||
@ -106,8 +107,14 @@ export default function LoginPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-6 px-[20px] py-12 items-center w-full">
|
<div className="lg:min-h-screen lg:flex lg:items-center lg:justify-center lg:bg-WHITE2 lg:py-12">
|
||||||
{enableVerifySection ? (
|
<div className="flex flex-col gap-6 px-[20px] py-12 items-center w-full lg:max-w-[460px] lg:bg-WHITE lg:rounded-2xl lg:border lg:border-WHITE3 lg:shadow-lg lg:px-8 lg:py-10 lg:my-12">
|
||||||
|
{/* Brand (desktop) */}
|
||||||
|
<div className="hidden lg:flex items-center gap-2.5 mb-1">
|
||||||
|
<img src={logo} alt="ویترون" className="w-7 h-9 object-contain" />
|
||||||
|
<b className="text-[22px] font-extrabold text-VITROWN_BLUE">ویترون</b>
|
||||||
|
</div>
|
||||||
|
{enableVerifySection ? (
|
||||||
<>
|
<>
|
||||||
<div className="flex flex-col gap-4 w-full items-center">
|
<div className="flex flex-col gap-4 w-full items-center">
|
||||||
<img alt="" src={verifyBanner} className="w-[85px] h-[70px]" />
|
<img alt="" src={verifyBanner} className="w-[85px] h-[70px]" />
|
||||||
@ -127,7 +134,7 @@ export default function LoginPage() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="w-full mt-2 flex justify-center">
|
<div className="w-full mt-2 flex justify-center">
|
||||||
<InputOTP
|
<InputOTP
|
||||||
maxLength={4}
|
maxLength={6}
|
||||||
onComplete={(finalValue) => {
|
onComplete={(finalValue) => {
|
||||||
const convertedValue = convertPersianToEnglish(finalValue);
|
const convertedValue = convertPersianToEnglish(finalValue);
|
||||||
setOtpValue(convertedValue);
|
setOtpValue(convertedValue);
|
||||||
@ -138,17 +145,19 @@ export default function LoginPage() {
|
|||||||
setOtpValue(convertedValue);
|
setOtpValue(convertedValue);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<InputOTPGroup dir="ltr" className="gap-4">
|
<InputOTPGroup dir="ltr" className="gap-1.5 sm:gap-3">
|
||||||
<InputOTPSlot index={0} />
|
<InputOTPSlot index={0} className="!w-9 sm:!w-12" />
|
||||||
<InputOTPSlot index={1} />
|
<InputOTPSlot index={1} className="!w-9 sm:!w-12" />
|
||||||
<InputOTPSlot index={2} />
|
<InputOTPSlot index={2} className="!w-9 sm:!w-12" />
|
||||||
<InputOTPSlot index={3} />
|
<InputOTPSlot index={3} className="!w-9 sm:!w-12" />
|
||||||
|
<InputOTPSlot index={4} className="!w-9 sm:!w-12" />
|
||||||
|
<InputOTPSlot index={5} className="!w-9 sm:!w-12" />
|
||||||
</InputOTPGroup>
|
</InputOTPGroup>
|
||||||
</InputOTP>
|
</InputOTP>
|
||||||
</div>
|
</div>
|
||||||
<div className="gap-4 w-full flex flex-col items-center">
|
<div className="gap-4 w-full flex flex-col items-center">
|
||||||
<Button
|
<Button
|
||||||
disabled={verifyOtpMutation.isPending || !(otpValue.length === 4)}
|
disabled={verifyOtpMutation.isPending || !(otpValue.length === 6)}
|
||||||
className="w-[90%]"
|
className="w-[90%]"
|
||||||
size={"xl"}
|
size={"xl"}
|
||||||
variant="dark"
|
variant="dark"
|
||||||
@ -235,6 +244,7 @@ export default function LoginPage() {
|
|||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user