login and signup
This commit is contained in:
parent
612bb9386b
commit
77944930d0
|
|
@ -65,7 +65,7 @@
|
||||||
--border: #2a2a2a;
|
--border: #2a2a2a;
|
||||||
--input: #222222;
|
--input: #222222;
|
||||||
--ring: #ff9800;
|
--ring: #ff9800;
|
||||||
|
|
||||||
/* HarifSport specific */
|
/* HarifSport specific */
|
||||||
--hs-orange: #ff9800;
|
--hs-orange: #ff9800;
|
||||||
--hs-maroon: #852222;
|
--hs-maroon: #852222;
|
||||||
|
|
@ -109,6 +109,7 @@
|
||||||
* {
|
* {
|
||||||
@apply border-border outline-ring/50;
|
@apply border-border outline-ring/50;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
|
@ -117,9 +118,17 @@
|
||||||
|
|
||||||
/* HarifSport odds button animation */
|
/* HarifSport odds button animation */
|
||||||
@keyframes odds-flash {
|
@keyframes odds-flash {
|
||||||
0% { background-color: oklch(0.55 0.18 145); }
|
0% {
|
||||||
50% { background-color: oklch(0.7 0.22 80); }
|
background-color: oklch(0.55 0.18 145);
|
||||||
100% { 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 {
|
.odds-selected {
|
||||||
|
|
@ -128,22 +137,48 @@
|
||||||
|
|
||||||
/* Live pulse */
|
/* Live pulse */
|
||||||
@keyframes live-pulse {
|
@keyframes live-pulse {
|
||||||
0%, 100% { opacity: 1; }
|
|
||||||
50% { opacity: 0.4; }
|
0%,
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.live-dot {
|
.live-dot {
|
||||||
animation: live-pulse 1.2s ease-in-out infinite;
|
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 */
|
/* Scrollbar */
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 4px;
|
width: 4px;
|
||||||
height: 4px;
|
height: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
background: var(--background);
|
background: var(--background);
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
background: var(--border);
|
background: var(--border);
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
|
|
||||||
|
|
@ -2,55 +2,126 @@
|
||||||
|
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
|
import { useRouter } from "next/navigation"
|
||||||
|
|
||||||
export default function LoginPage() {
|
export default function LoginPage() {
|
||||||
const [username, setUsername] = useState("")
|
const router = useRouter()
|
||||||
|
const [phone, setPhone] = useState("")
|
||||||
const [password, setPassword] = useState("")
|
const [password, setPassword] = useState("")
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-w-sm mx-auto mt-8">
|
/* Full-screen dark backdrop */
|
||||||
<div className="bg-card border border-border rounded-lg overflow-hidden">
|
<div
|
||||||
<div className="bg-primary px-6 py-4">
|
className="fixed inset-0 z-50 flex items-center justify-center bg-black/70"
|
||||||
<h1 className="text-base font-black text-primary-foreground uppercase tracking-wide">Login</h1>
|
onClick={() => router.back()}
|
||||||
<p className="text-[11px] text-primary-foreground/70 mt-0.5">Sign in to your Harifsport account</p>
|
>
|
||||||
</div>
|
{/* Modal card */}
|
||||||
<div className="p-6 space-y-4">
|
<div
|
||||||
<div>
|
className="relative w-[420px] bg-[#3a3a3a] shadow-2xl"
|
||||||
<label className="text-[11px] font-semibold text-muted-foreground uppercase block mb-1">Username</label>
|
onClick={(e) => e.stopPropagation()}
|
||||||
<input
|
>
|
||||||
type="text"
|
{/* Close button */}
|
||||||
value={username}
|
<button
|
||||||
onChange={(e) => setUsername(e.target.value)}
|
onClick={() => router.back()}
|
||||||
placeholder="Enter username"
|
className="absolute top-2 right-3 text-white/60 hover:text-white text-xl leading-none transition-colors z-10"
|
||||||
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"
|
aria-label="Close"
|
||||||
/>
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Logo */}
|
||||||
|
<div className="flex items-center justify-center py-5 border-b border-white/10 bg-[#2e2e2e]">
|
||||||
|
<div className="flex items-center">
|
||||||
|
{/* Red slashes */}
|
||||||
|
<div className="flex gap-[3px] mr-2">
|
||||||
|
{[0, 1, 2].map((i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className="w-[5px] h-[38px] bg-[#cc2222] -skew-x-12"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{/* HARIF box */}
|
||||||
|
<div className="bg-[#852222] px-3 py-1 -skew-x-12 flex items-center h-[38px]">
|
||||||
|
<span className="text-2xl font-black text-white italic tracking-tighter skew-x-12 inline-block leading-none">
|
||||||
|
HARIF
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{/* SPORT text */}
|
||||||
|
<span className="text-2xl font-black text-[#ff9800] italic tracking-tighter ml-1 leading-none">
|
||||||
|
SPORT
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Title */}
|
||||||
|
<div className="text-center py-4">
|
||||||
|
<h1 className="text-sm font-black text-white uppercase tracking-widest">
|
||||||
|
LOGIN
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Form */}
|
||||||
|
<div className="px-8 pb-6 space-y-4">
|
||||||
|
{/* Phone Number */}
|
||||||
<div>
|
<div>
|
||||||
<label className="text-[11px] font-semibold text-muted-foreground uppercase block mb-1">Password</label>
|
<label className="block text-[11px] font-semibold text-white/80 mb-1">
|
||||||
|
Phone Number
|
||||||
|
</label>
|
||||||
|
<div className="flex">
|
||||||
|
<span className="flex items-center justify-center bg-white text-[#333] text-[12px] font-bold px-3 border border-gray-300 whitespace-nowrap">
|
||||||
|
ET +251
|
||||||
|
</span>
|
||||||
|
<input
|
||||||
|
type="tel"
|
||||||
|
value={phone}
|
||||||
|
onChange={(e) => 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]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Password */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-[11px] font-semibold text-white/80 mb-1">
|
||||||
|
Password
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
placeholder="Enter password"
|
placeholder="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"
|
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]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between text-[11px]">
|
|
||||||
<label className="flex items-center gap-2 text-muted-foreground cursor-pointer">
|
{/* Forgot password */}
|
||||||
<input type="checkbox" className="rounded" />
|
<div className="text-right">
|
||||||
Remember me
|
<Link
|
||||||
</label>
|
href="/reset-password"
|
||||||
<Link href="/reset-password" className="text-primary hover:underline">
|
className="text-[11px] text-white/50 hover:text-[#ff9800] transition-colors"
|
||||||
|
>
|
||||||
Forgot password?
|
Forgot password?
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<button className="w-full bg-primary text-primary-foreground font-bold py-2.5 rounded uppercase text-sm hover:opacity-90 transition-opacity">
|
|
||||||
Login
|
{/* Login button */}
|
||||||
|
<button className="w-full bg-[#e6b800] hover:bg-[#ffcc00] text-black font-black py-3 uppercase text-sm tracking-widest transition-colors">
|
||||||
|
LOGIN
|
||||||
</button>
|
</button>
|
||||||
<p className="text-center text-[11px] text-muted-foreground">
|
|
||||||
|
{/* Support link */}
|
||||||
|
<p className="text-center text-[12px] text-white/60">
|
||||||
|
<Link href="/support" className="hover:text-white transition-colors">
|
||||||
|
Support
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Register link */}
|
||||||
|
<p className="text-center text-[11px] text-white/50">
|
||||||
Don't have an account?{" "}
|
Don't have an account?{" "}
|
||||||
<Link href="/register" className="text-primary hover:underline font-semibold">
|
<Link href="/register" className="text-[#ff9800] hover:underline font-semibold">
|
||||||
Register now
|
Register
|
||||||
</Link>
|
</Link>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -2,107 +2,147 @@
|
||||||
|
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
|
import { useRouter } from "next/navigation"
|
||||||
|
|
||||||
export default function RegisterPage() {
|
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 (
|
return (
|
||||||
<div className="max-w-md mx-auto mt-8">
|
/* Full-screen dark backdrop */
|
||||||
<div className="bg-card border border-border rounded-lg overflow-hidden">
|
<div
|
||||||
<div className="bg-primary px-6 py-4">
|
className="fixed inset-0 z-50 flex items-center justify-center bg-black/70"
|
||||||
<h1 className="text-base font-black text-primary-foreground uppercase tracking-wide">Create Account</h1>
|
onClick={() => router.back()}
|
||||||
<p className="text-[11px] text-primary-foreground/70 mt-0.5">Join Harifsport today</p>
|
>
|
||||||
|
{/* Modal card — stop clicks propagating to backdrop */}
|
||||||
|
<div
|
||||||
|
className="relative w-[420px] bg-[#3a3a3a] shadow-2xl"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
{/* Close button */}
|
||||||
|
<button
|
||||||
|
onClick={() => router.back()}
|
||||||
|
className="absolute top-2 right-3 text-white/60 hover:text-white text-xl leading-none transition-colors z-10"
|
||||||
|
aria-label="Close"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Logo */}
|
||||||
|
<div className="flex items-center justify-center py-5 border-b border-white/10 bg-[#2e2e2e]">
|
||||||
|
<div className="flex items-center">
|
||||||
|
{/* Red slashes */}
|
||||||
|
<div className="flex gap-[3px] mr-2">
|
||||||
|
{[0, 1, 2].map((i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className="w-[5px] h-[38px] bg-[#cc2222] -skew-x-12"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{/* HARIF box */}
|
||||||
|
<div className="bg-[#852222] px-3 py-1 -skew-x-12 flex items-center h-[38px]">
|
||||||
|
<span className="text-2xl font-black text-white italic tracking-tighter skew-x-12 inline-block leading-none">
|
||||||
|
HARIF
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{/* SPORT text */}
|
||||||
|
<span className="text-2xl font-black text-[#ff9800] italic tracking-tighter ml-1 leading-none">
|
||||||
|
SPORT
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Steps */}
|
{/* Title */}
|
||||||
<div className="flex border-b border-border">
|
<div className="text-center py-4">
|
||||||
{["Personal Info", "Account Details", "Verification"].map((s, i) => (
|
<h1 className="text-sm font-black text-white uppercase tracking-widest">
|
||||||
<div
|
REGISTER
|
||||||
key={s}
|
</h1>
|
||||||
className={`flex-1 py-2 text-center text-[10px] font-semibold uppercase transition-colors ${
|
|
||||||
i + 1 === step ? "bg-muted text-primary border-b-2 border-primary" :
|
|
||||||
i + 1 < step ? "text-muted-foreground bg-primary/5" : "text-muted-foreground"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{i + 1}. {s}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="p-6 space-y-4">
|
{/* Form */}
|
||||||
{step === 1 && (
|
<div className="px-8 pb-6 space-y-4">
|
||||||
<>
|
{/* Phone Number */}
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div>
|
||||||
<div>
|
<label className="block text-[11px] font-semibold text-white/80 mb-1">
|
||||||
<label className="text-[11px] font-semibold text-muted-foreground uppercase block mb-1">First Name</label>
|
Phone Number
|
||||||
<input type="text" placeholder="First name" 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" />
|
</label>
|
||||||
</div>
|
<div className="flex">
|
||||||
<div>
|
<span className="flex items-center justify-center bg-white text-[#333] text-[12px] font-bold px-3 border border-gray-300 whitespace-nowrap">
|
||||||
<label className="text-[11px] font-semibold text-muted-foreground uppercase block mb-1">Last Name</label>
|
ET +251
|
||||||
<input type="text" placeholder="Last name" 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" />
|
</span>
|
||||||
</div>
|
<input
|
||||||
</div>
|
type="tel"
|
||||||
<div>
|
value={phone}
|
||||||
<label className="text-[11px] font-semibold text-muted-foreground uppercase block mb-1">Phone Number</label>
|
onChange={(e) => setPhone(e.target.value)}
|
||||||
<input type="tel" placeholder="+251 xxx xxx xxxx" 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" />
|
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]"
|
||||||
</div>
|
/>
|
||||||
<div>
|
|
||||||
<label className="text-[11px] font-semibold text-muted-foreground uppercase block mb-1">Date of Birth</label>
|
|
||||||
<input type="date" className="w-full bg-input border border-border rounded px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary" />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{step === 2 && (
|
|
||||||
<>
|
|
||||||
<div>
|
|
||||||
<label className="text-[11px] font-semibold text-muted-foreground uppercase block mb-1">Username</label>
|
|
||||||
<input type="text" placeholder="Choose a 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" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className="text-[11px] font-semibold text-muted-foreground uppercase block mb-1">Email</label>
|
|
||||||
<input type="email" placeholder="your@email.com" 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" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className="text-[11px] font-semibold text-muted-foreground uppercase block mb-1">Password</label>
|
|
||||||
<input type="password" placeholder="Min 8 characters" 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" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className="text-[11px] font-semibold text-muted-foreground uppercase block mb-1">Confirm Password</label>
|
|
||||||
<input type="password" placeholder="Repeat 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" />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{step === 3 && (
|
|
||||||
<div className="text-center py-4">
|
|
||||||
<div className="text-4xl mb-3">✅</div>
|
|
||||||
<h3 className="font-bold text-foreground mb-2">Account Created!</h3>
|
|
||||||
<p className="text-[11px] text-muted-foreground">Please verify your phone number to start betting.</p>
|
|
||||||
<input type="text" placeholder="Enter OTP code" className="mt-4 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 text-center tracking-widest" maxLength={6} />
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="flex gap-3">
|
|
||||||
{step > 1 && (
|
|
||||||
<button onClick={() => setStep(step - 1)} className="flex-1 bg-secondary text-secondary-foreground font-semibold py-2.5 rounded uppercase text-sm hover:bg-muted transition-colors">
|
|
||||||
Back
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
<button
|
|
||||||
onClick={() => step < 3 ? setStep(step + 1) : null}
|
|
||||||
className="flex-1 bg-primary text-primary-foreground font-bold py-2.5 rounded uppercase text-sm hover:opacity-90 transition-opacity"
|
|
||||||
>
|
|
||||||
{step === 3 ? "Verify & Start" : "Next"}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{step === 1 && (
|
{/* Password */}
|
||||||
<p className="text-center text-[11px] text-muted-foreground">
|
<div>
|
||||||
Already have an account?{" "}
|
<label className="block text-[11px] font-semibold text-white/80 mb-1">
|
||||||
<Link href="/login" className="text-primary hover:underline font-semibold">Login</Link>
|
Password
|
||||||
</p>
|
</label>
|
||||||
)}
|
<input
|
||||||
|
type="password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => 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]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Repeat Password */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-[11px] font-semibold text-white/80 mb-1">
|
||||||
|
Repeat Password
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
value={repeatPassword}
|
||||||
|
onChange={(e) => 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]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Age confirmation */}
|
||||||
|
<label className="flex items-center gap-2 cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={ageConfirmed}
|
||||||
|
onChange={(e) => setAgeConfirmed(e.target.checked)}
|
||||||
|
className="w-4 h-4 accent-[#ff9800]"
|
||||||
|
/>
|
||||||
|
<span className="text-[12px] text-white/80">
|
||||||
|
I confirm I'm over 21 years old
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
{/* Register button */}
|
||||||
|
<button className="w-full bg-[#e6b800] hover:bg-[#ffcc00] text-black font-black py-3 uppercase text-sm tracking-widest transition-colors">
|
||||||
|
REGISTER
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Support link */}
|
||||||
|
<p className="text-center text-[12px] text-white/60">
|
||||||
|
<Link href="/support" className="hover:text-white transition-colors">
|
||||||
|
Support
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Login link */}
|
||||||
|
<p className="text-center text-[11px] text-white/50">
|
||||||
|
Already have an account?{" "}
|
||||||
|
<Link href="/login" className="text-[#ff9800] hover:underline font-semibold">
|
||||||
|
Login
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
215
components/layout/auth-modal.tsx
Normal file
215
components/layout/auth-modal.tsx
Normal file
|
|
@ -0,0 +1,215 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import { useState, useEffect, useCallback } from "react"
|
||||||
|
import Link from "next/link"
|
||||||
|
|
||||||
|
type Mode = "login" | "register"
|
||||||
|
|
||||||
|
function Logo() {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center">
|
||||||
|
{/* Red slashes */}
|
||||||
|
<div className="flex gap-[3px] mr-2">
|
||||||
|
{[0, 1, 2].map((i) => (
|
||||||
|
<div key={i} className="w-[5px] h-[38px] bg-[#cc2222] -skew-x-12" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{/* HARIF box */}
|
||||||
|
<div className="bg-[#852222] px-3 py-1 -skew-x-12 flex items-center h-[38px]">
|
||||||
|
<span className="text-2xl font-black text-white italic tracking-tighter skew-x-12 inline-block leading-none">
|
||||||
|
HARIF
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{/* SPORT text */}
|
||||||
|
<span className="text-2xl font-black text-[#ff9800] italic tracking-tighter ml-1 leading-none">
|
||||||
|
SPORT
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AuthModalProps {
|
||||||
|
open: boolean
|
||||||
|
defaultMode: Mode
|
||||||
|
onClose: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AuthModal({ open, defaultMode, onClose }: AuthModalProps) {
|
||||||
|
const [mode, setMode] = useState<Mode>(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 (
|
||||||
|
<div
|
||||||
|
className="fixed inset-0 z-[9999] flex items-center justify-center bg-black/70"
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="relative w-[420px] bg-[#3a3a3a] shadow-2xl animate-fade-in"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
{/* Close button */}
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="absolute top-2 right-3 text-white/60 hover:text-white text-xl leading-none transition-colors z-10"
|
||||||
|
aria-label="Close"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Logo */}
|
||||||
|
<div className="flex items-center justify-center py-5 border-b border-white/10 bg-[#2e2e2e]">
|
||||||
|
<Logo />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Title */}
|
||||||
|
<div className="text-center py-4">
|
||||||
|
<h2 className="text-sm font-black text-white uppercase tracking-widest">
|
||||||
|
{mode === "login" ? "LOGIN" : "REGISTER"}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Form */}
|
||||||
|
<div className="px-8 pb-6 space-y-4">
|
||||||
|
{/* Phone Number */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-[11px] font-semibold text-white/80 mb-1">
|
||||||
|
Phone Number
|
||||||
|
</label>
|
||||||
|
<div className="flex">
|
||||||
|
<span className="flex items-center justify-center bg-white text-[#333] text-[12px] font-bold px-3 border border-gray-300 whitespace-nowrap select-none">
|
||||||
|
ET +251
|
||||||
|
</span>
|
||||||
|
<input
|
||||||
|
type="tel"
|
||||||
|
value={phone}
|
||||||
|
onChange={(e) => 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]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Password */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-[11px] font-semibold text-white/80 mb-1">
|
||||||
|
Password
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => 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]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Repeat Password (register only) */}
|
||||||
|
{mode === "register" && (
|
||||||
|
<div>
|
||||||
|
<label className="block text-[11px] font-semibold text-white/80 mb-1">
|
||||||
|
Repeat Password
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
value={repeatPassword}
|
||||||
|
onChange={(e) => 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]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Age confirmation (register only) */}
|
||||||
|
{mode === "register" && (
|
||||||
|
<label className="flex items-center gap-2 cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={ageConfirmed}
|
||||||
|
onChange={(e) => setAgeConfirmed(e.target.checked)}
|
||||||
|
className="w-4 h-4 accent-[#ff9800]"
|
||||||
|
/>
|
||||||
|
<span className="text-[12px] text-white/80">
|
||||||
|
I confirm I'm over 21 years old
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Forgot password (login only) */}
|
||||||
|
{mode === "login" && (
|
||||||
|
<div className="text-right -mt-2">
|
||||||
|
<Link
|
||||||
|
href="/reset-password"
|
||||||
|
className="text-[11px] text-white/50 hover:text-[#ff9800] transition-colors"
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
Forgot password?
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Submit button */}
|
||||||
|
<button className="w-full bg-[#e6b800] hover:bg-[#ffcc00] text-black font-black py-3 uppercase text-sm tracking-widest transition-colors">
|
||||||
|
{mode === "login" ? "LOGIN" : "REGISTER"}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Support link */}
|
||||||
|
<p className="text-center text-[12px] text-white/60">
|
||||||
|
<Link
|
||||||
|
href="/support"
|
||||||
|
className="hover:text-white transition-colors"
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
Support
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Toggle mode */}
|
||||||
|
<p className="text-center text-[11px] text-white/50">
|
||||||
|
{mode === "login" ? (
|
||||||
|
<>
|
||||||
|
Don't have an account?{" "}
|
||||||
|
<button
|
||||||
|
className="text-[#ff9800] hover:underline font-semibold"
|
||||||
|
onClick={() => setMode("register")}
|
||||||
|
>
|
||||||
|
Register
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
Already have an account?{" "}
|
||||||
|
<button
|
||||||
|
className="text-[#ff9800] hover:underline font-semibold"
|
||||||
|
onClick={() => setMode("login")}
|
||||||
|
>
|
||||||
|
Login
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,24 +1,45 @@
|
||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { usePathname } from "next/navigation"
|
import { usePathname } from "next/navigation"
|
||||||
|
import { useState } from "react"
|
||||||
import { SiteHeader } from "@/components/layout/site-header"
|
import { SiteHeader } from "@/components/layout/site-header"
|
||||||
import { SportsSidebar } from "@/components/layout/sports-sidebar"
|
import { SportsSidebar } from "@/components/layout/sports-sidebar"
|
||||||
import { RightPanel } from "@/components/layout/right-panel"
|
import { RightPanel } from "@/components/layout/right-panel"
|
||||||
import { SiteFooter } from "@/components/layout/site-footer"
|
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 }) {
|
export default function LayoutClientWrapper({ children }: { children: React.ReactNode }) {
|
||||||
const pathname = usePathname()
|
const pathname = usePathname()
|
||||||
const isLivePage = pathname === "/live"
|
const isLivePage = pathname === "/live"
|
||||||
|
|
||||||
|
const [authOpen, setAuthOpen] = useState(false)
|
||||||
|
const [authMode, setAuthMode] = useState<AuthMode>("login")
|
||||||
|
|
||||||
|
function openAuth(mode: AuthMode) {
|
||||||
|
setAuthMode(mode)
|
||||||
|
setAuthOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen flex-col">
|
<div className="flex min-h-screen flex-col">
|
||||||
<SiteHeader />
|
<SiteHeader
|
||||||
|
onLoginClick={() => openAuth("login")}
|
||||||
|
onRegisterClick={() => openAuth("register")}
|
||||||
|
/>
|
||||||
<div className="flex w-full flex-1 gap-0">
|
<div className="flex w-full flex-1 gap-0">
|
||||||
{!isLivePage && <SportsSidebar />}
|
{!isLivePage && <SportsSidebar />}
|
||||||
<main className="flex-1 px-2 py-3 min-w-0">{children}</main>
|
<main className="flex-1 px-2 py-3 min-w-0">{children}</main>
|
||||||
<RightPanel />
|
<RightPanel />
|
||||||
</div>
|
</div>
|
||||||
<SiteFooter />
|
<SiteFooter />
|
||||||
|
|
||||||
|
<AuthModal
|
||||||
|
open={authOpen}
|
||||||
|
defaultMode={authMode}
|
||||||
|
onClose={() => setAuthOpen(false)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,12 @@ const extraNavItems = [
|
||||||
{ href: "/promo", label: "PROMO" },
|
{ href: "/promo", label: "PROMO" },
|
||||||
]
|
]
|
||||||
|
|
||||||
export function SiteHeader() {
|
interface SiteHeaderProps {
|
||||||
|
onLoginClick?: () => void
|
||||||
|
onRegisterClick?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SiteHeader({ onLoginClick, onRegisterClick }: SiteHeaderProps) {
|
||||||
const pathname = usePathname()
|
const pathname = usePathname()
|
||||||
const isLivePage = pathname === "/live"
|
const isLivePage = pathname === "/live"
|
||||||
const [time, setTime] = useState("")
|
const [time, setTime] = useState("")
|
||||||
|
|
@ -43,7 +48,7 @@ export function SiteHeader() {
|
||||||
<div className="bg-[#1a1a1a] px-3 py-1 flex items-center justify-between text-[11px] text-white">
|
<div className="bg-[#1a1a1a] px-3 py-1 flex items-center justify-between text-[11px] text-white">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<button className="flex items-center gap-1.5 hover:text-primary transition-colors">
|
<button className="flex items-center gap-1.5 hover:text-primary transition-colors">
|
||||||
<img src="https://flagcdn.com/w20/gb.png" srcSet="https://flagcdn.com/w40/gb.png 2x" width="16" alt="English" className="rounded-sm" />
|
<img src="https://flagcdn.com/w20/gb.png" width="16" alt="English" className="rounded-sm" />
|
||||||
<span className="font-bold flex items-center gap-1">en <span className="text-[8px]">▼</span></span>
|
<span className="font-bold flex items-center gap-1">en <span className="text-[8px]">▼</span></span>
|
||||||
</button>
|
</button>
|
||||||
<div className="flex items-center gap-2 font-bold">
|
<div className="flex items-center gap-2 font-bold">
|
||||||
|
|
@ -57,11 +62,17 @@ export function SiteHeader() {
|
||||||
<Input type="password" placeholder="Password" className="bg-[#2a2a2a] border-none px-2 py-0.5 w-32 text-[11px] h-7 rounded-none shadow-none text-white placeholder:text-gray-500" />
|
<Input type="password" placeholder="Password" className="bg-[#2a2a2a] border-none px-2 py-0.5 w-32 text-[11px] h-7 rounded-none shadow-none text-white placeholder:text-gray-500" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<Button asChild className="bg-[#e67e22] text-white hover:bg-[#d35400] px-5 h-7 text-[11px] font-bold rounded-none uppercase">
|
<Button
|
||||||
<Link href="/login">Login</Link>
|
onClick={onLoginClick}
|
||||||
|
className="bg-[#e67e22] text-white hover:bg-[#d35400] px-5 h-7 text-[11px] font-bold rounded-none uppercase"
|
||||||
|
>
|
||||||
|
Login
|
||||||
</Button>
|
</Button>
|
||||||
<Button asChild className="bg-[#d35400] text-white hover:bg-[#c0392b] px-5 h-7 text-[11px] font-bold rounded-none uppercase">
|
<Button
|
||||||
<Link href="/register">Sign Up</Link>
|
onClick={onRegisterClick}
|
||||||
|
className="bg-[#d35400] text-white hover:bg-[#c0392b] px-5 h-7 text-[11px] font-bold rounded-none uppercase"
|
||||||
|
>
|
||||||
|
Sign Up
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user