first project version mrDecor
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
import Image from 'next/image'
|
||||
import { Instagram, MessageCircle, Linkedin } from 'lucide-react'
|
||||
import { useContent } from '@/components/layout/content-provider'
|
||||
|
||||
export default function Footer5() {
|
||||
const content = useContent()
|
||||
const { footer } = content.navigation
|
||||
const logoAlt = footer.logoAlt
|
||||
const logoUrl = content.images.logo
|
||||
|
||||
return (
|
||||
<footer className="m-1 rounded-3xl border border-border dark:border-primary/20 bg-card">
|
||||
<div className="mx-auto max-w-5xl px-5 py-8">
|
||||
<div className="flex flex-wrap items-center justify-between gap-4 border-b pb-6">
|
||||
<Link
|
||||
href="/"
|
||||
aria-label="go home"
|
||||
className="flex items-center cursor-pointer">
|
||||
<Image
|
||||
src={logoUrl}
|
||||
alt={logoAlt}
|
||||
width={150}
|
||||
height={50}
|
||||
className="mx-auto h-10 w-auto object-contain object-center md:h-12"
|
||||
/>
|
||||
</Link>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-sm text-muted-foreground" dir="rtl">{footer.socialLabel}</span>
|
||||
<Link
|
||||
href="#"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="Instagram"
|
||||
className="text-muted-foreground transition-colors hover:text-primary dark:hover:text-primary">
|
||||
<Instagram className="size-5" />
|
||||
</Link>
|
||||
<Link
|
||||
href="#"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="Telegram"
|
||||
className="text-muted-foreground transition-colors hover:text-primary dark:hover:text-primary">
|
||||
<MessageCircle className="size-5" />
|
||||
</Link>
|
||||
<Link
|
||||
href="#"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="LinkedIn"
|
||||
className="text-muted-foreground transition-colors hover:text-primary dark:hover:text-primary">
|
||||
<Linkedin className="size-5" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-6 flex flex-wrap items-center justify-center gap-6">
|
||||
{footer.links.map((link, index) => (
|
||||
<Link
|
||||
key={index}
|
||||
href={link.href}
|
||||
className="text-sm text-muted-foreground hover:text-primary transition-colors duration-150">
|
||||
{link.name}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-6 flex flex-col items-center justify-center gap-2 text-center sm:flex-row sm:gap-4">
|
||||
{footer.phoneNumber && footer.phoneHref && (
|
||||
<a
|
||||
href={footer.phoneHref}
|
||||
className="text-sm text-muted-foreground hover:text-primary transition-colors duration-150"
|
||||
dir="ltr">
|
||||
{footer.phoneLabel && <span className="ms-1">{footer.phoneLabel}</span>}
|
||||
{footer.phoneNumber}
|
||||
</a>
|
||||
)}
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{footer.copyright}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,446 @@
|
||||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
import Image from 'next/image'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { BadgeCheck, BriefcaseBusiness, Gauge, Info, Menu, PhoneCall, ShieldCheck, Sparkles, Store, X } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import React from 'react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useScroll } from 'motion/react'
|
||||
import { useContent } from '@/components/layout/content-provider'
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import {
|
||||
NavigationMenu,
|
||||
NavigationMenuContent,
|
||||
NavigationMenuItem,
|
||||
NavigationMenuLink,
|
||||
NavigationMenuList,
|
||||
NavigationMenuTrigger,
|
||||
} from '@/components/ui/navigation-menu'
|
||||
|
||||
function isRouteActive(pathname: string, href: string): boolean {
|
||||
if (href === '/') return pathname === '/'
|
||||
return pathname === href || pathname.startsWith(`${href}/`)
|
||||
}
|
||||
|
||||
const HOME_HREF = '/'
|
||||
|
||||
export const HeroHeaderTailark = () => {
|
||||
const [menuState, setMenuState] = React.useState(false)
|
||||
const [scrolled, setScrolled] = React.useState(false)
|
||||
const pathname = usePathname() ?? ''
|
||||
const content = useContent()
|
||||
const { header } = content.navigation
|
||||
const { images } = content
|
||||
const menuItems = header.menuItems
|
||||
const productItems = header.productItems
|
||||
const wordmark = header.wordmark ?? content.meta.siteName
|
||||
const homeItem = menuItems.find((i) => i.href === HOME_HREF)
|
||||
const trailItems = menuItems.filter((i) => i.href !== HOME_HREF)
|
||||
const productMenuItems = productItems.map((item, index) => ({
|
||||
...item,
|
||||
icon: [Sparkles, Gauge, ShieldCheck][index] ?? BadgeCheck,
|
||||
}))
|
||||
const solutionMenuItems = [
|
||||
{
|
||||
name: 'درباره ما',
|
||||
href: '/about',
|
||||
description: 'آشنایی با چشم انداز، تجربه و مسیر توسعه مستر دکور',
|
||||
icon: Info,
|
||||
},
|
||||
{
|
||||
name: 'تماس با ما',
|
||||
href: '/contact',
|
||||
description: 'ارتباط مستقیم با تیم برای دریافت پاسخ سریع',
|
||||
icon: PhoneCall,
|
||||
},
|
||||
{
|
||||
name: 'مشاوره و قیمت',
|
||||
href: '/consultation',
|
||||
description: 'بررسی دقیق نیاز شما و پیشنهاد مسیر اجرایی مناسب',
|
||||
icon: BriefcaseBusiness,
|
||||
},
|
||||
]
|
||||
|
||||
const { scrollYProgress } = useScroll()
|
||||
|
||||
React.useEffect(() => {
|
||||
const unsubscribe = scrollYProgress.on('change', (latest) => {
|
||||
setScrolled(latest > 0.05)
|
||||
})
|
||||
return () => unsubscribe()
|
||||
}, [scrollYProgress])
|
||||
|
||||
return (
|
||||
<header className="pt-[env(safe-area-inset-top)]">
|
||||
<nav
|
||||
data-state={menuState && 'active'}
|
||||
className={cn(
|
||||
'fixed z-[100] w-full border-b transition-[background-color,box-shadow] duration-200',
|
||||
scrolled
|
||||
? 'border-border/80 bg-background/90 shadow-sm shadow-black/5 backdrop-blur-xl'
|
||||
: 'border-transparent bg-background/70 backdrop-blur-md'
|
||||
)}>
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6">
|
||||
<div className="relative flex min-h-[3.5rem] flex-wrap items-center justify-between gap-3 py-2.5 lg:min-h-0 lg:flex-nowrap lg:py-3">
|
||||
<div className="flex w-full min-w-0 flex-1 items-center gap-2 lg:w-auto lg:flex-initial">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setMenuState(!menuState)}
|
||||
aria-label={menuState ? 'بستن منو' : 'باز کردن منو'}
|
||||
className="relative z-20 -m-1.5 flex size-10 shrink-0 items-center justify-center rounded-full border border-transparent hover:bg-accent/60 lg:hidden">
|
||||
<AnimatePresence mode="wait">
|
||||
{menuState ? (
|
||||
<motion.span
|
||||
key="close"
|
||||
initial={{ rotate: -90, opacity: 0 }}
|
||||
animate={{ rotate: 0, opacity: 1 }}
|
||||
exit={{ rotate: 90, opacity: 0 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
className="inline-flex">
|
||||
<X className="size-5" />
|
||||
</motion.span>
|
||||
) : (
|
||||
<motion.span
|
||||
key="menu"
|
||||
initial={{ rotate: 90, opacity: 0 }}
|
||||
animate={{ rotate: 0, opacity: 1 }}
|
||||
exit={{ rotate: -90, opacity: 0 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
className="inline-flex">
|
||||
<Menu className="size-5" />
|
||||
</motion.span>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</button>
|
||||
|
||||
<Link
|
||||
href="/"
|
||||
className="flex min-w-0 shrink items-center gap-2.5 rounded-xl py-1 pe-1 ps-0 transition-opacity hover:opacity-90"
|
||||
aria-label={wordmark}>
|
||||
<Image
|
||||
src={images.logo}
|
||||
alt={header.logoAlt}
|
||||
width={180}
|
||||
height={60}
|
||||
className="mx-auto h-9 w-auto object-contain object-center sm:h-10"
|
||||
priority
|
||||
/>
|
||||
<span className="hidden truncate text-start text-base font-semibold tracking-tight text-foreground sm:inline md:text-lg">
|
||||
{wordmark}
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="order-last hidden w-full justify-center lg:order-none lg:flex lg:w-auto lg:flex-1 lg:justify-center">
|
||||
<NavigationMenu
|
||||
dir="rtl"
|
||||
className="mx-auto"
|
||||
>
|
||||
<NavigationMenuList className="gap-3">
|
||||
{homeItem ? (
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuLink
|
||||
asChild
|
||||
className={cn(
|
||||
'inline-flex h-8 w-max items-center justify-center rounded-md px-4 py-1 text-body-sm font-medium transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1',
|
||||
isRouteActive(pathname, homeItem.href)
|
||||
? 'bg-foreground/5 text-foreground'
|
||||
: 'text-muted-foreground hover:bg-foreground/5 hover:text-foreground'
|
||||
)}
|
||||
>
|
||||
<Link href={homeItem.href}>{homeItem.name}</Link>
|
||||
</NavigationMenuLink>
|
||||
</NavigationMenuItem>
|
||||
) : null}
|
||||
|
||||
<NavigationMenuItem >
|
||||
<NavigationMenuTrigger
|
||||
className={cn(
|
||||
'inline-flex h-8 w-max items-center justify-center rounded-md px-4 py-1 text-body-sm font-medium transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1',
|
||||
pathname.startsWith('/product')
|
||||
? 'bg-foreground/5 text-foreground'
|
||||
: 'text-muted-foreground hover:bg-foreground/5 hover:text-foreground',
|
||||
)}
|
||||
>
|
||||
{header.productsLabel}
|
||||
</NavigationMenuTrigger>
|
||||
<NavigationMenuContent className="p-3" dir="rtl">
|
||||
<div className="grid gap-3 lg:grid-cols-3 w-[75rem] max-w-[calc(100vw-2rem)]">
|
||||
<div className="space-y-2 lg:col-span-2">
|
||||
<div className="px-2">
|
||||
<p className="text-caption font-medium text-muted-foreground">محصولات</p>
|
||||
</div>
|
||||
<ul className="grid gap-1 sm:grid-cols-2">
|
||||
{productMenuItems.map((product) => {
|
||||
const Icon = product.icon
|
||||
return (
|
||||
<li key={`${product.name}-${product.href}`}>
|
||||
<NavigationMenuLink asChild>
|
||||
<Link
|
||||
href={product.href}
|
||||
className="flex items-start gap-3 rounded-xl border border-transparent px-3 py-3 text-start transition-colors hover:border-border/80 hover:bg-accent/60"
|
||||
>
|
||||
<span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-muted">
|
||||
<Icon className="h-4 w-4 text-muted-foreground" />
|
||||
</span>
|
||||
<span className="space-y-0.5">
|
||||
<span className="block text-body-sm font-medium text-foreground">{product.name}</span>
|
||||
<span className="block text-caption leading-snug text-muted-foreground">{product.description}</span>
|
||||
</span>
|
||||
</Link>
|
||||
</NavigationMenuLink>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href="/product"
|
||||
className="group relative hidden overflow-hidden rounded-2xl border border-border bg-card lg:block"
|
||||
>
|
||||
<Image
|
||||
src="/assets/main-pic/product/sampleghaleb.png"
|
||||
alt="نمای کلی محصولات"
|
||||
width={600}
|
||||
height={760}
|
||||
className="h-full w-full object-cover transition-transform duration-300 group-hover:scale-[1.02]"
|
||||
/>
|
||||
<div className="absolute inset-x-0 bottom-0 bg-gradient-to-t from-background/90 to-transparent p-4">
|
||||
<p className="text-body-sm font-medium text-foreground">اکوسیستم یکپارچه محصولات</p>
|
||||
<p className="mt-1 text-caption text-muted-foreground">برای مشاهده جزئیات کامل همه محصولات کلیک کنید</p>
|
||||
</div>
|
||||
<span className="absolute start-3 top-3 inline-flex items-center rounded-full bg-background/90 px-2 py-1 text-caption font-medium text-foreground">
|
||||
<Store className="me-1 h-3.5 w-3.5" />
|
||||
{productMenuItems.length} محصول
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
</NavigationMenuContent>
|
||||
</NavigationMenuItem>
|
||||
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuLink
|
||||
asChild
|
||||
className={cn(
|
||||
'inline-flex h-8 w-max items-center justify-center rounded-md px-4 py-1 text-body-sm font-medium transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1',
|
||||
isRouteActive(pathname, '/after-sales')
|
||||
? 'bg-foreground/5 text-foreground'
|
||||
: 'text-muted-foreground hover:bg-foreground/5 hover:text-foreground'
|
||||
)}
|
||||
>
|
||||
<Link href="/after-sales">خدمات پس از فروش</Link>
|
||||
</NavigationMenuLink>
|
||||
</NavigationMenuItem>
|
||||
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuLink
|
||||
asChild
|
||||
className={cn(
|
||||
'inline-flex h-8 w-max items-center justify-center rounded-md px-4 py-1 text-body-sm font-medium transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1',
|
||||
isRouteActive(pathname, '/warranty')
|
||||
? 'bg-foreground/5 text-foreground'
|
||||
: 'text-muted-foreground hover:bg-foreground/5 hover:text-foreground'
|
||||
)}
|
||||
>
|
||||
<Link href="/warranty">گارانتی</Link>
|
||||
</NavigationMenuLink>
|
||||
</NavigationMenuItem>
|
||||
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuLink
|
||||
asChild
|
||||
className={cn(
|
||||
'inline-flex h-8 w-max items-center justify-center rounded-md px-4 py-1 text-body-sm font-medium transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1',
|
||||
isRouteActive(pathname, '/faq')
|
||||
? 'bg-foreground/5 text-foreground'
|
||||
: 'text-muted-foreground hover:bg-foreground/5 hover:text-foreground'
|
||||
)}
|
||||
>
|
||||
<Link href="/faq">سوالات متداول</Link>
|
||||
</NavigationMenuLink>
|
||||
</NavigationMenuItem>
|
||||
|
||||
<NavigationMenuItem >
|
||||
<NavigationMenuTrigger className="inline-flex h-8 w-max items-center justify-center rounded-md px-4 py-1 text-body-sm font-medium text-muted-foreground transition-[color,box-shadow] hover:bg-foreground/5 hover:text-foreground focus-visible:ring-[3px] focus-visible:outline-1">
|
||||
پیوند ها
|
||||
</NavigationMenuTrigger>
|
||||
<NavigationMenuContent className="p-3" dir="rtl">
|
||||
<div className="grid gap-3 lg:grid-cols-3 w-[75rem] max-w-[calc(100vw-2rem)]">
|
||||
<div className="space-y-2 lg:col-span-2">
|
||||
<div className="px-2">
|
||||
<p className="text-caption font-medium text-muted-foreground">منوی اصلی</p>
|
||||
</div>
|
||||
<ul className="grid gap-1 sm:grid-cols-2">
|
||||
{solutionMenuItems.map((item) => {
|
||||
const Icon = item.icon
|
||||
const active = isRouteActive(pathname, item.href)
|
||||
return (
|
||||
<li key={item.href}>
|
||||
<NavigationMenuLink asChild>
|
||||
<Link
|
||||
href={item.href}
|
||||
className={cn(
|
||||
'flex items-start gap-3 rounded-xl border border-transparent px-3 py-3 text-start transition-colors hover:border-border/80 hover:bg-accent/60',
|
||||
active && 'border-border/80 bg-accent/60'
|
||||
)}
|
||||
>
|
||||
<span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-muted">
|
||||
<Icon className="h-4 w-4 text-muted-foreground" />
|
||||
</span>
|
||||
<span className="space-y-0.5">
|
||||
<span className="block text-body-sm font-medium text-foreground">{item.name}</span>
|
||||
<span className="block text-caption leading-snug text-muted-foreground">{item.description}</span>
|
||||
</span>
|
||||
</Link>
|
||||
</NavigationMenuLink>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href="/consultation"
|
||||
className="group relative hidden overflow-hidden rounded-2xl border border-border bg-card lg:block"
|
||||
>
|
||||
<Image
|
||||
src="/assets/main-pic/feature/feature1.webp"
|
||||
alt="راهکارهای مشاوره و پیاده سازی"
|
||||
width={600}
|
||||
height={760}
|
||||
className="h-full max-h-[22rem] w-full object-cover transition-transform duration-300 group-hover:scale-[1.02]"
|
||||
/>
|
||||
<div className="absolute inset-x-0 bottom-0 bg-gradient-to-t from-background/90 to-transparent p-4">
|
||||
<p className="text-body-sm font-medium text-foreground">همراهی تا استقرار نهایی</p>
|
||||
<p className="mt-1 text-caption text-muted-foreground">از مشاوره اولیه تا اجرای عملیاتی کنار شما هستیم</p>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</NavigationMenuContent>
|
||||
</NavigationMenuItem>
|
||||
</NavigationMenuList>
|
||||
</NavigationMenu>
|
||||
</div>
|
||||
|
||||
<div className="flex shrink-0 items-center gap-2 sm:gap-3">
|
||||
<Button asChild size="sm" variant="primary" className="hidden sm:inline-flex">
|
||||
<Link href="/consultation">{header.demoButton}</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="lg:hidden">
|
||||
<AnimatePresence>
|
||||
{menuState && (
|
||||
<>
|
||||
<motion.div
|
||||
aria-hidden
|
||||
onClick={() => setMenuState(false)}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
className="fixed inset-0 z-[99] bg-background/50 backdrop-blur-[2px]"
|
||||
/>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -8 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -8 }}
|
||||
transition={{ duration: 0.22, ease: [0.32, 0.72, 0, 1] }}
|
||||
className="relative z-[100] -mx-1 mb-3 overflow-hidden rounded-2xl border border-border/60 bg-card/95 shadow-lg shadow-black/10 backdrop-blur-xl">
|
||||
<nav className="max-h-[min(70vh,28rem)] overflow-y-auto overscroll-contain px-3 py-3" aria-label="منوی موبایل">
|
||||
{homeItem ? (
|
||||
<>
|
||||
<p className="text-caption mb-2 px-2 font-medium uppercase tracking-wider text-muted-foreground">
|
||||
منو
|
||||
</p>
|
||||
<ul className="space-y-1">
|
||||
<li>
|
||||
<Link
|
||||
href={homeItem.href}
|
||||
onClick={() => setMenuState(false)}
|
||||
className={cn(
|
||||
'flex items-center rounded-xl px-3 py-2.5 text-body-sm transition-colors',
|
||||
isRouteActive(pathname, homeItem.href)
|
||||
? 'bg-primary/12 font-medium text-foreground'
|
||||
: 'text-muted-foreground hover:bg-accent/50 hover:text-foreground'
|
||||
)}>
|
||||
{homeItem.name}
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="my-3 border-t border-border/60" />
|
||||
</>
|
||||
) : null}
|
||||
|
||||
<p className="text-caption mb-2 px-2 font-medium uppercase tracking-wider text-muted-foreground">
|
||||
{header.productsLabel}
|
||||
</p>
|
||||
<ul className="grid gap-2 sm:grid-cols-2">
|
||||
{productMenuItems.map((product) => {
|
||||
const Icon = product.icon
|
||||
return (
|
||||
<li key={`${product.name}-${product.href}`}>
|
||||
<Link
|
||||
href={product.href}
|
||||
onClick={() => setMenuState(false)}
|
||||
className="flex items-start gap-3 rounded-xl border border-border/60 bg-card/40 px-3 py-3 transition-colors hover:bg-muted/40">
|
||||
<span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-muted">
|
||||
<Icon className="h-4 w-4 text-muted-foreground" />
|
||||
</span>
|
||||
<span className="space-y-0.5">
|
||||
<span className="block text-body-sm font-medium text-foreground">{product.name}</span>
|
||||
<span className="block text-caption leading-snug text-muted-foreground">{product.description}</span>
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
)})}
|
||||
</ul>
|
||||
|
||||
{trailItems.length > 0 ? (
|
||||
<>
|
||||
<div className="my-3 border-t border-border/60" />
|
||||
<ul className="space-y-1">
|
||||
{trailItems.map((item, index) => {
|
||||
const active = isRouteActive(pathname, item.href)
|
||||
return (
|
||||
<li key={index}>
|
||||
<Link
|
||||
href={item.href}
|
||||
onClick={() => setMenuState(false)}
|
||||
className={cn(
|
||||
'flex items-center rounded-xl px-3 py-2.5 text-body-sm transition-colors',
|
||||
active
|
||||
? 'bg-primary/12 font-medium text-foreground'
|
||||
: 'text-muted-foreground hover:bg-accent/50 hover:text-foreground'
|
||||
)}>
|
||||
{item.name}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
<div className="mt-4 flex flex-col gap-2 border-t border-border/60 pt-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<Button asChild variant="primary" className="w-full sm:w-auto">
|
||||
<Link href="/consultation" onClick={() => setMenuState(false)}>
|
||||
{header.demoButton}
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</nav>
|
||||
</motion.div>
|
||||
</>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
'use client'
|
||||
import Link from 'next/link'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { Logo } from '@/components/navigation/logo'
|
||||
import { Menu, X } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import React from 'react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const menuItems = [
|
||||
{ name: 'صفحه اصلی', href: '/' },
|
||||
{ name: 'محصولات', href: '/product' },
|
||||
{ name: 'تماس با ما', href: '/contact' },
|
||||
]
|
||||
|
||||
|
||||
|
||||
function isLinkActive(pathname: string, href: string): boolean {
|
||||
if (href === '/') return pathname === '/'
|
||||
return pathname === href || pathname.startsWith(href + '/')
|
||||
}
|
||||
|
||||
export const HeroHeader = () => {
|
||||
const pathname = usePathname()
|
||||
const [menuState, setMenuState] = React.useState(false)
|
||||
const [isScrolled, setIsScrolled] = React.useState(false)
|
||||
|
||||
React.useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
setIsScrolled(window.scrollY > 50)
|
||||
}
|
||||
window.addEventListener('scroll', handleScroll)
|
||||
return () => window.removeEventListener('scroll', handleScroll)
|
||||
}, [])
|
||||
return (
|
||||
<header>
|
||||
<nav
|
||||
data-state={menuState && 'active'}
|
||||
className="fixed z-50 w-full px-2">
|
||||
<div className={cn('mx-auto mt-2 max-w-6xl px-2 transition-all duration-300 lg:px-2 bg-background/30 backdrop-blur-md rounded-md border border-border/10', isScrolled && 'bg-background/20 max-w-4xl shadow-lg lg:px-5')}>
|
||||
<div className="relative flex flex-wrap items-center justify-between gap-2 lg:gap-0">
|
||||
<div className="flex w-full justify-start gap-2 lg:w-auto">
|
||||
<button
|
||||
onClick={() => setMenuState(!menuState)}
|
||||
aria-label={menuState == true ? 'بستن منو' : 'باز کردن منو'}
|
||||
className="relative z-20 -m-2.5 -mr-4 block cursor-pointer p-2.5 lg:hidden">
|
||||
<Menu className="in-data-[state=active]:rotate-180 in-data-[state=active]:scale-0 in-data-[state=active]:opacity-0 m-auto size-4 duration-200" />
|
||||
<X className="in-data-[state=active]:rotate-0 in-data-[state=active]:scale-100 in-data-[state=active]:opacity-100 absolute inset-0 m-auto size-4 -rotate-180 scale-0 opacity-0 duration-200" />
|
||||
</button>
|
||||
<Logo />
|
||||
</div>
|
||||
|
||||
<div className="absolute inset-0 m-auto hidden size-fit lg:flex items-center">
|
||||
<ul className="flex gap-2 text-sm">
|
||||
{menuItems.map((item, index) => {
|
||||
const active = isLinkActive(pathname ?? '', item.href)
|
||||
return (
|
||||
<li key={index}>
|
||||
<Link
|
||||
href={item.href}
|
||||
className={cn(
|
||||
'text-muted-foreground hover:text-foreground hover:bg-accent/20 transition-all duration-300 relative group p-2 px-4 rounded-md',
|
||||
active && 'text-foreground bg-accent/40'
|
||||
)}>
|
||||
<span className="relative z-10">{item.name}</span>
|
||||
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
<div className="in-data-[state=active]:block lg:in-data-[state=active]:flex mb-3 hidden w-full flex-wrap items-center justify-end space-y-8 md:flex-nowrap lg:m-0 lg:flex lg:w-fit lg:gap-6 lg:space-y-0 lg:border-transparent lg:bg-transparent lg:p-0 lg:shadow-none dark:shadow-none dark:lg:bg-transparent">
|
||||
<div className="lg:hidden w-full">
|
||||
|
||||
<ul className="text-base flex flex-col items-stretch">
|
||||
{menuItems.map((item, index) => {
|
||||
const active = isLinkActive(pathname ?? '', item.href)
|
||||
return (
|
||||
<li key={index}>
|
||||
<Link
|
||||
href={item.href}
|
||||
onClick={() => setMenuState(false)}
|
||||
className={cn(
|
||||
'text-muted-foreground block w-full hover:text-foreground hover:bg-accent/20 transition-all duration-300 relative group p-2 px-4 rounded-md',
|
||||
active && 'text-foreground bg-accent/40'
|
||||
)}>
|
||||
<span className="relative z-10">{item.name}</span>
|
||||
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className={`flex justify-end max-md:absolute top-0.5 left-8 in-data-[state=active]:block`} />
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
export const LogoTailark = ({ className, uniColor }: { className?: string; uniColor?: boolean }) => {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 78 18"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={cn('text-foreground h-5 w-auto', className)}>
|
||||
<path
|
||||
d="M3 0H5V18H3V0ZM13 0H15V18H13V0ZM18 3V5H0V3H18ZM0 15V13H18V15H0Z"
|
||||
fill={uniColor ? 'currentColor' : 'url(#logo-gradient)'}
|
||||
/>
|
||||
<path
|
||||
d="M27.06 7.054V12.239C27.06 12.5903 27.1393 12.8453 27.298 13.004C27.468 13.1513 27.7513 13.225 28.148 13.225H29.338V14.84H27.808C26.9353 14.84 26.2667 14.636 25.802 14.228C25.3373 13.82 25.105 13.157 25.105 12.239V7.054H24V5.473H25.105V3.144H27.06V5.473H29.338V7.054H27.06ZM30.4782 10.114C30.4782 9.17333 30.6709 8.34033 31.0562 7.615C31.4529 6.88967 31.9855 6.32867 32.6542 5.932C33.3342 5.524 34.0822 5.32 34.8982 5.32C35.6349 5.32 36.2752 5.46733 36.8192 5.762C37.3745 6.04533 37.8165 6.40233 38.1452 6.833V5.473H40.1002V14.84H38.1452V13.446C37.8165 13.888 37.3689 14.2563 36.8022 14.551C36.2355 14.8457 35.5895 14.993 34.8642 14.993C34.0595 14.993 33.3229 14.789 32.6542 14.381C31.9855 13.9617 31.4529 13.3837 31.0562 12.647C30.6709 11.899 30.4782 11.0547 30.4782 10.114ZM38.1452 10.148C38.1452 9.502 38.0092 8.941 37.7372 8.465C37.4765 7.989 37.1309 7.62633 36.7002 7.377C36.2695 7.12767 35.8049 7.003 35.3062 7.003C34.8075 7.003 34.3429 7.12767 33.9122 7.377C33.4815 7.615 33.1302 7.972 32.8582 8.448C32.5975 8.91267 32.4672 9.468 32.4672 10.114C32.4672 10.76 32.5975 11.3267 32.8582 11.814C33.1302 12.3013 33.4815 12.6753 33.9122 12.936C34.3542 13.1853 34.8189 13.31 35.3062 13.31C35.8049 13.31 36.2695 13.1853 36.7002 12.936C37.1309 12.6867 37.4765 12.324 37.7372 11.848C38.0092 11.3607 38.1452 10.794 38.1452 10.148ZM43.6317 4.232C43.2803 4.232 42.9857 4.113 42.7477 3.875C42.5097 3.637 42.3907 3.34233 42.3907 2.991C42.3907 2.63967 42.5097 2.345 42.7477 2.107C42.9857 1.869 43.2803 1.75 43.6317 1.75C43.9717 1.75 44.2607 1.869 44.4987 2.107C44.7367 2.345 44.8557 2.63967 44.8557 2.991C44.8557 3.34233 44.7367 3.637 44.4987 3.875C44.2607 4.113 43.9717 4.232 43.6317 4.232ZM44.5837 5.473V14.84H42.6457V5.473H44.5837ZM49.0661 2.26V14.84H47.1281V2.26H49.0661ZM50.9645 10.114C50.9645 9.17333 51.1572 8.34033 51.5425 7.615C51.9392 6.88967 52.4719 6.32867 53.1405 5.932C53.8205 5.524 54.5685 5.32 55.3845 5.32C56.1212 5.32 56.7615 5.46733 57.3055 5.762C57.8609 6.04533 58.3029 6.40233 58.6315 6.833V5.473H60.5865V14.84H58.6315V13.446C58.3029 13.888 57.8552 14.2563 57.2885 14.551C56.7219 14.8457 56.0759 14.993 55.3505 14.993C54.5459 14.993 53.8092 14.789 53.1405 14.381C52.4719 13.9617 51.9392 13.3837 51.5425 12.647C51.1572 11.899 50.9645 11.0547 50.9645 10.114ZM58.6315 10.148C58.6315 9.502 58.4955 8.941 58.2235 8.465C57.9629 7.989 57.6172 7.62633 57.1865 7.377C56.7559 7.12767 56.2912 7.003 55.7925 7.003C55.2939 7.003 54.8292 7.12767 54.3985 7.377C53.9679 7.615 53.6165 7.972 53.3445 8.448C53.0839 8.91267 52.9535 9.468 52.9535 10.114C52.9535 10.76 53.0839 11.3267 53.3445 11.814C53.6165 12.3013 53.9679 12.6753 54.3985 12.936C54.8405 13.1853 55.3052 13.31 55.7925 13.31C56.2912 13.31 56.7559 13.1853 57.1865 12.936C57.6172 12.6867 57.9629 12.324 58.2235 11.848C58.4955 11.3607 58.6315 10.794 58.6315 10.148ZM65.07 6.833C65.3533 6.357 65.7273 5.98867 66.192 5.728C66.668 5.456 67.229 5.32 67.875 5.32V7.326H67.382C66.6227 7.326 66.0447 7.51867 65.648 7.904C65.2627 8.28933 65.07 8.958 65.07 9.91V14.84H63.132V5.473H65.07V6.833ZM73.3624 10.165L77.6804 14.84H75.0624L71.5944 10.811V14.84H69.6564V2.26H71.5944V9.57L74.9944 5.473H77.6804L73.3624 10.165Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="logo-gradient"
|
||||
x1="10"
|
||||
y1="0"
|
||||
x2="10"
|
||||
y2="20"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop stopColor="#9B99FE" />
|
||||
<stop
|
||||
offset="1"
|
||||
stopColor="#2BC8B7"
|
||||
/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { Separator } from "./ui/separator";
|
||||
|
||||
export const Logo = ({
|
||||
className,
|
||||
uniColor,
|
||||
hasCaption
|
||||
}: {
|
||||
className?: string;
|
||||
uniColor?: boolean;
|
||||
hasCaption?: boolean;
|
||||
}) => {
|
||||
const _logoText = <span className="font-bold text-xl leading-tight">مستر دکور</span>
|
||||
return (
|
||||
<Link href="/" className={cn('flex items-center justify-center', hasCaption ? 'flex-col text-center' : 'gap-2', className)}>
|
||||
<LogoIcon uniColor={uniColor} />
|
||||
{hasCaption ?
|
||||
<span className="flex flex-col items-center">
|
||||
{_logoText}
|
||||
<span className="text-xs text-muted-foreground font-normal leading-tight">
|
||||
زیرساخت دیجیتال بازار طلا
|
||||
</span>
|
||||
<Separator className="mt-2 max-w-10" />
|
||||
</span>
|
||||
: _logoText}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
export const LogoIcon = ({
|
||||
className,
|
||||
uniColor,
|
||||
}: {
|
||||
className?: string;
|
||||
uniColor?: boolean;
|
||||
}) => {
|
||||
return (
|
||||
<Image
|
||||
src="/assets/montazeranLogo.png"
|
||||
alt="Logo"
|
||||
width={50}
|
||||
height={50}
|
||||
className={cn("mx-auto h-10 w-auto object-contain object-center md:h-12 dark:invert", className)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const LogoStroke = ({ className }: { className?: string }) => {
|
||||
return (
|
||||
<svg
|
||||
className={cn("size-7 w-7", className)}
|
||||
viewBox="0 0 71 25"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M61.25 1.625L70.75 1.5625C70.75 4.77083 70.25 7.79167 69.25 10.625C68.2917 13.4583 66.8958 15.9583 65.0625 18.125C63.2708 20.25 61.125 21.9375 58.625 23.1875C56.1667 24.3958 53.4583 25 50.5 25C46.875 25 43.6667 24.2708 40.875 22.8125C38.125 21.3542 35.125 19.2083 31.875 16.375C29.75 14.4167 27.7917 12.8958 26 11.8125C24.2083 10.7292 22.2708 10.1875 20.1875 10.1875C18.0625 10.1875 16.25 10.7083 14.75 11.75C13.25 12.75 12.0833 14.1875 11.25 16.0625C10.4583 17.9375 10.0625 20.1875 10.0625 22.8125L0 22.9375C0 19.6875 0.479167 16.6667 1.4375 13.875C2.4375 11.0833 3.83333 8.64583 5.625 6.5625C7.41667 4.47917 9.54167 2.875 12 1.75C14.5 0.583333 17.2292 0 20.1875 0C23.8542 0 27.1042 0.770833 29.9375 2.3125C32.8125 3.85417 35.7708 5.97917 38.8125 8.6875C41.1042 10.7708 43.1042 12.3333 44.8125 13.375C46.5625 14.375 48.4583 14.875 50.5 14.875C52.6667 14.875 54.5417 14.3125 56.125 13.1875C57.75 12.0625 59 10.5 59.875 8.5C60.7917 6.5 61.25 4.20833 61.25 1.625Z"
|
||||
fill="none"
|
||||
strokeWidth={0.5}
|
||||
stroke="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
'use client'
|
||||
|
||||
import { Moon, Sun } from 'lucide-react'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
export function ThemeToggle() {
|
||||
const { theme, setTheme } = useTheme()
|
||||
const [mounted, setMounted] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true)
|
||||
}, [])
|
||||
|
||||
if (!mounted) {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
aria-label="Toggle theme"
|
||||
>
|
||||
<Sun className="size-4" />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
|
||||
aria-label="Toggle theme"
|
||||
>
|
||||
<Sun className="size-4 rotate-0 scale-100 transition-all duration-300 dark:-rotate-90 dark:scale-0" />
|
||||
<Moon className="absolute size-4 rotate-90 scale-0 transition-all duration-300 dark:rotate-0 dark:scale-100" />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user