MTMehitus / app.js
techprotrade's picture
Initial MTMehitus Space
49a50d3 verified
Raw
History Blame Contribute Delete
28.4 kB
/* MTM Project Studio – browser-native sales application.
Works without API keys or a backend. Leads are prepared as email drafts and
project drafts/favorites are stored only in the visitor's browser. */
const MTM_EMAIL = "info@mtmehitus.ee";
const MTM_PHONE = "3725071042";
const storageKeys = {
favorites: "mtm-project-favorites-v1",
draft: "mtm-project-draft-v1",
leads: "mtm-project-leads-v1"
};
const projects = [
{
id: "nordic-140",
name: "Nordic 140",
type: "home",
typeLabel: "Eramu",
area: 140,
rooms: 5,
floors: 1,
style: "Põhjamaine",
material: "Puit ja krohv",
designPrice: 1850,
status: "Kliendi valitud suund",
package: "3D-disain + kohandamine",
icon: "fa-house-chimney-window",
gradient: "linear-gradient(135deg,#78909c,#263238)",
description: "Valgusküllane ühekorruseline lahendus, kus köök ja elutuba avanevad terrassile. Selge ruumiloogika sobib nii linnalähedasele kui maapiirkonna krundile.",
customize: "Pindala 115–165 m², 3–4 magamistuba, fassaadimaterjalid, katusekalle, terrassi asukoht ja tehnosüsteemid."
},
{
id: "compact-92",
name: "Compact 92",
type: "home",
typeLabel: "Eramu",
area: 92,
rooms: 4,
floors: 1,
style: "Ajatu",
material: "Puitkarkass",
designPrice: 1450,
status: "Projektina valitud",
package: "3D-disain + ehitusprojekt",
icon: "fa-house",
gradient: "linear-gradient(135deg,#90a4ae,#455a64)",
description: "Kompaktne viilkatusega kodu, mis annab väikese ehitusaluse pinna juures hea ruumikasutuse ja kontrollitava ehituseelarve.",
customize: "Pindala 78–110 m², 2–3 magamistuba, varjualune, terrass, katusematerjal ja siseplaneering."
},
{
id: "courtyard-168",
name: "Courtyard 168",
type: "home",
typeLabel: "Eramu",
area: 168,
rooms: 6,
floors: 1,
style: "Modernne",
material: "Kergplokk ja puit",
designPrice: 2450,
status: "Täislahenduse lähteprojekt",
package: "Disainist teostuseni",
icon: "fa-building",
gradient: "linear-gradient(135deg,#8d6e63,#3e2723)",
description: "L-kujuline sisehooviga lahendus eraldab privaatse magamistubade tiiva ja avatud eluala. Hoov loob tuulevarjulise väliruumi.",
customize: "Pindala 145–195 m², garaaž või varjualune, sisehoovi suurus, fassaadikombinatsioon ja ruumide arv."
},
{
id: "forest-76",
name: "Forest 76",
type: "summer",
typeLabel: "Puhkemaja",
area: 76,
rooms: 3,
floors: 1,
style: "Looduslähedane",
material: "Termopuit",
designPrice: 1350,
status: "Kohandatav näidislahendus",
package: "3D-kontseptsioon",
icon: "fa-tree-city",
gradient: "linear-gradient(135deg,#7cb342,#1b5e20)",
description: "Kompaktne puhkemaja suure klaasfassaadi, saunaploki ja kaetud terrassiga. Sobib metsa, mere või järve lähedale.",
customize: "Pindala 55–95 m², saun, magamiskohtade arv, terrass, vai- või plaatvundament ja aastaringne kasutus."
},
{
id: "studio-48",
name: "Studio 48",
type: "outbuilding",
typeLabel: "Stuudio / abihoone",
area: 48,
rooms: 2,
floors: 1,
style: "Minimalistlik",
material: "Puit ja plekk",
designPrice: 980,
status: "Kohandatav näidislahendus",
package: "3D-disain",
icon: "fa-warehouse",
gradient: "linear-gradient(135deg,#607d8b,#102027)",
description: "Paindlik väikehoone kodukontoriks, stuudioks, külalistemajaks või rendipinnaks. Tehnosüsteemid saab lahendada põhiehitisega koos või eraldi.",
customize: "Pindala 30–60 m², pesuruum, kööginurk, eraldi sissepääsud, fassaad ja tehniline valmidus."
},
{
id: "workhub-210",
name: "WorkHub 210",
type: "office",
typeLabel: "Ärihoone",
area: 210,
rooms: 8,
floors: 2,
style: "Kaasaegne",
material: "Sandwich-paneel ja klaas",
designPrice: 2900,
status: "Ärilahenduse kontseptsioon",
package: "3D-disain + põhiprojekt",
icon: "fa-city",
gradient: "linear-gradient(135deg,#5c6bc0,#1a237e)",
description: "Kahekorruseline paindlik töö- ja teenindushoone, mille ruume saab jagada ühe või mitme kasutaja vahel.",
customize: "Pindala 160–320 m², töökohtade arv, lao- või teenindusala, parkimine, reklaampinnad ja energialahendus."
}
];
const packageMeta = {
concept: {
name: "3D-kontseptsioon",
duration: "3–6 nädalat",
stages: ["Lähteülesanne", "Eskiissuund", "3D-mudel", "Visualiseeringud", "Üleandmine"],
descriptions: [
"Vajadused, krundiinfo, ruumiprogramm ja eesmärgid.",
"Kaks ruumilist suunda ja eelistatud variandi valik.",
"Mahud, fassaadid, avad ja põhikompositsioon.",
"Materjalid, valgus ja esitlusvaated.",
"Failipakett ning soovitused järgmise etapi jaoks."
],
shares: [0.15, 0.2, 0.3, 0.25, 0.1],
deliverables: ["Lähteülesanne ja ruumiprogramm", "2 eskiissuunda", "3D-mudel ja 4 vaadet", "Materjali- ja värvikaart"]
},
project: {
name: "Ehitusprojekt",
duration: "8–16 nädalat",
stages: ["Lähteülesanne", "3D-kontseptsioon", "Põhijoonised", "Tehniline kooskõla", "Üleandmine"],
descriptions: [
"Krundiinfo, ruumiprogramm ja tehnilised eesmärgid.",
"Mahud, plaaniloogika, välisilme ja materjalid.",
"Plaanid, vaated, lõiked ja põhikonstruktsioonid.",
"Konstruktsioonide ja tehnosüsteemide lähteinfo.",
"Koondfailid, konsultatsioon ja järgmiste sammude plaan."
],
shares: [0.12, 0.22, 0.3, 0.24, 0.12],
deliverables: ["Lähteülesanne ja ruumiprogramm", "3D-kontseptsioon ja materjalid", "Plaanid, vaated ja lõiked", "Tehniliste osade lähteinfo"]
},
turnkey: {
name: "Täislahendus",
duration: "9–16 kuud",
stages: ["Projekteerimine", "Vundament", "Konstruktsioonid", "Tehnosüsteemid", "Viimistlus ja üleandmine"],
descriptions: [
"Disain, projekt, kooskõlastused ja detailne eelarve.",
"Pinnasetööd, vundament, soojustus ja aluspõrand.",
"Kandekonstruktsioonid, katus, avatäited ja fassaad.",
"Elekter, vesi, kanalisatsioon, küte ja ventilatsioon.",
"Siseviimistlus, seadistus, kontroll ja dokumentatsioon."
],
shares: [0.08, 0.14, 0.3, 0.21, 0.27],
deliverables: ["Disain ja ehitusprojekt", "Detailne eelarve ja ajakava", "Ehitusjuhtimine ja kvaliteedikontroll", "Valmis objekt ja üleandmisdokumendid"]
}
};
let appState = {
package: "project",
selectedProjectId: null,
favoritesOnly: false,
estimate: null
};
let configState = {
area: 120,
floors: 1,
foundation: "plate",
wall: "block",
roof: "gable-tin",
heating: "geo",
finish: "turnkey",
region: "harju",
use: "home"
};
function safeJsonRead(key, fallback) {
try {
const parsed = JSON.parse(localStorage.getItem(key));
return parsed ?? fallback;
} catch {
return fallback;
}
}
function saveJson(key, value) {
try {
localStorage.setItem(key, JSON.stringify(value));
return true;
} catch {
return false;
}
}
function formatCurrency(value) {
return Math.round(value).toLocaleString("et-EE") + " €";
}
function packageValueToForm(value) {
return packageMeta[value]?.name || "Ehitusprojekt";
}
function showToast(message) {
const toast = document.getElementById("toast");
toast.textContent = message;
toast.classList.add("show");
clearTimeout(showToast.timer);
showToast.timer = setTimeout(() => toast.classList.remove("show"), 3200);
}
window.selectOption = function(type, value, element) {
configState[type] = value;
element.parentElement.querySelectorAll(".option-card").forEach(card => card.classList.remove("selected"));
element.classList.add("selected");
updateCalculator();
};
window.switchTab = function(tabName) {
document.querySelectorAll(".tab-btn").forEach(btn => btn.classList.remove("active"));
document.querySelectorAll(".tab-content").forEach(content => content.classList.remove("active"));
const custom = tabName === "customizer";
document.querySelectorAll(".tab-btn")[custom ? 0 : 1].classList.add("active");
document.getElementById(custom ? "tab-customizer" : "tab-ai-prompt").classList.add("active");
};
window.selectPackage = function(packageId, element) {
appState.package = packageId;
document.querySelectorAll(".package-option").forEach(card => card.classList.remove("selected"));
const target = element || document.querySelector(`[data-package="${packageId}"]`);
if (target) target.classList.add("selected");
document.getElementById("form-package").value = packageValueToForm(packageId);
updateCalculator();
};
window.choosePackageAndScroll = function(packageId) {
selectPackage(packageId);
document.querySelector("#kalkulaator").scrollIntoView({behavior: "smooth"});
};
window.updateCalculator = function() {
const area = Number(document.getElementById("house-area").value);
configState.area = area;
configState.foundation = document.getElementById("foundation-type").value;
configState.wall = document.getElementById("wall-type").value;
configState.roof = document.getElementById("roof-type").value;
configState.heating = document.getElementById("heating-type").value;
configState.region = document.getElementById("project-region").value;
configState.use = document.getElementById("building-use").value;
document.getElementById("area-val").textContent = area;
let baseRate = 1200;
if (configState.floors === 1.5) baseRate += 100;
if (configState.floors === 2) baseRate += 180;
if (configState.foundation === "plate") baseRate += 60;
if (configState.foundation === "pile") baseRate += 90;
if (configState.wall === "clt") baseRate += 220;
if (configState.wall === "wood") baseRate += 50;
if (configState.heating === "geo") baseRate += 80;
if (configState.heating === "air-water") baseRate += 50;
if (configState.finish === "shell") baseRate -= 350;
if (configState.finish === "turnkey") baseRate += 250;
if (configState.region === "harju") baseRate *= 1.04;
let midpoint;
let lowFactor;
let highFactor;
if (appState.package === "concept") {
midpoint = 850 + area * 8.5 + (configState.floors > 1 ? 350 : 0);
lowFactor = 0.88;
highFactor = 1.18;
} else if (appState.package === "project") {
midpoint = 2600 + area * 31 + (configState.floors > 1 ? 800 : 0) + (configState.wall === "clt" ? 500 : 0);
lowFactor = 0.86;
highFactor = 1.22;
} else {
midpoint = area * baseRate + 6200;
lowFactor = 0.9;
highFactor = 1.13;
}
const low = Math.round(midpoint * lowFactor / 50) * 50;
const high = Math.round(midpoint * highFactor / 50) * 50;
const meta = packageMeta[appState.package];
const selectedProject = projects.find(p => p.id === appState.selectedProjectId);
const readiness = Math.min(95, 45 + (selectedProject ? 15 : 0) + (document.getElementById("ai-description").value.trim() ? 15 : 0) + (configState.region ? 10 : 0));
appState.estimate = { midpoint, low, high, baseRate, readiness };
document.getElementById("total-price").textContent = `${formatCurrency(low)}${formatCurrency(high)}`;
document.getElementById("price-per-m2").textContent = appState.package === "turnkey"
? `hinnanguliselt ${Math.round(baseRate)} € / m²`
: `${meta.name.toLowerCase()} valitud mahu põhjal`;
meta.shares.forEach((share, index) => {
document.getElementById(`stage-label-${index + 1}`).textContent = `${index + 1}. ${meta.stages[index]}`;
document.getElementById(`stage-desc-${index + 1}`).textContent = meta.descriptions[index];
document.getElementById(["cost-foundation","cost-walls","cost-roof","cost-utility","cost-finish"][index]).textContent = formatCurrency(midpoint * share);
});
meta.deliverables.forEach((item, index) => {
document.getElementById(`deliverable-${index + 1}`).textContent = item;
});
const useLabels = {home:"eramu", summer:"puhkemaja", office:"ärihoone", outbuilding:"abihoone"};
document.getElementById("brief-title").textContent = `${selectedProject ? selectedProject.name + " põhjal" : "Uus"} ${area}${useLabels[configState.use]}${meta.name.toLowerCase()}`;
document.getElementById("brief-package").textContent = meta.name;
document.getElementById("brief-range").textContent = `${formatCurrency(low)}${formatCurrency(high)}`;
document.getElementById("brief-duration").textContent = meta.duration;
document.getElementById("brief-readiness").textContent = `${readiness}%`;
};
function getBriefText() {
const selected = projects.find(p => p.id === appState.selectedProjectId);
const meta = packageMeta[appState.package];
const regionLabels = {harju:"Harjumaa / Tallinn", tartu:"Tartumaa", parnu:"Pärnumaa", rapla:"Raplamaa", other:"Muu Eesti piirkond"};
const useLabels = {home:"Eramu", summer:"Suvila / puhkemaja", office:"Äri- või büroohoone", outbuilding:"Abihoone / stuudio"};
const estimate = appState.estimate || {};
return `MTM EHITUS – PROJEKTI LÄHTEÜLESANNE
Koostatud: ${new Date().toLocaleDateString("et-EE")}
VALITUD LAHENDUS
Pakett: ${meta.name}
Lähteprojekt: ${selected ? selected.name : "Personaalne uus lahendus"}
Hoone: ${useLabels[configState.use]}
Piirkond: ${regionLabels[configState.region]}
Pindala: ${configState.area}
Korruseid: ${configState.floors}
TEHNILISED EELISTUSED
Vundament: ${configState.foundation}
Konstruktsioon: ${configState.wall}
Katus: ${configState.roof}
Küte: ${configState.heating}
Viimistlus: ${configState.finish}
IDEE JA ERISOOVID
${document.getElementById("ai-description").value.trim() || "Täpsustatakse konsultatsioonil."}
ESIALGNE PLANEERING
Hinnavahemik: ${formatCurrency(estimate.low || 0)}${formatCurrency(estimate.high || 0)}
Ajakava: ${meta.duration}
Lähteandmete valmidus: ${estimate.readiness || 0}%
Märkus: hinnavahemik on planeerimise lähtehinnang, mitte siduv pakkumine.`;
}
window.downloadBrief = function() {
const blob = new Blob([getBriefText()], {type: "text/plain;charset=utf-8"});
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = `MTM-projekti-lahteulesanne-${new Date().toISOString().slice(0,10)}.txt`;
link.click();
URL.revokeObjectURL(url);
showToast("Lähteülesanne laaditi alla.");
};
window.copyBrief = async function() {
try {
await navigator.clipboard.writeText(getBriefText());
showToast("Projektikokkuvõte kopeeriti.");
} catch {
showToast("Kopeerimine ei õnnestunud. Laadi fail alla.");
}
};
window.shareByEmail = function() {
const subject = encodeURIComponent(`MTM projektipäring – ${packageMeta[appState.package].name}`);
const body = encodeURIComponent(getBriefText());
window.location.href = `mailto:${MTM_EMAIL}?subject=${subject}&body=${body}`;
};
window.shareByWhatsApp = function() {
const text = encodeURIComponent(getBriefText());
window.open(`https://wa.me/${MTM_PHONE}?text=${text}`, "_blank", "noopener");
};
window.saveDraft = function() {
const draft = {
config: {...configState},
app: {package: appState.package, selectedProjectId: appState.selectedProjectId},
description: document.getElementById("ai-description").value,
savedAt: new Date().toISOString()
};
if (saveJson(storageKeys.draft, draft)) showToast("Projekt salvestati selles brauseris.");
else showToast("Brauser ei lubanud mustandit salvestada.");
};
function restoreDraft() {
const draft = safeJsonRead(storageKeys.draft, null);
if (!draft?.config) return;
Object.assign(configState, draft.config);
appState.package = draft.app?.package || "project";
appState.selectedProjectId = draft.app?.selectedProjectId || null;
document.getElementById("house-area").value = configState.area || 120;
["foundation","wall","roof","heating"].forEach(key => {
const ids = {foundation:"foundation-type",wall:"wall-type",roof:"roof-type",heating:"heating-type"};
if (configState[key]) document.getElementById(ids[key]).value = configState[key];
});
if (configState.region) document.getElementById("project-region").value = configState.region;
if (configState.use) document.getElementById("building-use").value = configState.use;
document.getElementById("ai-description").value = draft.description || "";
selectPackage(appState.package);
if (appState.selectedProjectId) updateStickyProject();
showToast("Varasem projektimustand taastati.");
}
window.analyzeProjectIdea = function() {
const desc = document.getElementById("ai-description").value.trim();
const file = document.getElementById("ai-image-upload").files[0];
if (!desc && !file) {
showToast("Lisa kirjeldus või inspiratsioonipilt.");
return;
}
const lower = desc.toLowerCase();
const recommendations = [];
if (/modern|minimal|lamekatus/.test(lower)) recommendations.push("Kontseptsioonis tasub kontrollida suure klaaspinna, varjestuse ja lamekatuse tehnilist sobivust.");
if (/puit|clt|loodus/.test(lower)) recommendations.push("Puitlahenduse puhul lisa lähteülesandesse fassaadi hooldusklass, tulepüsivus ja niiskusrežiim.");
if (/saun|spaa|bassein/.test(lower)) recommendations.push("Märgruumid vajavad varases etapis ventilatsiooni, äravoolude ja niiskustehnilise lahenduse koordineerimist.");
if (/äri|büroo|klient|teenindus/.test(lower)) recommendations.push("Ärihoone puhul tuleb kohe määrata kasutajate arv, ligipääsetavus, evakuatsioon ja parkimisvajadus.");
if (/odav|eelarve|soodne/.test(lower)) recommendations.push("Eelarve hoidmiseks eelista kompaktset mahtu, korduvaid avatäiteid ja lihtsat katusegeomeetriat.");
if (!recommendations.length) recommendations.push("Järgmine mõistlik samm on ruumiprogrammi, krundi piirangute ja soovitud eelarve ühine kinnitamine.");
if (file) recommendations.push(`Inspiratsioonipilt „${file.name}” lisatakse konsultatsiooni lähteinfo hulka.`);
const selected = projects.find(p => p.id === appState.selectedProjectId);
const intro = selected ? `Valitud ${selected.name} annab hea lähtekoha.` : "Kirjelduse põhjal soovitame alustada ruumilise kontseptsiooniga.";
document.getElementById("ai-text-output").textContent = `${intro} ${recommendations.join(" ")}`;
document.getElementById("ai-analysis-result").style.display = "block";
updateCalculator();
};
function getFavorites() {
return safeJsonRead(storageKeys.favorites, []);
}
window.toggleFavorite = function(id, event) {
event?.stopPropagation();
const favorites = getFavorites();
const next = favorites.includes(id) ? favorites.filter(item => item !== id) : [...favorites, id];
saveJson(storageKeys.favorites, next);
renderProjects();
showToast(next.includes(id) ? "Projekt lisati lemmikutesse." : "Projekt eemaldati lemmikutest.");
};
window.toggleFavoritesOnly = function() {
appState.favoritesOnly = !appState.favoritesOnly;
renderProjects();
};
window.renderProjects = function() {
const query = document.getElementById("catalog-search").value.trim().toLowerCase();
const type = document.getElementById("catalog-type").value;
const area = document.getElementById("catalog-area").value;
const sort = document.getElementById("catalog-sort").value;
const favorites = getFavorites();
let filtered = projects.filter(project => {
const searchable = `${project.name} ${project.typeLabel} ${project.style} ${project.material} ${project.description}`.toLowerCase();
const areaMatch = area === "all" || (area === "small" && project.area <= 100) || (area === "medium" && project.area > 100 && project.area <= 160) || (area === "large" && project.area > 160);
return (!query || searchable.includes(query)) && (type === "all" || project.type === type) && areaMatch && (!appState.favoritesOnly || favorites.includes(project.id));
});
if (sort === "area-asc") filtered.sort((a,b) => a.area - b.area);
if (sort === "area-desc") filtered.sort((a,b) => b.area - a.area);
if (sort === "price-asc") filtered.sort((a,b) => a.designPrice - b.designPrice);
document.getElementById("catalog-count").textContent = `${filtered.length} lahendust`;
document.getElementById("favorite-count").textContent = favorites.length;
const grid = document.getElementById("project-grid");
if (!filtered.length) {
grid.innerHTML = `<div class="empty-state"><i class="fas fa-magnifying-glass" style="font-size:2.4rem;margin-bottom:12px;"></i><h3>Sobivaid lahendusi ei leitud</h3><p>Muuda filtreid või koosta planeerijas personaalne projekt.</p></div>`;
return;
}
grid.innerHTML = filtered.map(project => `
<article class="project-card">
<div class="project-img" style="background:${project.gradient}">
<button class="favorite-btn ${favorites.includes(project.id) ? "active" : ""}" onclick="toggleFavorite('${project.id}', event)" aria-label="Lisa lemmikuks"><i class="${favorites.includes(project.id) ? "fas" : "far"} fa-heart"></i></button>
<i class="fas ${project.icon}"></i>
</div>
<div class="project-info">
<span class="tag">${project.package}</span>
<h3 style="margin-top:12px;">${project.name}</h3>
<p class="location"><i class="fas fa-circle-check"></i> ${project.status}</p>
<div class="project-specs">
<span>${project.area} m²</span><span>${project.rooms} ruumi</span><span>${project.floors} korrus${project.floors > 1 ? "t" : ""}</span><span>${project.style}</span>
</div>
<p style="font-size:.88rem;color:var(--gray-text);">${project.description}</p>
<p class="package-price">3D-disain alates ${formatCurrency(project.designPrice)}</p>
<div class="project-actions">
<button class="btn btn-secondary btn-small" onclick="openProjectModal('${project.id}')">Vaata detaile</button>
<button class="btn btn-primary btn-small" onclick="useProject('${project.id}')">Kasuta lähtepunktina</button>
</div>
</div>
</article>`).join("");
};
window.openProjectModal = function(id) {
const project = projects.find(item => item.id === id);
if (!project) return;
document.getElementById("modal-tag").textContent = project.package;
document.getElementById("modal-title").textContent = project.name;
document.getElementById("modal-description").textContent = project.description;
document.getElementById("modal-customize").textContent = project.customize;
document.getElementById("modal-hero").style.background = project.gradient;
document.getElementById("modal-hero").innerHTML = `<i class="fas ${project.icon}"></i>`;
document.getElementById("modal-specs").innerHTML = `<span>${project.area} m²</span><span>${project.rooms} ruumi</span><span>${project.floors} korrus${project.floors > 1 ? "t" : ""}</span><span>${project.material}</span>`;
document.getElementById("modal-use-btn").onclick = () => useProject(id);
document.getElementById("modal-quote-btn").onclick = () => chooseDesign(project.name);
document.getElementById("project-modal").classList.add("open");
document.body.style.overflow = "hidden";
};
window.closeProjectModal = function() {
document.getElementById("project-modal").classList.remove("open");
document.body.style.overflow = "";
};
window.closeModalFromBackdrop = function(event) {
if (event.target.id === "project-modal") closeProjectModal();
};
window.useProject = function(id) {
const project = projects.find(item => item.id === id);
if (!project) return;
appState.selectedProjectId = id;
document.getElementById("house-area").value = project.area;
document.getElementById("building-use").value = project.type;
configState.floors = project.floors;
updateStickyProject();
closeProjectModal();
updateCalculator();
document.querySelector("#kalkulaator").scrollIntoView({behavior:"smooth"});
showToast(`${project.name} valiti projekti lähtepunktiks.`);
};
function updateStickyProject() {
const project = projects.find(item => item.id === appState.selectedProjectId);
const bar = document.getElementById("sticky-project-bar");
if (!project) {
bar.classList.remove("show");
return;
}
document.getElementById("sticky-project-name").textContent = project.name;
bar.classList.add("show");
}
window.chooseDesign = function(designName) {
const project = projects.find(item => item.name === designName);
if (project) appState.selectedProjectId = project.id;
document.getElementById("form-message").value = `${getBriefText()}\n\nSoovin selle lahenduse kohta personaalset pakkumist.`;
document.getElementById("form-package").value = packageValueToForm(appState.package);
closeProjectModal();
document.querySelector("#kontakt").scrollIntoView({behavior:"smooth"});
};
window.sendProjectToForm = function() {
document.getElementById("form-message").value = `${getBriefText()}\n\nPalun võtke minuga ühendust lahenduse täpsustamiseks.`;
document.getElementById("form-package").value = packageValueToForm(appState.package);
document.querySelector("#kontakt").scrollIntoView({behavior:"smooth"});
};
window.handleFormSubmit = function(event) {
event.preventDefault();
if (document.getElementById("form-company").value) return;
const form = event.currentTarget;
if (!form.checkValidity()) {
form.reportValidity();
return;
}
const reference = `MTM-${new Date().toISOString().slice(0,10).replaceAll("-","")}-${String(Date.now()).slice(-4)}`;
const lead = {
reference,
name: document.getElementById("form-name").value.trim(),
email: document.getElementById("form-email").value.trim(),
phone: document.getElementById("form-phone").value.trim(),
package: document.getElementById("form-package").value,
message: document.getElementById("form-message").value.trim(),
createdAt: new Date().toISOString()
};
const leads = safeJsonRead(storageKeys.leads, []);
saveJson(storageKeys.leads, [...leads.slice(-9), lead]);
const status = document.getElementById("form-status");
status.className = "form-status success";
status.textContent = `Päring ${reference} koostati ja salvestati selles brauseris. Avame nüüd sinu e-posti rakenduse, et saaksid selle MTM Ehitusele saata.`;
const subject = encodeURIComponent(`Projektipäring ${reference}${lead.package}`);
const body = encodeURIComponent(`Nimi: ${lead.name}\nE-post: ${lead.email}\nTelefon: ${lead.phone || "-"}\n\n${lead.message}`);
setTimeout(() => { window.location.href = `mailto:${MTM_EMAIL}?subject=${subject}&body=${body}`; }, 350);
};
document.addEventListener("keydown", event => {
if (event.key === "Escape") closeProjectModal();
});
document.getElementById("menuToggle").addEventListener("click", () => {
document.getElementById("navMenu").classList.toggle("open");
});
document.getElementById("ai-image-upload").addEventListener("change", () => updateCalculator());
document.getElementById("ai-description").addEventListener("input", () => updateCalculator());
renderProjects();
updateCalculator();
restoreDraft();