feat: Add layout components including header, footer, sidebar, and tabs for improved navigation and user experience

This commit is contained in:
brooktewabe 2026-02-20 12:21:00 +03:00
parent 86bd615b9e
commit cfb6b61120
5 changed files with 334 additions and 46 deletions

View File

@ -0,0 +1,24 @@
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

@ -0,0 +1,24 @@
"use client"
import { usePathname } from "next/navigation"
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"
export default function LayoutClientWrapper({ children }: { children: React.ReactNode }) {
const pathname = usePathname()
const isLivePage = pathname === "/live"
return (
<div className="flex min-h-screen flex-col">
<SiteHeader />
<div className="flex w-full flex-1 gap-0">
{!isLivePage && <SportsSidebar />}
<main className="flex-1 px-2 py-3 min-w-0">{children}</main>
<RightPanel />
</div>
<SiteFooter />
</div>
)
}

View File

@ -1,24 +1,61 @@
"use client"
import { Betslip } from "@/components/betting/betslip" import { Betslip } from "@/components/betting/betslip"
import { ReloadTicket } from "@/components/betting/reload-ticket" import { ReloadTicket } from "@/components/betting/reload-ticket"
import { CheckYourBet } from "@/components/betting/check-your-bet" import { CheckYourBet } from "@/components/betting/check-your-bet"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { useState } from "react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
export function RightPanel() { export function RightPanel() {
const [activeTab, setActiveTab] = useState<"betslip" | "myBets">("betslip")
return ( return (
<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"> <aside className="hidden lg:flex w-[280px] shrink-0 flex-col gap-0 border-l border-border/30 bg-[#222]">
<Betslip /> {/* Search Header */}
<Card> <div className="p-3 bg-[#1a1a1a] flex items-center justify-between border-b border-border/20">
<CardHeader> <div className="flex items-center gap-2 text-[12px] font-bold text-white uppercase">
<CardTitle className="text-sm">Settings</CardTitle> Fast Bet <span className="text-[#ff9800]">QBET</span>
</CardHeader> <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>
<CardContent className="text-xs text-muted-foreground"> </div>
Decimal odds (more settings coming soon) </div>
</CardContent>
</Card> <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 /> <ReloadTicket />
<CheckYourBet /> <CheckYourBet />
</div>
</aside> </aside>
); )
} }

View File

@ -0,0 +1,82 @@
"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,42 +1,163 @@
"use client"
import Link from "next/link" import Link from "next/link"
import { usePathname } from "next/navigation" import { usePathname } from "next/navigation"
import { useState, useEffect } from "react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
const navItems = [ const mainNavItems = [
{ href: "/", label: "Sport Home" }, { href: "/", label: "ALL SPORTS" },
{ href: "/live", label: "Live" }, { href: "/live", label: "LIVE" },
{ href: "/virtual", label: "VIRTUAL" },
{ href: "/special-games", label: "SPECIAL GAMES" },
{ href: "/aviator", label: "AVIATOR" },
]
const extraNavItems = [
{ href: "/multi-hot-5", label: "MULTI HOT 5" },
{ href: "/poker", label: "POKER", isNew: true },
{ href: "/race", label: "RACE", isNew: true },
{ href: "/promo", label: "PROMO" },
] ]
export function SiteHeader() { export function SiteHeader() {
const pathname = usePathname() const pathname = usePathname()
const isLivePage = pathname === "/live"
const [time, setTime] = useState("")
useEffect(() => {
const updateClock = () => {
const now = new Date()
setTime(now.toLocaleTimeString("en-GB", { hour12: false }))
}
updateClock()
const interval = setInterval(updateClock, 1000)
return () => clearInterval(interval)
}, [])
return ( return (
<header className="border-b bg-card"> <header className="bg-[#121212] sticky top-0 z-50">
<div className="mx-auto flex max-w-6xl items-center justify-between px-4 py-3"> {/* Top bar */}
<div className="text-lg font-semibold tracking-tight">Harifsport</div> <div className="bg-[#1a1a1a] px-3 py-1 flex items-center justify-between text-[11px] text-white">
<nav className="flex gap-2 text-sm"> <div className="flex items-center gap-4">
{navItems.map((item) => { <button className="flex items-center gap-1.5 hover:text-primary transition-colors">
const isActive = <img src="https://flagcdn.com/w20/gb.png" srcSet="https://flagcdn.com/w40/gb.png 2x" width="16" alt="English" className="rounded-sm" />
item.href === "/" <span className="font-bold flex items-center gap-1">en <span className="text-[8px]"></span></span>
? pathname === "/" </button>
: pathname?.startsWith(item.href) <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-[#2a2a2a] border-none px-2 py-0.5 w-32 text-[11px] h-7 rounded-none shadow-none text-white placeholder:text-gray-500" />
</div>
<div className="flex items-center gap-1">
<Button asChild className="bg-[#e67e22] text-white hover:bg-[#d35400] px-5 h-7 text-[11px] font-bold rounded-none uppercase">
<Link href="/login">Login</Link>
</Button>
<Button asChild className="bg-[#d35400] text-white hover:bg-[#c0392b] px-5 h-7 text-[11px] font-bold rounded-none uppercase">
<Link href="/register">Sign Up</Link>
</Button>
</div>
</div>
</div>
{/* Main header */}
<div className="flex items-center px-0 bg-[#333] h-[60px]">
{/* Logo Section */}
<Link href="/" className="flex items-center shrink-0">
<div className="flex items-center bg-[#1a1a1a] h-[60px] px-4">
<div className="bg-[#852222] 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-[#ff9800] italic tracking-tighter ml-1 leading-none">SPORT</span>
</div>
</Link>
{/* Navigation Wrapper - Pushed to Right */}
<div className="flex items-center flex-1 justify-end px-4 h-full gap-0">
{/* Home icon as button */}
<Link href="/" className="flex items-center justify-center bg-[#ff9800] h-[60px] w-[50px] shrink-0 hover:bg-[#e68900] transition-colors">
<svg viewBox="0 0 24 24" className="size-6 fill-black"><path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/></svg>
</Link>
{/* Main Nav Group */}
<nav className="flex items-center text-[10.5px] font-bold h-full">
{mainNavItems.map((item) => {
const isActive = pathname === item.href
return ( return (
<Link <Link
key={item.href} key={item.href}
href={item.href} href={item.href}
className={cn( className={cn(
"rounded-full px-4 py-1.5 transition-colors", "px-4 flex items-center h-full transition-colors uppercase border-white/5",
"text-muted-foreground hover:text-foreground hover:bg-muted", isActive
isActive && "bg-primary text-primary-foreground" ? (item.label === "LIVE" ? "bg-[#ff9800] text-black" : "text-primary bg-black/10")
: "text-white hover:text-primary"
)} )}
> >
{item.label} {item.label}
</Link> </Link>
); )
})} })}
</nav> </nav>
{/* Spacing Gap between groups if needed, but the user says "All in the right" */}
{/* Extra Nav Group */}
<nav className="flex items-center text-[10.5px] font-bold h-full">
{extraNavItems.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>
{/* Secondary Sub Header */}
<div className="bg-[#2a2a2a] border-t border-white/5 h-8 px-3 flex 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-[#ff9800]" : "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-[#ff9800]" />
)}
</Link>
)
})}
</div> </div>
</header> </header>
) )
} }