Add premium configurator with split-screen layout
- Redesigned configurator page with split-screen interface - Left: Large visual preview with sticky positioning - Right: Premium white controls container with form steps - Added complete configurator wizard (5 steps) - Updated hero CTA to "Zelf ontwerpen" - Configured Shadcn UI with Slate theme - Added layout components (Navbar, Footer) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
186
components/layout/footer.tsx
Normal file
186
components/layout/footer.tsx
Normal file
@@ -0,0 +1,186 @@
|
||||
import Link from "next/link";
|
||||
import { Mail, Phone, Star, Facebook, Instagram, Linkedin, Youtube } from "lucide-react";
|
||||
|
||||
const contactInfo = [
|
||||
{ icon: Mail, text: "info@proinn.nl", href: "mailto:info@proinn.nl" },
|
||||
{ icon: Phone, text: "085 - 1234 567", href: "tel:0851234567" },
|
||||
];
|
||||
|
||||
const companyInfo = [
|
||||
{ label: "KVK", value: "12345678" },
|
||||
{ label: "BTW", value: "NL123456789B01" },
|
||||
{ label: "IBAN", value: "NL00 INGB 0000 0000 00" },
|
||||
];
|
||||
|
||||
const locations = [
|
||||
"Nunspeet",
|
||||
"Veghel",
|
||||
"Amsterdam",
|
||||
"Rotterdam",
|
||||
"Utrecht",
|
||||
];
|
||||
|
||||
const proinnLinks = [
|
||||
{ label: "Projecten", href: "/projecten" },
|
||||
{ label: "Configurator", href: "/offerte" },
|
||||
{ label: "Over ons", href: "/over-ons" },
|
||||
{ label: "Vacatures", href: "/vacatures" },
|
||||
{ label: "Showrooms", href: "/showrooms" },
|
||||
];
|
||||
|
||||
const serviceLinks = [
|
||||
{ label: "Contact", href: "/contact" },
|
||||
{ label: "Kennisbank", href: "/kennisbank" },
|
||||
{ label: "Veelgestelde vragen", href: "/faq" },
|
||||
{ label: "Garantie", href: "/garantie" },
|
||||
{ label: "Onderhoud", href: "/onderhoud" },
|
||||
];
|
||||
|
||||
const socialLinks = [
|
||||
{ icon: Facebook, href: "#", label: "Facebook" },
|
||||
{ icon: Instagram, href: "#", label: "Instagram" },
|
||||
{ icon: Linkedin, href: "#", label: "LinkedIn" },
|
||||
{ icon: Youtube, href: "#", label: "YouTube" },
|
||||
];
|
||||
|
||||
export function Footer() {
|
||||
return (
|
||||
<footer className="bg-[#1A2E2E]">
|
||||
{/* Main Footer */}
|
||||
<div className="mx-auto max-w-7xl px-4 pt-16 pb-12 sm:px-6 lg:px-8">
|
||||
<div className="grid grid-cols-1 gap-10 md:grid-cols-2 lg:grid-cols-5 lg:gap-8">
|
||||
{/* Col 1 - Logo & Contact */}
|
||||
<div className="lg:col-span-1">
|
||||
<Link href="/" className="text-2xl font-extrabold tracking-tight text-white">
|
||||
PROINN
|
||||
</Link>
|
||||
<div className="mt-6 space-y-3">
|
||||
{contactInfo.map((item) => (
|
||||
<a
|
||||
key={item.text}
|
||||
href={item.href}
|
||||
className="flex items-center gap-2 text-sm text-gray-400 transition-colors hover:text-white"
|
||||
>
|
||||
<item.icon className="size-4 shrink-0" />
|
||||
{item.text}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-5 space-y-2">
|
||||
{companyInfo.map((item) => (
|
||||
<p key={item.label} className="text-xs text-gray-500">
|
||||
<span className="text-gray-400">{item.label}:</span> {item.value}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Col 2 - Locaties */}
|
||||
<div>
|
||||
<h4 className="mb-4 text-sm font-semibold text-white">Locaties</h4>
|
||||
<ul className="space-y-2.5">
|
||||
{locations.map((city) => (
|
||||
<li key={city}>
|
||||
<Link
|
||||
href={`/showrooms/${city.toLowerCase()}`}
|
||||
className="text-sm text-gray-400 transition-colors hover:text-white"
|
||||
>
|
||||
{city}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Col 3 - Proinn */}
|
||||
<div>
|
||||
<h4 className="mb-4 text-sm font-semibold text-white">Proinn</h4>
|
||||
<ul className="space-y-2.5">
|
||||
{proinnLinks.map((link) => (
|
||||
<li key={link.label}>
|
||||
<Link
|
||||
href={link.href}
|
||||
className="text-sm text-gray-400 transition-colors hover:text-white"
|
||||
>
|
||||
{link.label}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Col 4 - Service */}
|
||||
<div>
|
||||
<h4 className="mb-4 text-sm font-semibold text-white">Service</h4>
|
||||
<ul className="space-y-2.5">
|
||||
{serviceLinks.map((link) => (
|
||||
<li key={link.label}>
|
||||
<Link
|
||||
href={link.href}
|
||||
className="text-sm text-gray-400 transition-colors hover:text-white"
|
||||
>
|
||||
{link.label}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Col 5 - Trustpilot */}
|
||||
<div>
|
||||
<div className="rounded-2xl bg-[#243636] p-6">
|
||||
<p className="mb-3 text-xs font-medium uppercase tracking-wider text-gray-400">
|
||||
Klantwaardering
|
||||
</p>
|
||||
<div className="mb-2 flex items-center gap-1">
|
||||
{[...Array(5)].map((_, i) => (
|
||||
<Star
|
||||
key={i}
|
||||
className="size-5 fill-yellow-400 text-yellow-400"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-2xl font-bold text-white">
|
||||
4.8<span className="text-sm font-normal text-gray-400">/5</span>
|
||||
</p>
|
||||
<div className="mt-2 flex items-center gap-1.5">
|
||||
<Star className="size-4 fill-green-500 text-green-500" />
|
||||
<span className="text-sm text-gray-400">Trustpilot</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom Bar */}
|
||||
<div className="border-t border-white/10">
|
||||
<div className="mx-auto flex max-w-7xl flex-col items-center justify-between gap-4 px-4 py-6 sm:flex-row sm:px-6 lg:px-8">
|
||||
{/* Social Icons */}
|
||||
<div className="flex items-center gap-4">
|
||||
{socialLinks.map((social) => (
|
||||
<a
|
||||
key={social.label}
|
||||
href={social.href}
|
||||
aria-label={social.label}
|
||||
className="flex size-9 items-center justify-center rounded-full bg-white/10 text-gray-400 transition-colors hover:bg-white/20 hover:text-white"
|
||||
>
|
||||
<social.icon className="size-4" />
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Legal Links */}
|
||||
<div className="flex items-center gap-6 text-xs text-gray-500">
|
||||
<span>© {new Date().getFullYear()} Proinn</span>
|
||||
<Link href="/privacy" className="transition-colors hover:text-gray-300">
|
||||
Privacybeleid
|
||||
</Link>
|
||||
<Link href="/voorwaarden" className="transition-colors hover:text-gray-300">
|
||||
Algemene voorwaarden
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
11
components/layout/header.tsx
Normal file
11
components/layout/header.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import { TopBar } from "./top-bar";
|
||||
import { MainNav } from "./main-nav";
|
||||
|
||||
export function Header() {
|
||||
return (
|
||||
<header className="sticky top-0 z-50 w-full shadow-sm">
|
||||
<TopBar />
|
||||
<MainNav />
|
||||
</header>
|
||||
);
|
||||
}
|
||||
46
components/layout/main-nav.tsx
Normal file
46
components/layout/main-nav.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { Menu } from "lucide-react";
|
||||
import { MobileMenu } from "./mobile-menu";
|
||||
import { useState } from "react";
|
||||
|
||||
export function MainNav() {
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="bg-white">
|
||||
<div className="mx-auto flex h-16 max-w-7xl items-center justify-between px-4 sm:px-6 lg:px-8">
|
||||
{/* Logo */}
|
||||
<Link
|
||||
href="/"
|
||||
className="text-2xl font-extrabold tracking-tight text-gray-900"
|
||||
>
|
||||
PROINN
|
||||
</Link>
|
||||
|
||||
{/* Right side: CTA + Hamburger */}
|
||||
<div className="flex items-center gap-3">
|
||||
<Link
|
||||
href="/offerte"
|
||||
className="inline-flex items-center rounded-md bg-[#C4D668] px-5 py-2.5 text-sm font-semibold text-black transition-colors hover:bg-[#b5c75a]"
|
||||
>
|
||||
Vraag Offerte
|
||||
</Link>
|
||||
|
||||
<button
|
||||
onClick={() => setMenuOpen(true)}
|
||||
className="inline-flex size-11 items-center justify-center rounded-md bg-gray-100 text-gray-600 transition-colors hover:bg-gray-200"
|
||||
aria-label="Menu openen"
|
||||
>
|
||||
<Menu className="size-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<MobileMenu open={menuOpen} onOpenChange={setMenuOpen} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
91
components/layout/mobile-menu.tsx
Normal file
91
components/layout/mobile-menu.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { ChevronDown, Phone, Mail } from "lucide-react";
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
} from "@/components/ui/sheet";
|
||||
|
||||
const menuLinks = [
|
||||
{ href: "/", label: "Home" },
|
||||
{ href: "/producten", label: "Producten", hasSubmenu: true },
|
||||
{ href: "/maatwerk", label: "Maatwerk", hasSubmenu: true },
|
||||
{ href: "/over-ons", label: "Over Ons" },
|
||||
{ href: "/contact", label: "Contact" },
|
||||
];
|
||||
|
||||
interface MobileMenuProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
}
|
||||
|
||||
export function MobileMenu({ open, onOpenChange }: MobileMenuProps) {
|
||||
return (
|
||||
<Sheet open={open} onOpenChange={onOpenChange}>
|
||||
<SheetContent side="right" className="flex w-full max-w-sm flex-col p-0">
|
||||
<SheetHeader className="border-b px-6 py-4">
|
||||
<SheetTitle className="text-left text-lg font-extrabold tracking-tight">
|
||||
PROINN
|
||||
</SheetTitle>
|
||||
</SheetHeader>
|
||||
|
||||
{/* Navigation Links */}
|
||||
<nav className="flex-1 overflow-y-auto px-6 py-4">
|
||||
<ul className="space-y-1">
|
||||
{menuLinks.map((link) => (
|
||||
<li key={link.href}>
|
||||
<Link
|
||||
href={link.href}
|
||||
onClick={() => onOpenChange(false)}
|
||||
className="flex items-center justify-between rounded-md px-3 py-3 text-base font-medium text-gray-800 transition-colors hover:bg-gray-50"
|
||||
>
|
||||
{link.label}
|
||||
{link.hasSubmenu && (
|
||||
<ChevronDown className="size-4 text-gray-400" />
|
||||
)}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
{/* CTA Button */}
|
||||
<div className="mt-6">
|
||||
<Link
|
||||
href="/offerte"
|
||||
onClick={() => onOpenChange(false)}
|
||||
className="flex w-full items-center justify-center rounded-md bg-[#C4D668] px-5 py-3 text-sm font-semibold text-black transition-colors hover:bg-[#b5c75a]"
|
||||
>
|
||||
Vraag Offerte
|
||||
</Link>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{/* Footer Block */}
|
||||
<div className="bg-[#2F3B3B] px-6 py-6">
|
||||
<p className="mb-3 text-sm font-semibold text-white">
|
||||
Wil je wat vragen?
|
||||
</p>
|
||||
<div className="space-y-2">
|
||||
<a
|
||||
href="tel:0851234567"
|
||||
className="flex items-center gap-2 text-sm text-gray-300 transition-colors hover:text-white"
|
||||
>
|
||||
<Phone className="size-4" />
|
||||
085 - 1234 567
|
||||
</a>
|
||||
<a
|
||||
href="mailto:info@proinn.nl"
|
||||
className="flex items-center gap-2 text-sm text-gray-300 transition-colors hover:text-white"
|
||||
>
|
||||
<Mail className="size-4" />
|
||||
info@proinn.nl
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
);
|
||||
}
|
||||
81
components/layout/navbar.tsx
Normal file
81
components/layout/navbar.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Menu, X } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const navLinks = [
|
||||
{ href: "/", label: "Home" },
|
||||
{ href: "/producten", label: "Producten" },
|
||||
{ href: "/maatwerk", label: "Maatwerk" },
|
||||
{ href: "/contact", label: "Contact" },
|
||||
];
|
||||
|
||||
export function Navbar() {
|
||||
const [mobileOpen, setMobileOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<nav className="fixed top-0 left-0 right-0 z-50 border-b border-border/40 bg-background/80 backdrop-blur-lg">
|
||||
<div className="mx-auto flex h-16 max-w-7xl items-center justify-between px-4 sm:px-6 lg:px-8">
|
||||
{/* Logo */}
|
||||
<Link href="/" className="text-xl font-bold tracking-tighter text-foreground">
|
||||
PROINN
|
||||
</Link>
|
||||
|
||||
{/* Desktop Navigation */}
|
||||
<div className="hidden items-center gap-1 md:flex">
|
||||
{navLinks.map((link) => (
|
||||
<Link
|
||||
key={link.href}
|
||||
href={link.href}
|
||||
className="rounded-md px-3 py-2 text-sm font-medium text-muted-foreground transition-colors hover:text-foreground"
|
||||
>
|
||||
{link.label}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* CTA Button + Mobile Toggle */}
|
||||
<div className="flex items-center gap-3">
|
||||
<Button
|
||||
asChild
|
||||
size="sm"
|
||||
className="bg-brand-orange text-white hover:bg-brand-orange/90"
|
||||
>
|
||||
<Link href="/offerte">Vraag Offerte</Link>
|
||||
</Button>
|
||||
|
||||
<button
|
||||
onClick={() => setMobileOpen(!mobileOpen)}
|
||||
className="inline-flex items-center justify-center rounded-md p-2 text-muted-foreground hover:text-foreground md:hidden"
|
||||
>
|
||||
{mobileOpen ? <X className="size-5" /> : <Menu className="size-5" />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Navigation */}
|
||||
<div
|
||||
className={cn(
|
||||
"overflow-hidden border-t border-border/40 bg-background/95 backdrop-blur-lg transition-all duration-200 md:hidden",
|
||||
mobileOpen ? "max-h-64" : "max-h-0 border-t-0"
|
||||
)}
|
||||
>
|
||||
<div className="space-y-1 px-4 py-3">
|
||||
{navLinks.map((link) => (
|
||||
<Link
|
||||
key={link.href}
|
||||
href={link.href}
|
||||
onClick={() => setMobileOpen(false)}
|
||||
className="block rounded-md px-3 py-2 text-sm font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
|
||||
>
|
||||
{link.label}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
46
components/layout/top-bar.tsx
Normal file
46
components/layout/top-bar.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
"use client";
|
||||
|
||||
import { Star, Phone, Globe } from "lucide-react";
|
||||
|
||||
export function TopBar() {
|
||||
return (
|
||||
<div className="bg-[#E8E8E6]">
|
||||
<div className="mx-auto flex h-10 max-w-7xl items-center justify-between px-4 sm:px-6 lg:px-8">
|
||||
{/* Trustpilot */}
|
||||
<div className="flex items-center gap-1.5">
|
||||
<div className="flex items-center gap-0.5">
|
||||
{[...Array(5)].map((_, i) => (
|
||||
<Star
|
||||
key={i}
|
||||
className="size-3.5 fill-yellow-400 text-yellow-400"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<span className="text-xs font-bold text-gray-700">4.8/5</span>
|
||||
<div className="flex items-center gap-1">
|
||||
<Star className="size-3.5 fill-green-600 text-green-600" />
|
||||
<span className="text-xs font-medium text-gray-600">
|
||||
Trustpilot
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Contact & Language */}
|
||||
<div className="flex items-center gap-4 text-xs text-gray-600">
|
||||
<a
|
||||
href="tel:0851234567"
|
||||
className="flex items-center gap-1.5 font-medium transition-colors hover:text-gray-900"
|
||||
>
|
||||
<Phone className="size-3.5" />
|
||||
<span>085 - 1234 567</span>
|
||||
</a>
|
||||
<div className="h-3.5 w-px bg-gray-400" />
|
||||
<div className="flex items-center gap-1.5 font-medium">
|
||||
<Globe className="size-3.5" />
|
||||
<span>NL</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user