diff --git a/backend/src/services/advancedGeometryService.js b/backend/src/services/advancedGeometryService.js index b54de22..498dee1 100644 --- a/backend/src/services/advancedGeometryService.js +++ b/backend/src/services/advancedGeometryService.js @@ -430,10 +430,16 @@ class AdvancedGeometryService { // Tagtype påvirkning switch (roofType?.toLowerCase()) { - case 'fladt_tag': complexity *= 0.8; break; + case 'fladt_tag': + case 'fladtag': complexity *= 0.8; break; + case 'sadeltag': case 'skraat_tag': complexity *= 1.0; break; - case 'mansard': complexity *= 1.4; break; - case 'komplekst': complexity *= 1.6; break; + case 'valmtag': complexity *= 1.2; break; + case 'koebenhavnertag': complexity *= 1.3; break; + case 'pulttag': complexity *= 0.9; break; + case 'tag_med_kviste': complexity *= 1.4; break; + case 'mansard': + case 'mansardtag': complexity *= 1.4; break; } // Hældning påvirkning @@ -532,10 +538,16 @@ class AdvancedGeometryService { 'b6': 'B6 Tagplader', 'vingetegl': 'Vingetegl', 'røde_teglsten': 'Røde Teglsten', - 'fladt_tag': 'Fladt Tag', - 'skraat_tag': 'Skråt Tag', + 'fladt_tag': 'Fladtag', + 'fladtag': 'Fladtag', + 'sadeltag': 'Sadeltag/Skråttag', + 'skraat_tag': 'Sadeltag/Skråttag', + 'valmtag': 'Valmtag', + 'koebenhavnertag': 'Københavnertag', + 'pulttag': 'Pulttag', + 'tag_med_kviste': 'Tag med kviste', 'mansard': 'Mansardtag', - 'komplekst': 'Komplekst Tag' + 'mansardtag': 'Mansardtag' }; return displayNames[roofType?.toLowerCase()] || roofType; diff --git a/frontend/src/components/EnhancedGeometry.js b/frontend/src/components/EnhancedGeometry.js index 56f3c1f..8b8f35b 100644 --- a/frontend/src/components/EnhancedGeometry.js +++ b/frontend/src/components/EnhancedGeometry.js @@ -268,7 +268,7 @@ const EnhancedGeometry = ({ apiBaseUrl, project, selectedPackages, onGeometryCal }, []); // Map Smart Pakker roof types to geometry roof types const mapRoofTypeFromPackages = (packages) => { - if (!packages || packages.length === 0) return 'skraat_tag'; + if (!packages || packages.length === 0) return 'sadeltag'; const hasB7Package = packages.some(pkg => pkg.roofType === 'b7' || @@ -296,12 +296,12 @@ const EnhancedGeometry = ({ apiBaseUrl, project, selectedPackages, onGeometryCal console.log('🏠 Auto-mapping B6 package to fladt_tag roof type'); return 'fladt_tag'; // B6 is also typically flat roof } else if (hasBetonteglPackage) { - console.log('🏠 Auto-mapping Betontegl package to skraat_tag roof type'); - return 'skraat_tag'; // Betontegl is for pitched roofs + console.log('🏠 Auto-mapping Betontegl package to sadeltag roof type'); + return 'sadeltag'; // Betontegl is for pitched roofs } // Fallback to pitched roof as default - return 'skraat_tag'; + return 'sadeltag'; }; const [geometryInput, setGeometryInput] = useState({ @@ -337,7 +337,7 @@ const EnhancedGeometry = ({ apiBaseUrl, project, selectedPackages, onGeometryCal width: geo.width_main || '', length: geo.length_main || '', pitch: geo.roof_pitch || 30, - roofType: geo.roof_type || 'skraat_tag', + roofType: geo.roof_type || 'sadeltag', complexity: geo.complexity_factor || 1.0, wallHeight: geo.wall_height || 0, // Væghøjde (stern højde) ridgeHeight: geo.stern_to_ridge_height || 0, // Højde stern til kip @@ -409,7 +409,7 @@ const EnhancedGeometry = ({ apiBaseUrl, project, selectedPackages, onGeometryCal setGeometryInput(prev => { // Only set if user has not interacted (width, length, or roofType changed from default) if ( - (!prev.width && !prev.length && prev.roofType === 'skraat_tag') || + (!prev.width && !prev.length && prev.roofType === 'sadeltag') || (prev.roofType === '' && !prev.width && !prev.length) ) { return { ...prev, roofType: newRoofType }; @@ -643,169 +643,11 @@ const EnhancedGeometry = ({ apiBaseUrl, project, selectedPackages, onGeometryCal } }; + /* REMOVED: Manual save button - geometry auto-saves after calculation const handleSaveGeometry = async () => { - console.log('💾 Saving geometry data...', { - geometryInput: geometryInput, - project: project, - apiBaseUrl: apiBaseUrl - }); - - // Comprehensive validation for realistic geometry calculations (same as calculateGeometry) - if (!geometryInput.width || !geometryInput.length || !geometryInput.roofType) { - setStatus('⚠️ Udfyld venligst alle påkrævede felter: bredde, længde og tagtype'); - return; - } - - const width = parseFloat(geometryInput.width); - const length = parseFloat(geometryInput.length); - const wallHeight = parseFloat(geometryInput.wallHeight) || 2.5; - const pitch = parseFloat(geometryInput.pitch) || 0; - - // Flexible size constraints for various building types - if (isNaN(width) || width < 1.0 || width > 100) { - setStatus('⚠️ Bygningsbredde skal være mellem 1,0 og 100 meter'); - return; - } - - if (isNaN(length) || length < 1.0 || length > 150) { - setStatus('⚠️ Bygningslængde skal være mellem 1,0 og 150 meter'); - return; - } - - // Wall height requirements - if (wallHeight < 1.0 || wallHeight > 10.0) { - setStatus('⚠️ Væghøjde skal være mellem 1,0 og 10,0 meter'); - return; - } - - // Roof type specific validation - if (geometryInput.roofType === 'skraat_tag') { - if (!geometryInput.pitch || pitch < 15 || pitch > 80) { - setStatus('⚠️ For skråt tag skal taghældning være mellem 15° og 80° (15-45° standard, 45-80° stejle tage som A-frame)'); - return; - } - - // Check realistic proportions for pitched roof (increased to 20m to allow steep roofs) - const roofHeight = Math.tan(pitch * Math.PI / 180) * (width / 2); - if (roofHeight > 20) { - setStatus('⚠️ Taghældning skaber urealistisk høj ryg (over 20m). Reducer hældning eller bygningsbredde'); - return; - } - } - - // Area constraints for flexible projects - const totalArea = width * length; - if (totalArea < 5) { - setStatus('⚠️ Minimum bygningsareal er 5 m²'); - return; - } - - if (totalArea > 10000) { - setStatus('⚠️ Maksimum bygningsareal er 10.000 m²'); - return; - } - - // Aspect ratio check - prevent unrealistic building shapes - const aspectRatio = Math.max(width, length) / Math.min(width, length); - if (aspectRatio > 20) { - setStatus('⚠️ Bygningen er meget smal/lang. Maksimum forhold mellem længde og bredde er 1:20'); - return; - } - - if (!project || !project.id) { - setStatus('⚠️ Projektinformation mangler. Prøv at genindlæse siden.'); - return; - } - - setLoading(true); - try { - console.log('📡 Sending save request to backend...'); - - // Save geometry to project - const geometryResponse = await fetch(`${apiBaseUrl}/api/customer-projects/${project.id}/geometry`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - roofWidth: parseFloat(geometryInput.width), - roofLength: parseFloat(geometryInput.length), - roofPitch: parseFloat(geometryInput.pitch), - roofType: geometryInput.roofType, - complexity: parseFloat(geometryInput.complexity), - wallHeight: parseFloat(geometryInput.wallHeight) || 2.5, - ridgeHeight: parseFloat(geometryInput.ridgeHeight) || null, - notes: geometryInput.notes - }) - }); - - console.log('📡 Save response:', geometryResponse.status); - - if (geometryResponse.ok) { - const geometryData = await geometryResponse.json(); - console.log('📊 Save data received:', geometryData); - - if (geometryData.success) { - setStatus('✅ Geometri data gemt succesfuldt!'); - - // Update results display with correct nested structure - setGeometryResult({ - // Fields that FinalReview expects - width: parseFloat(geometryInput.width), - length: parseFloat(geometryInput.length), - wallHeight: parseFloat(geometryInput.wallHeight) || 2.5, - roofPitch: parseFloat(geometryInput.pitch) || 30, - - // Existing fields - area: geometryData.area, - roofHeight: geometryData.roofHeight, - vindskede_lbm: geometryData.vindskede_lbm, - estimatedWorkHours: 16, - complexity: parseFloat(geometryInput.complexity) || 1.0, - - // Add nested objects that UI expects - basicDimensions: { - baseArea: geometryData.area - }, - materialQuantities: { - roofCovering: { - area: geometryData.area - } - }, - heightCalculations: { - ridgeHeight: geometryData.roofHeight || 0, - rafterLength: Math.sqrt(Math.pow(parseFloat(geometryInput.width) / 2, 2) + Math.pow(geometryData.roofHeight || 0, 2)) - }, - windboardCalculations: { - gableBoards: { - totalLength: geometryData.vindskede_lbm || 0 - }, - eavesBoards: { - totalLength: (parseFloat(geometryInput.length) * 2) || 0 - }, - ridgeBoard: { - length: parseFloat(geometryInput.length) || 0 - }, - totalLength: (geometryData.vindskede_lbm || 0) + ((parseFloat(geometryInput.length) * 3) || 0) - }, - svgIllustration: { - svg: generateRoofSVG(geometryInput.roofType, parseFloat(geometryInput.width), parseFloat(geometryInput.length), geometryInput.pitch || 25, parseFloat(geometryInput.wallHeight)) - } - }); - } else { - console.error('❌ Save failed:', geometryData); - setStatus(`❌ Gemning fejlede: ${geometryData.error || 'Ukendt fejl'}`); - } - } else { - const errorText = await geometryResponse.text(); - console.error('❌ Save API error:', geometryResponse.status, errorText); - setStatus(`❌ API fejl ved gemning: ${geometryResponse.status}`); - } - } catch (error) { - console.error('❌ Error saving geometry:', error); - setStatus(`❌ Fejl ved gemning af geometri: ${error.message}`); - } finally { - setLoading(false); - } + ... (removed for auto-save implementation) }; + */ const handleNext = () => { onGeometryCalculated({ @@ -847,10 +689,13 @@ const EnhancedGeometry = ({ apiBaseUrl, project, selectedPackages, onGeometryCal value={geometryInput.roofType} onChange={(e) => setGeometryInput({...geometryInput, roofType: e.target.value})} > - - + + + + + + - @@ -940,17 +785,6 @@ const EnhancedGeometry = ({ apiBaseUrl, project, selectedPackages, onGeometryCal /> Lodret højde fra stern til kip - bruges til vindskeder beregning - -