feat: complete UI/UX improvements and localization
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
export default function BougainvilleaMotif({ className = "" }: { className?: string }) {
|
||||
return (
|
||||
<svg
|
||||
className={`text-accent opacity-80 ${className}`}
|
||||
viewBox="0 0 100 100"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M49.9 22.8c-2.3-5.5-8.5-12.7-16-11.2-13.8 2.8-15.5 19.3-7.5 28.5C32.1 46.5 45 47.9 50 51.5c4.9-3.6 17.9-5 23.6-11.4 8-9.2 6.3-25.7-7.5-28.5-7.5-1.5-13.7 5.7-16 11.2zM49.9 77.2c2.3 5.5 8.5 12.7 16 11.2 13.8-2.8 15.5-19.3 7.5-28.5-5.7-6.4-18.7-7.8-23.6-11.4-4.9 3.6-17.9 5-23.6 11.4-8 9.2-6.3 25.7 7.5 28.5 7.5 1.5 13.7-5.7 16-11.2z" />
|
||||
<path d="M22.8 50.1c-5.5 2.3-12.7 8.5-11.2 16 2.8 13.8 19.3 15.5 28.5 7.5 6.4-5.7 7.8-18.7 11.4-23.6-3.6-4.9-5-17.9-11.4-23.6-9.2-8-25.7-6.3-28.5 7.5-1.5 7.5 5.7 13.7 11.2 16zM77.2 50.1c5.5-2.3 12.7-8.5 11.2-16-2.8-13.8-19.3-15.5-28.5-7.5-6.4 5.7-7.8 18.7-11.4 23.6 3.6 4.9 5 17.9 11.4 23.6 9.2 8 25.7 6.3 28.5-7.5 1.5-7.5-5.7-13.7-11.2-16z" />
|
||||
<circle cx="50" cy="50" r="4" fill="#FFC107" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { Link } from '@/i18n/routing';
|
||||
|
||||
export default function Footer() {
|
||||
const t = useTranslations('footer');
|
||||
|
||||
return (
|
||||
<footer className="border-t bg-gray-50 mt-auto">
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
<div>
|
||||
<h3 className="font-bold text-lg mb-4 text-[#2FB6C4]">Sitar Apart & Otel</h3>
|
||||
<p className="text-gray-600 text-sm">
|
||||
{t('about')}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-bold text-lg mb-4">{t('contact')}</h3>
|
||||
<p className="text-gray-600 text-sm mb-2">{t('address_1')}</p>
|
||||
<p className="text-gray-600 text-sm mb-2">{t('address_2')}</p>
|
||||
<p className="text-gray-600 text-sm">0 530 510 98 42</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-bold text-lg mb-4">{t('quick_links')}</h3>
|
||||
<ul className="text-gray-600 text-sm space-y-2">
|
||||
<li><Link href="/dairelerimiz" className="hover:text-[#2FB6C4] transition-colors duration-300 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#2FB6C4] rounded-sm px-1 cursor-pointer inline-block">{t('quick_links_rooms', { defaultMessage: 'Dairelerimiz' })}</Link></li>
|
||||
<li><Link href="/galeri" className="hover:text-[#2FB6C4] transition-colors duration-300 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#2FB6C4] rounded-sm px-1 cursor-pointer inline-block">{t('quick_links_gallery', { defaultMessage: 'Galeri' })}</Link></li>
|
||||
<li><Link href="/iletisim" className="hover:text-[#2FB6C4] transition-colors duration-300 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#2FB6C4] rounded-sm px-1 cursor-pointer inline-block">{t('quick_links_contact', { defaultMessage: 'İletişim' })}</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-t mt-8 pt-8 flex flex-col md:flex-row justify-between items-center text-sm text-gray-500">
|
||||
<p>{t('rights')}</p>
|
||||
<p>Created by <a href="https://ayris.tech" className="text-[#2FB6C4] hover:underline transition-all duration-300 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#2FB6C4] rounded-sm px-1 cursor-pointer inline-block" target="_blank" rel="noreferrer">ayris.tech</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
'use client'
|
||||
|
||||
import { motion } from 'framer-motion'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Link, usePathname } from '@/i18n/routing'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import LocaleSwitcher from './LocaleSwitcher'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Menu, X } from 'lucide-react'
|
||||
|
||||
export default function Header() {
|
||||
const [scrolled, setScrolled] = useState(false)
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
||||
const t = useTranslations('nav')
|
||||
const pathname = usePathname()
|
||||
|
||||
const isHome = pathname === '/'
|
||||
// If we are not on the home page, the header should always look "scrolled"
|
||||
// so the text is visible against the light background.
|
||||
const isScrolledOrInner = scrolled || !isHome
|
||||
|
||||
useEffect(() => {
|
||||
const handler = () => setScrolled(window.scrollY > 50)
|
||||
window.addEventListener('scroll', handler, { passive: true })
|
||||
// Initialize
|
||||
handler()
|
||||
return () => window.removeEventListener('scroll', handler)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<motion.header
|
||||
initial={{ y: -20, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
transition={{ duration: 0.6, ease: [0.22, 1, 0.36, 1] }}
|
||||
className={cn(
|
||||
'fixed top-0 inset-x-0 z-50 transition-all duration-300',
|
||||
isScrolledOrInner
|
||||
? 'bg-surface/85 backdrop-blur-md border-b border-outline-variant/20 shadow-sm py-4'
|
||||
: 'bg-transparent py-6'
|
||||
)}
|
||||
>
|
||||
<div className="max-w-container-max mx-auto px-margin-mobile md:px-gutter">
|
||||
<div className="flex items-center justify-between">
|
||||
{/* Logo */}
|
||||
<Link href="/" className="flex items-center z-50">
|
||||
<span className={cn(
|
||||
"font-serif text-2xl md:text-[32px] font-bold tracking-tight italic transition-colors duration-300",
|
||||
isScrolledOrInner ? "text-secondary" : "text-white"
|
||||
)}>
|
||||
Sitar Apart
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
{/* Desktop Navigation */}
|
||||
<nav className="hidden md:flex items-center gap-8">
|
||||
<Link
|
||||
href="/"
|
||||
className={cn(
|
||||
"text-[14px] font-bold tracking-wider uppercase transition-colors duration-300 hover:text-tertiary cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-primary rounded-sm px-1",
|
||||
isScrolledOrInner ? "text-secondary" : "text-white/90"
|
||||
)}
|
||||
>
|
||||
{t('home')}
|
||||
</Link>
|
||||
<Link
|
||||
href="/dairelerimiz"
|
||||
className={cn(
|
||||
"text-[14px] font-bold tracking-wider uppercase transition-colors duration-300 hover:text-tertiary cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-primary rounded-sm px-1",
|
||||
isScrolledOrInner ? "text-secondary" : "text-white/90"
|
||||
)}
|
||||
>
|
||||
{t('rooms')}
|
||||
</Link>
|
||||
<Link
|
||||
href="/galeri"
|
||||
className={cn(
|
||||
"text-[14px] font-bold tracking-wider uppercase transition-colors duration-300 hover:text-tertiary cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-primary rounded-sm px-1",
|
||||
isScrolledOrInner ? "text-secondary" : "text-white/90"
|
||||
)}
|
||||
>
|
||||
{t('gallery')}
|
||||
</Link>
|
||||
<Link
|
||||
href="/iletisim"
|
||||
className={cn(
|
||||
"text-[14px] font-bold tracking-wider uppercase transition-colors duration-300 hover:text-tertiary cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-primary rounded-sm px-1",
|
||||
isScrolledOrInner ? "text-secondary" : "text-white/90"
|
||||
)}
|
||||
>
|
||||
{t('contact')}
|
||||
</Link>
|
||||
</nav>
|
||||
|
||||
{/* Desktop Actions */}
|
||||
<div className="hidden md:flex items-center gap-4">
|
||||
<LocaleSwitcher scrolled={isScrolledOrInner} />
|
||||
<Link
|
||||
href="/iletisim"
|
||||
className={cn(
|
||||
"px-6 py-3 rounded-full text-[14px] font-bold tracking-wider uppercase transition-all duration-300 hover:opacity-80 cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2",
|
||||
isScrolledOrInner
|
||||
? "bg-accent-pink text-white shadow-md"
|
||||
: "bg-accent-pink text-white backdrop-blur-md"
|
||||
)}
|
||||
>
|
||||
{t('book_now')}
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu Button */}
|
||||
<button
|
||||
aria-label="Toggle menu"
|
||||
className="md:hidden z-50 p-2 cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-primary rounded-md transition-all duration-300"
|
||||
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
||||
>
|
||||
{mobileMenuOpen ? (
|
||||
<X className={cn("w-6 h-6", isScrolledOrInner ? "text-secondary" : "text-white")} />
|
||||
) : (
|
||||
<Menu className={cn("w-6 h-6", isScrolledOrInner ? "text-secondary" : "text-white")} />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu */}
|
||||
{mobileMenuOpen && (
|
||||
<div className="md:hidden absolute top-full left-0 right-0 bg-surface border-b border-outline-variant/20 shadow-lg p-6">
|
||||
<nav className="flex flex-col gap-6">
|
||||
<Link
|
||||
href="/"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
className="text-secondary hover:text-tertiary font-bold tracking-wider uppercase text-[14px]"
|
||||
>
|
||||
{t('home')}
|
||||
</Link>
|
||||
<Link
|
||||
href="/dairelerimiz"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
className="text-secondary hover:text-tertiary font-bold tracking-wider uppercase text-[14px]"
|
||||
>
|
||||
{t('rooms')}
|
||||
</Link>
|
||||
<Link
|
||||
href="/galeri"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
className="text-secondary hover:text-tertiary font-bold tracking-wider uppercase text-[14px]"
|
||||
>
|
||||
{t('gallery')}
|
||||
</Link>
|
||||
<Link
|
||||
href="/iletisim"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
className="text-secondary hover:text-tertiary font-bold tracking-wider uppercase text-[14px]"
|
||||
>
|
||||
{t('contact')}
|
||||
</Link>
|
||||
<div className="pt-6 border-t border-outline-variant/20 flex flex-col gap-4">
|
||||
<div className="self-end"><LocaleSwitcher scrolled={true} /></div>
|
||||
<Link
|
||||
href="/iletisim"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
className="bg-accent-pink text-white px-6 py-3 rounded-full text-[14px] font-bold tracking-wider uppercase text-center"
|
||||
>
|
||||
{t('book_now')}
|
||||
</Link>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
)}
|
||||
</motion.header>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
'use client'
|
||||
|
||||
import { useLocale } from 'next-intl'
|
||||
import { useRouter, usePathname } from '@/i18n/routing'
|
||||
import { ChangeEvent } from 'react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Globe } from 'lucide-react'
|
||||
|
||||
export default function LocaleSwitcher({ scrolled }: { scrolled: boolean }) {
|
||||
const locale = useLocale()
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
|
||||
const onSelectChange = (e: ChangeEvent<HTMLSelectElement>) => {
|
||||
const nextLocale = e.target.value
|
||||
router.replace({ pathname }, { locale: nextLocale as any })
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative flex items-center">
|
||||
<Globe className={cn(
|
||||
"w-4 h-4 absolute left-2 pointer-events-none transition-colors",
|
||||
scrolled ? "text-foreground" : "text-white"
|
||||
)} />
|
||||
<select
|
||||
value={locale}
|
||||
onChange={onSelectChange}
|
||||
className={cn(
|
||||
"appearance-none bg-transparent border-none py-1 pl-8 pr-4 text-sm font-medium focus:ring-0 focus:outline-none cursor-pointer uppercase transition-colors",
|
||||
scrolled ? "text-foreground" : "text-white"
|
||||
)}
|
||||
>
|
||||
<option value="tr" className="text-foreground">TR</option>
|
||||
<option value="en" className="text-foreground">EN</option>
|
||||
<option value="de" className="text-foreground">DE</option>
|
||||
</select>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user