Files
papa-kind-treff.info/public/page/index.php
2025-12-25 01:03:38 +01:00

311 lines
16 KiB
PHP
Executable File
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="de" class="scroll-smooth">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>schwarzesbrett.online Digitales Schwarzes Brett</title>
<meta name="description" content="Kostenlose Aushänge & Nachbarschaftshilfe lokal, einfach, ohne Schnickschnack." />
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ctext x='2' y='18' font-size='16'%3E%F0%9F%93%8C%3C/text%3E%3C/svg%3E">
<link rel="stylesheet" href="./assets/styles.css" />
</head>
<body>
<!-- Header -->
<header class="header">
<div class="container" style="display:flex;align-items:center;justify-content:space-between;height:64px;">
<a href="#" class="brand" aria-label="Startseite">
<span style="font-size:22px;">📌</span>
<span>schwarzesbrett<span style="color:#94a3b8">.online</span></span>
</a>
<nav class="nav">
<a href="#kategorien">Kategorien</a>
<a href="#neu">Neueste</a>
<a href="#sofunktionierts">So funktionierts</a>
<button id="openCreate" class="btn"> Inserat erstellen</button>
<a href="#" class="text-muted">Login</a>
</nav>
<button id="openMenu" class="menu-btn" aria-label="Menü öffnen"></button>
</div>
<div id="mobileMenu" class="mobile-menu">
<div class="container" style="display:flex;flex-direction:column;gap:12px;padding-block:12px;">
<a href="#kategorien">Kategorien</a>
<a href="#neu">Neueste</a>
<a href="#sofunktionierts">So funktionierts</a>
<button id="openCreateMobile" class="btn" style="width:fit-content;"> Inserat erstellen</button>
</div>
</div>
</header>
<!-- Hero -->
<section class="hero">
<div class="container" style="padding-block:48px;display:grid;gap:24px;grid-template-columns: 1.2fr .8fr;">
<div>
<h1>Dein digitales Schwarzes Brett für <span id="heroOrt" style="text-decoration: underline wavy #f59e0b;">deine Nachbarschaft</span></h1>
<p class="sub mt-2">Inserate, Nachbarschaftshilfe & Veranstaltungen lokal, übersichtlich und frei von Spam.</p>
<form id="searchForm" class="search mt-3" aria-label="Suche">
<label class="sr-only" for="q">Stichwort</label>
<input id="q" class="input" type="search" placeholder="Suche nach Stichwort (z.B. Fahrrad, Babysitter)" />
<label class="sr-only" for="zip">PLZ</label>
<input id="zip" class="input" type="text" inputmode="numeric" maxlength="5" placeholder="PLZ" />
<button class="btn" type="submit">Suchen</button>
</form>
<div class="mt-2 text-muted" style="display:flex;gap:12px;align-items:center;flex-wrap:wrap;">
<button id="detectLocation" class="btn ghost">PLZ automatisch erkennen</button>
<button id="openCreateHero" class="btn ghost">Jetzt kostenlos Aushang erstellen</button>
</div>
</div>
<div>
<div class="card p-4">
<div class="grid grid-3">
<button data-cat="Flohmarkt" class="note pin p-3 rounded">
<div style="font-weight:600;">🛒 Flohmarkt</div>
<div class="text-muted" style="font-size:12px;">Verkaufen & Finden</div>
</button>
<button data-cat="Jobs" class="note pin p-3 rounded">
<div style="font-weight:600;">💼 Jobs</div>
<div class="text-muted" style="font-size:12px;">Mini- & Nebenjobs</div>
</button>
<button data-cat="Nachbarschaftshilfe" class="note pin p-3 rounded">
<div style="font-weight:600;">🤝 Hilfe</div>
<div class="text-muted" style="font-size:12px;">Leihen, Mitnehmen</div>
</button>
<button data-cat="Veranstaltungen" class="note pin p-3 rounded">
<div style="font-weight:600;">🎉 Veranstaltungen</div>
<div class="text-muted" style="font-size:12px;">Heute & bald</div>
</button>
<button data-cat="Immobilien" class="note pin p-3 rounded">
<div style="font-weight:600;">🏡 Immobilien</div>
<div class="text-muted" style="font-size:12px;">Miete & WG</div>
</button>
<button data-cat="Sonstiges" class="note pin p-3 rounded">
<div style="font-weight:600;">📌 Sonstiges</div>
<div class="text-muted" style="font-size:12px;">Alles andere</div>
</button>
</div>
</div>
</div>
</div>
</section>
<!-- Kategorien -->
<section id="kategorien" class="container" style="padding-block:24px;">
<h2 style="font-size:20px;font-weight:600;" class="mb-2">Entdecke Kategorien</h2>
<div class="chips">
<button class="chip" data-filter="">Alle</button>
<button class="chip" data-filter="Flohmarkt">🛒 Flohmarkt</button>
<button class="chip" data-filter="Jobs">💼 Jobs</button>
<button class="chip" data-filter="Nachbarschaftshilfe">🤝 Hilfe</button>
<button class="chip" data-filter="Veranstaltungen">🎉 Veranstaltungen</button>
<button class="chip" data-filter="Immobilien">🏡 Immobilien</button>
<button class="chip" data-filter="Sonstiges">📌 Sonstiges</button>
</div>
</section>
<!-- Neueste Einträge -->
<section id="neu" class="container" style="padding-block:8px 48px;">
<div style="display:flex;align-items:baseline;justify-content:space-between;margin-bottom:12px;">
<h2 style="font-size:20px;font-weight:600;">Neueste Einträge</h2>
<button id="loadMore" class="btn ghost" style="font-size:14px;">Mehr anzeigen</button>
</div>
<div id="cards" class="results"></div>
</section>
<!-- CTA -->
<section class="cta">
<div class="container" style="padding-block:40px;display:grid;gap:16px;grid-template-columns: 1fr auto; align-items:center;">
<div>
<h3 style="font-size:24px;font-weight:600;">Mach dein Anliegen sichtbar</h3>
<p class="text-muted mt-1">Erstelle jetzt kostenlos einen Aushang. Optional mit Bild läuft automatisch nach 30 Tagen aus.</p>
</div>
<button id="openCreateCta" class="btn">Jetzt Aushang erstellen</button>
</div>
</section>
<!-- So funktionierts -->
<section id="sofunktionierts" class="container" style="padding-block:40px;">
<h2 style="font-size:20px;font-weight:600;" class="mb-4">So funktionierts</h2>
<div class="grid grid-3">
<div class="surface border rounded p-4">
<div style="font-size:24px;">1️⃣</div>
<h3 class="mt-2" style="font-weight:600;">Inserat erstellen</h3>
<p class="text-muted mt-1" style="font-size:14px;">Titel, Beschreibung, Kategorie, Ort optional ein Bild. Fertig.</p>
</div>
<div class="surface border rounded p-4">
<div style="font-size:24px;">2️⃣</div>
<h3 class="mt-2" style="font-weight:600;">Lokal sichtbar</h3>
<p class="text-muted mt-1" style="font-size:14px;">Dein Aushang erscheint bei Leuten in der Nähe ohne AlgorithmusChaos.</p>
</div>
<div class="surface border rounded p-4">
<div style="font-size:24px;">3️⃣</div>
<h3 class="mt-2" style="font-weight:600;">Kontakt aufnehmen</h3>
<p class="text-muted mt-1" style="font-size:14px;">Direkt per EMail oder Telefon ohne Zwischenhändler.</p>
</div>
</div>
</section>
<!-- Footer -->
<footer class="footer">
<div class="container" style="padding-block:24px;display:flex;gap:12px;align-items:center;justify-content:space-between;flex-wrap:wrap;">
<p>© 2025 schwarzesbrett.online</p>
<nav style="display:flex;gap:16px;">
<a href="#">Impressum</a>
<a href="#">Datenschutz</a>
<a href="#">Kontakt</a>
</nav>
</div>
</footer>
<!-- Modal: Inserat erstellen -->
<div id="createModal" class="modal" role="dialog" aria-modal="true" aria-labelledby="createTitle">
<div class="panel">
<div class="head">
<h3 id="createTitle" style="font-weight:600;">Inserat erstellen</h3>
<button id="closeCreate" class="btn ghost" aria-label="Schließen"></button>
</div>
<form id="createForm" class="grid">
<div class="form-row">
<div>
<label class="label" for="title">Titel</label>
<input required id="title" name="title" class="input mt-1" />
</div>
<div>
<label class="label" for="category">Kategorie</label>
<select required id="category" name="category" class="select mt-1">
<option value="Flohmarkt">Flohmarkt</option>
<option value="Jobs">Jobs</option>
<option value="Nachbarschaftshilfe">Nachbarschaftshilfe</option>
<option value="Veranstaltungen">Veranstaltungen</option>
<option value="Immobilien">Immobilien</option>
<option value="Sonstiges">Sonstiges</option>
</select>
</div>
</div>
<div>
<label class="label" for="desc">Beschreibung</label>
<textarea required id="desc" name="desc" class="textarea mt-1" rows="4"></textarea>
</div>
<div class="form-row" style="grid-template-columns: 120px 1fr 1fr;">
<div>
<label class="label" for="zip2">PLZ</label>
<input required id="zip2" name="zip" class="input mt-1" maxlength="5" inputmode="numeric" />
</div>
<div>
<label class="label" for="city">Ort</label>
<input required id="city" name="city" class="input mt-1" />
</div>
<div>
<label class="label" for="contact">Kontakt (EMail o. Tel.)</label>
<input required id="contact" name="contact" class="input mt-1" />
</div>
</div>
<div>
<label class="label" for="img">BildURL (optional)</label>
<input id="img" name="img" class="input mt-1" placeholder="https://…" />
</div>
<div style="display:flex;justify-content:flex-end;gap:12px;">
<button type="button" id="cancelCreate" class="btn outline">Abbrechen</button>
<button class="btn" type="submit">Veröffentlichen</button>
</div>
</form>
</div>
</div>
<!-- Toast -->
<div id="toast" class="toast">Aushang veröffentlicht</div>
<script>
// Demo-State (ohne Backend) kann später durch API ersetzt werden
const seed = [
{id:1, title:"Mountainbike zu verkaufen", desc:"Gut gepflegtes 26'' MTB, neue Bremsen, Probefahrt möglich.", category:"Flohmarkt", zip:"10115", city:"Berlin", contact:"maria@example.com", img:"https://images.unsplash.com/photo-1520975693416-35e09df6f242?q=80&w=1200&auto=format&fit=crop", date:"2025-08-10"},
{id:2, title:"Babysitter gesucht", desc:"Suche zuverlässige Betreuung für 2 Abende/Woche.", category:"Jobs", zip:"50667", city:"Köln", contact:"0176 123456", img:"", date:"2025-08-12"},
{id:3, title:"Bohrmaschine leihen", desc:"Brauche am Wochenende eine Bohrmaschine Gegen Kuchen 😀", category:"Nachbarschaftshilfe", zip:"22767", city:"Hamburg", contact:"timo@example.com", img:"", date:"2025-08-09"}
];
const state = { ads: seed, perPage: 6, currentCategory: "", search: "", zip: "" };
const $ = (s, r=document)=> r.querySelector(s);
const $$ = (s, r=document)=> Array.from(r.querySelectorAll(s));
const el = {
cards: $('#cards'), q: $('#q'), zip: $('#zip'), loadMore: $('#loadMore'),
chips: $$('.chip'), noteCats: $$('[data-cat]'),
createModal: $('#createModal'), openCreate: $('#openCreate'), openCreateMobile: $('#openCreateMobile'), openCreateHero: $('#openCreateHero'), openCreateCta: $('#openCreateCta'),
closeCreate: $('#closeCreate'), cancelCreate: $('#cancelCreate'), createForm: $('#createForm'),
openMenu: $('#openMenu'), mobileMenu: $('#mobileMenu'), detectLocation: $('#detectLocation'),
toast: $('#toast')
};
function fmtDate(d){ const date = new Date(d); return date.toLocaleDateString('de-DE'); }
function expiry(d){ const dt = new Date(d); dt.setDate(dt.getDate()+30); return dt.toLocaleDateString('de-DE'); }
function card(item){
const img = item.img && item.img.startsWith('http') ? `<img class="img" src="${item.img}" alt="Bild zu ${item.title}" loading="lazy">` : '';
return `<article class="card">
${img}
<div class="body">
<div class="row"><span class="badge">${item.category}</span><span>${fmtDate(item.date)}</span></div>
<h3>${item.title}</h3>
<p class="clamp-3 mt-1">${item.desc}</p>
<div class="row mt-2" style="font-size:14px;">
<div>📍 ${item.zip} ${item.city}</div>
<div>läuft ab: ${expiry(item.date)}</div>
</div>
<div class="mt-2" style="display:flex;gap:12px;align-items:center;">
<a class="btn outline" href="mailto:${encodeURIComponent(item.contact)}">Kontakt</a>
<button class="btn ghost" style="font-size:14px;">Merken</button>
</div>
</div>
</article>`;
}
function render(append=false){
const start = append ? el.cards.children.length : 0;
if(!append) el.cards.innerHTML = '';
const filtered = state.ads.filter(a=>{
const byCat = state.currentCategory ? a.category===state.currentCategory : true;
const byQ = state.search ? (a.title+" "+a.desc).toLowerCase().includes(state.search.toLowerCase()) : true;
const byZip = state.zip ? a.zip.startsWith(state.zip) : true;
return byCat && byQ && byZip;
});
const slice = filtered.slice(start, start + state.perPage);
slice.forEach(item=> el.cards.insertAdjacentHTML('beforeend', card(item)) );
$('#loadMore').classList.toggle('hide', (start + state.perPage) >= filtered.length);
}
// Events
$('#searchForm').addEventListener('submit', e=>{ e.preventDefault(); state.search = el.q.value.trim(); state.zip = el.zip.value.trim(); render(false); });
$('#loadMore').addEventListener('click', ()=> render(true));
el.chips.forEach(c=> c.addEventListener('click', ()=>{ state.currentCategory = c.dataset.filter; el.chips.forEach(x=>x.classList.toggle('active', x===c)); render(false); }));
el.noteCats.forEach(n=> n.addEventListener('click', ()=>{ state.currentCategory = n.dataset.cat; document.getElementById('kategorien').scrollIntoView({behavior:'smooth'}); el.chips.forEach(x=> x.classList.toggle('active', x.dataset.filter===state.currentCategory)); render(false); }));
// Modal
function openCreate(){ el.createModal.classList.add('open'); $('#title').focus(); }
function closeCreate(){ el.createModal.classList.remove('open'); }
[el.openCreate, el.openCreateMobile, el.openCreateHero, el.openCreateCta].forEach(b=> b&&b.addEventListener('click', openCreate));
el.closeCreate.addEventListener('click', closeCreate);
el.cancelCreate.addEventListener('click', closeCreate);
el.createModal.addEventListener('click', (e)=>{ if(e.target===el.createModal) closeCreate(); });
el.createForm.addEventListener('submit', e=>{
e.preventDefault();
const fd = new FormData(e.target); const ad = Object.fromEntries(fd.entries());
const id = Math.max(0, ...state.ads.map(a=>a.id))+1; const today = new Date();
state.ads.unshift({ id, title: ad.title.trim(), desc: ad.desc.trim(), category: ad.category, zip: ad.zip.trim(), city: ad.city.trim(), contact: ad.contact.trim(), img: ad.img?.trim()||"", date: today.toISOString().slice(0,10) });
closeCreate(); e.target.reset(); state.currentCategory = ""; render(false);
el.toast.textContent = 'Aushang veröffentlicht'; el.toast.classList.add('show'); setTimeout(()=> el.toast.classList.remove('show'), 2200);
window.scrollTo({top:0, behavior:'smooth'});
});
// Mobile Menu
el.openMenu.addEventListener('click', ()=> el.mobileMenu.classList.toggle('open'));
// Geolocation (Demo)
$('#detectLocation').addEventListener('click', ()=>{
if(!navigator.geolocation) return alert('Geolocation wird nicht unterstützt.');
navigator.geolocation.getCurrentPosition(()=>{ document.getElementById('heroOrt').textContent = 'deine Nähe'; alert('Für genaue Ergebnisse gib deine PLZ ein.'); }, ()=> alert('Standort konnte nicht ermittelt werden.'));
});
// Initial
render(false);
</script>
</body>
</html>