foamking/components/calculator/calculation-details.tsx
mikl0s 7d2bbae1c6 Initial implementation of Foam King Gulve price calculator
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>
2026-01-10 14:27:28 +00:00

169 lines
No EOL
7.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { formatPrice, type CalculationDetails } from "@/lib/calculations"
import { PRICES, CONSTRAINTS } from "@/lib/constants"
interface CalculationDetailsProps {
details: CalculationDetails
}
export function CalculationDetailsView({ details }: CalculationDetailsProps) {
return (
<Card className="w-full">
<CardHeader>
<CardTitle>Detaljeret Prisberegning</CardTitle>
<CardDescription>
Komplet oversigt over alle delpriser og beregninger
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
{/* Input Values */}
<div>
<h3 className="mb-2 font-semibold">Indtastede værdier</h3>
<div className="grid gap-2 text-sm">
<div className="flex justify-between">
<span className="text-muted-foreground">Gulvareal:</span>
<span>{details.area} m²</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Gulvhøjde:</span>
<span>{details.height} cm</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Postnummer:</span>
<span>{details.postalCode}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Afstand (tur-retur):</span>
<span>{details.distance} km</span>
</div>
</div>
</div>
{/* Calculated Values */}
<div>
<h3 className="mb-2 font-semibold">Beregnede værdier</h3>
<div className="grid gap-2 text-sm">
<div className="flex justify-between">
<span className="text-muted-foreground">Isoleringstykkelse:</span>
<span>{details.insulationThickness} cm ({details.height} - {CONSTRAINTS.CONCRETE_THICKNESS} cm beton)</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Isoleringsvolumen:</span>
<span>{details.insulationVolume.toFixed(2)} m³</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Spartelvægt:</span>
<span>{details.compoundWeight.toLocaleString("da-DK")} kg ({details.area} m² × {PRICES.COMPOUND_WEIGHT_PER_M2} kg/m²)</span>
</div>
</div>
</div>
{/* Component Prices */}
<div>
<h3 className="mb-2 font-semibold">Komponent priser</h3>
<div className="grid gap-2 text-sm">
<div className="flex justify-between">
<span className="text-muted-foreground">
Isolering {details.insulationThickness > 0 ? `(${details.insulationVolume.toFixed(2)}× ${formatPrice(PRICES.INSULATION_TOTAL)}/m³)` : "(simpel arbejdsløn)"}:
</span>
<span className="font-medium">{formatPrice(details.insulation)}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">
Gulvvarme ({details.area} m² × {formatPrice(PRICES.FLOOR_HEATING_TOTAL)}/m²):
</span>
<span className="font-medium">{formatPrice(details.floorHeating)}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">
Syntetisk net ({details.area} m² × {formatPrice(PRICES.SYNTHETIC_NET_TOTAL)}/m²):
</span>
<span className="font-medium">{formatPrice(details.syntheticNet)}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">
Flydespartel ({details.area} m² × {formatPrice(PRICES.SELF_LEVELING_COMPOUND)}/m²):
</span>
<span className="font-medium">{formatPrice(details.selfLevelingCompound)}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">
Pumpebil-tillæg ({details.compoundWeight.toLocaleString("da-DK")} kg):
</span>
<span className="font-medium">{formatPrice(details.pumpTruckFee)}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Startgebyr:</span>
<span className="font-medium">{formatPrice(details.startFee)}</span>
</div>
</div>
</div>
{/* Subtotal and Fees */}
<div>
<h3 className="mb-2 font-semibold">Subtotal og tillæg</h3>
<div className="grid gap-2 text-sm">
<div className="flex justify-between border-t pt-2">
<span className="text-muted-foreground">Subtotal:</span>
<span className="font-semibold">{formatPrice(details.subtotal)}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">
Afdækning ({(PRICES.COVERING_PERCENTAGE * 100).toFixed(1)}%):
</span>
<span>{formatPrice(details.coveringFee)}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">
Affald ({(PRICES.WASTE_PERCENTAGE * 100).toFixed(2)}%):
</span>
<span>{formatPrice(details.wasteFee)}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Tillæg i alt:</span>
<span className="font-medium">{formatPrice(details.totalFees)}</span>
</div>
</div>
</div>
{/* Transport */}
<div>
<h3 className="mb-2 font-semibold">Transport</h3>
<div className="grid gap-2 text-sm">
<div className="flex justify-between">
<span className="text-muted-foreground">
Kørsel ({details.distance} km × {formatPrice(PRICES.TRANSPORT_PER_KM)}/km):
</span>
<span>{formatPrice(details.transport)}</span>
</div>
{details.bridgeFee > 0 && (
<div className="flex justify-between">
<span className="text-muted-foreground">Storebælt-tillæg:</span>
<span>{formatPrice(details.bridgeFee)}</span>
</div>
)}
</div>
</div>
{/* Final Total */}
<div>
<h3 className="mb-2 font-semibold">Total</h3>
<div className="grid gap-2 text-sm">
<div className="flex justify-between border-t pt-2">
<span className="text-muted-foreground">Total ekskl. moms:</span>
<span className="font-semibold">{formatPrice(details.totalExclVat)}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Moms (25%):</span>
<span>{formatPrice(details.vat)}</span>
</div>
<div className="flex justify-between border-t pt-2 text-lg">
<span className="font-semibold">Total inkl. moms:</span>
<span className="font-bold text-primary">{formatPrice(details.totalInclVat)}</span>
</div>
</div>
</div>
</CardContent>
</Card>
)
}