fix: Update tag types and remove komplekst tag option

- Replaced 'Komplekst tag' with proper tag types: Sadeltag/Skråttag, Valmtag, Københavnertag, Fladtag, Pulttag, Tag med kviste, Mansardtag
- Removed parentheses from tag type names for cleaner UI
- Removed 'Komplekse detaljer' checkbox (karnapper, kviste)
- Removed manual 'Gem Geometri' button - geometry now auto-saves after calculation
- Updated backend complexity calculations to support new tag types
- Updated getRoofTypeDisplayName to include all new tag types

Customer feedback addressed:
- Tag type cleanup and proper Danish names
- Removed komplekst tag option
- Removed manual save button
This commit is contained in:
alexpolo1
2025-11-05 07:43:02 +00:00
parent a3badedb2c
commit e86aef7107
2 changed files with 33 additions and 196 deletions
@@ -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;
+15 -190
View File
@@ -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})}
>
<option value="skraat_tag">Skråtag (Betontegl/Tegl)</option>
<option value="fladt_tag">Fladt tag (Eternit B7/B6)</option>
<option value="sadeltag">Sadeltag/Skråttag</option>
<option value="valmtag">Valmtag</option>
<option value="koebenhavnertag">Københavnertag</option>
<option value="fladt_tag">Fladtag</option>
<option value="pulttag">Pulttag</option>
<option value="tag_med_kviste">Tag med kviste</option>
<option value="mansard">Mansardtag</option>
<option value="komplekst">Komplekst tag</option>
</select>
</div>
@@ -940,17 +785,6 @@ const EnhancedGeometry = ({ apiBaseUrl, project, selectedPackages, onGeometryCal
/>
<small className="form-help">Lodret højde fra stern til kip - bruges til vindskeder beregning</small>
</div>
<div className="form-group">
<label>
<input
type="checkbox"
checked={geometryInput.hasComplexFeatures}
onChange={(e) => setGeometryInput({...geometryInput, hasComplexFeatures: e.target.checked})}
/>
Komplekse detaljer (karnapper, kviste)
</label>
</div>
</div>
<button
@@ -961,15 +795,6 @@ const EnhancedGeometry = ({ apiBaseUrl, project, selectedPackages, onGeometryCal
{loading ? 'Beregner...' : '🧮 Beregn Geometri'}
</button>
<button
className="save-btn"
onClick={handleSaveGeometry}
disabled={loading || !geometryInput.width || !geometryInput.length}
style={{marginLeft: '10px', backgroundColor: '#28a745'}}
>
💾 Gem Geometri
</button>
{status && (
<div className="status-message" style={{
marginTop: '10px',