Compare commits

..

No commits in common. "6d475650dad8e1701acd8343c10365c6a7fef84d" and "6fa5ba58d7740316c3570eff1696bf40094707ef" have entirely different histories.

39 changed files with 393 additions and 3163 deletions

View File

@ -1,69 +1,6 @@
"use client"
import { useState } from "react"
export default function CheckTicketPage() {
const [ticketId, setTicketId] = useState("")
const [result, setResult] = useState<null | object>(null)
const handleCheck = () => {
if (!ticketId.trim()) return
setResult({
id: ticketId,
event: "Arsenal vs Manchester City",
selection: "Arsenal Win",
odds: "2.80",
stake: "100 ETB",
potentialWin: "280 ETB",
status: "pending",
date: "2026-02-19 17:30",
})
}
return (
<div className="max-w-md mx-auto mt-8 space-y-4">
<div className="border-b border-border pb-2">
<h1 className="text-sm font-black uppercase tracking-wide text-foreground">Check Ticket</h1>
</div>
<div className="bg-card border border-border rounded-lg p-6 space-y-4">
<div>
<label className="text-[11px] font-semibold text-muted-foreground uppercase block mb-2">Ticket ID / Bet Code</label>
<div className="flex gap-2">
<input
type="text"
value={ticketId}
onChange={(e) => setTicketId(e.target.value)}
placeholder="Enter ticket ID or bet code"
className="flex-1 bg-input border border-border rounded px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:border-primary"
onKeyDown={(e) => e.key === "Enter" && handleCheck()}
/>
<button
onClick={handleCheck}
className="bg-primary text-primary-foreground font-bold px-4 py-2 rounded uppercase text-sm hover:opacity-90 transition-opacity"
>
Check
</button>
</div>
</div>
{result && (
<div className="border border-border rounded overflow-hidden">
<div className="bg-secondary px-3 py-2 flex justify-between">
<span className="text-[11px] font-bold uppercase text-foreground">Ticket Details</span>
<span className="text-[10px] text-yellow-500 font-bold uppercase bg-yellow-500/20 px-2 py-0.5 rounded">Pending</span>
</div>
<div className="p-3 space-y-1.5 text-[11px]">
{Object.entries(result as Record<string, string>).filter(([k]) => k !== "status").map(([key, val]) => (
<div key={key} className="flex justify-between">
<span className="text-muted-foreground capitalize">{key.replace(/([A-Z])/g, ' $1')}:</span>
<span className="text-foreground font-medium">{val}</span>
</div>
))}
</div>
</div>
)}
</div>
</div>
<div className="text-sm text-foreground">Check ticket page placeholder</div>
)
}
}

View File

@ -1,94 +1,6 @@
"use client"
import { useState } from "react"
const methods = [
{ id: "telebirr", name: "Telebirr", icon: "📱", desc: "Ethiopian mobile payment" },
{ id: "cbe", name: "CBE Birr", icon: "🏦", desc: "Commercial Bank of Ethiopia" },
{ id: "awash", name: "Awash Bank", icon: "🏛️", desc: "Awash Bank transfer" },
{ id: "abyssinia", name: "Bank of Abyssinia", icon: "💳", desc: "Bank of Abyssinia" },
]
const quickAmounts = [50, 100, 200, 500, 1000, 2000]
export default function DepositPage() {
const [method, setMethod] = useState("telebirr")
const [amount, setAmount] = useState("")
return (
<div className="max-w-md mx-auto mt-8">
<div className="bg-card border border-border rounded-lg overflow-hidden">
<div className="bg-primary px-6 py-4">
<h1 className="text-base font-black text-primary-foreground uppercase tracking-wide">Deposit</h1>
<p className="text-[11px] text-primary-foreground/70 mt-0.5">Add funds to your account</p>
</div>
<div className="p-6 space-y-5">
{/* Balance */}
<div className="bg-secondary/50 border border-border rounded p-3 flex justify-between items-center">
<span className="text-[11px] text-muted-foreground uppercase font-semibold">Current Balance</span>
<span className="font-bold text-primary text-lg">0.00 ETB</span>
</div>
{/* Payment methods */}
<div>
<label className="text-[11px] font-semibold text-muted-foreground uppercase block mb-2">Payment Method</label>
<div className="grid grid-cols-2 gap-2">
{methods.map((m) => (
<button
key={m.id}
onClick={() => setMethod(m.id)}
className={`flex items-center gap-2 p-2.5 rounded border transition-all text-left ${
method === m.id
? "border-primary bg-primary/10 text-foreground"
: "border-border bg-secondary/30 text-muted-foreground hover:border-primary/50"
}`}
>
<span className="text-xl">{m.icon}</span>
<div>
<div className="text-[11px] font-semibold">{m.name}</div>
<div className="text-[10px] opacity-70">{m.desc}</div>
</div>
</button>
))}
</div>
</div>
{/* Amount */}
<div>
<label className="text-[11px] font-semibold text-muted-foreground uppercase block mb-2">Amount (ETB)</label>
<input
type="number"
value={amount}
onChange={(e) => setAmount(e.target.value)}
placeholder="Enter amount"
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"
min="50"
/>
<div className="flex flex-wrap gap-1.5 mt-2">
{quickAmounts.map((a) => (
<button
key={a}
onClick={() => setAmount(String(a))}
className={`text-[11px] px-2.5 py-1 rounded border transition-colors font-semibold ${
amount === String(a)
? "bg-primary text-primary-foreground border-primary"
: "border-border text-muted-foreground hover:border-primary hover:text-primary"
}`}
>
{a} ETB
</button>
))}
</div>
</div>
<p className="text-[10px] text-muted-foreground">Min deposit: 50 ETB · Max deposit: 50,000 ETB</p>
<button className="w-full bg-primary text-primary-foreground font-bold py-3 rounded uppercase text-sm hover:opacity-90 transition-opacity">
Deposit {amount ? `${amount} ETB` : ""}
</button>
</div>
</div>
</div>
<div className="text-sm text-foreground">Deposit page placeholder</div>
)
}
}

View File

@ -42,183 +42,85 @@
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
/* Brand Theme Parameters */
--color-brand-primary: var(--brand-primary);
--color-brand-primary-hover: var(--brand-primary-hover);
--color-brand-secondary: var(--brand-secondary);
--color-brand-bg: var(--brand-bg);
--color-brand-surface: var(--brand-surface);
--color-brand-surface-light: var(--brand-surface-light);
--color-brand-accent: var(--brand-accent);
--color-brand-live: var(--brand-live);
--radius-2xl: calc(var(--radius) + 8px);
--radius-3xl: calc(var(--radius) + 12px);
--radius-4xl: calc(var(--radius) + 16px);
}
:root {
--radius: 0rem;
/* Brand Colors (Black, Yellow, White theme) */
--brand-primary: #ff9800;
--brand-primary-hover: #e68900;
--brand-secondary: #ff9800;
/* Usually same as primary for now */
--brand-bg: #121212;
--brand-surface: #1a1a1a;
--brand-surface-light: #2a2a2a;
--brand-accent: #852222;
/* Maroon */
--brand-live: #ff3b3b;
/* Shadcn default mappings */
--background: var(--brand-bg);
--foreground: #ffffff;
--card: #1e1e1e;
--card-foreground: #ffffff;
--popover: #1e1e1e;
--popover-foreground: #ffffff;
--primary: var(--brand-primary);
--primary-foreground: #ffffff;
--secondary: #222222;
--secondary-foreground: #a0a0a0;
--muted: #1a1a1a;
--muted-foreground: #808080;
--accent: var(--brand-primary);
--accent-foreground: #121212;
--destructive: #ef4444;
--border: #2a2a2a;
--input: #222222;
--ring: var(--brand-primary);
--radius: 0.625rem;
--background: oklch(1 0 0);
--foreground: oklch(0.141 0.005 285.823);
--card: oklch(1 0 0);
--card-foreground: oklch(0.141 0.005 285.823);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.141 0.005 285.823);
--primary: oklch(0.21 0.006 285.885);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.967 0.001 286.375);
--secondary-foreground: oklch(0.21 0.006 285.885);
--muted: oklch(0.967 0.001 286.375);
--muted-foreground: oklch(0.552 0.016 285.938);
--accent: oklch(0.967 0.001 286.375);
--accent-foreground: oklch(0.21 0.006 285.885);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.92 0.004 286.32);
--input: oklch(0.92 0.004 286.32);
--ring: oklch(0.705 0.015 286.067);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.141 0.005 285.823);
--sidebar-primary: oklch(0.21 0.006 285.885);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.967 0.001 286.375);
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
--sidebar-border: oklch(0.92 0.004 286.32);
--sidebar-ring: oklch(0.705 0.015 286.067);
}
.dark {
--background: oklch(0.13 0.008 250);
--foreground: oklch(0.93 0.005 250);
--card: oklch(0.17 0.01 250);
--card-foreground: oklch(0.93 0.005 250);
--popover: oklch(0.17 0.01 250);
--popover-foreground: oklch(0.93 0.005 250);
--primary: oklch(0.55 0.18 145);
--primary-foreground: oklch(0.98 0 0);
--secondary: oklch(0.22 0.01 250);
--secondary-foreground: oklch(0.85 0.005 250);
--muted: oklch(0.2 0.008 250);
--muted-foreground: oklch(0.58 0.01 250);
--accent: oklch(0.55 0.18 145);
--accent-foreground: oklch(0.98 0 0);
--background: oklch(0.141 0.005 285.823);
--foreground: oklch(0.985 0 0);
--card: oklch(0.21 0.006 285.885);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.21 0.006 285.885);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.92 0.004 286.32);
--primary-foreground: oklch(0.21 0.006 285.885);
--secondary: oklch(0.274 0.006 286.033);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.274 0.006 286.033);
--muted-foreground: oklch(0.705 0.015 286.067);
--accent: oklch(0.274 0.006 286.033);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--border: oklch(0.25 0.01 250);
--input: oklch(0.22 0.01 250);
--ring: oklch(0.55 0.18 145);
--sidebar: oklch(0.15 0.01 250);
--sidebar-foreground: oklch(0.9 0.005 250);
--sidebar-primary: oklch(0.55 0.18 145);
--sidebar-primary-foreground: oklch(0.98 0 0);
--sidebar-accent: oklch(0.22 0.01 250);
--sidebar-accent-foreground: oklch(0.93 0.005 250);
--sidebar-border: oklch(0.25 0.01 250);
--sidebar-ring: oklch(0.55 0.18 145);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.552 0.016 285.938);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.21 0.006 285.885);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.274 0.006 286.033);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.552 0.016 285.938);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
font-size: 13px;
}
}
/* 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);
}
}
.odds-selected {
animation: odds-flash 0.4s ease;
}
/* Live pulse */
@keyframes live-pulse {
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;
}
/* Mobile drawer slide-in from left */
@keyframes slide-in-left {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0);
}
}
.animate-slide-in-left {
animation: slide-in-left 0.22s ease-out both;
}
/* Hide scrollbar for mobile nav */
.scrollbar-none {
-ms-overflow-style: none;
scrollbar-width: none;
}
.scrollbar-none::-webkit-scrollbar {
display: none;
}
/* Scrollbar */
::-webkit-scrollbar {
width: 4px;
height: 4px;
}
::-webkit-scrollbar-track {
background: var(--background);
}
::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 2px;
}

View File

@ -1,65 +1,6 @@
const mockHistory = [
{ id: "BET001", event: "Arsenal vs Man City", selection: "Arsenal Win", odds: 2.80, stake: 100, status: "won", date: "2026-02-18" },
{ id: "BET002", event: "Bayern vs Dortmund", selection: "Over 2.5 Goals", odds: 1.65, stake: 50, status: "lost", date: "2026-02-17" },
{ id: "BET003", event: "NBA: Lakers vs Celtics", selection: "Lakers ML", odds: 1.85, stake: 200, status: "pending", date: "2026-02-19" },
{ id: "BET004", event: "St. George vs Dire Dawa", selection: "St. George Win", odds: 1.65, stake: 500, status: "won", date: "2026-02-16" },
]
const statusStyles: Record<string, string> = {
won: "text-primary bg-primary/20",
lost: "text-destructive bg-destructive/20",
pending: "text-yellow-500 bg-yellow-500/20",
}
export default function HistoryPage() {
return (
<div className="space-y-4">
<div className="border-b border-border pb-2 flex items-center justify-between">
<h1 className="text-sm font-black uppercase tracking-wide text-foreground">Bet History</h1>
<span className="text-[11px] text-muted-foreground">Please login to see full history</span>
</div>
{/* Filter */}
<div className="flex gap-2">
{["All", "Won", "Lost", "Pending"].map((f) => (
<button key={f} className="px-3 py-1 text-[11px] font-semibold rounded border border-border text-muted-foreground hover:border-primary hover:text-primary transition-colors">
{f}
</button>
))}
</div>
<div className="bg-card border border-border rounded overflow-hidden">
<table className="w-full text-[11px]">
<thead>
<tr className="bg-secondary border-b border-border text-muted-foreground uppercase text-[10px]">
<th className="px-3 py-2 text-left font-semibold">Bet ID</th>
<th className="px-3 py-2 text-left font-semibold">Event</th>
<th className="px-3 py-2 text-left font-semibold">Selection</th>
<th className="px-3 py-2 text-right font-semibold">Odds</th>
<th className="px-3 py-2 text-right font-semibold">Stake</th>
<th className="px-3 py-2 text-center font-semibold">Status</th>
<th className="px-3 py-2 text-left font-semibold">Date</th>
</tr>
</thead>
<tbody className="divide-y divide-border">
{mockHistory.map((bet) => (
<tr key={bet.id} className="hover:bg-muted/30 transition-colors">
<td className="px-3 py-2 font-mono text-muted-foreground">{bet.id}</td>
<td className="px-3 py-2 font-medium text-foreground">{bet.event}</td>
<td className="px-3 py-2 text-muted-foreground">{bet.selection}</td>
<td className="px-3 py-2 text-right font-bold text-primary">{bet.odds.toFixed(2)}</td>
<td className="px-3 py-2 text-right">{bet.stake} ETB</td>
<td className="px-3 py-2 text-center">
<span className={`px-2 py-0.5 rounded text-[10px] font-bold uppercase ${statusStyles[bet.status]}`}>
{bet.status}
</span>
</td>
<td className="px-3 py-2 text-muted-foreground">{bet.date}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
<div className="text-sm text-foreground">History page placeholder</div>
)
}
}

View File

@ -1,7 +1,9 @@
import type { Metadata } from "next"
import { Geist, Geist_Mono } from "next/font/google"
import "./globals.css"
import LayoutClientWrapper from "@/components/layout/layout-client-wrapper"
import { SiteHeader } from "@/components/layout/site-header"
import { SportsSidebar } from "@/components/layout/sports-sidebar"
import { RightPanel } from "@/components/layout/right-panel"
const geistSans = Geist({
variable: "--font-geist-sans",
@ -14,8 +16,8 @@ const geistMono = Geist_Mono({
})
export const metadata: Metadata = {
title: "Harifsport - Sports Betting",
description: "Harifsport sportsbook - Live betting, in-play events, and more",
title: "Harifsport",
description: "Harifsport-style sportsbook interface built with Next.js",
}
export default function RootLayout({
@ -28,8 +30,16 @@ export default function RootLayout({
<body
className={`${geistSans.variable} ${geistMono.variable} bg-background text-foreground antialiased`}
>
<LayoutClientWrapper>{children}</LayoutClientWrapper>
<div className="flex min-h-screen flex-col bg-background">
<SiteHeader />
<div className="mx-auto flex w-full max-w-6xl flex-1 gap-0 px-2 py-3 lg:px-4">
<SportsSidebar />
<main className="flex-1 px-1 lg:px-3">{children}</main>
<RightPanel />
</div>
</div>
</body>
</html>
)
}
}

View File

@ -1,9 +1,6 @@
import { LiveEventsList } from "@/components/betting/live-events-list"
import { InPlayPage } from "@/components/betting/in-play-page"
export default function LivePage() {
return (
<div className="space-y-4">
<LiveEventsList />
</div>
)
return <InPlayPage />
}

View File

@ -1,131 +1,4 @@
"use client"
import { useState } from "react"
import Link from "next/link"
import { useRouter } from "next/navigation"
export default function LoginPage() {
const router = useRouter()
const [phone, setPhone] = useState("")
const [password, setPassword] = useState("")
return <div className="text-sm text-foreground">Login page placeholder</div>
}
return (
/* Full-screen dark backdrop */
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black/70"
onClick={() => router.back()}
>
{/* Modal card */}
<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>
{/* 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>
<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
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>
{/* Forgot password */}
<div className="text-right">
<Link
href="/reset-password"
className="text-[11px] text-white/50 hover:text-[#ff9800] transition-colors"
>
Forgot password?
</Link>
</div>
{/* 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>
{/* 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&apos;t have an account?{" "}
<Link href="/register" className="text-[#ff9800] hover:underline font-semibold">
Register
</Link>
</p>
</div>
</div>
</div>
)
}

View File

@ -1,6 +1,6 @@
import { SportHome } from "@/components/betting/sport-home"
import { InPlayPage } from "@/components/betting/in-play-page"
export default function Home() {
return <SportHome />
return <InPlayPage />
}

View File

@ -1,50 +1,6 @@
import Link from "next/link"
export default function ProfilePage() {
return (
<div className="max-w-md mx-auto mt-8 space-y-4">
<div className="border-b border-border pb-2">
<h1 className="text-sm font-black uppercase tracking-wide text-foreground">My Profile</h1>
</div>
<div className="bg-card border border-border rounded-lg overflow-hidden">
{/* Avatar */}
<div className="bg-gradient-to-r from-primary/30 to-primary/10 px-6 py-6 flex items-center gap-4">
<div className="size-16 bg-primary/20 rounded-full flex items-center justify-center text-2xl border-2 border-primary/30">
👤
</div>
<div>
<div className="font-bold text-foreground">Guest User</div>
<div className="text-[11px] text-muted-foreground">Not logged in</div>
<Link href="/login" className="inline-block mt-2 bg-primary text-primary-foreground text-[11px] font-bold px-3 py-1 rounded hover:opacity-90">
Login
</Link>
</div>
</div>
{/* Menu items */}
<div className="divide-y divide-border">
{[
{ icon: "🏆", label: "My Bets", href: "/history" },
{ icon: "💰", label: "Deposit", href: "/deposit" },
{ icon: "🎁", label: "Bonus", href: "/bonus" },
{ icon: "🔔", label: "Notifications", href: "/notifications" },
{ icon: "📋", label: "Rules", href: "/rules" },
].map((item) => (
<Link
key={item.label}
href={item.href}
className="flex items-center justify-between px-4 py-3 hover:bg-muted transition-colors group"
>
<div className="flex items-center gap-3">
<span className="text-lg">{item.icon}</span>
<span className="text-[12px] font-medium text-foreground">{item.label}</span>
</div>
<span className="text-muted-foreground group-hover:text-primary transition-colors"></span>
</Link>
))}
</div>
</div>
</div>
<div className="text-sm text-foreground">Profile page placeholder</div>
)
}
}

View File

@ -1,60 +1,6 @@
const promotions = [
{
id: 1,
title: "Welcome Bonus",
description: "Get 100% up to 500 ETB on your first deposit",
badge: "NEW",
color: "from-primary/20 to-primary/5",
icon: "🎁",
},
{
id: 2,
title: "Accumulator Boost",
description: "Get up to 50% extra winnings on accumulators of 5+ selections",
badge: "POPULAR",
color: "from-yellow-500/20 to-yellow-500/5",
icon: "⚡",
},
{
id: 3,
title: "Live Bet Cashback",
description: "5% cashback on all losing live bets, credited every Monday",
badge: "",
color: "from-blue-500/20 to-blue-500/5",
icon: "🔄",
},
]
export default function PromotionsPage() {
return (
<div className="space-y-4">
<div className="border-b border-border pb-2">
<h1 className="text-sm font-black uppercase tracking-wide text-foreground">Promotions</h1>
</div>
<div className="grid gap-3">
{promotions.map((promo) => (
<div
key={promo.id}
className={`bg-gradient-to-r ${promo.color} border border-border rounded-lg p-4 flex items-center gap-4`}
>
<div className="text-4xl">{promo.icon}</div>
<div className="flex-1">
<div className="flex items-center gap-2 mb-1">
<h3 className="font-bold text-sm text-foreground">{promo.title}</h3>
{promo.badge && (
<span className="bg-primary text-primary-foreground text-[10px] font-bold px-1.5 py-0.5 rounded">
{promo.badge}
</span>
)}
</div>
<p className="text-[11px] text-muted-foreground">{promo.description}</p>
</div>
<button className="shrink-0 bg-primary text-primary-foreground text-[11px] font-bold px-4 py-2 rounded hover:opacity-90 transition-opacity uppercase">
Claim
</button>
</div>
))}
</div>
</div>
<div className="text-sm text-foreground">Promotions page placeholder</div>
)
}
}

View File

@ -1,58 +1,4 @@
"use client"
import { useState } from "react"
export default function RafflePage() {
const [tickets, setTickets] = useState(0)
return <div className="text-sm text-foreground">Raffle page placeholder</div>
}
return (
<div className="max-w-lg mx-auto mt-8 space-y-4">
<div className="border-b border-border pb-2">
<h1 className="text-sm font-black uppercase tracking-wide text-foreground">Raffle</h1>
</div>
{/* Current raffle */}
<div className="bg-gradient-to-br from-primary/20 to-card border border-primary/30 rounded-xl p-6 text-center">
<div className="text-5xl mb-3">🎰</div>
<h2 className="text-lg font-black text-foreground mb-1">Weekend Mega Raffle</h2>
<p className="text-[11px] text-muted-foreground mb-4">Prize pool: 100,000 ETB · Draw: Saturday 20:00</p>
<div className="bg-black/20 rounded-lg p-4 mb-4">
<div className="text-[10px] text-muted-foreground mb-1">Your tickets</div>
<div className="text-3xl font-black text-primary">{tickets}</div>
</div>
<div className="text-[11px] text-muted-foreground mb-4">
Earn 1 raffle ticket for every 100 ETB wagered
</div>
<a href="/deposit" className="inline-block w-full bg-primary text-primary-foreground font-bold py-3 rounded uppercase hover:opacity-90 transition-opacity">
Get More Tickets
</a>
</div>
{/* Past winners */}
<div className="bg-card border border-border rounded-lg overflow-hidden">
<div className="bg-secondary px-4 py-2 text-[11px] font-bold uppercase text-muted-foreground">Past Winners</div>
<div className="divide-y divide-border">
{[
{ name: "John D.", prize: "50,000 ETB", date: "Feb 15" },
{ name: "Sarah M.", prize: "20,000 ETB", date: "Feb 8" },
{ name: "Abebe K.", prize: "30,000 ETB", date: "Feb 1" },
].map((winner, i) => (
<div key={i} className="flex items-center justify-between px-4 py-3">
<div className="flex items-center gap-3">
<span className="text-lg">🏆</span>
<div>
<div className="text-[12px] font-semibold text-foreground">{winner.name}</div>
<div className="text-[10px] text-muted-foreground">{winner.date}</div>
</div>
</div>
<span className="text-primary font-bold text-[12px]">{winner.prize}</span>
</div>
))}
</div>
</div>
</div>
)
}

View File

@ -1,150 +1,6 @@
"use client"
import { useState } from "react"
import Link from "next/link"
import { useRouter } from "next/navigation"
export default function RegisterPage() {
const router = useRouter()
const [phone, setPhone] = useState("")
const [password, setPassword] = useState("")
const [repeatPassword, setRepeatPassword] = useState("")
const [ageConfirmed, setAgeConfirmed] = useState(false)
return (
/* Full-screen dark backdrop */
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black/70"
onClick={() => router.back()}
>
{/* 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>
{/* Title */}
<div className="text-center py-4">
<h1 className="text-sm font-black text-white uppercase tracking-widest">
REGISTER
</h1>
</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">
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
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&apos;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 className="text-sm text-foreground">Register page placeholder</div>
)
}
}

View File

@ -1,48 +1,4 @@
const rules = [
{
title: "General Betting Rules",
content: "All bets are subject to Harifsport terms and conditions. By placing a bet, you agree to abide by these rules. The minimum bet amount is 5 ETB and the maximum payout is 500,000 ETB per bet.",
},
{
title: "Live Betting",
content: "Live bets are accepted subject to availability. Odds may change at any time during live events. Bets placed during network disruptions may be voided at management's discretion.",
},
{
title: "Void Bets",
content: "Bets may be voided in cases of obvious error in odds, match postponement/cancellation, or system malfunction. Voided bets are returned to the account at stake value.",
},
{
title: "Responsible Gambling",
content: "Harifsport is committed to responsible gambling. Users may set deposit limits, loss limits, or self-exclude at any time. Gambling should be entertaining, not a source of income.",
},
{
title: "Account Rules",
content: "Each person may hold only one account. Duplicate accounts will be closed. Users must be 18+ years of age. Accounts showing signs of bonus abuse may be restricted.",
},
]
export default function RulesPage() {
return (
<div className="max-w-2xl space-y-4">
<div className="border-b border-border pb-2">
<h1 className="text-sm font-black uppercase tracking-wide text-foreground">Betting Rules</h1>
</div>
return <div className="text-sm text-foreground">Rules page placeholder</div>
}
<div className="space-y-3">
{rules.map((rule, i) => (
<div key={i} className="bg-card border border-border rounded-lg overflow-hidden">
<div className="bg-secondary px-4 py-2.5 flex items-center gap-2">
<span className="bg-primary text-primary-foreground size-5 rounded-full text-[10px] font-bold flex items-center justify-center shrink-0">
{i + 1}
</span>
<h3 className="text-[12px] font-bold text-foreground">{rule.title}</h3>
</div>
<div className="px-4 py-3 text-[11px] text-muted-foreground leading-relaxed">
{rule.content}
</div>
</div>
))}
</div>
</div>
)
}

View File

@ -1,29 +1,25 @@
import { BarChart2, PrinterIcon, Trophy } from "lucide-react"
import { Separator } from "@/components/ui/separator"
const services = [
{ label: "Live Score", icon: BarChart2 },
{ label: "Results", icon: Trophy },
{ label: "Print Odds", icon: PrinterIcon },
]
const services = ["Live Score", "Results", "Print Odds"]
export function BetServices() {
return (
<div className="flex items-center gap-0 bg-secondary rounded border border-border overflow-hidden text-[11px]">
<span className="px-3 py-2 font-bold uppercase text-muted-foreground text-[10px] tracking-wider border-r border-border">
Services
<div className="flex items-center gap-3 rounded-md border bg-card/60 px-3 py-2 text-[11px]">
<span className="font-medium uppercase tracking-wide text-muted-foreground">
Bet Services
</span>
{services.map((service, i) => {
const Icon = service.icon
return (
<Separator orientation="vertical" className="h-4" />
<div className="flex flex-wrap gap-2">
{services.map((service) => (
<button
key={service.label}
className={`flex items-center gap-1.5 px-3 py-2 text-muted-foreground hover:text-primary hover:bg-muted transition-colors ${i < services.length - 1 ? "border-r border-border" : ""}`}
key={service}
className="text-[11px] text-primary underline-offset-2 hover:underline"
>
<Icon className="size-3" />
{service.label}
{service}
</button>
)
})}
))}
</div>
</div>
)
}
}

View File

@ -1,187 +1,64 @@
"use client"
import { useState } from "react"
import { useBetslipStore } from "@/lib/store/betslip-store"
import { X, ChevronDown, Trash2 } from "lucide-react"
import { cn } from "@/lib/utils"
const quickStakes = [5, 10, 20, 50, 100, 200]
import { useBetslipStore } from "@/lib/store/betslip-store";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
export function Betslip() {
const { bets, removeBet, clearBets, updateStake, getPotentialWin, getTotalOdds } = useBetslipStore()
const [activeTab, setActiveTab] = useState<"single" | "accumulator">("single")
const [globalStake, setGlobalStake] = useState("10")
const potentialWin = getPotentialWin()
const totalOdds = getTotalOdds()
const { bets, removeBet, clearBets } = useBetslipStore();
return (
<div className="bg-card rounded border border-border overflow-hidden">
{/* Header */}
<div className="bg-primary px-3 py-2 flex items-center justify-between">
<div className="flex items-center gap-2">
<span className="text-[11px] font-bold text-primary-foreground uppercase">Betslip</span>
{bets.length > 0 && (
<span className="bg-primary-foreground text-primary text-[10px] font-bold px-1.5 py-0.5 rounded-full min-w-[18px] text-center">
{bets.length}
</span>
)}
</div>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0">
<CardTitle className="text-sm font-semibold">
Betslip {bets.length}
</CardTitle>
{bets.length > 0 && (
<button
onClick={clearBets}
className="text-primary-foreground/70 hover:text-primary-foreground transition-colors"
className="text-xs text-muted-foreground hover:underline"
>
<Trash2 className="size-3.5" />
Clear all
</button>
)}
</div>
{/* Tabs */}
{bets.length > 1 && (
<div className="flex border-b border-border">
{(["single", "accumulator"] as const).map((tab) => (
<button
key={tab}
onClick={() => setActiveTab(tab)}
className={cn(
"flex-1 py-1.5 text-[10px] font-semibold uppercase transition-colors",
activeTab === tab
? "bg-muted text-foreground border-b-2 border-primary"
: "text-muted-foreground hover:text-foreground"
)}
>
{tab}
</button>
))}
</div>
)}
{/* Content */}
<div className="p-2 space-y-2">
</CardHeader>
<CardContent className="space-y-3 text-xs">
{bets.length === 0 ? (
<div className="py-6 text-center">
<div className="text-2xl mb-2">🎯</div>
<p className="text-[11px] text-muted-foreground leading-relaxed">
No bets selected. Click on odds to add selections.
</p>
</div>
<p className="text-muted-foreground">
No bet has been selected. To select a bet, please click on the
respective odds.
</p>
) : (
<>
{/* Bet items */}
<div className="space-y-2">
{bets.map((bet) => (
<div key={bet.id} className="bg-secondary/50 rounded border border-border/50 p-2">
<div className="flex items-start justify-between gap-2 mb-2">
<div className="flex-1 min-w-0">
<div className="text-[11px] font-semibold text-foreground truncate">{bet.event}</div>
<div className="text-[10px] text-muted-foreground truncate">{bet.league}</div>
<div className="text-[10px] text-primary font-medium mt-0.5">
{bet.market}: <span className="text-foreground">{bet.selection}</span>
</div>
</div>
<div className="flex items-center gap-1.5 shrink-0">
<span className="text-sm font-bold text-primary">{bet.odds.toFixed(2)}</span>
<button
onClick={() => removeBet(bet.id)}
className="text-muted-foreground hover:text-destructive transition-colors"
>
<X className="size-3.5" />
</button>
<div className="space-y-2">
{bets.map((bet) => (
<div
key={bet.id}
className="rounded border bg-card/40 p-2 text-xs"
>
<div className="flex items-center justify-between">
<div>
<div className="font-medium">{bet.event}</div>
<div className="text-[10px] text-muted-foreground">
{bet.market} {bet.selection}
</div>
</div>
{/* Stake input for single */}
{(activeTab === "single" || bets.length === 1) && (
<div>
<div className="text-[10px] text-muted-foreground mb-1">Stake (ETB)</div>
<div className="flex gap-1">
<input
type="number"
value={bet.stake ?? 10}
onChange={(e) => updateStake(bet.id, Number(e.target.value))}
className="flex-1 bg-input border border-border rounded px-2 py-1 text-xs text-foreground w-full min-w-0 focus:outline-none focus:border-primary"
min="1"
/>
</div>
<div className="flex gap-1 mt-1 flex-wrap">
{quickStakes.map((s) => (
<button
key={s}
onClick={() => updateStake(bet.id, s)}
className={cn(
"text-[10px] px-1.5 py-0.5 rounded border transition-colors",
(bet.stake ?? 10) === s
? "bg-primary text-primary-foreground border-primary"
: "border-border text-muted-foreground hover:border-primary hover:text-primary"
)}
>
{s}
</button>
))}
</div>
{/* Potential win */}
<div className="mt-1.5 flex justify-between text-[10px]">
<span className="text-muted-foreground">Potential win:</span>
<span className="text-primary font-semibold">
{((bet.stake ?? 10) * bet.odds).toFixed(2)} ETB
</span>
</div>
</div>
)}
</div>
))}
</div>
{/* Accumulator stake section */}
{activeTab === "accumulator" && bets.length > 1 && (
<div className="bg-secondary/50 rounded border border-border/50 p-2 space-y-2">
<div className="flex justify-between text-[11px]">
<span className="text-muted-foreground">Total Odds:</span>
<span className="font-bold text-primary">{totalOdds.toFixed(2)}</span>
</div>
<div>
<div className="text-[10px] text-muted-foreground mb-1">Stake (ETB)</div>
<input
type="number"
value={globalStake}
onChange={(e) => setGlobalStake(e.target.value)}
className="w-full bg-input border border-border rounded px-2 py-1 text-xs text-foreground focus:outline-none focus:border-primary"
min="1"
/>
<div className="flex gap-1 mt-1 flex-wrap">
{quickStakes.map((s) => (
<button
key={s}
onClick={() => setGlobalStake(String(s))}
className={cn(
"text-[10px] px-1.5 py-0.5 rounded border transition-colors",
globalStake === String(s)
? "bg-primary text-primary-foreground border-primary"
: "border-border text-muted-foreground hover:border-primary hover:text-primary"
)}
>
{s}
</button>
))}
</div>
<div className="mt-1.5 flex justify-between text-[10px]">
<span className="text-muted-foreground">Potential win:</span>
<span className="text-primary font-semibold">
{(Number(globalStake) * totalOdds).toFixed(2)} ETB
</span>
<div className="text-right">
<div className="font-semibold">{bet.odds.toFixed(2)}</div>
<button
onClick={() => removeBet(bet.id)}
className="text-[10px] text-destructive hover:underline"
>
Remove
</button>
</div>
</div>
</div>
)}
{/* Place bet button */}
<button className="w-full bg-primary text-primary-foreground text-[12px] font-bold py-2.5 rounded hover:opacity-90 active:scale-95 transition-all uppercase tracking-wide">
Place Bet
</button>
</>
))}
<Button className="w-full text-xs" size="sm">
Place bet
</Button>
</div>
)}
</div>
</div>
)
}
</CardContent>
</Card>
);
}

View File

@ -1,50 +1,26 @@
"use client"
import { useState } from "react"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Button } from "@/components/ui/button"
export function CheckYourBet() {
const [betId, setBetId] = useState("")
const [result, setResult] = useState<null | "pending" | "won" | "lost">(null)
const handleCheck = () => {
if (!betId.trim()) return
// Mock result
const results = ["pending", "won", "lost"] as const
setResult(results[Math.floor(Math.random() * results.length)])
}
return (
<div className="bg-transparent border-t border-border/20 pt-4 pb-2">
<div className="px-1 text-center space-y-3">
<h3 className="text-[12px] font-bold uppercase text-[#ff9800]">Check Your Bet</h3>
<p className="text-[11px] text-white">Your bet ID</p>
<div className="flex gap-1">
<input
type="text"
value={betId}
onChange={(e) => setBetId(e.target.value)}
className="flex-1 bg-[#121212] border border-border/40 px-2 py-2 text-[11px] text-white outline-none focus:border-primary"
onKeyDown={(e) => e.key === "Enter" && handleCheck()}
<Card>
<CardHeader>
<CardTitle className="text-sm">Check your bet</CardTitle>
</CardHeader>
<CardContent className="space-y-2 text-xs">
<div className="text-[11px] text-muted-foreground">Your bet ID</div>
<div className="flex gap-2">
<Input
placeholder="Bet ID"
className="h-8 text-xs placeholder:text-[11px]"
/>
<button
onClick={handleCheck}
className="bg-[#ff9800] text-black px-2 py-1.5 flex items-center justify-center min-w-[32px] hover:bg-[#ffa726] transition-colors"
>
<svg viewBox="0 0 24 24" className="size-5 fill-current"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>
</button>
<Button size="sm" className="text-xs">
Check
</Button>
</div>
{result && (
<div className={`text-[11px] font-semibold p-2 rounded text-center mt-2 ${
result === "won" ? "bg-primary/20 text-primary" :
result === "lost" ? "bg-destructive/20 text-destructive" :
"bg-secondary text-muted-foreground"
}`}>
{result === "won" && "🎉 Bet Won!"}
{result === "lost" && "❌ Bet Lost"}
{result === "pending" && "⏳ Bet Pending"}
</div>
)}
</div>
</div>
</CardContent>
</Card>
)
}
}

View File

@ -1,310 +1,80 @@
"use client"
import { useState, useEffect } from "react"
import { useSearchParams } from "next/navigation"
import { Button } from "@/components/ui/button"
import { Card, CardContent } from "@/components/ui/card"
import { ScrollArea } from "@/components/ui/scroll-area"
import { useBetslipStore } from "@/lib/store/betslip-store"
import { mockEvents, popularLeagues, type Event } from "@/lib/mock-data"
import { cn } from "@/lib/utils"
import { ChevronDown, BarChart2, TrendingUp, Plus } from "lucide-react"
function OddsButton({ odds, onClick, isSelected }: {
odds: number
onClick: () => void
isSelected: boolean
}) {
const mockEvents = [
{
id: "1",
sport: "Soccer",
name: "Team A vs Team B",
markets: [
{ id: "1x2_home", label: "1", odds: 1.85 },
{ id: "1x2_draw", label: "X", odds: 3.4 },
{ id: "1x2_away", label: "2", odds: 4.1 },
],
},
{
id: "2",
sport: "Basketball",
name: "City Wolves vs Lake Bears",
markets: [
{ id: "spread_home", label: "Home -4.5", odds: 1.9 },
{ id: "spread_away", label: "Away +4.5", odds: 1.9 },
],
},
]
export function EventsList() {
const { addBet } = useBetslipStore()
return (
<button
onClick={onClick}
className={cn(
"flex items-center justify-center py-2 px-0.5 border-r border-border/10 text-[10px] transition-all min-w-0 bg-[#262626] hover:bg-[#333] h-full",
isSelected && "bg-[#ff9800] text-black font-bold border-none"
)}
>
<span className={cn("font-bold tracking-tighter", isSelected ? "text-black" : "text-[#ff9800]")}>{odds.toFixed(2)}</span>
</button>
)
}
function EventRow({ event }: { event: Event }) {
const { bets, addBet } = useBetslipStore()
return (
<div className="bg-[#1a1a1a] border-b border-border/20 hover:bg-[#222] transition-colors h-[38px] flex items-center">
{/* Small Icons & ID Column */}
<div className="flex items-center gap-1.5 px-2 w-[80px] shrink-0 border-r border-border/10 h-full">
<BarChart2 className="size-3 text-muted-foreground hover:text-primary cursor-pointer shrink-0" />
<TrendingUp className="size-3 text-muted-foreground hover:text-primary cursor-pointer shrink-0" />
<span className="text-[9.5px] text-[#ff9800] font-bold tabular-nums italic ml-0.5">{event.id || "01682"}</span>
</div>
{/* Time & Team Column */}
<div className="flex items-center gap-3 px-3 w-[240px] shrink-0 border-r border-border/10 h-full">
<div className="flex flex-col text-[9.5px] font-bold text-white leading-tight italic shrink-0 w-[45px]">
<span>11:00</span>
<span className="text-white/40 uppercase font-medium">PM</span>
</div>
<span className="text-[10px] font-bold text-white truncate max-w-[180px]">{event.homeTeam} - {event.awayTeam}</span>
</div>
{/* Market Columns Grid (10 Markets) */}
<div className="flex-1 grid grid-cols-10 h-full shrink-0">
{event.markets.slice(0, 10).map((market) => {
const betId = `${event.id}-${market.id}`
const isSelected = bets.some((b) => b.id === betId)
return (
<OddsButton
key={market.id}
odds={market.odds}
isSelected={isSelected}
onClick={() => addBet({
id: betId,
event: `${event.homeTeam} vs ${event.awayTeam}`,
league: event.league,
market: "1X2",
selection: `${event.homeTeam} (${market.label})`,
odds: market.odds,
})}
/>
)
})}
</div>
{/* More Markets Button */}
<button className="w-10 flex items-center justify-center h-full hover:bg-white/5 transition-colors border-l border-border/10 group">
<Plus className="size-3 text-white group-hover:scale-110 transition-transform" />
</button>
</div>
)
}
const MARKET_HEADERS = ["1", "x", "2", "Over (2.5)", "Under (2.5)", "1X", "12", "X2", "Yes", "No"];
export function EventsList({ filter = "All", sport = "all", search = "" }: {
filter?: string
sport?: string
search?: string
}) {
const searchParams = useSearchParams()
const leagueQuery = searchParams.get("league")
const [selectedLeague, setSelectedLeague] = useState<string | null>(leagueQuery)
const { bets, addBet } = useBetslipStore()
useEffect(() => {
setSelectedLeague(leagueQuery)
}, [leagueQuery])
const handleClose = () => {
const url = new URL(window.location.href)
url.searchParams.delete("league")
window.history.pushState({}, "", url)
setSelectedLeague(null)
}
const events = selectedLeague
? mockEvents.filter(e => e.league.toLowerCase() === selectedLeague.toLowerCase())
: mockEvents.filter((e) => {
if (filter === "Live" && !e.isLive) return false
if (sport !== "all" && e.sport.toLowerCase() !== sport.toLowerCase()) return false
return true
})
// Common Header Rendering
const renderTableHeaders = () => (
<>
{/* Table Header Categories */}
<div className="bg-[#1a1a1a] border-b border-border/40 grid grid-cols-5 text-[11px] font-bold text-white uppercase text-center items-center h-9">
<div className="h-full flex items-center justify-center hover:bg-[#222] transition-colors cursor-pointer border-r border-border/20">Main</div>
<div className="h-full flex items-center justify-center hover:bg-[#222] transition-colors cursor-pointer border-r border-border/20">Goals</div>
<div className="h-full flex items-center justify-center hover:bg-[#222] transition-colors cursor-pointer border-r border-border/20">Handicap</div>
<div className="h-full flex items-center justify-center hover:bg-[#222] transition-colors cursor-pointer border-r border-border/20 text-[9px] leading-tight">Half Time / Full Time</div>
<div className="h-full flex items-center justify-center hover:bg-[#222] transition-colors cursor-pointer">Correct Score</div>
</div>
{/* Sub Headers */}
<div className="bg-[#1a1a1a] border-b border-border/40 grid grid-cols-5 text-[10px] font-bold text-white uppercase text-center items-center h-8">
<div className="h-full flex items-center justify-center hover:bg-[#222] transition-colors cursor-pointer border-r border-border/20">1st Half</div>
<div className="h-full flex items-center justify-center hover:bg-[#222] transition-colors cursor-pointer border-r border-border/20">2nd Half</div>
<div className="h-full flex items-center justify-center bg-[#ff9800] text-black border-r border-border/10">Combo</div>
<div className="h-full flex items-center justify-center hover:bg-[#222] transition-colors cursor-pointer border-r border-border/20">Chance Mix</div>
<div className="h-full flex items-center justify-center hover:bg-[#222] transition-colors cursor-pointer">Home</div>
</div>
</>
)
const renderColumnHeaders = () => (
<div className="bg-[#222] border-b border-white/5 h-8 flex items-center text-[9px] font-black text-white/40 uppercase">
<div className="w-[180px] px-3 flex items-center gap-1.5 border-r border-border/10 h-full">Main</div>
<div className="w-[180px] flex items-center justify-center border-r border-border/10 h-full">Over/Under</div>
<div className="flex-1 grid grid-cols-10 text-center h-full items-center tracking-tighter">
{MARKET_HEADERS.map(h => <span key={h}>{h}</span>)}
</div>
<div className="w-10 border-l border-border/10 h-full" />
</div>
)
const renderEventItem = (event: Event) => (
<div key={event.id} className="h-[34px] group flex items-center border-b border-white/5 bg-[#121212] hover:bg-white/5 transition-colors">
{/* Stats & Icons */}
<div className="w-[35px] flex items-center justify-center gap-1 px-2 border-r border-white/5 h-full opacity-40 group-hover:opacity-100">
<BarChart2 className="size-3 cursor-pointer hover:text-primary" />
</div>
{/* ID */}
<div className="w-[45px] text-[10px] font-black text-[#ff9800] italic tabular-nums text-center border-r border-white/5 h-full flex items-center justify-center">
{event.id}
</div>
{/* Time */}
<div className="w-[50px] flex flex-col items-center justify-center border-r border-white/5 h-full leading-none italic font-black text-[9px]">
<span>{event.time}</span>
<span className="text-[7px] text-white/30 uppercase mt-0.5">PM</span>
</div>
{/* Event Name */}
<div className="flex-1 px-4 text-[10.5px] font-black text-white truncate max-w-[200px]">
{event.homeTeam} - {event.awayTeam}
</div>
{/* Odds Grid */}
<div className="flex-1 grid grid-cols-10 h-full">
{event.markets.slice(0, 10).map((market) => {
const betId = `${event.id}-${market.id}`
const isSelected = bets.some((b) => b.id === betId)
return (
<button
key={market.id}
onClick={() => addBet({
id: betId,
event: `${event.homeTeam} vs ${event.awayTeam}`,
league: event.league,
market: "1X2",
selection: `${event.homeTeam} (${market.label})`,
odds: market.odds,
})}
className={cn(
"flex items-center justify-center text-[10.5px] font-black tabular-nums transition-all border-r border-white/5",
isSelected ? "bg-[#ff9800] text-black" : "text-[#ff9800] hover:bg-white/5"
)}
>
{market.odds.toFixed(2)}
</button>
)
})}
</div>
{/* More Button */}
<button className="w-10 flex items-center justify-center h-full hover:bg-white/5 transition-colors border-l border-white/5 text-white/40">
<Plus className="size-3" />
</button>
</div>
)
if (selectedLeague) {
// Group by date for league view
const groupedEvents = events.reduce((acc, event) => {
if (!acc[event.date]) acc[event.date] = []
acc[event.date].push(event)
return acc
}, {} as Record<string, Event[]>)
return (
<div className="flex flex-col bg-[#121212] rounded overflow-hidden shadow-2xl">
{/* League Header / Breadcrumbs */}
<div className="bg-[#1a1a1a] px-3 py-2 flex items-center justify-between border-b border-border/20">
<div className="flex items-center gap-2 text-[11px] font-bold text-white/60">
<Plus className="size-3 cursor-pointer hover:text-white" />
<div className="flex items-center gap-1.5">
<span className="text-white font-black"></span>
<span className="uppercase">Football</span>
<span className="mx-1 opacity-20">|</span>
<span className="text-white uppercase">{selectedLeague === "LaLiga" ? "Spain - LaLiga" : selectedLeague}</span>
</div>
</div>
<button onClick={handleClose} className="text-white hover:text-primary transition-colors">
<Plus className="size-5 rotate-45" />
</button>
</div>
{/* Large Market Tab Grid */}
<div className="grid grid-cols-5 bg-[#121212] border-b border-border/10">
{[
{ label: "Main", active: true }, { label: "Goals" }, { label: "Handicap" }, { label: "Half Time / Full Time" }, { label: "Correct Score" },
{ label: "1st Half" }, { label: "2nd Half" }, { label: "Asian Markets" }, { label: "Corners" }, { label: "Home" }
].map((m, i) => (
<button
key={i}
className={cn(
"h-8 border-r border-b border-border/10 flex items-center justify-center text-[10px] font-black uppercase transition-all",
m.active ? "bg-[#ff9800] text-black" : "text-white/60 hover:bg-[#222]"
)}
>
{m.label}
</button>
))}
</div>
{/* Column Headers */}
{renderColumnHeaders()}
{/* Grouped Events */}
<div className="overflow-y-auto max-h-[700px]">
{Object.entries(groupedEvents).map(([date, dateEvents]) => (
<div key={date} className="flex flex-col">
<div className="bg-[#1a1a1a] px-2 py-1 text-[10px] font-black text-white border-b border-white/5">
{date}
<Card className="flex-1">
<CardContent className="px-0 pb-4 pt-3">
<ScrollArea className="h-[360px]">
<div className="space-y-2 px-3 pb-2">
{mockEvents.map((event) => (
<div
key={event.id}
className="rounded-md border bg-card/50 p-2 text-xs"
>
<div className="flex items-center justify-between">
<div>
<div className="font-medium">{event.name}</div>
<div className="text-[11px] text-muted-foreground">
{event.sport}
</div>
</div>
</div>
<div className="mt-2 flex flex-wrap gap-1.5">
{event.markets.map((market) => (
<Button
key={market.id}
size="xs"
className="min-w-[60px] justify-between rounded-sm bg-secondary text-secondary-foreground text-[11px] hover:bg-primary hover:text-primary-foreground"
onClick={() =>
addBet({
id: `${event.id}-${market.id}`,
event: event.name,
market: event.sport,
selection: market.label,
odds: market.odds,
})
}
>
<span>{market.label}</span>
<span className="font-semibold">
{market.odds.toFixed(2)}
</span>
</Button>
))}
</div>
</div>
{dateEvents.map(event => renderEventItem(event))}
</div>
))}
</div>
</div>
)
}
// Home View (No League Selected)
const homeEventsByLeague = events.reduce((acc, event) => {
if (!acc[event.league]) acc[event.league] = []
acc[event.league].push(event)
return acc
}, {} as Record<string, Event[]>)
return (
<div className="flex flex-col bg-[#121212] rounded overflow-hidden">
<div className="flex flex-col">
{Object.entries(homeEventsByLeague).map(([leagueName, leagueEvents]) => (
<div key={leagueName} className="flex flex-col border-b border-white/5 last:border-none">
{/* League Box Header */}
<div className="bg-[#2a2a2a] px-3 py-1.5 text-[10px] font-bold text-[#ff9800] uppercase border-y border-border/20 flex items-center justify-between">
<div className="flex items-center gap-2">
<span className="text-[14px]">
{popularLeagues.find(l => l.name === leagueName)?.icon ||
popularLeagues.find(l => l.id.toLowerCase() === leagueName.toLowerCase())?.icon ||
"⚽"}
</span>
<span>{leagueName === "LaLiga" ? "Spain - LaLiga" :
leagueName === "Premier League" ? "England - Premier League" :
leagueName === "Bundesliga" ? "Germany - Bundesliga" :
leagueName === "Ligue 1" ? "France - Ligue 1" :
leagueName}</span>
</div>
<ChevronDown className="size-4 text-white" />
</div>
{/* Column Headers for each league box */}
<div className="bg-[#222] px-3 py-1 flex items-center text-[8px] font-black text-white/40 uppercase border-b border-border/20">
<div className="w-[180px] flex gap-4">
<span className="w-5 text-center">Stats</span>
<span className="w-6">ID</span>
<span className="w-10">Time</span>
<span>Event</span>
</div>
<div className="flex-1 grid grid-cols-10 text-center tracking-tighter">
{MARKET_HEADERS.map(h => <span key={h}>{h}</span>)}
</div>
<div className="w-10" />
</div>
{/* Matches in this league */}
<div className="flex flex-col">
{leagueEvents.map(event => renderEventItem(event))}
</div>
))}
</div>
))}
</div>
</div>
</ScrollArea>
</CardContent>
</Card>
)
}

View File

@ -1,62 +0,0 @@
"use client"
import { useState, useEffect } from "react"
import { ChevronLeft, ChevronRight } from "lucide-react"
const images = [
"https://api-new.harifsport.com/home/img/editable/casj-1822.jpg",
"https://api-new.harifsport.com/home/img/editable/casj-18s.jpg"
]
export function HeroBanner() {
const [currentIndex, setCurrentIndex] = useState(0)
useEffect(() => {
const timer = setInterval(() => {
setCurrentIndex((prev) => (prev + 1) % images.length)
}, 5000)
return () => clearInterval(timer)
}, [])
return (
<div className="w-full h-[240px] md:h-[300px] relative overflow-hidden rounded-none group shadow-lg bg-[#111]">
{images.map((src, index) => (
<img
key={src}
src={src}
alt={`Banner ${index + 1}`}
className={`absolute inset-0 object-contain w-full h-full transition-opacity duration-1000 ${
index === currentIndex ? "opacity-100" : "opacity-0"
}`}
/>
))}
{/* Navigation Arrows */}
<button
onClick={() => setCurrentIndex((prev) => (prev - 1 + images.length) % images.length)}
className="absolute left-4 top-1/2 -translate-y-1/2 bg-black/60 p-2 text-white/80 hover:text-white transition-colors opacity-0 group-hover:opacity-100"
>
<ChevronLeft className="size-6" />
</button>
<button
onClick={() => setCurrentIndex((prev) => (prev + 1) % images.length)}
className="absolute right-4 top-1/2 -translate-y-1/2 bg-black/60 p-2 text-white/80 hover:text-white transition-colors opacity-0 group-hover:opacity-100"
>
<ChevronRight className="size-6" />
</button>
{/* Indicators */}
<div className="absolute bottom-4 left-1/2 -translate-x-1/2 flex gap-1.5 px-3 py-1.5 rounded-full bg-black/30 backdrop-blur-sm">
{images.map((_, index) => (
<button
key={index}
onClick={() => setCurrentIndex(index)}
className={`w-1.5 h-1.5 rounded-full transition-all ${
index === currentIndex ? "bg-[#ff9800] scale-125" : "bg-white/40"
}`}
/>
))}
</div>
</div>
)
}

View File

@ -1,24 +0,0 @@
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"
const tabs = [
{ id: "today", label: "Today Events" },
{ id: "upcoming", label: "Upcoming Events" },
{ id: "most-played", label: "Most Played" },
{ id: "top-occurrences", label: "Top Occurrences" },
]
export function HomeTabs() {
return (
<div className="flex flex-col">
<Tabs defaultValue="today" className="w-full">
<TabsList variant="hs-home">
{tabs.map((tab) => (
<TabsTrigger key={tab.id} value={tab.id}>
{tab.label}
</TabsTrigger>
))}
</TabsList>
</Tabs>
</div>
)
}

View File

@ -1,12 +1,11 @@
export function InPlayHeader() {
return (
<div className="flex items-center justify-between border-b border-border pb-2">
<div className="flex items-center gap-2">
<h1 className="text-sm font-black uppercase tracking-wide text-foreground">
IN-PLAY
</h1>
<span className="text-[10px] text-muted-foreground">/ Today&apos;s Events</span>
</div>
<div className="flex items-center justify-between border-b pb-2">
<h1 className="text-sm font-semibold uppercase tracking-wide text-foreground">
IN-PLAY
</h1>
<span className="text-[11px] text-muted-foreground">Quick Filter</span>
</div>
)
}
}

View File

@ -1,195 +0,0 @@
"use client"
import { useBetslipStore } from "@/lib/store/betslip-store"
import { mockEvents, type Event } from "@/lib/mock-data"
import { cn } from "@/lib/utils"
import { BarChart2, TrendingUp, Monitor, Tv } from "lucide-react"
function LiveEventRow({ event, isNoOdds }: { event: Event, isNoOdds?: boolean }) {
const { bets, addBet } = useBetslipStore()
// Dummy data for demonstration
const score = event.homeScore !== undefined ? `${event.homeScore} - ${event.awayScore}` : "0 - 0"
const time = event.liveMinute ? `${event.liveMinute}:00` : "83:10"
const period = "H2"
return (
<div className="bg-[#121212] border-b border-white/5 hover:bg-white/5 transition-colors h-[50px] flex items-center group">
{/* Match Info Column (Time & Score) */}
<div className="flex items-center gap-3 px-3 w-[360px] shrink-0 h-full border-r border-white/5">
<div className="flex flex-col text-[10px] font-black leading-tight italic w-[55px] shrink-0 tabular-nums">
<span className="text-[#ff9800]">{time}</span>
<span className="text-white/40">{period}</span>
</div>
<div className="flex items-center min-w-0 flex-1 gap-2">
<span className="text-[11.5px] font-black text-white truncate italic uppercase">
{event.homeTeam} <span className="text-[#ff9800] mx-1 tabular-nums">{score}</span> {event.awayTeam}
</span>
</div>
<div className="flex items-center gap-2 shrink-0 px-1 opacity-20 group-hover:opacity-100 transition-opacity">
<BarChart2 className="size-3.5 text-white" />
<Monitor className="size-3.5 text-white" />
</div>
</div>
{/* Odds Grid or Placeholder */}
<div className="flex-1 h-full flex items-center">
{isNoOdds ? (
<div className="flex-1 h-full bg-[#161616] flex items-center justify-center text-[10.5px] font-black text-white/20 uppercase italic tracking-tight">
Sorry, no odds for this match
</div>
) : (
<div className="flex-1 grid grid-cols-3 h-full">
{event.markets.slice(0, 3).map((m, idx) => {
const labels = ["Home", "Draw", "Away"]
return (
<button
key={m.id}
onClick={() => addBet({
id: `${event.id}-${m.id}`,
event: `${event.homeTeam} vs ${event.awayTeam}`,
league: event.league,
market: "1X2",
selection: `${m.label}`,
odds: m.odds,
})}
className="bg-[#1a1a1a] hover:bg-[#2a2a2a] flex items-center justify-between px-4 h-full border-r border-white/5 transition-colors group/btn"
>
<span className="text-[10px] font-black text-white/40 group-hover/btn:text-white uppercase">{labels[idx]}</span>
<span className="text-[11px] font-black text-[#ff9800] tabular-nums">{m.odds.toFixed(2)}</span>
</button>
)
})}
</div>
)}
</div>
{/* Right Indicator */}
<div className="w-[4px] bg-[#ff9800] h-full" />
</div>
)
}
const liveSports = [
{ id: "soccer", label: "Soccer", icon: "⚽", count: 25, active: true },
{ id: "basketball", label: "Basketball", icon: "🏀", count: 39 },
{ id: "ice-hockey", label: "Ice Hockey", icon: "🏒", count: 3 },
{ id: "tennis", label: "Tennis", icon: "🎾", count: 4 },
{ id: "handball", label: "Handball", icon: "🤾", count: 10 },
{ id: "rugby", label: "Rugby", icon: "🏉", count: 2 },
{ id: "table-tennis", label: "Table Tennis", icon: "🏓", count: 8 },
{ id: "volleyball", label: "Volleyball", icon: "🏐", count: 7 },
{ id: "futsal", label: "Futsal", icon: "⚽", count: 2 },
{ id: "esport-counter-strike", label: "ESport Cou...", icon: "🎮", count: 2 },
{ id: "esport-league-of-legends", label: "ESport Lea...", icon: "🎮", count: 1 },
{ id: "esport-dota-2", label: "ESport Dota", icon: "🎮", count: 1 },
{ id: "efootball", label: "eFootball", icon: "⚽", count: 4 },
{ id: "ebasketball", label: "eBasketball", icon: "🏀", count: 1 },
]
export function LiveEventsList() {
// Enhanced mock data local to live view to match screenshot exactly
const liveMatches = [
{
league: "Algeria - Ligue 1",
flag: "https://flagcdn.com/w20/dz.png",
matches: [
{ ...mockEvents[0], id: "l1", homeTeam: "Paradou AC", awayTeam: "Ben Aknoun", homeScore: 3, awayScore: 5, liveMinute: 91, noOdds: true }
]
},
{
league: "Australia - U23 Victoria NPL",
flag: "https://flagcdn.com/w20/au.png",
matches: [
{ ...mockEvents[1], id: "l2", homeTeam: "Oakleigh Cannons FC", awayTeam: "Altona Magic SC", homeScore: 5, awayScore: 1, liveMinute: 87, noOdds: true }
]
},
{
league: "Australia - U23 Victoria Premier League 1",
flag: "https://flagcdn.com/w20/au.png",
matches: [
{ ...mockEvents[2], id: "l3", homeTeam: "Northcote City FC", awayTeam: "Western United FC", homeScore: 4, awayScore: 0, liveMinute: 83, noOdds: false },
{ ...mockEvents[3], id: "l4", homeTeam: "Melbourne Knights FC", awayTeam: "Melbourne Victory FC", homeScore: 0, awayScore: 3, liveMinute: 81, noOdds: true }
]
},
{
league: "Australia - Victoria NPL, Women",
flag: "https://flagcdn.com/w20/au.png",
matches: [
{ ...mockEvents[4], id: "l5", homeTeam: "Preston Lions FC", awayTeam: "South Melbourne FC", homeScore: 1, awayScore: 1, liveMinute: 52, noOdds: true },
{ ...mockEvents[0], id: "l6", homeTeam: "Bentleigh Greens SC", awayTeam: "Box Hill United", homeScore: 0, awayScore: 6, liveMinute: 83, noOdds: true }
]
}
]
return (
<div className="flex flex-col min-h-screen bg-[#111]">
{/* Sport Navigation Carousel */}
<div className="bg-[#1a1a1a] border-b border-border/20 px-2 flex items-center h-[54px] overflow-x-auto scrollbar-hide">
<div className="flex items-center gap-0 h-full">
{/* Favourites & Prematch */}
<button className="flex flex-col items-center justify-center px-4 h-full border-r border-white/5 min-w-[70px]">
<span className="text-[14px]"></span>
<span className="text-[9px] font-bold text-white/40 uppercase mt-0.5">Favourites</span>
</button>
<button className="flex flex-col items-center justify-center px-4 h-full border-r border-white/5 min-w-[70px]">
<span className="text-[14px]"></span>
<span className="text-[9px] font-bold text-white/40 uppercase mt-0.5">Prematch</span>
</button>
{/* Live Sports */}
{liveSports.map((sport) => (
<button
key={sport.id}
className={cn(
"flex flex-col items-center justify-center px-3 h-full border-r border-white/5 min-w-[75px] relative transition-colors",
sport.active ? "bg-black/20" : "hover:bg-white/5"
)}
>
<span className="absolute top-1 right-2 text-[8.5px] font-black text-white/40">{sport.count}</span>
<span className="text-[16px]">{sport.icon}</span>
<span className={cn(
"text-[9px] font-bold uppercase mt-1 tracking-tighter whitespace-nowrap",
sport.active ? "text-[#ff9800]" : "text-white/40"
)}>
{sport.label}
</span>
{sport.active && (
<div className="absolute bottom-0 left-0 right-0 h-[2px] bg-[#ff9800]" />
)}
</button>
))}
</div>
</div>
{/* Category Header (Soccer) */}
<div className="bg-[#009688] px-3 py-1.5 flex items-center gap-2 border-l-[4px] border-[#ff9800]">
<span className="text-[16px]"></span>
<h2 className="text-[14px] font-black text-white uppercase tracking-tight">Soccer</h2>
</div>
{/* Grouped Live Matches */}
<div className="flex flex-col mb-10">
{liveMatches.map((group, gIdx) => (
<div key={gIdx} className="flex flex-col">
{/* League Header */}
<div className="bg-[#1a1a1a] px-3 py-1 border-b border-border/10 flex items-center gap-2">
<img src={group.flag} width="14" alt={group.league} className="rounded-sm opacity-60" />
<span className="text-[9.5px] font-black text-white/60 uppercase tracking-widest leading-none">
{group.league}
</span>
</div>
{/* Matches in this league */}
<div className="flex flex-col">
{group.matches.map((match, mIdx) => (
<LiveEventRow key={match.id} event={match as any} isNoOdds={match.noOdds} />
))}
</div>
</div>
))}
</div>
</div>
)
}

View File

@ -1,32 +1,18 @@
"use client"
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { cn } from "@/lib/utils"
const filters = ["All", "Today", "3h", "6h", "9h", "12h"]
const filters = ["All", "Live", "Today", "3h", "6h", "12h"]
export function QuickFilterBar({ active, onChange }: {
active: string
onChange: (f: string) => void
}) {
export function QuickFilterBar() {
return (
<div className="flex items-center gap-1 overflow-x-auto pb-0.5">
{filters.map((filter) => (
<button
key={filter}
onClick={() => onChange(filter)}
className={cn(
"shrink-0 px-3 py-1 rounded text-[11px] font-semibold transition-colors",
active === filter
? "bg-primary text-primary-foreground"
: "bg-secondary text-muted-foreground hover:bg-muted hover:text-foreground"
)}
>
{filter}
{filter === "Live" && (
<span className="ml-1 live-dot inline-block size-1.5 rounded-full bg-[var(--hs-live-red)] align-middle" />
)}
</button>
))}
</div>
<Tabs defaultValue="All" className="w-full">
<TabsList className="h-8">
{filters.map((filter) => (
<TabsTrigger key={filter} value={filter} className="px-3 text-xs">
{filter}
</TabsTrigger>
))}
</TabsList>
</Tabs>
)
}
}

View File

@ -1,27 +1,28 @@
"use client"
import { useState } from "react"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Button } from "@/components/ui/button"
export function ReloadTicket() {
const [code, setCode] = useState("")
return (
<div className="bg-transparent border-t border-border/20 pt-4 pb-2">
<div className="px-1 text-center space-y-3">
<h3 className="text-[12px] font-bold uppercase text-[#ff9800]">Reload Ticket</h3>
<p className="text-[11px] text-white">Insert the code to load</p>
<div className="flex gap-1">
<input
type="text"
value={code}
onChange={(e) => setCode(e.target.value)}
className="flex-1 bg-[#121212] border border-border/40 px-2 py-2 text-[11px] text-white outline-none focus:border-primary"
/>
<button className="bg-[#ff9800] text-black px-2 py-1.5 flex items-center justify-center min-w-[32px] hover:bg-[#ffa726] transition-colors">
<svg viewBox="0 0 24 24" className="size-5 fill-current"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>
</button>
<Card>
<CardHeader>
<CardTitle className="text-sm">Reload Ticket</CardTitle>
</CardHeader>
<CardContent className="space-y-2 text-xs">
<div className="text-[11px] text-muted-foreground">
Insert the code to load
</div>
</div>
</div>
<div className="flex gap-2">
<Input
placeholder="Ticket code"
className="h-8 text-xs placeholder:text-[11px]"
/>
<Button size="sm" className="text-xs">
Reload
</Button>
</div>
</CardContent>
</Card>
)
}
}

View File

@ -1,21 +1,19 @@
"use client"
import { Input } from "@/components/ui/input"
import { Search } from "lucide-react"
export function SearchEvent({ value, onChange }: {
value: string
onChange: (v: string) => void
}) {
export function SearchEvent() {
return (
<div className="relative">
<Search className="absolute left-2.5 top-1/2 -translate-y-1/2 size-3.5 text-muted-foreground" />
<input
type="text"
value={value}
onChange={(e) => onChange(e.target.value)}
placeholder="Search by event, team or league..."
className="w-full bg-secondary border border-border rounded pl-8 pr-3 py-2 text-[12px] text-foreground placeholder:text-muted-foreground focus:outline-none focus:border-primary transition-colors"
<div className="space-y-1 rounded-md border bg-card/60 p-3">
<div className="text-[11px] font-medium uppercase tracking-wide text-muted-foreground">
Search Event
</div>
<p className="text-[11px] text-muted-foreground">
Insert the events name or at least one team in the form below
</p>
<Input
placeholder="Search by event or team"
className="h-8 text-xs placeholder:text-[11px]"
/>
</div>
)
}
}

View File

@ -1,27 +0,0 @@
"use client"
import { useSearchParams } from "next/navigation"
import { HeroBanner } from "./hero-banner"
import { TopMatches } from "./top-matches"
import { SportsNav } from "./sports-nav"
import { HomeTabs } from "./home-tabs"
import { EventsList } from "./events-list"
export function SportHome() {
const searchParams = useSearchParams()
const isLeagueView = !!searchParams.get("league")
return (
<div className="flex flex-col gap-3">
{!isLeagueView && (
<>
<HeroBanner />
<TopMatches />
<SportsNav />
<HomeTabs />
</>
)}
<EventsList />
</div>
)
}

View File

@ -1,33 +0,0 @@
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"
const sports = [
{ id: "football", name: "Football", icon: "⚽" },
{ id: "tennis", name: "Tennis", icon: "🎾" },
{ id: "basketball", name: "Basketball", icon: "🏀" },
{ id: "ice-hockey", name: "Ice Hockey", icon: "🏒" },
{ id: "mma", name: "MMA", icon: "🥋" },
{ id: "handball", name: "Handball", icon: "🤾" },
{ id: "darts", name: "Darts", icon: "🎯" },
{ id: "snooker", name: "Snooker", icon: "🎱" },
{ id: "cricket", name: "Cricket", icon: "🏏" },
{ id: "dota2", name: "Dota 2", icon: "🎮" },
]
export function SportsNav() {
return (
<Tabs defaultValue="football" className="w-full">
<TabsList variant="hs-nav" className="h-auto">
{sports.map((sport) => (
<TabsTrigger
key={sport.id}
value={sport.id}
className="flex-col min-w-[70px] py-2 gap-1"
>
<span className="text-xl">{sport.icon}</span>
<span className="text-[10px] font-bold uppercase">{sport.name}</span>
</TabsTrigger>
))}
</TabsList>
</Tabs>
)
}

View File

@ -1,102 +0,0 @@
import { ChevronRight } from "lucide-react"
import { Button } from "@/components/ui/button"
const topMatches = [
{
id: "tm1",
league: "England - Premier League",
time: "05:00 PM",
homeTeam: "Nottingham Forest",
awayTeam: "Liverpool",
odds: { home: 4.09, draw: 3.93, away: 1.82 }
},
{
id: "tm2",
league: "England - Premier League",
time: "11:00 PM",
homeTeam: "Man City",
awayTeam: "Newcastle",
odds: { home: 1.50, draw: 5.17, away: 5.93 }
},
{
id: "tm3",
league: "England - Premier League",
time: "06:00 PM",
homeTeam: "Chelsea",
awayTeam: "Burnley",
odds: { home: 1.21, draw: 6.91, away: 11.50 }
},
{
id: "tm4",
league: "Spain - LaLiga",
time: "07:30 PM",
homeTeam: "Arsenal",
awayTeam: "Wolves",
odds: { home: 1.56, draw: 4.16, away: 5.80 }
},
{
id: "tm5",
league: "Italy - Serie A",
time: "09:45 PM",
homeTeam: "Inter Milan",
awayTeam: "Napoli",
odds: { home: 1.85, draw: 3.60, away: 4.20 }
}
]
export function TopMatches() {
return (
<div className="flex gap-3 overflow-x-auto pb-2 scrollbar-hide -mx-1 px-1">
{topMatches.map((match) => (
<div
key={match.id}
className="min-w-[280px] bg-[#222] border border-border/20 rounded-sm overflow-hidden flex flex-col relative group"
>
{/* Top Label Ribbon */}
<div className="absolute top-0 right-0 w-12 h-12 overflow-hidden z-10 pointer-events-none">
<div className="absolute top-[6px] right-[-14px] bg-[#ff9800] text-black text-[8px] font-black py-0.5 px-6 rotate-45 shadow-sm uppercase tracking-tighter">
TOP
</div>
</div>
<div className="bg-[#1a1a1a] px-3 py-1.5 flex items-center justify-between text-[10px] text-muted-foreground border-b border-border/10">
<div className="flex items-center gap-1">
<span className="font-bold text-[#ff9800]">{match.league}</span>
<span className="font-black text-white ml-1 italic">{match.time}</span>
</div>
<ChevronRight className="size-3 text-white/20 mr-4" />
</div>
<div className="p-3 flex flex-col gap-3">
<div className="flex items-center justify-between gap-1">
<div className="flex items-center gap-2 flex-1 min-w-0">
<div className="size-4 shrink-0 bg-white/5 rounded-sm flex items-center justify-center text-[9px] border border-white/10"></div>
<span className="text-[11px] font-black text-white truncate uppercase italic">{match.homeTeam}</span>
</div>
<span className="text-[#ff9800] text-[9.5px] font-black italic shrink-0 px-2 opacity-60">VS</span>
<div className="flex items-center gap-2 flex-1 min-w-0 justify-end">
<span className="text-[11px] font-black text-white truncate uppercase italic text-right">{match.awayTeam}</span>
<div className="size-4 shrink-0 bg-white/5 rounded-sm flex items-center justify-center text-[9px] border border-white/10"></div>
</div>
</div>
</div>
<div className="grid grid-cols-3 gap-[1px] bg-white/5 mx-2 mb-2 rounded-sm overflow-hidden border border-white/5">
<button className="bg-[#1a1a1a] hover:bg-[#333] flex items-center justify-between px-2.5 py-1.5 transition-colors group/btn">
<span className="text-[10px] font-black text-white/40 group-hover/btn:text-white">1</span>
<span className="text-[11px] font-black text-[#ff9800] tabular-nums">{match.odds.home.toFixed(2)}</span>
</button>
<button className="bg-[#1a1a1a] hover:bg-[#333] flex items-center justify-between px-2.5 py-1.5 transition-colors border-x border-white/5 group/btn">
<span className="text-[10px] font-black text-white/40 group-hover/btn:text-white">X</span>
<span className="text-[11px] font-black text-[#ff9800] tabular-nums">{match.odds.draw.toFixed(2)}</span>
</button>
<button className="bg-[#1a1a1a] hover:bg-[#333] flex items-center justify-between px-2.5 py-1.5 transition-colors group/btn">
<span className="text-[10px] font-black text-white/40 group-hover/btn:text-white">2</span>
<span className="text-[11px] font-black text-[#ff9800] tabular-nums">{match.odds.away.toFixed(2)}</span>
</button>
</div>
</div>
))}
</div>
)
}

View File

@ -1,215 +0,0 @@
"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-brand-accent 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-brand-primary 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-brand-surface-light 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-brand-surface">
<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-brand-primary"
/>
</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-brand-primary"
/>
</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-brand-primary"
/>
</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-brand-primary"
/>
<span className="text-[12px] text-white/80">
I confirm I&apos;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-brand-primary transition-colors"
onClick={onClose}
>
Forgot password?
</Link>
</div>
)}
{/* Submit button */}
<button className="w-full bg-brand-primary hover:bg-brand-primary-hover 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&apos;t have an account?{" "}
<button
className="text-brand-primary hover:underline font-semibold"
onClick={() => setMode("register")}
>
Register
</button>
</>
) : (
<>
Already have an account?{" "}
<button
className="text-brand-primary hover:underline font-semibold"
onClick={() => setMode("login")}
>
Login
</button>
</>
)}
</p>
</div>
</div>
</div>
)
}

View File

@ -1,58 +0,0 @@
"use client"
import { usePathname } from "next/navigation"
import { useState, useCallback } 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"
import { MobileBottomNav } from "@/components/layout/mobile-bottom-nav"
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<AuthMode>("login")
const openAuth = useCallback((mode: AuthMode) => {
setAuthMode(mode)
setAuthOpen(true)
}, [])
return (
<div className="flex min-h-screen flex-col pb-14 md:pb-0">
<SiteHeader
onLoginClick={() => openAuth("login")}
onRegisterClick={() => openAuth("register")}
/>
<div className="flex w-full flex-1 gap-0">
{/* Sidebar: hidden on mobile */}
{!isLivePage && (
<div className="hidden md:block">
<SportsSidebar />
</div>
)}
<main className="flex-1 min-w-0 px-2 py-3">{children}</main>
{/* Right panel: hidden on mobile */}
<div className="hidden md:block">
<RightPanel />
</div>
</div>
<SiteFooter />
{/* Mobile fixed bottom nav */}
<MobileBottomNav />
{/* Auth modal */}
<AuthModal
open={authOpen}
defaultMode={authMode}
onClose={() => setAuthOpen(false)}
/>
</div>
)
}

View File

@ -1,80 +0,0 @@
"use client"
import Link from "next/link"
import { usePathname } from "next/navigation"
import { cn } from "@/lib/utils"
const bottomNavItems = [
{
href: "/",
label: "Home",
icon: (active: boolean) => (
<svg viewBox="0 0 24 24" className={cn("size-6", active ? "fill-brand-primary" : "fill-white/60")}>
<path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>
</svg>
),
},
{
href: "/sports",
label: "Sports",
icon: (active: boolean) => (
<svg viewBox="0 0 24 24" className={cn("size-6", active ? "fill-brand-primary" : "fill-white/60")}>
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/>
</svg>
),
},
{
href: "/betslip",
label: "Betslip",
icon: (active: boolean) => (
<svg viewBox="0 0 24 24" className={cn("size-6", active ? "fill-brand-primary" : "fill-white/60")}>
<path d="M20 4H4c-1.11 0-2 .89-2 2v12c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6c0-1.11-.89-2-2-2zm0 14H4v-6h16v6zm0-10H4V6h16v2z"/>
</svg>
),
},
{
href: "/games",
label: "Casino",
icon: (active: boolean) => (
<svg viewBox="0 0 24 24" className={cn("size-6", active ? "fill-brand-primary" : "fill-white/60")}>
<path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 14l-5-5 1.41-1.41L12 14.17l7.59-7.59L21 8l-9 9z"/>
</svg>
),
},
{
href: "/support",
label: "Help",
icon: (active: boolean) => (
<svg viewBox="0 0 24 24" className={cn("size-6", active ? "fill-brand-primary" : "fill-white/60")}>
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z"/>
</svg>
),
},
]
export function MobileBottomNav() {
const pathname = usePathname()
return (
<nav className="fixed bottom-0 left-0 right-0 z-50 flex md:hidden h-14 bg-brand-bg border-t border-white/10">
{bottomNavItems.map((item) => {
const isActive = pathname === item.href
return (
<Link
key={item.href}
href={item.href}
className="flex-1 flex flex-col items-center justify-center gap-0.5 transition-colors"
>
{item.icon(isActive)}
<span className={cn(
"text-[10px] font-semibold",
isActive ? "text-brand-primary" : "text-white/60"
)}>
{item.label}
</span>
</Link>
)
})}
</nav>
)
}

View File

@ -1,61 +1,24 @@
"use client"
import { Betslip } from "@/components/betting/betslip"
import { ReloadTicket } from "@/components/betting/reload-ticket"
import { CheckYourBet } from "@/components/betting/check-your-bet"
import { useState } from "react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
export function RightPanel() {
const [activeTab, setActiveTab] = useState<"betslip" | "myBets">("betslip")
return (
<aside className="hidden lg:flex w-[280px] shrink-0 flex-col gap-0 border-l border-border/30 bg-[#222]">
{/* Search Header */}
<div className="p-3 bg-[#1a1a1a] flex items-center justify-between border-b border-border/20">
<div className="flex items-center gap-2 text-[12px] font-bold text-white uppercase">
Fast Bet <span className="text-[#ff9800]">QBET</span>
<svg viewBox="0 0 24 24" className="size-4 fill-[#ff9800] ml-1"><path d="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
</div>
</div>
<div className="p-2 space-y-2">
<div className="grid grid-cols-2 gap-0.5">
<Input
type="text"
placeholder="Event Code"
className="bg-[#333] border border-border/40 px-2 py-1.5 h-auto text-[11px] outline-none text-white focus:border-[#ff9800] rounded-none shadow-none"
/>
<div className="bg-[#333] border border-border/40 px-2 py-1.5 text-[11px] text-muted-foreground flex items-center justify-center">...</div>
</div>
{/* Tab switcher */}
<div className="flex items-center justify-between text-[11px] font-bold py-2.5 px-1 border-b border-border/10">
<div className="flex items-center gap-2">
<span className="text-[#ff9800] uppercase cursor-pointer">Betslip</span>
<span className="bg-[#ff9800] text-black px-1.5 rounded-full text-[10px] font-bold">0</span>
<Button
variant="ghost"
className="text-white uppercase flex items-center gap-1 ml-2 opacity-80 hover:opacity-100 p-0 h-auto font-bold text-[11px]"
>
<svg viewBox="0 0 24 24" className="size-3 fill-[#ff9800]"><path d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/></svg>
Settings
</Button>
</div>
<span className="text-white uppercase cursor-pointer opacity-80 hover:opacity-100">Decimal</span>
</div>
<div className="py-10 px-4 text-center">
<p className="text-[11px] text-muted-foreground font-medium leading-relaxed">
No bet has been selected. To select a bet, please click on the respective odds
</p>
</div>
<ReloadTicket />
<CheckYourBet />
</div>
<aside className="flex w-full flex-col gap-3 border-t bg-sidebar px-3 py-3 lg:h-full lg:w-72 lg:shrink-0 lg:border-l lg:border-t-0 lg:pl-4">
<Betslip />
<Card>
<CardHeader>
<CardTitle className="text-sm">Settings</CardTitle>
</CardHeader>
<CardContent className="text-xs text-muted-foreground">
Decimal odds (more settings coming soon)
</CardContent>
</Card>
<ReloadTicket />
<CheckYourBet />
</aside>
)
}
);
}

View File

@ -1,82 +0,0 @@
"use client"
import Link from "next/link"
export function SiteFooter() {
return (
<footer className="bg-[#2a2a2a] text-white pt-12">
<div className="container mx-auto px-6 grid grid-cols-1 md:grid-cols-4 gap-12 text-center md:text-left">
{/* ABOUT */}
<div>
<h3 className="text-[12px] font-black uppercase mb-6 tracking-widest">ABOUT</h3>
<ul className="space-y-2 text-[11px] text-white/60 font-medium tracking-tight">
<li><Link href="/about" className="hover:text-primary transition-colors">About us</Link></li>
<li><Link href="/privacy" className="hover:text-primary transition-colors">Privacy Policy</Link></li>
<li><Link href="/responsible-gaming" className="hover:text-primary transition-colors">Responsible Gaming</Link></li>
<li><Link href="/contact" className="hover:text-primary transition-colors">Contact us</Link></li>
</ul>
</div>
{/* INFORMATION */}
<div>
<h3 className="text-[12px] font-black uppercase mb-6 tracking-widest">INFORMATION</h3>
<ul className="space-y-2 text-[11px] text-white/60 font-medium tracking-tight">
<li><Link href="/terms" className="hover:text-primary transition-colors">Terms & Conditions</Link></li>
<li><Link href="/faq" className="hover:text-primary transition-colors">FAQ</Link></li>
<li><Link href="/rules" className="hover:text-primary transition-colors">Betting Rules</Link></li>
<li><Link href="/info" className="hover:text-primary transition-colors">Betting Information</Link></li>
</ul>
</div>
{/* SPORTS */}
<div>
<h3 className="text-[12px] font-black uppercase mb-6 tracking-widest">SPORTS</h3>
<ul className="space-y-2 text-[11px] text-white/60 font-medium tracking-tight">
<li><Link href="/live" className="hover:text-primary transition-colors text-blue-400">Live betting</Link></li>
<li><Link href="/football" className="hover:text-primary transition-colors">Football</Link></li>
<li><Link href="/basketball" className="hover:text-primary transition-colors">Basketball</Link></li>
<li><Link href="/tennis" className="hover:text-primary transition-colors">Tennis</Link></li>
<li><Link href="/volleyball" className="hover:text-primary transition-colors">Volleyball</Link></li>
</ul>
</div>
{/* PLAY NOW */}
<div>
<h3 className="text-[12px] font-black uppercase mb-6 tracking-widest">PLAY NOW</h3>
<ul className="space-y-2 text-[11px] text-white/60 font-medium tracking-tight">
<li><Link href="/virtual" className="hover:text-primary transition-colors">Virtual</Link></li>
<li><Link href="/special-games" className="hover:text-primary transition-colors">Special Games</Link></li>
</ul>
</div>
</div>
{/* Logo Section */}
<div className="flex flex-col items-center justify-center py-16 border-t border-white/5 mt-12 bg-[#222]">
<div className="flex items-center bg-[#1a1a1a] px-5 py-2">
<div className="bg-[#852222] px-3 py-1 -skew-x-12">
<span className="text-3xl font-black text-white italic tracking-tighter skew-x-12 inline-block">HARIF</span>
</div>
<span className="text-3xl font-black text-[#ff9800] italic tracking-tighter ml-1">SPORT</span>
</div>
{/* Footer Links */}
<div className="flex flex-wrap items-center justify-center gap-6 mt-12 text-[11px] font-bold tracking-tight text-white/80">
<Link href="/affiliates" className="hover:text-primary uppercase transition-colors">Affiliates</Link>
<span className="size-1 bg-white/10 rounded-full" />
<Link href="/complaints" className="hover:text-primary uppercase transition-colors">Complaints</Link>
<span className="size-1 bg-white/10 rounded-full" />
<Link href="/deposits" className="hover:text-primary uppercase transition-colors">Deposits and Withdrawals</Link>
</div>
</div>
{/* Cookie Text */}
<div className="bg-[#1a1a1a] py-10 px-6 text-center">
<div className="container mx-auto max-w-5xl">
<p className="text-[10px] text-white/40 leading-relaxed font-medium uppercase tracking-tight">
By accessing, or continuing to use or browse this site, you consent to our use of certain cookies to improve your experience with us. We only use cookies that will enhance your experience and will not interfere with your privacy. Please look at our Cookie Policy for further informations on our use of the cookie and how you can disable it or manage it if you so choose.
</p>
</div>
</div>
</footer>
)
}

View File

@ -1,293 +1,42 @@
"use client"
import Link from "next/link"
import { usePathname } from "next/navigation"
import { useState, useEffect } from "react"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
const allNavItems = [
{ href: "/", label: "SPORTS" },
{ href: "/today", label: "TODAY" },
{ href: "/live", label: "LIVE" },
{ href: "/virtual", label: "VIRTUAL" },
{ href: "/special-games", label: "SPECIAL G." },
{ href: "/multi-hot-5", label: "MULTI HOT 5" },
{ href: "/poker", label: "POKER", isNew: true },
{ href: "/race", label: "RACE", isNew: true },
{ href: "/promo", label: "PROMO" },
{ href: "/aviator", label: "AVIATOR" },
const navItems = [
{ href: "/", label: "Sport Home" },
{ href: "/live", label: "Live" },
]
const drawerLinks = [
{ href: "/", label: "All Sports" },
{ href: "/live", label: "Live Betting" },
{ href: "/virtual", label: "Virtual" },
{ href: "/special-games", label: "Special Games" },
{ href: "/poker", label: "Poker" },
{ href: "/race", label: "Race" },
{ href: "/promo", label: "Promotions" },
{ href: "/deposit", label: "Deposit" },
{ href: "/bonus", label: "Bonus" },
{ href: "/rules", label: "Betting Rules" },
{ href: "/terms", label: "Terms & Conditions" },
]
interface SiteHeaderProps {
onLoginClick?: () => void
onRegisterClick?: () => void
}
export function SiteHeader({ onLoginClick, onRegisterClick }: SiteHeaderProps) {
export function SiteHeader() {
const pathname = usePathname()
const isLivePage = pathname === "/live"
const [time, setTime] = useState("")
const [prevPathname, setPrevPathname] = useState(pathname)
const [drawerOpen, setDrawerOpen] = useState(false)
// Adjust state during render when pathname changes
if (pathname !== prevPathname) {
setPrevPathname(pathname)
setDrawerOpen(false)
}
useEffect(() => {
const updateClock = () => {
setTime(new Date().toLocaleTimeString("en-GB", { hour12: false }))
}
const interval = setInterval(updateClock, 1000)
updateClock()
return () => clearInterval(interval)
}, [])
return (
<>
<header className="bg-brand-bg sticky top-0 z-50">
{/* ===== DESKTOP: Top bar (hidden on mobile) ===== */}
<div className="hidden md:flex bg-brand-surface px-3 py-1 items-center justify-between text-[11px] text-white">
<div className="flex items-center gap-4">
<button className="flex items-center gap-1.5 hover:text-primary transition-colors">
<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>
</button>
<div className="flex items-center gap-2 font-bold">
<span className="text-[14px]">🕒</span>
<span className="tabular-nums">{time || "00:00:00"}</span>
</div>
</div>
<div className="flex items-center gap-6">
<div className="flex items-center gap-3">
<span className="text-white font-bold tracking-tight">+251 (0) Number</span>
<Input type="password" placeholder="Password" className="bg-brand-surface-light 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 className="flex items-center gap-1">
<Button onClick={onLoginClick} className="bg-[#e67e22] text-white hover:bg-[#d35400] px-5 h-7 text-[11px] font-bold rounded-none uppercase">
Login
</Button>
<Button onClick={onRegisterClick} className="bg-[#d35400] text-white hover:bg-[#c0392b] px-5 h-7 text-[11px] font-bold rounded-none uppercase">
Sign Up
</Button>
</div>
</div>
</div>
{/* ===== MOBILE: Top bar (hidden on desktop) ===== */}
<div className="flex md:hidden items-center h-12 bg-brand-bg border-b border-white/5 px-2 gap-2">
{/* Hamburger */}
<button
onClick={() => setDrawerOpen(true)}
className="flex flex-col justify-center gap-[5px] p-2 shrink-0"
aria-label="Menu"
>
<span className="w-5 h-0.5 bg-white block" />
<span className="w-5 h-[2px] bg-white block" />
<span className="w-5 h-[2px] bg-white block" />
</button>
{/* Logo (centered, grows to fill) */}
<Link href="/" className="flex-1 flex items-center justify-center">
<div className="flex items-center">
<div className="bg-brand-accent px-2 py-0.5 -skew-x-12 flex items-center h-[28px]">
<span className="text-xl font-black text-white italic tracking-tighter skew-x-12 leading-none">HARIF</span>
</div>
<span className="text-xl font-black text-brand-primary italic tracking-tighter ml-1 leading-none">SPORT</span>
</div>
</Link>
{/* LOGIN / SIGN UP */}
<div className="flex items-center gap-1 shrink-0">
<button
onClick={onLoginClick}
className="bg-[#e67e22] text-white px-3 h-8 text-[11px] font-bold uppercase"
>
LOGIN
</button>
<button
onClick={onRegisterClick}
className="bg-[#d35400] text-white px-3 h-8 text-[11px] font-bold uppercase"
>
SIGN UP
</button>
</div>
</div>
{/* ===== DESKTOP: Main header bar ===== */}
<div className="hidden md:flex items-center px-0 bg-[#333] h-[60px]">
<Link href="/" className="flex items-center shrink-0">
<div className="flex items-center bg-brand-surface h-[60px] px-4">
<div className="bg-brand-accent px-3 py-1 -skew-x-12 flex items-center h-[34px]">
<span className="text-2xl font-black text-white italic tracking-tighter skew-x-12 inline-block leading-none">HARIF</span>
</div>
<span className="text-2xl font-black text-brand-primary italic tracking-tighter ml-1 leading-none">SPORT</span>
</div>
</Link>
<div className="flex items-center flex-1 justify-end px-4 h-full gap-0">
<Link href="/" className="flex items-center justify-center bg-brand-primary h-[60px] w-[50px] shrink-0 hover:bg-brand-primary-hover transition-colors">
<svg viewBox="0 0 24 24" className="size-6 fill-black"><path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/></svg>
</Link>
<nav className="flex items-center text-[10.5px] font-bold h-full">
{allNavItems.slice(0, 5).map((item) => {
const isActive = pathname === item.href
return (
<Link key={item.href} href={item.href}
className={cn(
"px-4 flex items-center h-full transition-colors uppercase",
isActive ? (item.label === "LIVE" ? "bg-brand-primary text-black" : "text-primary bg-black/10") : "text-white hover:text-primary"
)}
>
{item.label}
</Link>
)
})}
</nav>
<nav className="flex items-center text-[10.5px] font-bold h-full">
{allNavItems.slice(5).map((item) => {
const isActive = pathname === item.href
return (
<Link key={item.href} href={item.href}
className={cn(
"px-4 flex flex-col items-center justify-center h-full transition-colors relative uppercase",
isActive ? "text-primary bg-black/10" : "text-white hover:text-primary"
)}
>
{item.isNew && (
<span className="absolute top-3 text-[7px] text-primary font-black tracking-tighter leading-none">NEW</span>
)}
<span className={cn(item.isNew && "mt-1.5")}>{item.label}</span>
</Link>
)
})}
</nav>
</div>
</div>
{/* ===== MOBILE: Horizontally scrollable nav tabs ===== */}
<div className="flex md:hidden overflow-x-auto scrollbar-none bg-brand-surface-light border-t border-white/5">
<Link href="/" className="flex-none px-4 h-9 flex items-center bg-brand-primary">
<svg viewBox="0 0 24 24" className="size-5 fill-black"><path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/></svg>
</Link>
{allNavItems.map((item) => {
const isActive = pathname === item.href
<header className="border-b bg-card">
<div className="mx-auto flex max-w-6xl items-center justify-between px-4 py-3">
<div className="text-lg font-semibold tracking-tight">Harifsport</div>
<nav className="flex gap-2 text-sm">
{navItems.map((item) => {
const isActive =
item.href === "/"
? pathname === "/"
: pathname?.startsWith(item.href)
return (
<Link
key={item.href}
href={item.href}
className={cn(
"relative flex-none px-4 h-9 flex items-center text-[11px] font-bold uppercase whitespace-nowrap transition-colors",
isActive
? "text-brand-primary border-b-2 border-brand-primary"
: "text-white/70 hover:text-white"
"rounded-full px-4 py-1.5 transition-colors",
"text-muted-foreground hover:text-foreground hover:bg-muted",
isActive && "bg-primary text-primary-foreground"
)}
>
{item.isNew && (
<span className="absolute top-1 right-1 text-[7px] text-primary font-black leading-none">NEW</span>
)}
{item.label}
</Link>
)
);
})}
</div>
{/* ===== MOBILE: Sport Category Icons Row ===== */}
<div className="flex md:hidden overflow-x-auto scrollbar-none bg-[#333] border-t border-white/5 py-2 px-2 gap-4">
{[
{ label: "Check Bet", icon: (active: boolean) => <svg viewBox="0 0 24 24" className="size-5 fill-white/80"><path d="M20 4H4c-1.11 0-2 .89-2 2v12c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6c0-1.11-.89-2-2-2zm0 14H4v-6h16v6zm0-10H4V6h16v2z"/></svg>, count: 99 },
{ label: "Live", icon: (active: boolean) => <div className="size-2 bg-brand-live rounded-full animate-pulse" />, count: 1247 },
{ label: "Football", icon: (active: boolean) => <svg viewBox="0 0 24 24" className="size-5 fill-white/80"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-14c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6z"/></svg>, count: 95 },
{ label: "Tennis", icon: (active: boolean) => <svg viewBox="0 0 24 24" className="size-5 fill-white/80"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></svg>, count: 384 },
{ label: "Basketball", icon: (active: boolean) => <svg viewBox="0 0 24 24" className="size-5 fill-white/80"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-14c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6z"/></svg>, count: 173 },
].map((item, idx) => (
<div key={idx} className="flex flex-col items-center gap-1 shrink-0 px-2 min-w-[60px] relative">
<span className="absolute -top-1 right-0 bg-white/20 text-white text-[9px] px-1 rounded-sm font-bold">{item.count}</span>
<div className="size-10 rounded-full bg-white/5 flex items-center justify-center border border-white/5">
{typeof item.icon === 'function' ? item.icon(false) : item.icon}
</div>
<span className="text-[9px] text-white/60 font-medium uppercase tracking-tighter">{item.label}</span>
</div>
))}
</div>
{/* ===== DESKTOP: Secondary sub-header ===== */}
<div className="hidden md:flex bg-brand-surface-light border-t border-white/5 h-8 px-3 items-center gap-6 text-[11px]">
{[
{ label: "Sport Home", href: "/" },
{ label: "General View", href: "/live", forceActive: isLivePage },
{ label: "Event View", href: "/live/event" },
].map((tab) => {
const isActive = tab.forceActive || pathname === tab.href
return (
<Link key={tab.label} href={tab.href}
className={cn(
"relative h-full flex items-center text-[10px] font-bold uppercase transition-colors tracking-tight px-1",
isActive ? "text-brand-primary" : "text-white/60 hover:text-white"
)}
>
{tab.label}
{isActive && tab.label !== "Sport Home" && (
<div className="absolute bottom-0 left-0 right-0 h-[2px] bg-brand-primary" />
)}
</Link>
)
})}
</div>
</header>
{/* ===== MOBILE Drawer ===== */}
{drawerOpen && (
<div className="fixed inset-0 z-[9998] flex md:hidden">
{/* Backdrop */}
<div className="absolute inset-0 bg-black/60" onClick={() => setDrawerOpen(false)} />
{/* Drawer panel */}
<div className="relative w-72 bg-brand-bg h-full flex flex-col shadow-2xl animate-slide-in-left overflow-y-auto">
{/* Drawer header */}
<div className="flex items-center justify-between px-4 py-3 bg-brand-surface border-b border-white/10">
<div className="flex items-center">
<div className="bg-brand-accent px-2 py-0.5 -skew-x-12 flex items-center h-[24px]">
<span className="text-base font-black text-white italic tracking-tighter skew-x-12 leading-none">HARIF</span>
</div>
<span className="text-base font-black text-brand-primary italic tracking-tighter ml-1 leading-none">SPORT</span>
</div>
<button onClick={() => setDrawerOpen(false)} className="text-white/60 hover:text-white text-2xl leading-none">×</button>
</div>
{/* Nav links */}
<nav className="flex-1 py-2">
{drawerLinks.map((link) => (
<Link
key={link.href}
href={link.href}
className={cn(
"flex items-center px-5 py-3 text-[13px] font-semibold border-b border-white/5 transition-colors",
pathname === link.href ? "text-brand-primary bg-white/5" : "text-white/80 hover:text-white hover:bg-white/5"
)}
>
{link.label}
</Link>
))}
</nav>
</div>
</div>
)}
</>
</nav>
</div>
</header>
)
}
}

View File

@ -1,164 +1,32 @@
"use client"
import { useState } from "react"
import Link from "next/link"
import { popularLeagues } from "@/lib/mock-data"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
const sportCategories = [
{ id: "football", name: "Football", icon: "⚽", count: 1412 },
{ id: "tennis", name: "Tennis", icon: "🎾", count: 67 },
{ id: "basketball", name: "Basketball", icon: "🏀", count: 255 },
{ id: "ice-hockey", name: "Ice Hockey", icon: "🏒", count: 238 },
{ id: "mma", name: "MMA", icon: "🥊", count: 51 },
{ id: "handball", name: "Handball", icon: "🤾", count: 92 },
{ id: "darts", name: "Darts", icon: "🎯", count: 25 },
{ id: "snooker", name: "Snooker", icon: "🎱", count: 3 },
{ id: "cricket", name: "Cricket", icon: "🏏", count: 42 },
{ id: "dota2", name: "Dota 2", icon: "🎮", count: 2 },
{ id: "rugby", name: "Rugby", icon: "🏉", count: 41 },
{ id: "volleyball", name: "Volleyball", icon: "🏐", count: 69 },
const sports = [
"Soccer",
"Basketball",
"Tennis",
"Ice Hockey",
"Volleyball",
"Handball",
"Baseball",
"American Football",
"Cricket",
"Rugby",
]
export function SportsSidebar() {
const [activeSport, setActiveSport] = useState("football")
return (
<aside className="hidden h-full w-[240px] shrink-0 bg-brand-surface-light lg:block overflow-y-auto border-r border-border/40 scrollbar-hide">
{/* Sports Menu Header */}
<div className="bg-brand-surface px-3 py-2 text-[11px] font-black text-brand-primary uppercase tracking-tighter flex items-center justify-between border-b border-border/30">
<aside className="hidden h-full w-52 shrink-0 border-r bg-sidebar px-3 py-3 text-sm text-sidebar-foreground lg:block">
<div className="mb-3 font-semibold uppercase tracking-wide text-xs text-muted-foreground">
Sports Menu
<svg viewBox="0 0 24 24" className="size-3.5 fill-current opacity-60"><path d="M15.41 16.59L10.83 12l4.58-4.59L14 6l-6 6 6 6 1.41-1.41z"/></svg>
</div>
{/* Top Leagues Header */}
<div className="bg-brand-surface-light px-3 py-2.5 text-[11px] font-black text-brand-primary uppercase text-center border-b border-border/20 flex items-center justify-center gap-2">
Top Leagues
</div>
{/* Popular Leagues */}
<div className="flex flex-col">
{popularLeagues.map((league) => (
<Link
key={league.id}
href={`/?league=${league.id}`}
className="w-full flex items-center justify-between px-3 py-2 text-left text-white/90 hover:bg-brand-surface transition-colors border-b border-border/10 group h-9"
>
<div className="flex items-center gap-2.5 min-w-0">
<div className="size-4 shrink-0 overflow-hidden rounded-sm flex items-center justify-center bg-white/10 group-hover:bg-white/20 transition-colors">
{league.logo ? (
<img src={league.logo} alt="" className="size-full object-contain" />
) : (
<span className="text-[10px] leading-none invert"></span>
)}
</div>
<span className="text-[10.5px] font-bold leading-tight truncate max-w-[150px]">{league.name}</span>
</div>
<div className="size-3.5 rounded-full border border-white/20 flex items-center justify-center group-hover:border-brand-primary transition-colors shrink-0">
<div className="size-1.5 bg-white/40 rounded-full group-hover:bg-brand-primary" />
</div>
</Link>
<ul className="space-y-1">
{sports.map((sport) => (
<li key={sport}>
<button className="w-full rounded px-2 py-1 text-left text-xs text-muted-foreground hover:bg-muted hover:text-foreground">
{sport}
</button>
</li>
))}
</div>
{/* In-Play Strip */}
<Button className="w-full bg-[#004d40] text-[#00bfa5] hover:bg-[#003d33] border-none py-2.5 h-auto text-[11px] font-black uppercase rounded-none tracking-[2px]">
<span className="size-2 rounded-full bg-[#ff9800] mr-2 live-dot shadow-[0_0_8px_#ff9800]"></span>
IN-PLAY
</Button>
{/* Quick Filter Section */}
<div className="bg-brand-surface p-3 border-b border-border/30">
<span className="text-brand-primary text-[10.5px] uppercase font-black block mb-2 tracking-tight">Quick Filter</span>
<div className="grid grid-cols-6 gap-[1px]">
{["All", "Today", "3h", "6h", "9h", "12h"].map((t) => (
<button key={t} className={cn(
"text-[10px] py-1.5 font-bold transition-colors",
t === "All" ? "bg-[#333] text-white" : "bg-[#2a2a2a] text-white/50 hover:text-white"
)}>{t}</button>
))}
</div>
</div>
{/* Search Event Section */}
<div className="bg-brand-surface p-3 border-b border-border/40">
<span className="text-brand-primary text-[10.5px] uppercase font-black block mb-1 tracking-tight">Search Event</span>
<p className="text-[9px] text-white/30 mb-2 leading-tight font-medium uppercase">Insert the events name or at least one team in the form below</p>
<div className="flex flex-col gap-1.5">
<div className="relative">
<input type="text" className="bg-brand-surface-light border-none text-white text-[11px] px-3 py-2 w-full focus:ring-0 placeholder:text-white/20" placeholder="Search" />
</div>
<Button className="bg-brand-primary text-black hover:bg-brand-primary-hover py-2 h-auto rounded-none text-[11px] font-black uppercase tracking-wider">Search</Button>
</div>
</div>
{/* Sport categories */}
<div className="divide-y divide-border/10 bg-brand-surface-light">
{sportCategories.map((sport) => (
<button
key={sport.id}
onClick={() => setActiveSport(sport.id)}
className={cn(
"w-full flex items-center justify-between px-3 py-2 text-left transition-colors border-b border-border/10 h-9",
activeSport === sport.id
? "bg-brand-surface text-white"
: "text-white/70 hover:bg-brand-surface hover:text-white"
)}
>
<div className="flex items-center gap-3">
<span className="text-[12px] opacity-80 shrink-0">{sport.icon}</span>
<span className="text-[10.5px] font-bold tracking-tight">{sport.name}</span>
</div>
<div className="flex items-center gap-2">
<span className="text-[10px] font-bold text-white/40">{sport.count}</span>
<span className="text-[11px] text-white/20"></span>
</div>
</button>
))}
</div>
{/* Bet Services */}
<div className="mt-2 text-[11px] font-bold text-brand-primary px-3 py-2 uppercase border-y border-border/20 bg-brand-surface">
Bet Services
</div>
<div className="grid grid-cols-3 gap-0 border-b border-border/10">
<Button variant="ghost" className="flex flex-col items-center justify-center py-4 h-auto rounded-none border-r border-border/10 hover:bg-brand-surface group">
<svg viewBox="0 0 24 24" className="size-5 mb-1.5 fill-muted-foreground group-hover:fill-brand-primary transition-colors"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm3.3 14.71L11 12.41V7h2v4.59l3.71 3.71-1.42 1.41z"/></svg>
<span className="text-[9px] text-white font-medium">Live Score</span>
</Button>
<Button variant="ghost" className="flex flex-col items-center justify-center py-4 h-auto rounded-none border-r border-border/10 hover:bg-brand-surface group">
<svg viewBox="0 0 24 24" className="size-5 mb-1.5 fill-muted-foreground group-hover:fill-brand-primary transition-colors"><path d="M5 9.2h3V19H5zM10.6 5h2.8v14h-2.8zm5.6 8H19v6h-2.8z"/></svg>
<span className="text-[9px] text-white font-medium">Results</span>
</Button>
<Button variant="ghost" className="flex flex-col items-center justify-center py-4 h-auto rounded-none hover:bg-brand-surface group">
<svg viewBox="0 0 24 24" className="size-5 mb-1.5 fill-muted-foreground group-hover:fill-brand-primary transition-colors"><path d="M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z"/></svg>
<span className="text-[9px] text-white font-medium">Print Odds</span>
</Button>
</div>
<div className="p-2 space-y-1">
<Button className="w-full bg-[#004d40] text-[#00bfa5] hover:bg-[#003d33] border-none py-2 h-auto text-[11px] font-bold uppercase rounded-none tracking-widest">
<span className="size-2 rounded-full bg-brand-primary mr-2 live-dot"></span>
IN-PLAY
</Button>
<div className="bg-brand-surface p-3 border border-border/10">
<span className="text-brand-primary text-[10px] uppercase font-bold block mb-2">Quick Filter</span>
<div className="grid grid-cols-6 gap-0.5">
{["All", "Today", "3h", "6h", "9h", "12h"].map((t) => (
<button key={t} className="text-[9px] py-1 bg-brand-surface-light text-white/70 hover:bg-brand-surface transition-colors">{t}</button>
))}
</div>
</div>
<div className="bg-brand-surface p-3 border border-border/10">
<span className="text-brand-primary text-[10px] uppercase font-bold block mb-2">Search Event</span>
<p className="text-[9px] text-white/40 mb-2 leading-tight">Insert the events name or at least one team in the form below</p>
<div className="flex flex-col gap-1.5">
<input type="text" className="bg-brand-surface-light border-none text-white text-[10px] px-2 py-1.5 focus:ring-0" placeholder="Search" />
<Button className="bg-brand-surface text-white hover:bg-brand-bg py-1.5 h-auto rounded-none text-[10px] font-bold uppercase transition-colors">Search</Button>
</div>
</div>
</div>
</ul>
</aside>
)
);
}

View File

@ -19,11 +19,6 @@ const buttonVariants = cva(
ghost:
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
link: "text-primary underline-offset-4 hover:underline",
"hs-primary": "bg-[#ff9800] text-black font-bold hover:bg-[#ffa726] rounded-none",
"hs-secondary": "bg-[#333] text-white hover:bg-[#444] border border-border/20 rounded-none",
"hs-maroon": "bg-[#852222] text-white font-bold hover:bg-[#962d2d] rounded-none",
"hs-inplay": "bg-[#004242] text-[#ff9800] font-bold hover:bg-[#005252] border-y border-border/10 rounded-none",
"hs-nav": "bg-[#1a1a1a] text-[#ff9800] font-bold hover:bg-[#222] border-r border-border/10 rounded-none",
},
size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3",

View File

@ -26,14 +26,12 @@ function Tabs({
}
const tabsListVariants = cva(
"rounded-lg p-[3px] group-data-[orientation=horizontal]/tabs:h-9 data-[variant=line]:rounded-none data-[variant=hs-home]:rounded-none data-[variant=hs-home]:bg-[#1a1a1a] data-[variant=hs-nav]:rounded-none data-[variant=hs-nav]:bg-[#1a1a1a] group/tabs-list text-muted-foreground inline-flex w-fit items-center justify-center group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col",
"rounded-lg p-[3px] group-data-[orientation=horizontal]/tabs:h-9 data-[variant=line]:rounded-none group/tabs-list text-muted-foreground inline-flex w-fit items-center justify-center group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col",
{
variants: {
variant: {
default: "bg-muted",
line: "gap-1 bg-transparent",
"hs-home": "w-full gap-0 border border-border/20",
"hs-nav": "w-full gap-0 border border-border/20 overflow-x-auto justify-start",
},
},
defaultVariants: {
@ -68,10 +66,8 @@ function TabsTrigger({
className={cn(
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring text-foreground/60 hover:text-foreground dark:text-muted-foreground dark:hover:text-foreground relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 group-data-[variant=default]/tabs-list:data-[state=active]:shadow-sm group-data-[variant=line]/tabs-list:data-[state=active]:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent",
"group-data-[variant=hs-home]/tabs-list:rounded-none group-data-[variant=hs-home]/tabs-list:h-12 group-data-[variant=hs-home]/tabs-list:text-[13px] group-data-[variant=hs-home]/tabs-list:font-extrabold group-data-[variant=hs-home]/tabs-list:uppercase group-data-[variant=hs-home]/tabs-list:data-[state=active]:bg-transparent group-data-[variant=hs-home]/tabs-list:data-[state=active]:text-white",
"group-data-[variant=hs-nav]/tabs-list:rounded-none group-data-[variant=hs-nav]/tabs-list:flex-col group-data-[variant=hs-nav]/tabs-list:min-w-[70px] group-data-[variant=hs-nav]/tabs-list:py-2 group-data-[variant=hs-nav]/tabs-list:gap-1 group-data-[variant=hs-nav]/tabs-list:border-r group-data-[variant=hs-nav]/tabs-list:border-border/10 group-data-[variant=hs-nav]/tabs-list:data-[state=active]:bg-[#222]",
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 data-[state=active]:text-foreground",
"after:bg-[#ff9800] after:absolute after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-0 group-data-[orientation=horizontal]/tabs:after:h-[3px] group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100 group-data-[variant=hs-home]/tabs-list:data-[state=active]:after:opacity-100",
"after:bg-foreground after:absolute after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100",
className
)}
{...props}

View File

@ -1,261 +0,0 @@
export type Market = {
id: string;
label: string;
odds: number;
};
export type Event = {
id: string;
sport: string;
sportIcon: string;
league: string;
country: string;
homeTeam: string;
awayTeam: string;
time: string;
date: string; // Added date for grouping
isLive: boolean;
liveMinute?: number;
homeScore?: number;
awayScore?: number;
markets: Market[];
totalMarkets: number;
};
export type SportCategory = {
id: string;
name: string;
icon: string;
count: number;
};
export const sportCategories: SportCategory[] = [
{ id: "soccer", name: "Soccer", icon: "⚽", count: 142 },
{ id: "basketball", name: "Basketball", icon: "🏀", count: 38 },
{ id: "tennis", name: "Tennis", icon: "🎾", count: 26 },
{ id: "ice-hockey", name: "Ice Hockey", icon: "🏒", count: 18 },
{ id: "volleyball", name: "Volleyball", icon: "🏐", count: 14 },
{ id: "handball", name: "Handball", icon: "🤾", count: 12 },
{ id: "baseball", name: "Baseball", icon: "⚾", count: 8 },
{ id: "american-football", name: "American Football", icon: "🏈", count: 6 },
{ id: "cricket", name: "Cricket", icon: "🏏", count: 4 },
{ id: "rugby", name: "Rugby", icon: "🏉", count: 3 },
{ id: "boxing", name: "Boxing", icon: "🥊", count: 2 },
{ id: "esports", name: "E-Sports", icon: "🎮", count: 22 },
];
export const mockEvents: Event[] = [
{
id: "01682",
sport: "Soccer",
sportIcon: "⚽",
league: "LaLiga",
country: "Spain",
homeTeam: "Athletic Bilbao",
awayTeam: "Elche CF",
time: "11:00",
date: "Feb 20, 26",
isLive: false,
markets: [
{ id: "1", label: "1", odds: 1.64 },
{ id: "x", label: "x", odds: 3.91 },
{ id: "2", label: "2", odds: 5.45 },
{ id: "o25", label: "Over (2.5)", odds: 1.93 },
{ id: "u25", label: "Under (2.5)", odds: 1.88 },
{ id: "1x", label: "1X", odds: 1.15 },
{ id: "12", label: "12", odds: 1.24 },
{ id: "x2", label: "X2", odds: 2.13 },
{ id: "yes", label: "Yes", odds: 1.96 },
{ id: "no", label: "No", odds: 1.86 },
],
totalMarkets: 112,
},
{
id: "01570",
sport: "Soccer",
sportIcon: "⚽",
league: "LaLiga",
country: "Spain",
homeTeam: "Real Sociedad",
awayTeam: "Real Oviedo",
time: "04:00",
date: "Feb 21, 26",
isLive: false,
markets: [
{ id: "1", label: "1", odds: 1.52 },
{ id: "x", label: "x", odds: 4.41 },
{ id: "2", label: "2", odds: 6.91 },
{ id: "o25", label: "Over (2.5)", odds: 1.98 },
{ id: "u25", label: "Under (2.5)", odds: 1.87 },
{ id: "1x", label: "1X", odds: 1.11 },
{ id: "12", label: "12", odds: 1.21 },
{ id: "x2", label: "X2", odds: 2.43 },
{ id: "yes", label: "Yes", odds: 2.15 },
{ id: "no", label: "No", odds: 1.72 },
],
totalMarkets: 98,
},
{
id: "01541",
sport: "Soccer",
sportIcon: "⚽",
league: "LaLiga",
country: "Spain",
homeTeam: "Betis",
awayTeam: "Rayo Vallecano",
time: "06:15",
date: "Feb 21, 26",
isLive: false,
markets: [
{ id: "1", label: "1", odds: 1.92 },
{ id: "x", label: "x", odds: 3.55 },
{ id: "2", label: "2", odds: 4.10 },
{ id: "o25", label: "Over (2.5)", odds: 1.92 },
{ id: "u25", label: "Under (2.5)", odds: 1.88 },
{ id: "1x", label: "1X", odds: 1.23 },
{ id: "12", label: "12", odds: 1.28 },
{ id: "x2", label: "X2", odds: 1.80 },
{ id: "yes", label: "Yes", odds: 1.81 },
{ id: "no", label: "No", odds: 2.00 },
],
totalMarkets: 104,
},
{
id: "01605",
sport: "Soccer",
sportIcon: "⚽",
league: "LaLiga",
country: "Spain",
homeTeam: "Osasuna",
awayTeam: "Real Madrid",
time: "08:30",
date: "Feb 21, 26",
isLive: false,
markets: [
{ id: "1", label: "1", odds: 4.73 },
{ id: "x", label: "x", odds: 4.05 },
{ id: "2", label: "2", odds: 1.69 },
{ id: "o25", label: "Over (2.5)", odds: 1.67 },
{ id: "u25", label: "Under (2.5)", odds: 2.20 },
{ id: "1x", label: "1X", odds: 2.03 },
{ id: "12", label: "12", odds: 1.23 },
{ id: "x2", label: "X2", odds: 1.18 },
{ id: "yes", label: "Yes", odds: 1.70 },
{ id: "no", label: "No", odds: 2.15 },
],
totalMarkets: 128,
},
{
id: "01604",
sport: "Soccer",
sportIcon: "⚽",
league: "LaLiga",
country: "Spain",
homeTeam: "Atletico Madrid",
awayTeam: "Espanyol",
time: "11:00",
date: "Feb 21, 26",
isLive: false,
markets: [
{ id: "1", label: "1", odds: 1.51 },
{ id: "x", label: "x", odds: 4.57 },
{ id: "2", label: "2", odds: 6.81 },
{ id: "o25", label: "Over (2.5)", odds: 1.88 },
{ id: "u25", label: "Under (2.5)", odds: 1.96 },
{ id: "1x", label: "1X", odds: 1.11 },
{ id: "12", label: "12", odds: 1.20 },
{ id: "x2", label: "X2", odds: 2.43 },
{ id: "yes", label: "Yes", odds: 2.05 },
{ id: "no", label: "No", odds: 1.78 },
],
totalMarkets: 156,
},
{
id: "01672",
sport: "Soccer",
sportIcon: "⚽",
league: "Ligue 1",
country: "France",
homeTeam: "Brest",
awayTeam: "Marseille",
time: "10:45",
date: "Feb 20, 26",
isLive: false,
markets: [
{ id: "1", label: "1", odds: 3.91 },
{ id: "x", label: "x", odds: 3.80 },
{ id: "2", label: "2", odds: 1.97 },
{ id: "o25", label: "Over (2.5)", odds: 1.75 },
{ id: "u25", label: "Under (2.5)", odds: 2.15 },
{ id: "1x", label: "1X", odds: 1.80 },
{ id: "12", label: "12", odds: 1.27 },
{ id: "x2", label: "X2", odds: 1.26 },
{ id: "yes", label: "Yes", odds: 1.67 },
{ id: "no", label: "No", odds: 2.20 },
],
totalMarkets: 142,
},
{
id: "00241",
sport: "Soccer",
sportIcon: "⚽",
league: "LaLiga 2",
country: "Spain",
homeTeam: "AD Ceuta",
awayTeam: "Granada",
time: "10:30",
date: "Feb 20, 26",
isLive: false,
markets: [
{ id: "1", label: "1", odds: 2.55 },
{ id: "x", label: "x", odds: 3.20 },
{ id: "2", label: "2", odds: 2.75 },
{ id: "o25", label: "Over (2.5)", odds: 2.15 },
{ id: "u25", label: "Under (2.5)", odds: 1.66 },
{ id: "1x", label: "1X", odds: 1.40 },
{ id: "12", label: "12", odds: 1.32 },
{ id: "x2", label: "X2", odds: 1.45 },
{ id: "yes", label: "Yes", odds: 1.85 },
{ id: "no", label: "No", odds: 1.85 },
],
totalMarkets: 110,
},
{
id: "01124",
sport: "Soccer",
sportIcon: "⚽",
league: "Bundesliga",
country: "Germany",
homeTeam: "Mainz",
awayTeam: "Hamburger SV",
time: "10:30",
date: "Feb 20, 26",
isLive: false,
markets: [
{ id: "1", label: "1", odds: 2.11 },
{ id: "x", label: "x", odds: 3.40 },
{ id: "2", label: "2", odds: 3.58 },
{ id: "o25", label: "Over (2.5)", odds: 1.85 },
{ id: "u25", label: "Under (2.5)", odds: 1.96 },
{ id: "1x", label: "1X", odds: 1.28 },
{ id: "12", label: "12", odds: 1.30 },
{ id: "x2", label: "X2", odds: 1.66 },
{ id: "yes", label: "Yes", odds: 1.71 },
{ id: "no", label: "No", odds: 2.15 },
],
totalMarkets: 125,
},
];
export const popularLeagues = [
{ id: "ucl", name: "UEFA Champions League", logo: "https://upload.wikimedia.org/wikipedia/en/thumb/b/bf/UEFA_Champions_League_logo_2.svg/300px-UEFA_Champions_League_logo_2.svg.png" },
{ id: "uel", name: "UEFA Europa League", logo: "https://upload.wikimedia.org/wikipedia/en/thumb/0/0c/UEFA_Europa_League_logo_%282021%29.svg/300px-UEFA_Europa_League_logo_%282021%29.svg.png" },
{ id: "epl", name: "Premier League", country: "England", icon: "🏴󠁧󠁢󠁥󠁮󠁧󠁿", logo: "https://upload.wikimedia.org/wikipedia/en/thumb/f/f2/Premier_League_Logo.svg/300px-Premier_League_Logo.svg.png" },
{ id: "laliga", name: "La Liga", country: "Spain", icon: "🇪<>", logo: "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0f/LaLiga_logo_2023.svg/300px-LaLiga_logo_2023.svg.png" },
{ id: "laliga2", name: "LaLiga 2", country: "Spain", icon: "🇪🇸", logo: "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0f/LaLiga_logo_2023.svg/300px-LaLiga_logo_2023.svg.png" },
{ id: "bundesliga", name: "Bundesliga", country: "Germany", icon: "🇩🇪", logo: "https://upload.wikimedia.org/wikipedia/en/thumb/d/df/Bundesliga_logo_%282017%29.svg/300px-Bundesliga_logo_%282017%29.svg.png" },
{ id: "seriea", name: "Serie A", country: "Italy", icon: "🇮🇹", logo: "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e1/Serie_A_logo_%282019%29.svg/300px-Serie_A_logo_%282019%29.svg.png" },
{ id: "ligue1", name: "Ligue 1", country: "France", icon: "🇫🇷", logo: "https://upload.wikimedia.org/wikipedia/en/thumb/d/d4/Ligue_1_Uber_Eats_logo.svg/300px-Ligue_1_Uber_Eats_logo.svg.png" },
{ id: "ligue2", name: "Ligue 2", country: "France", icon: "<22><>", logo: "https://upload.wikimedia.org/wikipedia/en/thumb/9/91/Ligue_2_logo_2020.svg/300px-Ligue_2_logo_2020.svg.png" },
{ id: "eredivisie", name: "Eredivisie", country: "Netherlands", icon: "<22><>", logo: "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0f/Eredivisie_logo_2017.svg/300px-Eredivisie_logo_2017.svg.png" },
];

View File

@ -1,75 +1,38 @@
import { create } from "zustand";
export type OddsFormat = "decimal" | "fractional" | "american";
export type OddsFormat = "decimal";
export type Bet = {
id: string;
event: string;
league: string;
market: string;
selection: string;
odds: number;
stake?: number;
};
type BetslipState = {
bets: Bet[];
oddsFormat: OddsFormat;
defaultStake: number;
addBet: (bet: Bet) => void;
removeBet: (id: string) => void;
clearBets: () => void;
updateStake: (id: string, stake: number) => void;
setOddsFormat: (format: OddsFormat) => void;
setDefaultStake: (stake: number) => void;
getTotalOdds: () => number;
getTotalStake: () => number;
getPotentialWin: () => number;
};
export const useBetslipStore = create<BetslipState>((set, get) => ({
export const useBetslipStore = create<BetslipState>((set) => ({
bets: [],
oddsFormat: "decimal",
defaultStake: 10,
addBet: (bet) =>
set((state) => {
const exists = state.bets.some((b) => b.id === bet.id);
if (exists) {
// Toggle off if already selected
return { bets: state.bets.filter((b) => b.id !== bet.id) };
return state;
}
return { bets: [...state.bets, { ...bet, stake: state.defaultStake }] };
return { bets: [...state.bets, bet] };
}),
removeBet: (id) =>
set((state) => ({
bets: state.bets.filter((bet) => bet.id !== id),
})),
clearBets: () => set({ bets: [] }),
updateStake: (id, stake) =>
set((state) => ({
bets: state.bets.map((b) => (b.id === id ? { ...b, stake } : b)),
})),
setOddsFormat: (format) => set({ oddsFormat: format }),
setDefaultStake: (stake) => set({ defaultStake: stake }),
getTotalOdds: () => {
const bets = get().bets;
if (bets.length === 0) return 0;
return bets.reduce((acc, b) => acc * b.odds, 1);
},
getTotalStake: () => {
const bets = get().bets;
return bets.reduce((acc, b) => acc + (b.stake ?? 0), 0);
},
getPotentialWin: () => {
const bets = get().bets;
if (bets.length === 0) return 0;
if (bets.length === 1) {
const b = bets[0];
return (b.stake ?? 0) * b.odds;
}
// Accumulator: use first bet's stake
const stake = bets[0].stake ?? 0;
const totalOdds = bets.reduce((acc, b) => acc * b.odds, 1);
return stake * totalOdds;
},
}));
}));

4
package-lock.json generated
View File

@ -1,11 +1,11 @@
{
"name": "fortune-play",
"name": "harifsport-ui",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "fortune-play",
"name": "harifsport-ui",
"version": "0.1.0",
"dependencies": {
"class-variance-authority": "^0.7.1",