foamking/app/page.tsx
mikl0s 3ebb63dc6c Add admin dashboard, authentication, step wizard, and quote management
Expand the calculator with a multi-step wizard flow, admin dashboard with
quote tracking, login/auth system, distance API integration, and history
page. Add new UI components (dialog, progress, select, slider, switch),
update pricing logic, and improve the overall design with new assets.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 20:59:11 +00:00

335 lines
12 KiB
TypeScript

"use client"
import { useState } from "react"
import Image from "next/image"
import { StepWizard } from "@/components/calculator/step-wizard"
import { Button } from "@/components/ui/button"
import { formatEstimate, type CalculationDetails } from "@/lib/calculations"
import {
Phone,
Mail,
MapPin,
CheckCircle2,
ArrowRight,
RotateCcw,
Loader2,
} from "lucide-react"
export default function Home() {
const [result, setResult] = useState<CalculationDetails | null>(null)
const [customerData, setCustomerData] = useState<any>(null)
const [showResult, setShowResult] = useState(false)
const handleComplete = (calculationResult: CalculationDetails, formData: any) => {
setResult(calculationResult)
setCustomerData(formData)
setShowResult(true)
}
const handleReset = () => {
setResult(null)
setCustomerData(null)
setShowResult(false)
}
const [isRequesting, setIsRequesting] = useState(false)
const handleRequestQuote = async () => {
if (!result || !customerData) return
setIsRequesting(true)
try {
const response = await fetch("/api/quote-request", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
customerInfo: {
name: customerData.name,
email: customerData.email,
phone: customerData.phone,
postalCode: customerData.postalCode,
address: customerData.address,
remarks: customerData.remarks,
},
calculationDetails: result,
}),
})
const data = await response.json()
if (response.ok) {
alert(data.message)
} else {
alert(data.error || "Der opstod en fejl. Prøv igen senere.")
}
} catch (error) {
alert("Der opstod en fejl. Prøv igen senere.")
} finally {
setIsRequesting(false)
}
}
return (
<main className="min-h-screen bg-background">
{/* Hero Section */}
<section className="relative flex min-h-[70vh] items-center justify-center overflow-hidden">
{/* Background Image */}
<div className="absolute inset-0 z-0">
<Image
src="/gulv.jpeg"
alt="Smukt gulv i moderne hjem"
fill
className="object-cover"
priority
/>
<div className="absolute inset-0 bg-gradient-to-b from-black/60 via-black/40 to-black/70" />
</div>
{/* Hero Content */}
<div className="container relative z-10 mx-auto px-4 text-center text-white">
<div className="mb-6">
<Image
src="/foam-king-logo.png"
alt="Foam King"
width={180}
height={72}
className="mx-auto h-16 w-auto brightness-0 invert"
priority
/>
</div>
<h1 className="mb-4 text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl">
Gulvarbejde i<br />
<span className="text-secondary">verdensklasse</span>
</h1>
<p className="mx-auto mb-8 max-w-2xl text-lg text-white/90 sm:text-xl">
Professionel udførelse af betongulve, gulvvarme og isolering. Vi leverer kvalitet der
holder i mange år fremover.
</p>
<div className="mb-8 flex flex-wrap justify-center gap-4">
<div className="flex items-center gap-2 rounded-full bg-white/10 px-4 py-2 backdrop-blur-sm">
<CheckCircle2 className="h-5 w-5 text-secondary" />
<span>Stor erfaring</span>
</div>
<div className="flex items-center gap-2 rounded-full bg-white/10 px-4 py-2 backdrop-blur-sm">
<CheckCircle2 className="h-5 w-5 text-secondary" />
<span>Byg Garanti</span>
</div>
<div className="flex items-center gap-2 rounded-full bg-white/10 px-4 py-2 backdrop-blur-sm">
<CheckCircle2 className="h-5 w-5 text-secondary" />
<span>Gratis tilbud</span>
</div>
</div>
<Button
size="lg"
className="h-14 bg-secondary px-8 text-lg text-secondary-foreground hover:bg-secondary/90"
onClick={() =>
document.getElementById("calculator")?.scrollIntoView({ behavior: "smooth" })
}
>
dit prisoverslag
<ArrowRight className="ml-2 h-5 w-5" />
</Button>
</div>
{/* Scroll Indicator */}
<div className="absolute bottom-8 left-1/2 z-10 -translate-x-1/2 animate-bounce">
<div className="flex h-12 w-8 items-start justify-center rounded-full border-2 border-white/50 pt-2">
<div className="h-3 w-1 rounded-full bg-white/70" />
</div>
</div>
</section>
{/* Calculator Section */}
<section
id="calculator"
className="bg-gradient-to-b from-muted/50 to-background py-16 sm:py-24"
>
<div className="container mx-auto px-4">
<div className="mb-12 text-center">
<p className="mb-2 font-semibold text-secondary">Prisberegner</p>
<h2 className="mb-4 text-3xl font-bold sm:text-4xl"> dit personlige tilbud</h2>
<p className="mx-auto max-w-xl text-muted-foreground">
Besvar nogle spørgsmål, kan give dig den mest nøjagtige prisberegning.
<br />
Det tager kun 2 minutter.
</p>
</div>
{!showResult ? (
<StepWizard onComplete={handleComplete} />
) : (
/* Result Card */
<div className="mx-auto max-w-lg">
<div className="rounded-2xl bg-white p-8 text-center shadow-lg">
<div className="mx-auto mb-6 flex h-16 w-16 items-center justify-center rounded-full bg-green-100">
<CheckCircle2 className="h-8 w-8 text-green-600" />
</div>
<h3 className="mb-2 text-2xl font-bold">Dit prisoverslag</h3>
<p className="mb-6 text-muted-foreground">
Baseret {result?.area} m² gulv i {customerData?.postalCode}
</p>
<div className="mb-6 rounded-xl bg-gradient-to-br from-primary/10 to-secondary/10 p-6">
<p className="mb-2 text-3xl font-bold text-primary sm:text-5xl">
{result && formatEstimate(result.totalInclVat)}
</p>
<p className="text-muted-foreground">inkl. moms</p>
</div>
<div className="mb-6 rounded-lg bg-muted/30 p-4 text-left text-sm">
<p className="mb-2 font-medium">Inkluderet i prisen:</p>
<ul className="space-y-1 text-muted-foreground">
{result?.includeInsulation && (
<li className="flex items-center gap-2">
<CheckCircle2 className="h-4 w-4 text-green-600" />
Isolering ({result.insulationThickness} cm)
</li>
)}
{result?.includeFloorHeating && (
<li className="flex items-center gap-2">
<CheckCircle2 className="h-4 w-4 text-green-600" />
Gulvvarme syntetisk net + Ø16 PEX (excl. tilslutning)
</li>
)}
{result?.includeCompound && (
<li className="flex items-center gap-2">
<CheckCircle2 className="h-4 w-4 text-green-600" />
Flydespartel (støbning)
</li>
)}
<li className="flex items-center gap-2">
<CheckCircle2 className="h-4 w-4 text-green-600" />
Transport til {customerData?.postalCode}
</li>
</ul>
</div>
<p className="mb-6 text-xs text-muted-foreground">
*Prisen er vejledende og kan variere med ±10.000 kr afhængigt af konkrete forhold
</p>
<div className="flex flex-col gap-3">
<Button
size="lg"
className="h-12 w-full bg-secondary text-secondary-foreground hover:bg-secondary/90"
onClick={handleRequestQuote}
disabled={isRequesting}
>
{isRequesting ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Sender...
</>
) : (
<>
<Mail className="mr-2 h-4 w-4" />
Anmod om bindende tilbud
</>
)}
</Button>
<Button
variant="outline"
size="lg"
className="h-12 w-full"
onClick={handleReset}
>
<RotateCcw className="mr-2 h-4 w-4" />
Ny beregning
</Button>
</div>
</div>
</div>
)}
</div>
</section>
{/* Features Section */}
<section className="bg-muted/30 py-16 sm:py-24">
<div className="container mx-auto px-4">
<div className="grid gap-6 md:grid-cols-3">
<div className="flex h-32 items-center justify-center rounded-2xl bg-white p-6 shadow-md transition-shadow hover:shadow-lg">
<Image
src="/dansk_kvalitet.png"
alt="Dansk Kvalitet"
width={75}
height={100}
className="h-20 w-auto object-contain"
/>
</div>
<div className="flex h-32 items-center justify-center rounded-2xl bg-white p-6 shadow-md transition-shadow hover:shadow-lg">
<Image
src="/byg_trans.png"
alt="Byg Garanti"
width={360}
height={97}
className="h-20 w-auto object-contain"
/>
</div>
<div className="flex h-32 items-center justify-center rounded-2xl bg-white p-6 shadow-md transition-shadow hover:shadow-lg">
<Image
src="/tilfredshed_service.png"
alt="Tilfredshed & Service"
width={75}
height={100}
className="h-20 w-auto object-contain"
/>
</div>
</div>
</div>
</section>
{/* Coverage Section */}
<section className="bg-white py-16">
<div className="container mx-auto px-4 text-center">
<h2 className="mb-4 text-2xl font-bold">Vi dækker hele Østdanmark</h2>
<p className="mb-6 text-muted-foreground">
Sjælland · København · Nordsjælland · Lolland-Falster · Fyn
</p>
<div className="flex justify-center gap-4">
<a href="tel:35901066" className="flex items-center gap-2 text-primary hover:underline">
<Phone className="h-4 w-4" />
35 90 10 66
</a>
<a
href="mailto:info@foamking.dk"
className="flex items-center gap-2 text-primary hover:underline"
>
<Mail className="h-4 w-4" />
info@foamking.dk
</a>
</div>
</div>
</section>
{/* Footer */}
<footer className="bg-foreground py-8 text-background">
<div className="container mx-auto px-4">
<div className="flex flex-col items-center justify-between gap-4 md:flex-row">
<div className="flex items-center gap-4">
<Image
src="/foam-king-logo.png"
alt="Foam King"
width={120}
height={48}
className="h-10 w-auto brightness-0 invert"
/>
<div className="text-sm text-background/70">
<p>Foam King ApS · CVR: 44 48 54 51</p>
<p className="flex items-center gap-1">
<MapPin className="h-3 w-3" />
Søgårdsvej 7, 4550 Asnæs
</p>
</div>
</div>
<p className="text-sm text-background/50">
© {new Date().getFullYear()} Foam King. Alle rettigheder forbeholdes.
</p>
</div>
</div>
</footer>
</main>
)
}