- Added a comprehensive manual testing guide for the countdown functionality in `test_countdown_manual.md`. - Developed a detailed Selenium test suite in `test_countdown_selenium.py` to verify countdown behavior, including server accessibility, login, countdown progression, and UI elements. - Created a minimal Selenium test version in `test_countdown_selenium_minimal.py` for quick checks without complex setup. - Introduced a verification script in `test_countdown_verification.py` to analyze code correctness, including server checks, build file existence, and countdown logic.
345 lines
15 KiB
JavaScript
345 lines
15 KiB
JavaScript
require('dotenv').config();
|
|
const mysql = require('mysql2/promise');
|
|
const axios = require('axios');
|
|
|
|
async function getMaterialByName(allMaterials, searchTerms) {
|
|
for (const term of searchTerms) {
|
|
const found = allMaterials.find(m =>
|
|
m.name && m.name.toLowerCase().includes(term.toLowerCase()) &&
|
|
m.price && parseFloat(m.price) > 0
|
|
);
|
|
if (found) return found;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
async function createRoofPackage(conn, allMaterials, packageData) {
|
|
// Tjek om pakken allerede findes
|
|
const [existing] = await conn.execute(
|
|
'SELECT id FROM material_packages WHERE name = ? AND is_active = 1',
|
|
[packageData.name]
|
|
);
|
|
|
|
if (existing.length > 0) {
|
|
console.log(` ⏭️ Pakke "${packageData.name}" findes allerede (ID ${existing[0].id})`);
|
|
return existing[0].id;
|
|
}
|
|
|
|
// Opret pakke
|
|
const [result] = await conn.execute(
|
|
`INSERT INTO material_packages
|
|
(name, description, category, difficulty_level, estimated_hours, hourly_rate,
|
|
installation_notes, created_by, total_estimated_price, is_active, is_template,
|
|
area_based, time_per_sqm, auto_calculated)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
[
|
|
packageData.name,
|
|
packageData.description,
|
|
'Tag',
|
|
packageData.difficulty || 'svær',
|
|
packageData.hours,
|
|
packageData.hourlyRate || 580,
|
|
packageData.notes,
|
|
'System',
|
|
0, // Will be calculated from materials
|
|
1, // is_active
|
|
1, // is_template
|
|
1, // area_based
|
|
packageData.timePerSqm,
|
|
1 // auto_calculated
|
|
]
|
|
);
|
|
|
|
const packageId = result.insertId;
|
|
console.log(` ✓ Oprettet pakke: ${packageData.name} (ID ${packageId})`);
|
|
|
|
// Tilføj materialer
|
|
let totalPrice = 0;
|
|
for (const matDef of packageData.materials) {
|
|
const material = await getMaterialByName(allMaterials, matDef.searchTerms);
|
|
if (material) {
|
|
const quantity = matDef.quantity;
|
|
const price = parseFloat(material.price);
|
|
const total = quantity * price;
|
|
totalPrice += total;
|
|
|
|
await conn.execute(
|
|
`INSERT INTO package_materials
|
|
(package_id, material_name, material_category, quantity, unit, unit_price,
|
|
total_price, supplier, notes, base_quantity, geometry_multiplier, waste_factor)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
[
|
|
packageId,
|
|
material.name,
|
|
material.category || 'Tag',
|
|
quantity,
|
|
material.enhed || 'stk',
|
|
price,
|
|
total,
|
|
material.leverandoer || 'Bygma',
|
|
matDef.notes || '',
|
|
matDef.baseQty || quantity,
|
|
matDef.multiplier || null,
|
|
matDef.wasteFactor || 1.05
|
|
]
|
|
);
|
|
console.log(` + Material: ${material.name} (${quantity} ${material.enhed || 'stk'})`);
|
|
} else {
|
|
console.log(` ⚠️ Kunne ikke finde: ${matDef.searchTerms[0]}`);
|
|
}
|
|
}
|
|
|
|
// Opdater total pris
|
|
await conn.execute(
|
|
'UPDATE material_packages SET total_estimated_price = ? WHERE id = ?',
|
|
[totalPrice, packageId]
|
|
);
|
|
|
|
// Tilføj opgaver
|
|
for (const task of packageData.tasks) {
|
|
await conn.execute(
|
|
`INSERT INTO package_tasks
|
|
(package_id, task_name, task_description, estimated_hours, task_order, task_phase)
|
|
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
[packageId, task.name, task.desc, task.hours, task.order, task.phase]
|
|
);
|
|
}
|
|
console.log(` + ${packageData.tasks.length} opgaver tilføjet\n`);
|
|
|
|
return packageId;
|
|
}
|
|
|
|
(async () => {
|
|
const conn = await mysql.createConnection({
|
|
host: process.env.DB_HOST,
|
|
port: process.env.DB_PORT,
|
|
user: process.env.DB_USER,
|
|
password: process.env.DB_PASSWORD,
|
|
database: process.env.DB_NAME
|
|
});
|
|
|
|
// Hent alle materialer
|
|
const response = await axios.get('http://localhost:4031/api/materials');
|
|
const allMaterials = response.data.materials || [];
|
|
|
|
console.log('🏗️ OPRETTER SMARTPAKKER FOR FORSKELLIGE TAGTYPER\n');
|
|
|
|
// 1. BETONTEGL TAG
|
|
await createRoofPackage(conn, allMaterials, {
|
|
name: 'Betontegl - Komplet tagudskiftning',
|
|
description: `Komplet tagudskiftning med betontegl. Professionel løsning til traditionelle danske huse.
|
|
|
|
🏠 INKLUDERER:
|
|
• Nedrivning af gammelt tag
|
|
• Nye bærelægter og kontralægter
|
|
• Undertag med membran
|
|
• NIBRA H10 betontegl i naturrød
|
|
• Vindskeder og afslutninger
|
|
• Tagrende og nedløb
|
|
|
|
⏱ TID: 8 timer per 10m²
|
|
💰 TIMEPRIS: 580 kr/time
|
|
|
|
✨ BETONTEGL FORDELE:
|
|
- Klassisk dansk udtryk
|
|
- Lang levetid (50+ år)
|
|
- God lydisolering
|
|
- Vejrbestandig`,
|
|
hours: 8.0,
|
|
timePerSqm: 0.8,
|
|
hourlyRate: 580,
|
|
difficulty: 'svær',
|
|
notes: 'Betontegl kræver solid bærekonstruktion. 8 timer/10m² inkl. nedrivning og opsætning.',
|
|
materials: [
|
|
{ searchTerms: ['NIBRA H10 Teglsten', 'tegl'], quantity: 12, baseQty: 12, notes: 'Ca 12 stk pr m²' },
|
|
{ searchTerms: ['Bærelægte', 'lægte 38x73'], quantity: 20, baseQty: 2, multiplier: 'areal', notes: 'Bærelægter' },
|
|
{ searchTerms: ['undertag', 'tagpap'], quantity: 11, baseQty: 11, notes: 'Undertag med 10% overlap' },
|
|
{ searchTerms: ['Tagisolering 200mm', 'isolering'], quantity: 10, baseQty: 10, notes: 'Isolering' }
|
|
],
|
|
tasks: [
|
|
{ name: 'Sikkerhedsforanstaltninger', desc: 'Opsætte stillads og sikkerhedsudstyr', hours: 1.0, phase: 'forberedelse', order: 1 },
|
|
{ name: 'Nedrivning af gammelt tag', desc: 'Nedtage gamle tagsten og undertag', hours: 2.5, phase: 'hovedarbejde', order: 1 },
|
|
{ name: 'Kontrol af spærkonstruktion', desc: 'Kontrollere og reparere spær ved behov', hours: 1.0, phase: 'hovedarbejde', order: 2 },
|
|
{ name: 'Montering af lægter', desc: 'Montere kontra- og bærelægter med korrekt afstand', hours: 2.0, phase: 'hovedarbejde', order: 3 },
|
|
{ name: 'Undertag og membran', desc: 'Lægge undertag med korrekt overlæg', hours: 1.5, phase: 'hovedarbejde', order: 4 },
|
|
{ name: 'Lægning af betontegl', desc: 'Lægge betontagsten med korrekt overlæg og fastgørelse', hours: 4.0, phase: 'hovedarbejde', order: 5 },
|
|
{ name: 'Montering af vindskeder', desc: 'Montere vindskeder og afslutninger', hours: 1.5, phase: 'afslutning', order: 1 },
|
|
{ name: 'Slutkontrol', desc: 'Gennemgå taget for kvalitet og tæthed', hours: 0.5, phase: 'inspektion', order: 1 }
|
|
]
|
|
});
|
|
|
|
// 2. ETERNIT B6 TAG
|
|
await createRoofPackage(conn, allMaterials, {
|
|
name: 'Eternit B6 - Komplet tagudskiftning',
|
|
description: `Komplet tagudskiftning med Eternit B6 tagsten. Moderne og vedligeholdelsesfri løsning.
|
|
|
|
🏠 INKLUDERER:
|
|
• Nedrivning af gammelt tag
|
|
• Kontralægter for optimal ventilation
|
|
• Bærelægter med præcis afstand
|
|
• Eternit B6 tagsten i rød
|
|
• Eternit rygningsten
|
|
• Eternit vindskeder
|
|
|
|
⏱ TID: 6 timer per 10m²
|
|
💰 TIMEPRIS: 580 kr/time
|
|
|
|
✨ ETERNIT B6 FORDELE:
|
|
- Vedligeholdelsesfri
|
|
- Nem og hurtig montering
|
|
- Perfekt ventilation
|
|
- Moderne look`,
|
|
hours: 6.0,
|
|
timePerSqm: 0.6,
|
|
hourlyRate: 580,
|
|
difficulty: 'mellem',
|
|
notes: 'Eternit B6 er lettere end betontegl. 6 timer/10m² inkl. nedrivning.',
|
|
materials: [
|
|
{ searchTerms: ['Eternit tagsten B6', 'eternit'], quantity: 11, baseQty: 11, notes: 'Ca 11 stk pr m²' },
|
|
{ searchTerms: ['Eternit rygningsten', 'rygning'], quantity: 1, baseQty: 0.1, multiplier: 'rygning', notes: 'Rygningsten pr løbemeter' },
|
|
{ searchTerms: ['Eternit vindskeder', 'vindskede'], quantity: 2, baseQty: 0.2, multiplier: 'facade', notes: 'Vindskeder' },
|
|
{ searchTerms: ['Bærelægte', 'lægte'], quantity: 20, baseQty: 2, multiplier: 'areal', notes: 'Lægter' }
|
|
],
|
|
tasks: [
|
|
{ name: 'Forberedelse og stillads', desc: 'Opsætte stillads og sikkerhedsudstyr', hours: 0.8, phase: 'forberedelse', order: 1 },
|
|
{ name: 'Nedrivning', desc: 'Nedtage gammelt tag', hours: 2.0, phase: 'hovedarbejde', order: 1 },
|
|
{ name: 'Montering af lægter', desc: 'Montere lægter med korrekt afstand til Eternit B6', hours: 1.5, phase: 'hovedarbejde', order: 2 },
|
|
{ name: 'Lægning af Eternit tagsten', desc: 'Lægge Eternit B6 tagsten', hours: 3.5, phase: 'hovedarbejde', order: 3 },
|
|
{ name: 'Montering af rygning', desc: 'Montere Eternit rygningsten', hours: 1.0, phase: 'hovedarbejde', order: 4 },
|
|
{ name: 'Vindskeder og afslutning', desc: 'Montere Eternit vindskeder', hours: 1.0, phase: 'afslutning', order: 1 },
|
|
{ name: 'Kvalitetskontrol', desc: 'Kontrollere montering og tæthed', hours: 0.5, phase: 'inspektion', order: 1 }
|
|
]
|
|
});
|
|
|
|
// 3. B7 BØLGEPLADE TAG (forbedret version)
|
|
await createRoofPackage(conn, allMaterials, {
|
|
name: 'B7 Bølgeplade - Komplet tagudskiftning',
|
|
description: `Komplet tagudskiftning med B7 bølgeplader. Økonomisk og pålidelig løsning.
|
|
|
|
🏠 INKLUDERER:
|
|
• Nedrivning af gammelt tag
|
|
• Kontralægter for ventilation (VIGTIGT!)
|
|
• Bærelægter med korrekt afstand
|
|
• B7 bølgeplader med overlap
|
|
• Rustfri tagskruer med gummipakninger
|
|
• Underlægspap T400
|
|
|
|
⏱ TID: 5 timer per 10m²
|
|
💰 TIMEPRIS: 580 kr/time
|
|
|
|
✨ B7 BØLGEPLADE FORDELE:
|
|
- Økonomisk løsning
|
|
- Hurtig montering
|
|
- God ventilation med kontralægter
|
|
- Robust og vejrbestandig`,
|
|
hours: 5.0,
|
|
timePerSqm: 0.5,
|
|
hourlyRate: 580,
|
|
difficulty: 'mellem',
|
|
notes: 'B7 er hurtigere at montere. Kontralægter er kritisk vigtige for ventilation!',
|
|
materials: [
|
|
{ searchTerms: ['B7 tagplader', 'bølgeplade'], quantity: 10.5, baseQty: 10.5, wasteFactor: 1.05, notes: 'Med 5% spild' },
|
|
{ searchTerms: ['Skruer til tagplader', 'tagskrue'], quantity: 150, baseQty: 15, multiplier: 'areal', notes: 'Rustfri skruer' },
|
|
{ searchTerms: ['Tagpap underlag', 'undertag'], quantity: 11, baseQty: 11, notes: 'T400 underlægspap' },
|
|
{ searchTerms: ['Bærelægte', 'lægte'], quantity: 22, baseQty: 2.2, multiplier: 'areal', notes: 'Bære- og kontralægter' }
|
|
],
|
|
tasks: [
|
|
{ name: 'Forberedelse', desc: 'Opsætte arbejdsområde og sikkerhed', hours: 0.5, phase: 'forberedelse', order: 1 },
|
|
{ name: 'Nedrivning', desc: 'Nedtage gammelt tagmateriale', hours: 1.5, phase: 'hovedarbejde', order: 1 },
|
|
{ name: 'Kontralægter (VIGTIGT)', desc: 'Montere kontralægter for ventilation - essentielt!', hours: 1.0, phase: 'hovedarbejde', order: 2 },
|
|
{ name: 'Bærelægter', desc: 'Montere bærelægter med præcis afstand', hours: 0.8, phase: 'hovedarbejde', order: 3 },
|
|
{ name: 'Underlægspap', desc: 'Lægge T400 underlægspap', hours: 0.5, phase: 'hovedarbejde', order: 4 },
|
|
{ name: 'Montering af B7 plader', desc: 'Lægge B7 bølgeplader med korrekt overlap', hours: 2.0, phase: 'hovedarbejde', order: 5 },
|
|
{ name: 'Skruning', desc: 'Skrue plader fast med rustfri skruer', hours: 0.8, phase: 'hovedarbejde', order: 6 },
|
|
{ name: 'Slutkontrol', desc: 'Kontrollere tæthed og finish', hours: 0.4, phase: 'inspektion', order: 1 }
|
|
]
|
|
});
|
|
|
|
// 4. B9 BØLGEPLADE TAG (dyb bølge)
|
|
await createRoofPackage(conn, allMaterials, {
|
|
name: 'B9 Bølgeplade - Komplet tagudskiftning',
|
|
description: `Komplet tagudskiftning med B9 bølgeplader (dyb bølge). Ekstra styrke til større spænd.
|
|
|
|
🏠 INKLUDERER:
|
|
• Nedrivning af gammelt tag
|
|
• Forstærket lægter til B9
|
|
• B9 bølgeplader (dyb bølge)
|
|
• Specialskruer til B9
|
|
• Underlægspap
|
|
|
|
⏱ TID: 5.5 timer per 10m²
|
|
💰 TIMEPRIS: 580 kr/time
|
|
|
|
✨ B9 BØLGEPLADE FORDELE:
|
|
- Dybere bølge = større styrke
|
|
- Kan dække længere spænd
|
|
- Bedre selvrensning (vand)
|
|
- Professionelt look`,
|
|
hours: 5.5,
|
|
timePerSqm: 0.55,
|
|
hourlyRate: 580,
|
|
difficulty: 'mellem',
|
|
notes: 'B9 kræver lidt længere monteringstid end B7 pga. størrelse.',
|
|
materials: [
|
|
{ searchTerms: ['B9', 'bølgeplade dyb', 'trapez'], quantity: 10.3, baseQty: 10.3, wasteFactor: 1.03, notes: 'B9 eller tilsvarende dyb bølge' },
|
|
{ searchTerms: ['Skruer til tagplader', 'tagskrue'], quantity: 140, baseQty: 14, multiplier: 'areal', notes: 'Færre skruer pga. større plader' },
|
|
{ searchTerms: ['Tagpap underlag', 'undertag'], quantity: 11, baseQty: 11, notes: 'Underlægspap' },
|
|
{ searchTerms: ['Bærelægte', 'lægte'], quantity: 20, baseQty: 2, multiplier: 'areal', notes: 'Forstærkede lægter' }
|
|
],
|
|
tasks: [
|
|
{ name: 'Forberedelse', desc: 'Klargøre arbejdsområde', hours: 0.5, phase: 'forberedelse', order: 1 },
|
|
{ name: 'Nedrivning', desc: 'Nedtage gammelt tag', hours: 1.5, phase: 'hovedarbejde', order: 1 },
|
|
{ name: 'Forstærket lægter', desc: 'Montere forstærkede lægter til B9', hours: 1.2, phase: 'hovedarbejde', order: 2 },
|
|
{ name: 'Underlægspap', desc: 'Lægge underlægspap', hours: 0.5, phase: 'hovedarbejde', order: 3 },
|
|
{ name: 'Montering af B9 plader', desc: 'Lægge B9 dyb bølgeplader', hours: 2.2, phase: 'hovedarbejde', order: 4 },
|
|
{ name: 'Fastgørelse', desc: 'Skrue plader fast korrekt', hours: 0.8, phase: 'hovedarbejde', order: 5 },
|
|
{ name: 'Kvalitetskontrol', desc: 'Kontrollere tæthed og stabilitet', hours: 0.5, phase: 'inspektion', order: 1 }
|
|
]
|
|
});
|
|
|
|
// 5. TRAPEZPLADE TAG (industri/moderne)
|
|
await createRoofPackage(conn, allMaterials, {
|
|
name: 'Trapezplade - Komplet tagudskiftning',
|
|
description: `Komplet tagudskiftning med trapezplader. Moderne og robust løsning til erhverv og carporte.
|
|
|
|
🏠 INKLUDERER:
|
|
• Nedrivning af gammelt tag
|
|
• Forstærkede lægter
|
|
• Trapezplader i stål
|
|
• Specialskruer med tætning
|
|
• Afslutningslister
|
|
|
|
⏱ TID: 4.5 timer per 10m²
|
|
💰 TIMEPRIS: 580 kr/time
|
|
|
|
✨ TRAPEZPLADE FORDELE:
|
|
- Meget robust
|
|
- Hurtig montering
|
|
- Moderne industrilook
|
|
- Perfekt til carporte/udhuse`,
|
|
hours: 4.5,
|
|
timePerSqm: 0.45,
|
|
hourlyRate: 580,
|
|
difficulty: 'mellem',
|
|
notes: 'Trapezplader er hurtige at montere. Perfekt til erhverv og carporte.',
|
|
materials: [
|
|
{ searchTerms: ['trapez', 'stålplade', 'tagplade'], quantity: 10.2, baseQty: 10.2, notes: 'Trapezplader' },
|
|
{ searchTerms: ['Skruer til tagplader', 'tagskrue'], quantity: 120, baseQty: 12, multiplier: 'areal', notes: 'Specialskruer' },
|
|
{ searchTerms: ['Bærelægte', 'lægte'], quantity: 18, baseQty: 1.8, multiplier: 'areal', notes: 'Lægter' }
|
|
],
|
|
tasks: [
|
|
{ name: 'Forberedelse', desc: 'Klargøre område og materiale', hours: 0.4, phase: 'forberedelse', order: 1 },
|
|
{ name: 'Nedrivning', desc: 'Nedtage eksisterende tag', hours: 1.3, phase: 'hovedarbejde', order: 1 },
|
|
{ name: 'Montering af lægter', desc: 'Montere lægter med korrekt afstand til trapezplader', hours: 1.0, phase: 'hovedarbejde', order: 2 },
|
|
{ name: 'Montering af trapezplader', desc: 'Lægge trapezplader med overlap', hours: 2.0, phase: 'hovedarbejde', order: 3 },
|
|
{ name: 'Fastgørelse og tætning', desc: 'Skrue fast og tætne samlinger', hours: 1.0, phase: 'hovedarbejde', order: 4 },
|
|
{ name: 'Afslutning', desc: 'Montere afslutningslister', hours: 0.5, phase: 'afslutning', order: 1 },
|
|
{ name: 'Inspektion', desc: 'Kontrollere kvalitet', hours: 0.3, phase: 'inspektion', order: 1 }
|
|
]
|
|
});
|
|
|
|
console.log('\n✅ FÆRDIG! Alle tagtype-pakker er oprettet\n');
|
|
|
|
await conn.end();
|
|
})();
|