diff --git a/api/search.php b/api/search.php new file mode 100644 index 0000000..81eb617 --- /dev/null +++ b/api/search.php @@ -0,0 +1,68 @@ + 'Query cannot be empty.']); + http_response_code(400); + exit; +} + +// New, more detailed mock data for "HuisWiki" +$mock_data = [ + // Basisgegevens + 'adres' => 'Coolsingel 40, 3011 AD Rotterdam', + 'postcode' => '3011 AD', + 'plaats' => 'Rotterdam', + 'gemeente' => 'Rotterdam', + 'provincie' => 'Zuid-Holland', + 'buurt' => 'Cool', + 'wijk' => 'Stadsdriehoek', + + // WOZ Waarde + 'woz_waarde' => 2500000, // Current value in EUR + 'woz_ontwikkeling' => [ + ['jaar' => 2019, 'waarde' => 2100000], + ['jaar' => 2020, 'waarde' => 2200000], + ['jaar' => 2021, 'waarde' => 2350000], + ['jaar' => 2022, 'waarde' => 2400000], + ['jaar' => 2023, 'waarde' => 2450000], + ['jaar' => 2024, 'waarde' => 2500000], + ], + + // Kadastrale info + 'perceelnummer' => 'RTD01-A-1234', + 'eigendomssituatie' => 'Volledig eigendom', + 'type' => 'Winkelpand', + 'bouwjaar' => 1957, + 'oppervlakte' => 1573, // in m² + + // Energielabel + 'energielabel' => 'A++', + 'geschatte_energiekosten' => 450, // per maand + + // Verkoophistorie + 'verkoophistorie' => [ + ['datum' => '2012-05-20', 'prijs' => 1800000, 'type' => 'Verkocht'], + ['datum' => '2005-11-15', 'prijs' => 1500000, 'type' => 'Verkocht'], + ['datum' => '1998-01-30', 'prijs' => 950000, 'type' => 'Verkocht'], + ], + + // Buurtstatistieken + 'buurtstatistieken' => [ + 'gemiddelde_woz' => 450000, + 'inwoners' => 8500, + 'gemiddelde_leeftijd' => 38, + 'groen_score' => 7.2, // out of 10 + 'veiligheid_score' => 8.1, // out of 10 + ] +]; + +// Simulate network delay +sleep(1); + +echo json_encode($mock_data); \ No newline at end of file diff --git a/assets/css/custom.css b/assets/css/custom.css new file mode 100644 index 0000000..b090507 --- /dev/null +++ b/assets/css/custom.css @@ -0,0 +1,250 @@ +/* --- Base & Variables --- */ +:root { + --bg-primary: #fefefe; + --bg-secondary: #f8f9fa; + --bg-accent: #e9ecef; + --text-primary: #212529; + --text-secondary: #495057; + --text-muted: #6c757d; + --accent-blue: #0066cc; + --accent-green: #059669; + --accent-orange: #ea580c; + --accent-purple: #7c3aed; + --border-color: #dee2e6; + --shadow: rgba(0, 0, 0, 0.07); + --gradient-1: linear-gradient(135deg, #667eea 0%, #764ba2 100%); +} + +.dark { + /* Dark mode variables if needed */ +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Work Sans', sans-serif; + background: var(--bg-primary); + color: var(--text-primary); + line-height: 1.6; +} + +.mono { + font-family: 'JetBrains Mono', monospace; +} + +/* --- Layout & Header --- */ +.container { + max-width: 1200px; + margin: 0 auto; + padding: 2rem 1rem; +} + +.header { + background: var(--bg-secondary); + border-bottom: 1px solid var(--border-color); + padding: 1.5rem 1rem; + position: sticky; + top: 0; + z-index: 100; + box-shadow: 0 2px 10px var(--shadow); +} + +.header-content { + max-width: 1200px; + margin: 0 auto; + display: flex; + justify-content: space-between; + align-items: center; +} + +.logo { + font-size: 1.5rem; + font-weight: 800; + background: var(--gradient-1); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.tagline { + color: var(--text-muted); + font-size: 0.85rem; +} + +/* --- Hero & Search --- */ +.hero { + text-align: center; + padding: 3rem 1rem; + background: var(--bg-secondary); + border-radius: 16px; + margin-bottom: 2rem; + border: 1px solid var(--border-color); +} + +.hero-title { + font-size: 2.5rem; + font-weight: 800; + margin-bottom: 1rem; +} + +.hero-subtitle { + font-size: 1.1rem; + color: var(--text-secondary); + margin-bottom: 2rem; + max-width: 600px; + margin-left: auto; + margin-right: auto; +} + +.search-wrapper { + display: flex; + gap: 0.75rem; + max-width: 700px; + margin: 0 auto; +} + +.search-input { + flex: 1; + padding: 1rem 1.5rem; + border: 2px solid var(--border-color); + background: var(--bg-primary); + border-radius: 12px; + font-size: 16px; +} + +.search-btn { + padding: 1rem 2.5rem; + background: var(--gradient-1); + color: white; + border: none; + border-radius: 12px; + font-size: 1rem; + font-weight: 700; + cursor: pointer; + transition: transform 0.2s; +} + +.search-btn:hover { transform: translateY(-2px); } + +.examples { margin-top: 1rem; color: var(--text-muted); } +.example-link { color: var(--accent-blue); text-decoration: none; margin: 0 0.5rem; } + +/* --- Results --- */ +.results-container { + display: none; + animation: fadeIn 0.5s ease; +} + +@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } + +.results-header { + padding: 2rem; + border-radius: 16px; + margin-bottom: 2rem; + border: 1px solid var(--border-color); + background: var(--bg-secondary); +} + +.address-title { font-size: 2rem; font-weight: 800; } +.address-subtitle { font-size: 1.1rem; color: var(--text-secondary); } + +/* --- Info Grid & Cards --- */ +.info-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); + gap: 1.5rem; +} + +.info-card { + background: var(--bg-secondary); + padding: 1.5rem; + border-radius: 12px; + border: 1px solid var(--border-color); + grid-column: span 1; +} + +.card-large { + grid-column: span 1; +} + +@media (min-width: 992px) { + .card-large { + grid-column: span 2; + } +} + +.card-header { + display: flex; + align-items: center; + gap: 0.75rem; + margin-bottom: 1.5rem; + padding-bottom: 1rem; + border-bottom: 1px solid var(--border-color); +} + +.card-icon { font-size: 1.5rem; } +.card-title { font-size: 1.2rem; font-weight: 700; } + +.info-row { + display: flex; + justify-content: space-between; + padding: 0.75rem 0; + border-bottom: 1px solid var(--bg-accent); +} +.info-row:last-child { border-bottom: none; } + +.info-label { color: var(--text-muted); } +.info-value { font-weight: 600; text-align: right; } + +/* --- Specific Cards --- */ +.woz-main { + text-align: center; + padding: 1rem 0 2rem; +} + +.woz-label { color: var(--text-muted); margin-bottom: 0.5rem; } +.woz-value { font-size: 2.5rem; font-weight: 800; color: var(--accent-blue); } + +.chart-container { + position: relative; + height: 250px; +} + +.history-table { + width: 100%; + border-collapse: collapse; +} + +.history-table th, .history-table td { + padding: 0.75rem; + text-align: left; + border-bottom: 1px solid var(--border-color); +} + +.history-table th { + background-color: var(--bg-accent); + font-weight: 600; +} + +.history-table td:last-child { + text-align: right; + font-weight: 600; + font-family: 'JetBrains Mono', monospace; +} + +/* --- Loader --- */ +.loader { + width: 18px; + height: 18px; + border: 2px solid #FFF; + border-bottom-color: transparent; + border-radius: 50%; + display: inline-block; + animation: rotation 1s linear infinite; +} + +@keyframes rotation { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } \ No newline at end of file diff --git a/assets/js/main.js b/assets/js/main.js new file mode 100644 index 0000000..57ba996 --- /dev/null +++ b/assets/js/main.js @@ -0,0 +1,179 @@ +document.addEventListener('DOMContentLoaded', () => { + const searchForm = document.getElementById('search-form'); + const searchInput = document.getElementById('search-input'); + const searchBtn = document.getElementById('search-btn'); + const resultsContainer = document.getElementById('results-container'); + const examples = document.querySelectorAll('.example-link'); + + let wozChart = null; // To hold the chart instance + + // Helper to format currency + const formatCurrency = (value) => new Intl.NumberFormat('nl-NL', { style: 'currency', currency: 'EUR', minimumFractionDigits: 0 }).format(value); + + const handleSearch = async (query) => { + if (!query) return; + + const originalBtnText = searchBtn.innerHTML; + searchBtn.innerHTML = ''; + searchBtn.disabled = true; + resultsContainer.style.display = 'none'; + + try { + const response = await fetch(`/api/search.php?query=${encodeURIComponent(query)}`); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = await response.json(); + displayResults(data); + } catch (error) { + console.error("Fetch error: ", error); + displayError('Er is een fout opgetreden. Probeer het later opnieuw.'); + } finally { + searchBtn.innerHTML = originalBtnText; + searchBtn.disabled = false; + } + }; + + searchForm.addEventListener('submit', (e) => { + e.preventDefault(); + handleSearch(searchInput.value); + }); + + examples.forEach(link => { + link.addEventListener('click', (e) => { + e.preventDefault(); + const exampleQuery = e.target.textContent; + searchInput.value = exampleQuery; + handleSearch(exampleQuery); + }); + }); + + const displayResults = (data) => { + if (data.error) { + displayError(data.error); + return; + } + + // --- Main Header --- + document.getElementById('address-title').textContent = data.adres; + document.getElementById('address-subtitle').textContent = `Gegevens voor ${data.postcode}, ${data.plaats}`; + + // --- Basisgegevens --- + document.getElementById('info-postcode').textContent = data.postcode; + document.getElementById('info-plaats').textContent = data.plaats; + document.getElementById('info-gemeente').textContent = data.gemeente; + document.getElementById('info-provincie').textContent = data.provincie; + document.getElementById('info-buurt').textContent = data.buurt; + document.getElementById('info-wijk').textContent = data.wijk; + + // --- Kadastrale Info --- + document.getElementById('info-perceelnummer').textContent = data.perceelnummer; + document.getElementById('info-eigendomssituatie').textContent = data.eigendomssituatie; + document.getElementById('info-type').textContent = data.type; + document.getElementById('info-bouwjaar').textContent = data.bouwjaar; + document.getElementById('info-oppervlakte').textContent = `${data.oppervlakte} m²`; + + // --- WOZ Waarde --- + document.getElementById('info-woz-waarde').textContent = formatCurrency(data.woz_waarde); + renderWozChart(data.woz_ontwikkeling); + + // --- Energielabel --- + document.getElementById('info-energielabel').textContent = data.energielabel; + document.getElementById('info-energiekosten').textContent = `${formatCurrency(data.geschatte_energiekosten)}`; + + // --- Buurtstatistieken --- + document.getElementById('info-gem-woz').textContent = formatCurrency(data.buurtstatistieken.gemiddelde_woz); + document.getElementById('info-inwoners').textContent = data.buurtstatistieken.inwoners.toLocaleString('nl-NL'); + document.getElementById('info-gem-leeftijd').textContent = `${data.buurtstatistieken.gemiddelde_leeftijd} jaar`; + document.getElementById('info-groen-score').textContent = `${data.buurtstatistieken.groen_score} / 10`; + document.getElementById('info-veiligheid-score').textContent = `${data.buurtstatistieken.veiligheid_score} / 10`; + + // --- Verkoophistorie --- + const historyBody = document.getElementById('verkoophistorie-body'); + historyBody.innerHTML = ''; // Clear previous results + data.verkoophistorie.forEach(tx => { + const row = document.createElement('tr'); + row.innerHTML = ` + ${new Date(tx.datum).toLocaleDateString('nl-NL')} + ${tx.type} + ${formatCurrency(tx.prijs)} + `; + historyBody.appendChild(row); + }); + + resultsContainer.style.display = 'block'; + resultsContainer.scrollIntoView({ behavior: 'smooth' }); + }; + + const renderWozChart = (wozData) => { + const ctx = document.getElementById('woz-chart').getContext('2d'); + const labels = wozData.map(d => d.jaar); + const values = wozData.map(d => d.waarde); + + if (wozChart) { + wozChart.destroy(); // Destroy previous chart instance + } + + wozChart = new Chart(ctx, { + type: 'line', + data: { + labels: labels, + datasets: [{ + label: 'WOZ-Waarde', + data: values, + borderColor: '#0066cc', + backgroundColor: 'rgba(0, 102, 204, 0.1)', + fill: true, + tension: 0.4, + pointBackgroundColor: '#0066cc', + pointRadius: 4, + }] + }, + options: { + responsive: true, + maintainAspectRatio: false, + scales: { + y: { + ticks: { + callback: (value) => formatCurrency(value) + } + } + }, + plugins: { + legend: { + display: false + } + } + } + }); + }; + + const displayError = (message) => { + alert(message); + }; +}); + +// Add some CSS for the loader +const style = document.createElement('style'); +style.innerHTML = ` +.loader { + width: 18px; + height: 18px; + border: 2px solid #FFF; + border-bottom-color: transparent; + border-radius: 50%; + display: inline-block; + box-sizing: border-box; + animation: rotation 1s linear infinite; +} + +@keyframes rotation { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} +`; +document.head.appendChild(style); \ No newline at end of file diff --git a/index.php b/index.php index 7205f3d..4808660 100644 --- a/index.php +++ b/index.php @@ -1,150 +1,166 @@ - - - + + - - - New Style - - - - - - - - - - - - - - - - - - - + + + + + HuisWiki - Complete adresinformatie Nederland + + + + + + + + + + + + + + + + + + + + + + + -
-
-

Analyzing your requirements and generating your website…

-
- Loading… -
-

AI is collecting your requirements and applying the first changes.

-

This page will update automatically as the plan is implemented.

-

Runtime: PHP — UTC

-
-
- + +
+
+
+

HuisWiki

+

Complete adresinformatie van Nederland

+
+
+
+ +
+
+

Vind alles over elk adres

+

Voer een postcode en huisnummer in en krijg direct toegang tot uitgebreide informatie over elk adres in Nederland.

+ +
+
+ + +
+
+ Voorbeeld: + Coolsingel 40 + Dam 1 +
+
+
+ +
+
+

+

+
+ +
+ +
+
+ 📍 +

Basisgegevens

+
+
Postcode
+
Plaats
+
Gemeente
+
Provincie
+
Buurt
+
Wijk
+
+ + +
+
+ 🏠 +

Kadastrale Informatie

+
+
Perceel
+
Eigendom
+
Type
+
Bouwjaar
+
Oppervlakte
+
+ + +
+
+ 📈 +

WOZ-Waarde

+
+
+

Huidige Waarde

+

+
+
+ +
+
+ + +
+
+ ⚡️ +

Energielabel

+
+
+ Label + +
+
+ Gechatte Kosten p/m + +
+
+ + +
+
+ 📊 +

Buurtstatistieken

+
+
Gem. WOZ
+
Inwoners
+
Gem. Leeftijd
+
Groenscore
+
Veiligheidsscore
+
+ + +
+
+ 📜 +

Verkoophistorie

+
+ + + + + + + + + + + +
DatumTypePrijs
+
+
+
+
+ + +