diff --git a/app/globals.css b/app/globals.css index 5621fc0..b66ace5 100644 --- a/app/globals.css +++ b/app/globals.css @@ -65,7 +65,7 @@ --border: #2a2a2a; --input: #222222; --ring: #ff9800; - + /* HarifSport specific */ --hs-orange: #ff9800; --hs-maroon: #852222; @@ -109,6 +109,7 @@ * { @apply border-border outline-ring/50; } + body { @apply bg-background text-foreground; font-size: 13px; @@ -117,9 +118,17 @@ /* HarifSport odds button animation */ @keyframes odds-flash { - 0% { background-color: oklch(0.55 0.18 145); } - 50% { background-color: oklch(0.7 0.22 80); } - 100% { background-color: oklch(0.55 0.18 145); } + 0% { + background-color: oklch(0.55 0.18 145); + } + + 50% { + background-color: oklch(0.7 0.22 80); + } + + 100% { + background-color: oklch(0.55 0.18 145); + } } .odds-selected { @@ -128,22 +137,48 @@ /* Live pulse */ @keyframes live-pulse { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.4; } + + 0%, + 100% { + opacity: 1; + } + + 50% { + opacity: 0.4; + } } .live-dot { animation: live-pulse 1.2s ease-in-out infinite; } +/* Auth modal fade-in */ +@keyframes fade-in { + from { + opacity: 0; + transform: translateY(-10px) scale(0.98); + } + + to { + opacity: 1; + transform: translateY(0) scale(1); + } +} + +.animate-fade-in { + animation: fade-in 0.18s ease-out both; +} + /* Scrollbar */ ::-webkit-scrollbar { width: 4px; height: 4px; } + ::-webkit-scrollbar-track { background: var(--background); } + ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; diff --git a/app/login/page.tsx b/app/login/page.tsx index 65a3808..cc75a26 100644 --- a/app/login/page.tsx +++ b/app/login/page.tsx @@ -2,55 +2,126 @@ import { useState } from "react" import Link from "next/link" +import { useRouter } from "next/navigation" export default function LoginPage() { - const [username, setUsername] = useState("") + const router = useRouter() + const [phone, setPhone] = useState("") const [password, setPassword] = useState("") return ( -
-
-
-

Login

-

Sign in to your Harifsport account

-
-
-
- - setUsername(e.target.value)} - placeholder="Enter username" - className="w-full bg-input border border-border rounded px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:border-primary transition-colors" - /> + /* Full-screen dark backdrop */ +
router.back()} + > + {/* Modal card */} +
e.stopPropagation()} + > + {/* Close button */} + + + {/* Logo */} +
+
+ {/* Red slashes */} +
+ {[0, 1, 2].map((i) => ( +
+ ))} +
+ {/* HARIF box */} +
+ + HARIF + +
+ {/* SPORT text */} + + SPORT +
+
+ + {/* Title */} +
+

+ LOGIN +

+
+ + {/* Form */} +
+ {/* Phone Number */}
- + +
+ + ET +251 + + setPhone(e.target.value)} + className="flex-1 bg-white border border-gray-300 px-3 py-2 text-sm text-[#333] placeholder:text-gray-400 focus:outline-none focus:border-[#ff9800]" + /> +
+
+ + {/* Password */} +
+ setPassword(e.target.value)} - placeholder="Enter password" - className="w-full bg-input border border-border rounded px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:border-primary transition-colors" + placeholder="Password" + className="w-full bg-white border border-gray-300 px-3 py-2 text-sm text-[#333] placeholder:text-gray-400 focus:outline-none focus:border-[#ff9800]" />
-
- - + + {/* Forgot password */} +
+ Forgot password?
- -

+ + {/* Support link */} +

+ + Support + +

+ + {/* Register link */} +

Don't have an account?{" "} - - Register now + + Register

diff --git a/app/register/page.tsx b/app/register/page.tsx index ace0ac1..22fef68 100644 --- a/app/register/page.tsx +++ b/app/register/page.tsx @@ -2,107 +2,147 @@ import { useState } from "react" import Link from "next/link" +import { useRouter } from "next/navigation" export default function RegisterPage() { - const [step, setStep] = useState(1) + const router = useRouter() + const [phone, setPhone] = useState("") + const [password, setPassword] = useState("") + const [repeatPassword, setRepeatPassword] = useState("") + const [ageConfirmed, setAgeConfirmed] = useState(false) return ( -
-
-
-

Create Account

-

Join Harifsport today

+ /* Full-screen dark backdrop */ +
router.back()} + > + {/* Modal card — stop clicks propagating to backdrop */} +
e.stopPropagation()} + > + {/* Close button */} + + + {/* Logo */} +
+
+ {/* Red slashes */} +
+ {[0, 1, 2].map((i) => ( +
+ ))} +
+ {/* HARIF box */} +
+ + HARIF + +
+ {/* SPORT text */} + + SPORT + +
- {/* Steps */} -
- {["Personal Info", "Account Details", "Verification"].map((s, i) => ( -
- {i + 1}. {s} -
- ))} + {/* Title */} +
+

+ REGISTER +

-
- {step === 1 && ( - <> -
-
- - -
-
- - -
-
-
- - -
-
- - -
- - )} - - {step === 2 && ( - <> -
- - -
-
- - -
-
- - -
-
- - -
- - )} - - {step === 3 && ( -
-
-

Account Created!

-

Please verify your phone number to start betting.

- + {/* Form */} +
+ {/* Phone Number */} +
+ +
+ + ET +251 + + setPhone(e.target.value)} + className="flex-1 bg-white border border-gray-300 px-3 py-2 text-sm text-[#333] placeholder:text-gray-400 focus:outline-none focus:border-[#ff9800]" + />
- )} - -
- {step > 1 && ( - - )} -
- {step === 1 && ( -

- Already have an account?{" "} - Login -

- )} + {/* Password */} +
+ + setPassword(e.target.value)} + placeholder="Password" + className="w-full bg-white border border-gray-300 px-3 py-2 text-sm text-[#333] placeholder:text-gray-400 focus:outline-none focus:border-[#ff9800]" + /> +
+ + {/* Repeat Password */} +
+ + setRepeatPassword(e.target.value)} + placeholder="Repeat Password" + className="w-full bg-white border border-gray-300 px-3 py-2 text-sm text-[#333] placeholder:text-gray-400 focus:outline-none focus:border-[#ff9800]" + /> +
+ + {/* Age confirmation */} + + + {/* Register button */} + + + {/* Support link */} +

+ + Support + +

+ + {/* Login link */} +

+ Already have an account?{" "} + + Login + +

diff --git a/components/layout/auth-modal.tsx b/components/layout/auth-modal.tsx new file mode 100644 index 0000000..cdc71f5 --- /dev/null +++ b/components/layout/auth-modal.tsx @@ -0,0 +1,215 @@ +"use client" + +import { useState, useEffect, useCallback } from "react" +import Link from "next/link" + +type Mode = "login" | "register" + +function Logo() { + return ( +
+ {/* Red slashes */} +
+ {[0, 1, 2].map((i) => ( +
+ ))} +
+ {/* HARIF box */} +
+ + HARIF + +
+ {/* SPORT text */} + + SPORT + +
+ ) +} + +interface AuthModalProps { + open: boolean + defaultMode: Mode + onClose: () => void +} + +export function AuthModal({ open, defaultMode, onClose }: AuthModalProps) { + const [mode, setMode] = useState(defaultMode) + const [phone, setPhone] = useState("") + const [password, setPassword] = useState("") + const [repeatPassword, setRepeatPassword] = useState("") + const [ageConfirmed, setAgeConfirmed] = useState(false) + + // Sync mode if parent changes defaultMode while open + useEffect(() => { + if (open) setMode(defaultMode) + }, [defaultMode, open]) + + // Close on Escape + const handleKey = useCallback( + (e: KeyboardEvent) => { + if (e.key === "Escape") onClose() + }, + [onClose] + ) + useEffect(() => { + if (open) window.addEventListener("keydown", handleKey) + return () => window.removeEventListener("keydown", handleKey) + }, [open, handleKey]) + + if (!open) return null + + return ( +
+
e.stopPropagation()} + > + {/* Close button */} + + + {/* Logo */} +
+ +
+ + {/* Title */} +
+

+ {mode === "login" ? "LOGIN" : "REGISTER"} +

+
+ + {/* Form */} +
+ {/* Phone Number */} +
+ +
+ + ET +251 + + setPhone(e.target.value)} + className="flex-1 bg-white border border-l-0 border-gray-300 px-3 py-2 text-sm text-[#333] placeholder:text-gray-400 focus:outline-none focus:border-[#ff9800]" + /> +
+
+ + {/* Password */} +
+ + setPassword(e.target.value)} + placeholder="Password" + className="w-full bg-white border border-gray-300 px-3 py-2 text-sm text-[#333] placeholder:text-gray-400 focus:outline-none focus:border-[#ff9800]" + /> +
+ + {/* Repeat Password (register only) */} + {mode === "register" && ( +
+ + setRepeatPassword(e.target.value)} + placeholder="Repeat Password" + className="w-full bg-white border border-gray-300 px-3 py-2 text-sm text-[#333] placeholder:text-gray-400 focus:outline-none focus:border-[#ff9800]" + /> +
+ )} + + {/* Age confirmation (register only) */} + {mode === "register" && ( + + )} + + {/* Forgot password (login only) */} + {mode === "login" && ( +
+ + Forgot password? + +
+ )} + + {/* Submit button */} + + + {/* Support link */} +

+ + Support + +

+ + {/* Toggle mode */} +

+ {mode === "login" ? ( + <> + Don't have an account?{" "} + + + ) : ( + <> + Already have an account?{" "} + + + )} +

+
+
+
+ ) +} diff --git a/components/layout/layout-client-wrapper.tsx b/components/layout/layout-client-wrapper.tsx index 9159684..0ba5d1c 100644 --- a/components/layout/layout-client-wrapper.tsx +++ b/components/layout/layout-client-wrapper.tsx @@ -1,24 +1,45 @@ "use client" import { usePathname } from "next/navigation" +import { useState } from "react" import { SiteHeader } from "@/components/layout/site-header" import { SportsSidebar } from "@/components/layout/sports-sidebar" import { RightPanel } from "@/components/layout/right-panel" import { SiteFooter } from "@/components/layout/site-footer" +import { AuthModal } from "@/components/layout/auth-modal" + +type AuthMode = "login" | "register" export default function LayoutClientWrapper({ children }: { children: React.ReactNode }) { const pathname = usePathname() const isLivePage = pathname === "/live" + const [authOpen, setAuthOpen] = useState(false) + const [authMode, setAuthMode] = useState("login") + + function openAuth(mode: AuthMode) { + setAuthMode(mode) + setAuthOpen(true) + } + return (
- + openAuth("login")} + onRegisterClick={() => openAuth("register")} + />
{!isLivePage && }
{children}
+ + setAuthOpen(false)} + />
) } diff --git a/components/layout/site-header.tsx b/components/layout/site-header.tsx index d3ef4b9..af8a47e 100644 --- a/components/layout/site-header.tsx +++ b/components/layout/site-header.tsx @@ -22,7 +22,12 @@ const extraNavItems = [ { href: "/promo", label: "PROMO" }, ] -export function SiteHeader() { +interface SiteHeaderProps { + onLoginClick?: () => void + onRegisterClick?: () => void +} + +export function SiteHeader({ onLoginClick, onRegisterClick }: SiteHeaderProps) { const pathname = usePathname() const isLivePage = pathname === "/live" const [time, setTime] = useState("") @@ -43,7 +48,7 @@ export function SiteHeader() {
@@ -57,11 +62,17 @@ export function SiteHeader() {
- -