feat: Latest production version with interior scene and glass
Includes room interior with floor, walls, glass you can see through, and all uncommitted production changes that were running live. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Pricing Engine for Proinn Configurator
|
||||
* Based on Dutch market standard pricing (Metalworks/Aluwdoors reference)
|
||||
* Based on Dutch market standard pricing
|
||||
*/
|
||||
|
||||
import {
|
||||
@@ -28,22 +28,64 @@ const HANDLE_PRICES: Record<string, number> = {
|
||||
'geen': 0,
|
||||
};
|
||||
|
||||
// Premium finish surcharges
|
||||
const FINISH_SURCHARGES: Record<string, number> = {
|
||||
'zwart': 0,
|
||||
'grijs': 0,
|
||||
'brons': 0,
|
||||
'goud': 150,
|
||||
'beige': 75,
|
||||
'ral': 200,
|
||||
};
|
||||
|
||||
// Frame size multipliers (relative to standard 40mm)
|
||||
const FRAME_SIZE_MULTIPLIERS: Record<number, number> = {
|
||||
20: 0.7,
|
||||
30: 0.85,
|
||||
40: 1.0,
|
||||
};
|
||||
|
||||
function getHorizontalDividerCount(gridLayout: GridLayout): number {
|
||||
switch (gridLayout) {
|
||||
case '2-vlak': return 1;
|
||||
case '3-vlak': return 2;
|
||||
case '4-vlak': return 3;
|
||||
case '6-vlak': return 2;
|
||||
case '8-vlak': return 3;
|
||||
case 'kruis': return 1;
|
||||
case 'ongelijk-3': return 2;
|
||||
case 'boerderij': return 1;
|
||||
case 'herenhuis': return 2;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function hasVerticalDividers(gridLayout: GridLayout): boolean {
|
||||
return ['6-vlak', '8-vlak', 'kruis', 'boerderij'].includes(gridLayout);
|
||||
}
|
||||
|
||||
function calculateSteelLength(
|
||||
doorWidth: number,
|
||||
doorHeight: number,
|
||||
gridLayout: GridLayout,
|
||||
hasVerticalDivider: boolean
|
||||
hasPaneelVerticalDivider: boolean
|
||||
): number {
|
||||
const innerWidth = doorWidth - PROFILE_WIDTH * 2;
|
||||
// Perimeter
|
||||
let totalLength = doorHeight * 2 + innerWidth * 2;
|
||||
|
||||
if (gridLayout === '3-vlak') {
|
||||
totalLength += innerWidth * 2;
|
||||
} else if (gridLayout === '4-vlak') {
|
||||
totalLength += innerWidth * 3;
|
||||
// Horizontal dividers
|
||||
const hDividers = getHorizontalDividerCount(gridLayout);
|
||||
totalLength += innerWidth * hDividers;
|
||||
|
||||
// Vertical dividers from grid pattern
|
||||
if (hasVerticalDividers(gridLayout)) {
|
||||
const innerHeight = doorHeight - RAIL_HEIGHT_ROBUST * 2;
|
||||
totalLength += innerHeight;
|
||||
}
|
||||
|
||||
if (hasVerticalDivider) {
|
||||
// Paneel type adds center vertical divider
|
||||
if (hasPaneelVerticalDivider) {
|
||||
totalLength += doorHeight - RAIL_HEIGHT_ROBUST * 2;
|
||||
}
|
||||
|
||||
@@ -58,11 +100,12 @@ function calculateGlassArea(
|
||||
const glassWidth = doorWidth - PROFILE_WIDTH * 2;
|
||||
const glassHeight = doorHeight - RAIL_HEIGHT_ROBUST * 2;
|
||||
|
||||
let dividerArea = 0;
|
||||
if (gridLayout === '3-vlak') {
|
||||
dividerArea = glassWidth * RAIL_HEIGHT_SLIM * 2;
|
||||
} else if (gridLayout === '4-vlak') {
|
||||
dividerArea = glassWidth * RAIL_HEIGHT_SLIM * 3;
|
||||
const hDividers = getHorizontalDividerCount(gridLayout);
|
||||
let dividerArea = glassWidth * RAIL_HEIGHT_SLIM * hDividers;
|
||||
|
||||
// Vertical divider area
|
||||
if (hasVerticalDividers(gridLayout)) {
|
||||
dividerArea += PROFILE_WIDTH * (glassHeight - RAIL_HEIGHT_SLIM * hDividers);
|
||||
}
|
||||
|
||||
return (glassWidth * glassHeight - dividerArea) / 1_000_000;
|
||||
@@ -75,6 +118,7 @@ export interface PriceBreakdown {
|
||||
mechanismSurcharge: number;
|
||||
sidePanelSurcharge: number;
|
||||
handleCost: number;
|
||||
finishSurcharge: number;
|
||||
totalPrice: number;
|
||||
steelLengthM: number;
|
||||
glassAreaSqm: number;
|
||||
@@ -87,12 +131,15 @@ export function calculatePrice(
|
||||
gridLayout: GridLayout,
|
||||
doorConfig: 'enkele' | 'dubbele',
|
||||
sidePanel: 'geen' | 'links' | 'rechts' | 'beide',
|
||||
handle: string
|
||||
handle: string,
|
||||
finish: string = 'zwart',
|
||||
frameSize: number = 40
|
||||
): PriceBreakdown {
|
||||
const leafCount = doorConfig === 'dubbele' ? 2 : 1;
|
||||
const hasVerticalDivider = doorType === 'paneel';
|
||||
const hasPaneelVerticalDivider = doorType === 'paneel';
|
||||
const frameSizeMultiplier = FRAME_SIZE_MULTIPLIERS[frameSize] ?? 1.0;
|
||||
|
||||
const steelLengthPerLeaf = calculateSteelLength(doorWidth, doorHeight, gridLayout, hasVerticalDivider);
|
||||
const steelLengthPerLeaf = calculateSteelLength(doorWidth, doorHeight, gridLayout, hasPaneelVerticalDivider);
|
||||
const glassAreaPerLeaf = calculateGlassArea(doorWidth, doorHeight, gridLayout);
|
||||
|
||||
const totalSteelLength = steelLengthPerLeaf * leafCount;
|
||||
@@ -100,7 +147,7 @@ export function calculatePrice(
|
||||
|
||||
const sidePanelCount = sidePanel === 'beide' ? 2 : (sidePanel === 'geen' ? 0 : 1);
|
||||
|
||||
const steelCost = Math.round(totalSteelLength * STEEL_PRICE_PER_METER);
|
||||
const steelCost = Math.round(totalSteelLength * STEEL_PRICE_PER_METER * frameSizeMultiplier);
|
||||
const glassCost = Math.round(totalGlassArea * GLASS_PRICE_PER_SQM);
|
||||
|
||||
let mechanismSurcharge = 0;
|
||||
@@ -109,8 +156,9 @@ export function calculatePrice(
|
||||
|
||||
const handleCost = HANDLE_PRICES[handle] || 0;
|
||||
const sidePanelSurchrg = sidePanelCount * SIDE_PANEL_SURCHARGE;
|
||||
const finishSurcharge = FINISH_SURCHARGES[finish] ?? 0;
|
||||
|
||||
const totalPrice = steelCost + glassCost + BASE_FEE + mechanismSurcharge + sidePanelSurchrg + handleCost;
|
||||
const totalPrice = steelCost + glassCost + BASE_FEE + mechanismSurcharge + sidePanelSurchrg + handleCost + finishSurcharge;
|
||||
|
||||
return {
|
||||
steelCost,
|
||||
@@ -119,6 +167,7 @@ export function calculatePrice(
|
||||
mechanismSurcharge,
|
||||
sidePanelSurcharge: sidePanelSurchrg,
|
||||
handleCost,
|
||||
finishSurcharge,
|
||||
totalPrice,
|
||||
steelLengthM: Math.round(totalSteelLength * 100) / 100,
|
||||
glassAreaSqm: Math.round(totalGlassArea * 100) / 100,
|
||||
|
||||
Reference in New Issue
Block a user