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:
Arda Samadi 2026-06-14 21:36:42 +03:30
parent 232f7d1b3c
commit d0571f8253
2 changed files with 30 additions and 12 deletions

View File

@ -36,6 +36,10 @@ const MyLayout = React.memo(({ children }: { children: React.ReactNode }) => {
p.startsWith("/search/") ||
p.startsWith("/seller/") ||
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];
return (
@ -43,18 +47,22 @@ const MyLayout = React.memo(({ children }: { children: React.ReactNode }) => {
{isDesktopReady && <DesktopHeader />}
<div
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"
: ""
}`}
>
<div
className={`pb-[calc(50px+env(safe-area-inset-bottom))] ${
isDesktopReady ? "lg:pb-0" : ""
expandOnDesktop ? "lg:pb-0" : ""
}`}
>
{children}
{isThread ? <></> : <MobileBottomNavigation hideOnDesktop={isDesktopReady} />}
{isThread ? (
<></>
) : (
<MobileBottomNavigation hideOnDesktop={expandOnDesktop} />
)}
</div>
</div>
{isDesktopReady && <DesktopFooter />}

View File

@ -21,6 +21,7 @@ import AppInput from "~/components/AppInput";
import { useToast } from "~/hooks/use-toast";
import { useSubmit, useLoaderData } from "@remix-run/react";
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
// (https://evil.com) and protocol-relative URLs (//evil.com) to prevent
@ -106,8 +107,14 @@ export default function LoginPage() {
};
return (
<div className="flex flex-col gap-6 px-[20px] py-12 items-center w-full">
{enableVerifySection ? (
<div className="lg:min-h-screen lg:flex lg:items-center lg:justify-center lg:bg-WHITE2 lg:py-12">
<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">
<img alt="" src={verifyBanner} className="w-[85px] h-[70px]" />
@ -127,7 +134,7 @@ export default function LoginPage() {
</div>
<div className="w-full mt-2 flex justify-center">
<InputOTP
maxLength={4}
maxLength={6}
onComplete={(finalValue) => {
const convertedValue = convertPersianToEnglish(finalValue);
setOtpValue(convertedValue);
@ -138,17 +145,19 @@ export default function LoginPage() {
setOtpValue(convertedValue);
}}
>
<InputOTPGroup dir="ltr" className="gap-4">
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
<InputOTPSlot index={3} />
<InputOTPGroup dir="ltr" className="gap-1.5 sm:gap-3">
<InputOTPSlot index={0} className="!w-9 sm:!w-12" />
<InputOTPSlot index={1} className="!w-9 sm:!w-12" />
<InputOTPSlot index={2} className="!w-9 sm:!w-12" />
<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>
</InputOTP>
</div>
<div className="gap-4 w-full flex flex-col items-center">
<Button
disabled={verifyOtpMutation.isPending || !(otpValue.length === 4)}
disabled={verifyOtpMutation.isPending || !(otpValue.length === 6)}
className="w-[90%]"
size={"xl"}
variant="dark"
@ -235,6 +244,7 @@ export default function LoginPage() {
</Button>
</>
)}
</div>
</div>
);
}