Files
konesaliturku.fi/tarjouspyynto.html
Jukka Lampikoski b78345e4c8 Lisää laskutoimituscaptcha tarjouspyyntölomakkeeseen
Satunnainen yhteenlaskukysymys (esim. "Paljonko on 3 + 5?")
tarkistetaan sekä client- että serverpuolella. Vain sähköposti
on nyt pakollinen kenttä.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 21:12:09 +02:00

481 lines
24 KiB
HTML

<!DOCTYPE html>
<html lang="fi">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jätä tarjouspyyntö | Konesali Turku</title>
<meta name="description" content="Pyydä tarjous konesalipalveluista. Automaattinen hintalaskuri auttaa arvioimaan kuukausikustannukset.">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- Header -->
<header class="header" id="header">
<div class="container header-inner">
<a href="/" class="logo">
<svg class="logo-icon" viewBox="0 0 38 38" fill="none">
<rect class="logo-icon-bg" width="38" height="38" rx="10"/>
<rect x="10" y="8" width="18" height="22" rx="2.5" fill="none" stroke="white" stroke-width="1.2" opacity="0.9"/>
<rect x="13" y="11.5" width="12" height="3.5" rx="1" class="logo-icon-rack" opacity="0.85"/>
<rect x="13" y="17" width="12" height="3.5" rx="1" class="logo-icon-rack" opacity="0.85"/>
<rect x="13" y="22.5" width="12" height="3.5" rx="1" class="logo-icon-rack" opacity="0.85"/>
<circle cx="15.5" cy="13.25" r="1" class="logo-icon-led"/>
<circle cx="15.5" cy="18.75" r="1" class="logo-icon-led"/>
<circle cx="15.5" cy="24.25" r="1" class="logo-icon-led"/>
<path d="M15 33a4 4 0 014-4" stroke="white" stroke-width="1.2" stroke-linecap="round" opacity="0.5"/>
<path d="M12.5 35a7 7 0 017-7" stroke="white" stroke-width="1" stroke-linecap="round" opacity="0.3"/>
</svg>
<span class="logo-text">
<span class="logo-text-top">Konesali</span>
<span class="logo-text-bottom">TURKU</span>
</span>
</a>
<nav class="nav" id="nav">
<a href="index.html#palvelut">Palvelut</a>
<a href="index.html#hinnat">Hinnat</a>
<a href="konesali.html">Konesali</a>
<a href="yhteydet.html">Yhteydet</a>
<a href="tarjouspyynto.html" class="nav-cta active">Jätä tarjouspyyntö</a>
</nav>
<button class="nav-toggle" id="nav-toggle" aria-label="Avaa valikko">
<span></span>
<span></span>
<span></span>
</button>
</div>
</header>
<!-- Page Hero -->
<section class="page-hero">
<div class="container">
<h1>Jätä tarjouspyyntö</h1>
<p>Valitse tarvitsemasi palvelut ja näet arviohinnan heti. Lähetä tarjouspyyntö niin otamme yhteyttä vuorokauden sisällä.</p>
</div>
</section>
<!-- Calculator + Form -->
<section class="section">
<div class="container">
<div class="calculator-layout">
<!-- Form side -->
<div class="calculator-form">
<form id="quote-form">
<!-- Yhteystiedot -->
<div class="form-section-title">Yhteystiedot</div>
<div class="form-group">
<label for="name">Nimi</label>
<input type="text" id="name" name="name">
</div>
<div class="form-group">
<label for="company">Yritys</label>
<input type="text" id="company" name="company">
</div>
<div class="form-group">
<label for="email">Sähköposti *</label>
<input type="email" id="email" name="email" required>
</div>
<div class="form-group">
<label for="phone">Puhelinnumero</label>
<input type="tel" id="phone" name="phone">
</div>
<!-- Laitepaikat -->
<div class="form-section-title">Laitepaikat</div>
<div class="form-group form-group-full">
<label for="units-1u">1U laitepaikka &mdash; &euro;49/kk</label>
<select id="units-1u" name="units_1u" data-price="49">
<option value="0">Ei tarvetta</option>
<option value="1">1 kpl</option>
<option value="2">2 kpl</option>
<option value="3">3 kpl</option>
<option value="4">4 kpl</option>
<option value="5">5+ kpl</option>
</select>
</div>
<div class="form-group form-group-full">
<label for="units-2u">2U laitepaikka &mdash; &euro;79/kk</label>
<select id="units-2u" name="units_2u" data-price="79">
<option value="0">Ei tarvetta</option>
<option value="1">1 kpl</option>
<option value="2">2 kpl</option>
<option value="3">3 kpl</option>
<option value="4">4 kpl</option>
<option value="5">5+ kpl</option>
</select>
</div>
<div class="form-group form-group-full">
<label for="units-4u">4U laitepaikka &mdash; &euro;139/kk</label>
<select id="units-4u" name="units_4u" data-price="139">
<option value="0">Ei tarvetta</option>
<option value="1">1 kpl</option>
<option value="2">2 kpl</option>
<option value="3">3 kpl</option>
<option value="5">5+ kpl</option>
</select>
</div>
<div class="form-group form-group-full">
<label for="units-rack">Kokokaappi (42U)</label>
<select id="units-rack" name="units_rack" data-price="0">
<option value="0">Ei tarvetta</option>
<option value="1" data-custom>1 kpl (pyydä tarjous)</option>
<option value="2" data-custom>2 kpl (pyydä tarjous)</option>
<option value="3" data-custom>3+ kpl (pyydä tarjous)</option>
</select>
</div>
<!-- Yhteysnopeus -->
<div class="form-section-title">Yhteysnopeus</div>
<div class="form-group form-group-full">
<label for="connection">Haluttu yhteysnopeus</label>
<select id="connection" name="connection" data-type="connection">
<option value="0">1 Gbit/s jaettu (sis. hintaan)</option>
<option value="99">1 Gbit/s dedicated (&euro;99/kk)</option>
<option value="299">10 Gbit/s dedicated (&euro;299/kk)</option>
<option value="custom">100 Gbit/s (räätälöity)</option>
</select>
</div>
<div class="checkbox-group">
<label class="checkbox-label">
<input type="checkbox" name="redundant_port" value="1" data-price="10">
<span>Varmennettu portti (&euro;10/kk)</span>
</label>
</div>
<div class="form-group form-group-full">
<label for="bgp">BGP-reititys</label>
<select id="bgp" name="bgp">
<option value="0">Ei tarvetta</option>
<option value="20">Default-route (&euro;20/kk)</option>
<option value="50">Koko internet (&euro;50/kk)</option>
</select>
</div>
<!-- VPN -->
<div class="form-section-title">Site-to-Site VPN</div>
<div class="form-group form-group-full">
<label for="vpn">VPN toimitilan ja konesalin väliin</label>
<select id="vpn" name="vpn">
<option value="0">Ei tarvetta</option>
<option value="95">1G Site-to-Site (alk. &euro;95/kk)</option>
<option value="129">10G Site-to-Site (alk. &euro;129/kk)</option>
<option value="custom">Dedicated L2/MPLS (räätälöity)</option>
</select>
</div>
<!-- Lisäpalvelut -->
<div class="form-section-title">Lisäpalvelut</div>
<div class="checkbox-group">
<label class="checkbox-label">
<input type="checkbox" name="anycast_dns" value="1" data-price="19">
<span>Anycast DNS (&euro;19/kk)</span>
</label>
<label class="checkbox-label">
<input type="checkbox" name="block_storage" value="1" data-price="29">
<span>Blokkitason storage (alk. &euro;29/kk)</span>
</label>
<label class="checkbox-label">
<input type="checkbox" name="s3_storage" value="1" data-price="19">
<span>S3-levypinta (alk. &euro;19/kk)</span>
</label>
<label class="checkbox-label">
<input type="checkbox" name="remote_hands" value="1" data-price="0">
<span>Remote Hands (tuntiveloitus)</span>
</label>
</div>
<!-- Lisätiedot -->
<div class="form-section-title">Lisätiedot</div>
<div class="form-group form-group-full">
<label for="message">Viesti</label>
<textarea id="message" name="message" rows="4" placeholder="Kerro lisää tarpeistasi: tehovaatimukset, aikataulu, erityistoiveet..."></textarea>
</div>
<!-- Captcha -->
<div class="form-section-title">Varmistus</div>
<div class="form-group">
<label for="captcha" id="captcha-label"></label>
<input type="text" id="captcha" name="captcha" required autocomplete="off" inputmode="numeric" placeholder="Vastaus">
<input type="hidden" id="captcha-answer" name="captcha_answer">
</div>
<!-- Honeypot -->
<div style="display:none" aria-hidden="true">
<input type="text" name="website" tabindex="-1" autocomplete="off">
</div>
<button type="submit" class="btn btn-primary btn-submit">Lähetä tarjouspyyntö</button>
<div class="form-status" id="form-status"></div>
</form>
</div>
<!-- Price calculator side (sticky) -->
<div class="calculator-sidebar">
<div class="price-calculator" id="price-calculator">
<h3>Arvioitu kuukausihinta</h3>
<div class="calculator-total">
<span class="calculator-amount" id="calc-total">&euro;0</span>
<span class="calculator-period">/kk</span>
</div>
<p class="calculator-vat">Hinnat alv 0%</p>
<div class="calculator-breakdown" id="calc-breakdown">
<p class="calculator-empty">Valitse palvelut vasemmalta niin näet arviohinnan täällä.</p>
</div>
<div class="calculator-note" id="calc-note" style="display:none;">
<svg viewBox="0 0 20 20" width="16" height="16" fill="currentColor"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"/></svg>
<span>Osa valituista palveluista on räätälöitäviä &mdash; lopullinen hinta tarjouksessa.</span>
</div>
</div>
<div class="calculator-contact">
<h4>Tarvitsetko apua?</h4>
<p>Soita tai lähetä sähköpostia, autamme mielellämme.</p>
<a href="mailto:asiakaspalvelu@konesaliturku.fi" class="calculator-email">asiakaspalvelu@konesaliturku.fi</a>
<div class="calculator-address">
<strong>Web1 Oy</strong><br>
Latokarinkatu 3<br>
20200 Turku
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Footer -->
<footer class="footer">
<div class="container footer-inner">
<div class="footer-brand">
<a href="/" class="logo">
<svg class="logo-icon" viewBox="0 0 38 38" fill="none">
<rect class="logo-icon-bg" width="38" height="38" rx="10"/>
<rect x="10" y="8" width="18" height="22" rx="2.5" fill="none" stroke="white" stroke-width="1.2" opacity="0.9"/>
<rect x="13" y="11.5" width="12" height="3.5" rx="1" class="logo-icon-rack" opacity="0.85"/>
<rect x="13" y="17" width="12" height="3.5" rx="1" class="logo-icon-rack" opacity="0.85"/>
<rect x="13" y="22.5" width="12" height="3.5" rx="1" class="logo-icon-rack" opacity="0.85"/>
<circle cx="15.5" cy="13.25" r="1" class="logo-icon-led"/>
<circle cx="15.5" cy="18.75" r="1" class="logo-icon-led"/>
<circle cx="15.5" cy="24.25" r="1" class="logo-icon-led"/>
<path d="M15 33a4 4 0 014-4" stroke="white" stroke-width="1.2" stroke-linecap="round" opacity="0.5"/>
<path d="M12.5 35a7 7 0 017-7" stroke="white" stroke-width="1" stroke-linecap="round" opacity="0.3"/>
</svg>
<span class="logo-text">
<span class="logo-text-top">Konesali</span>
<span class="logo-text-bottom">TURKU</span>
</span>
</a>
<p>Luotettavat konesalipalvelut Turussa.</p>
</div>
<div class="footer-links">
<h4>Sivusto</h4>
<a href="index.html#palvelut">Palvelut</a>
<a href="konesali.html">Konesali</a>
<a href="yhteydet.html">Yhteydet</a>
<a href="index.html#hinnat">Hinnat</a>
<a href="tarjouspyynto.html">Tarjouspyyntö</a>
</div>
<div class="footer-links">
<h4>Yhteystiedot</h4>
<a href="mailto:asiakaspalvelu@konesaliturku.fi">asiakaspalvelu@konesaliturku.fi</a>
<span>Web1 Oy</span>
<span>Latokarinkatu 3</span>
<span>20200 Turku</span>
</div>
</div>
<div class="footer-bottom">
<div class="container">
<p>&copy; 2026 Konesali Turku. Kaikki oikeudet pidätetään.</p>
</div>
</div>
</footer>
<script src="script.js"></script>
<script>
// Captcha
(function() {
function generateCaptcha() {
const a = Math.floor(Math.random() * 10) + 1;
const b = Math.floor(Math.random() * 10) + 1;
const answer = a + b;
document.getElementById('captcha-label').textContent = 'Paljonko on ' + a + ' + ' + b + '? *';
document.getElementById('captcha-answer').value = answer;
}
generateCaptcha();
window._refreshCaptcha = generateCaptcha;
})();
// Price Calculator
(function() {
const form = document.getElementById('quote-form');
const totalEl = document.getElementById('calc-total');
const breakdownEl = document.getElementById('calc-breakdown');
const noteEl = document.getElementById('calc-note');
function calculate() {
let total = 0;
let lines = [];
let hasCustom = false;
// Laitepaikat
const units = [
{ el: document.getElementById('units-1u'), name: '1U laitepaikka', price: 49 },
{ el: document.getElementById('units-2u'), name: '2U laitepaikka', price: 79 },
{ el: document.getElementById('units-4u'), name: '4U laitepaikka', price: 139 },
];
units.forEach(u => {
const qty = parseInt(u.el.value) || 0;
if (qty > 0) {
const subtotal = qty * u.price;
total += subtotal;
lines.push({ label: u.name + ' x' + qty, amount: subtotal });
}
});
// Kokokaappi
const rackQty = parseInt(document.getElementById('units-rack').value) || 0;
if (rackQty > 0) {
hasCustom = true;
lines.push({ label: 'Kokokaappi x' + rackQty, amount: null });
}
// Yhteysnopeus
const connEl = document.getElementById('connection');
const connVal = connEl.value;
let connPrice = 0;
if (connVal === 'custom') {
hasCustom = true;
lines.push({ label: '100 Gbit/s yhteys', amount: null });
} else {
connPrice = parseInt(connVal) || 0;
if (connPrice > 0) {
total += connPrice;
lines.push({ label: connEl.options[connEl.selectedIndex].text.split('(')[0].trim(), amount: connPrice });
}
}
// Varmennettu portti
const redundant = form.querySelector('[name="redundant_port"]');
if (redundant.checked) {
total += 10;
lines.push({ label: 'Varmennettu portti', amount: 10 });
}
// BGP
const bgpEl = document.getElementById('bgp');
const bgpPrice = parseInt(bgpEl.value) || 0;
if (bgpPrice > 0) {
total += bgpPrice;
lines.push({ label: bgpEl.options[bgpEl.selectedIndex].text.split('(')[0].trim(), amount: bgpPrice });
}
// VPN
const vpnEl = document.getElementById('vpn');
const vpnVal = vpnEl.value;
if (vpnVal === 'custom') {
hasCustom = true;
lines.push({ label: 'Dedicated L2/MPLS', amount: null });
} else {
const vpnPrice = parseInt(vpnVal) || 0;
if (vpnPrice > 0) {
total += vpnPrice;
lines.push({ label: vpnEl.options[vpnEl.selectedIndex].text.split('(')[0].trim(), amount: vpnPrice });
}
}
// Lisäpalvelut
const extras = [
{ name: 'anycast_dns', label: 'Anycast DNS', price: 19 },
{ name: 'block_storage', label: 'Blokkitason storage', price: 29 },
{ name: 's3_storage', label: 'S3-levypinta', price: 19 },
{ name: 'remote_hands', label: 'Remote Hands', price: 0, custom: true },
];
extras.forEach(e => {
const cb = form.querySelector('[name="' + e.name + '"]');
if (cb && cb.checked) {
if (e.custom || e.price === 0) {
hasCustom = true;
lines.push({ label: e.label, amount: null });
} else {
total += e.price;
lines.push({ label: e.label, amount: e.price });
}
}
});
// Render
totalEl.textContent = '€' + total;
if (lines.length === 0) {
breakdownEl.innerHTML = '<p class="calculator-empty">Valitse palvelut vasemmalta niin näet arviohinnan täällä.</p>';
} else {
let html = '';
lines.forEach(l => {
html += '<div class="calc-line">';
html += '<span>' + l.label + '</span>';
html += '<span>' + (l.amount !== null ? '€' + l.amount : 'Tarjouksessa') + '</span>';
html += '</div>';
});
breakdownEl.innerHTML = html;
}
noteEl.style.display = hasCustom ? 'flex' : 'none';
}
// Listen to all form changes
form.addEventListener('change', calculate);
form.addEventListener('input', calculate);
// Form submit
form.addEventListener('submit', function(e) {
e.preventDefault();
const status = document.getElementById('form-status');
const honeypot = form.querySelector('[name="website"]');
if (honeypot && honeypot.value) return;
// Captcha check
const captchaInput = document.getElementById('captcha').value.trim();
const captchaAnswer = document.getElementById('captcha-answer').value;
if (captchaInput !== captchaAnswer) {
status.textContent = 'Väärä vastaus varmistuskysymykseen.';
status.className = 'form-status form-status-error';
window._refreshCaptcha();
document.getElementById('captcha').value = '';
return;
}
const submitBtn = form.querySelector('.btn-submit');
submitBtn.disabled = true;
status.textContent = 'Lähetetään...';
status.className = 'form-status';
const formData = new FormData(form);
fetch('api.php?action=quote', {
method: 'POST',
body: formData
})
.then(function(res) { return res.json(); })
.then(function(data) {
if (data.error) {
status.textContent = data.error;
status.className = 'form-status form-status-error';
} else {
status.textContent = 'Kiitos! Tarjouspyyntösi on vastaanotettu. Otamme yhteyttä vuorokauden sisällä.';
status.className = 'form-status form-status-success';
form.reset();
calculate();
window._refreshCaptcha();
}
})
.catch(function() {
status.textContent = 'Virhe lähetyksessä. Yritä uudelleen tai ota yhteyttä sähköpostilla.';
status.className = 'form-status form-status-error';
})
.finally(function() {
submitBtn.disabled = false;
});
});
})();
</script>
</body>
</html>