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>
116 lines
No EOL
3.4 KiB
TypeScript
116 lines
No EOL
3.4 KiB
TypeScript
import { NextResponse } from "next/server"
|
|
import { z } from "zod"
|
|
import { formatPrice } from "@/lib/calculations"
|
|
import type { CalculationDetails } from "@/lib/calculations"
|
|
|
|
const quoteRequestSchema = z.object({
|
|
customerInfo: z.object({
|
|
name: z.string(),
|
|
email: z.string().email(),
|
|
phone: z.string(),
|
|
postalCode: z.string(),
|
|
address: z.string().optional(),
|
|
remarks: z.string().optional(),
|
|
}),
|
|
calculationDetails: z.object({
|
|
area: z.number(),
|
|
height: z.number(),
|
|
postalCode: z.string(),
|
|
distance: z.number(),
|
|
totalInclVat: z.number(),
|
|
// We'll validate other fields exist but not their exact shape
|
|
}) as z.ZodType<CalculationDetails>,
|
|
})
|
|
|
|
export async function POST(request: Request) {
|
|
try {
|
|
const body = await request.json()
|
|
const { customerInfo, calculationDetails } = quoteRequestSchema.parse(body)
|
|
|
|
// Format email content
|
|
const emailContent = formatEmailContent(customerInfo, calculationDetails)
|
|
|
|
// In production, you would send this via an email service
|
|
// For now, we'll just log it and return success
|
|
console.log("Quote request email:", emailContent)
|
|
|
|
// TODO: Implement actual email sending using a service like:
|
|
// - SendGrid
|
|
// - AWS SES
|
|
// - Resend
|
|
// - Nodemailer with SMTP
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
message: "Tilbudsanmodning modtaget. Vi kontakter dig snarest muligt.",
|
|
})
|
|
} catch (error) {
|
|
console.error("Quote request error:", error)
|
|
|
|
if (error instanceof z.ZodError) {
|
|
return NextResponse.json(
|
|
{ error: "Ugyldige data", details: error.errors },
|
|
{ status: 400 }
|
|
)
|
|
}
|
|
|
|
return NextResponse.json(
|
|
{ error: "Der opstod en fejl. Prøv igen senere." },
|
|
{ status: 500 }
|
|
)
|
|
}
|
|
}
|
|
|
|
function formatEmailContent(
|
|
customerInfo: z.infer<typeof quoteRequestSchema>["customerInfo"],
|
|
details: CalculationDetails
|
|
): string {
|
|
return `
|
|
Ny tilbudsanmodning fra Foam King Gulve Prisberegner
|
|
|
|
KUNDEOPLYSNINGER:
|
|
-----------------
|
|
Navn: ${customerInfo.name}
|
|
Email: ${customerInfo.email}
|
|
Telefon: ${customerInfo.phone}
|
|
Postnummer: ${customerInfo.postalCode}
|
|
Adresse: ${customerInfo.address || "Ikke angivet"}
|
|
|
|
PROJEKTDETALJER:
|
|
----------------
|
|
Gulvareal: ${details.area} m²
|
|
Gulvhøjde: ${details.height} cm
|
|
Isoleringstykkelse: ${details.insulationThickness} cm
|
|
Isoleringsvolumen: ${details.insulationVolume.toFixed(2)} m³
|
|
Spartelvægt: ${details.compoundWeight.toLocaleString("da-DK")} kg
|
|
|
|
PRISBEREGNING:
|
|
--------------
|
|
Isolering: ${formatPrice(details.insulation)}
|
|
Gulvvarme: ${formatPrice(details.floorHeating)}
|
|
Syntetisk net: ${formatPrice(details.syntheticNet)}
|
|
Flydespartel: ${formatPrice(details.selfLevelingCompound)}
|
|
Pumpebil-tillæg: ${formatPrice(details.pumpTruckFee)}
|
|
Startgebyr: ${formatPrice(details.startFee)}
|
|
|
|
Subtotal: ${formatPrice(details.subtotal)}
|
|
Tillæg (afdækning + affald): ${formatPrice(details.totalFees)}
|
|
Transport: ${formatPrice(details.transport)}
|
|
${details.bridgeFee > 0 ? `Storebælt-tillæg: ${formatPrice(details.bridgeFee)}` : ""}
|
|
|
|
Total ekskl. moms: ${formatPrice(details.totalExclVat)}
|
|
Moms (25%): ${formatPrice(details.vat)}
|
|
TOTAL INKL. MOMS: ${formatPrice(details.totalInclVat)}
|
|
|
|
BEMÆRKNINGER:
|
|
-------------
|
|
${customerInfo.remarks || "Ingen bemærkninger"}
|
|
|
|
AFSTAND:
|
|
--------
|
|
Kørselsafstand (tur-retur): ${details.distance} km
|
|
|
|
---
|
|
Sendt fra beregner.foamking.dk
|
|
`.trim()
|
|
} |