Features: - Complete Next.js 16 app with TypeScript and Tailwind CSS - Customer-facing price calculator form with validation - Admin mode showing detailed price breakdowns - Accurate price calculations matching business requirements - Responsive design with custom shadcn/ui theme - API endpoint for quote requests - Danish postal code distance calculations - Complete test coverage against documentation examples 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
149 lines
No EOL
4.9 KiB
TypeScript
149 lines
No EOL
4.9 KiB
TypeScript
"use client"
|
|
|
|
import { useState } from "react"
|
|
import Image from "next/image"
|
|
import { CalculatorForm } from "@/components/calculator/calculator-form"
|
|
import { CalculationDetailsView } from "@/components/calculator/calculation-details"
|
|
import { Button } from "@/components/ui/button"
|
|
import type { CalculationDetails } from "@/lib/calculations"
|
|
import { formatEstimate } from "@/lib/calculations"
|
|
import { Send, Eye, EyeOff } from "lucide-react"
|
|
|
|
export default function Home() {
|
|
const [calculationResult, setCalculationResult] = useState<CalculationDetails | null>(null)
|
|
const [showAdminMode, setShowAdminMode] = useState(false)
|
|
const [isRequestingQuote, setIsRequestingQuote] = useState(false)
|
|
const [customerInfo, setCustomerInfo] = useState<any>(null)
|
|
|
|
const handleCalculation = (result: CalculationDetails, formData?: any) => {
|
|
setCalculationResult(result)
|
|
if (formData) {
|
|
setCustomerInfo(formData)
|
|
}
|
|
}
|
|
|
|
const handleQuoteRequest = async () => {
|
|
if (!calculationResult || !customerInfo) return
|
|
|
|
setIsRequestingQuote(true)
|
|
try {
|
|
const response = await fetch("/api/quote-request", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
customerInfo,
|
|
calculationDetails: calculationResult,
|
|
}),
|
|
})
|
|
|
|
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 {
|
|
setIsRequestingQuote(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<main className="min-h-screen bg-gradient-to-b from-background to-muted/20">
|
|
<div className="container mx-auto px-4 py-8">
|
|
{/* Header */}
|
|
<div className="mb-8 text-center">
|
|
<div className="mb-4 flex justify-center">
|
|
<Image
|
|
src="/foam-king-logo.png"
|
|
alt="Foam King Gulve"
|
|
width={200}
|
|
height={80}
|
|
priority
|
|
className="h-20 w-auto"
|
|
/>
|
|
</div>
|
|
<h1 className="text-3xl font-bold">Foam King Gulve</h1>
|
|
<p className="mt-2 text-lg text-muted-foreground">
|
|
Professionelle gulvløsninger med isolering, gulvvarme og støbning
|
|
</p>
|
|
</div>
|
|
|
|
{/* Admin Mode Toggle */}
|
|
<div className="mb-4 flex justify-center">
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={() => setShowAdminMode(!showAdminMode)}
|
|
className="gap-2"
|
|
>
|
|
{showAdminMode ? (
|
|
<>
|
|
<EyeOff className="h-4 w-4" />
|
|
Skjul detaljer
|
|
</>
|
|
) : (
|
|
<>
|
|
<Eye className="h-4 w-4" />
|
|
Vis detaljer
|
|
</>
|
|
)}
|
|
</Button>
|
|
</div>
|
|
|
|
{/* Calculator */}
|
|
<div className="mx-auto max-w-6xl">
|
|
<div className="grid gap-8 lg:grid-cols-2">
|
|
<div className="flex justify-center">
|
|
<CalculatorForm
|
|
onCalculation={handleCalculation}
|
|
showDetails={showAdminMode}
|
|
/>
|
|
</div>
|
|
|
|
{/* Results */}
|
|
{calculationResult && (
|
|
<div className="space-y-6">
|
|
{!showAdminMode ? (
|
|
<div className="rounded-xl bg-card p-8 text-center shadow">
|
|
<h2 className="mb-4 text-xl font-semibold">Dit prisoverslag</h2>
|
|
<p className="text-4xl font-bold text-primary">
|
|
{formatEstimate(calculationResult.totalInclVat)}
|
|
</p>
|
|
<p className="mt-2 text-sm text-muted-foreground">inkl. moms</p>
|
|
<p className="mt-4 text-sm text-muted-foreground">
|
|
*Prisen er vejledende og kan variere med ±10.000 kr afhængigt af konkrete forhold
|
|
</p>
|
|
<Button
|
|
onClick={handleQuoteRequest}
|
|
size="lg"
|
|
className="mt-6 gap-2"
|
|
disabled={isRequestingQuote}
|
|
>
|
|
<Send className="h-4 w-4" />
|
|
Anmod om bindende tilbud
|
|
</Button>
|
|
</div>
|
|
) : (
|
|
<CalculationDetailsView details={calculationResult} />
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Footer */}
|
|
<footer className="mt-16 border-t pt-8 text-center text-sm text-muted-foreground">
|
|
<p>Foam King Gulve · Asnæs · CVR: 12345678</p>
|
|
<p className="mt-1">
|
|
Vi dækker Sjælland, Lolland-Falster og Fyn
|
|
</p>
|
|
</footer>
|
|
</div>
|
|
</main>
|
|
)
|
|
} |