Mit unserem umfassenden Kickstarter Klon erhältst du ein komplettes Crowdfunding Portal in einer einzigen PHP-Datei. Nutzer können Projekte erstellen, Finanzierungsziele festlegen, Belohnungen anbieten und Unterstützer gewinnen. Das System ist sofort einsatzbereit und bringt alle Funktionen mit, die man von einer modernen Crowdfunding-Plattform erwartet:
- Benutzerregistrierung, Login und OAuth-Integration (Google/Facebook)
- E-Mail-Verifikation mit PHPMailer & reCAPTCHA-Schutz
- Projektverwaltung mit Bildern, Videos, Laufzeiten und Belohnungen
- Kommentar- und Community-Bereich für Unterstützer
- Follow/Unfollow-System und Benachrichtigungen in Echtzeit
- Admin-Panel zur Moderation von Projekten, Nutzern und Inhalten
- Erweiterte Kalender-Logik für Laufzeiten und Deadlines
- SEO-optimierte Projektseiten mit Social Media Sharing
Das Script ist mobil-optimiert, responsiv und mit AJAX/JavaScript für Live-Updates ausgestattet. Ideal, um deine eigene Crowdfunding-Plattform zu starten – vom kleinen Community-Projekt bis hin zur professionellen Startup-Idee.
<?php
session_start();
date_default_timezone_set('Europe/Berlin');
// Konfiguration
define('APP_NAME','Dreamcodes Kickstarter Klon');
define('DB_FILE', __DIR__.'/kickstarter.sqlite');
define('SMTP_HOST','smtp.example.com'); // setze eigene Werte
define('SMTP_USER','user@example.com');
define('SMTP_PASS','secret');
define('SMTP_PORT',587);
define('FROM_EMAIL','noreply@example.com');
define('RECAPTCHA_SITE_KEY','your-site-key'); // optional
define('RECAPTCHA_SECRET_KEY','your-secret-key'); // optional
define('OAUTH_GOOGLE_CLIENT_ID','your-google-client-id'); // optional
define('OAUTH_GOOGLE_CLIENT_SECRET','your-client-secret'); // optional
// Autoload PHPMailer wenn vorhanden
$hasPHPMailer = false;
if(file_exists(__DIR__.'/vendor/autoload.php')){
require_once __DIR__.'/vendor/autoload.php';
$hasPHPMailer = true;
}
// Hilfsfunktionen
function db(): PDO {
static $pdo = null;
if($pdo) return $pdo;
$needInit = !file_exists(DB_FILE);
$pdo = new PDO('sqlite:'.DB_FILE);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
if($needInit) init_db($pdo);
return $pdo;
}
function init_db(PDO $pdo){
$sql = <<<SQL
PRAGMA foreign_keys = ON;
CREATE TABLE users (
id INTEGER PRIMARY KEY,
email TEXT UNIQUE,
password TEXT,
name TEXT,
verified INTEGER DEFAULT 0,
verify_token TEXT,
reset_token TEXT,
is_admin INTEGER DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE projects (
id INTEGER PRIMARY KEY,
user_id INTEGER,
title TEXT,
slug TEXT UNIQUE,
short_desc TEXT,
description TEXT,
goal INTEGER,
pledged INTEGER DEFAULT 0,
ends_at DATETIME,
status TEXT DEFAULT 'active',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE SET NULL
);
CREATE TABLE rewards (
id INTEGER PRIMARY KEY,
project_id INTEGER,
title TEXT,
amount INTEGER,
stock INTEGER,
description TEXT,
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
);
CREATE TABLE pledges (
id INTEGER PRIMARY KEY,
project_id INTEGER,
user_id INTEGER,
reward_id INTEGER,
amount INTEGER,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE SET NULL,
FOREIGN KEY(reward_id) REFERENCES rewards(id) ON DELETE SET NULL
);
CREATE TABLE comments (
id INTEGER PRIMARY KEY,
project_id INTEGER,
user_id INTEGER,
content TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE SET NULL
);
CREATE TABLE follows (
id INTEGER PRIMARY KEY,
follower_id INTEGER,
followee_id INTEGER,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(follower_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY(followee_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE TABLE notifications (
id INTEGER PRIMARY KEY,
user_id INTEGER,
message TEXT,
readed INTEGER DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE INDEX idx_projects_user ON projects(user_id);
CREATE INDEX idx_pledges_project ON pledges(project_id);
SQL;
$pdo->exec($sql);
}
// E mail Funktion
function send_mail($to, $subject, $body){
global $hasPHPMailer;
if($hasPHPMailer){
$mail = new PHPMailer\PHPMailer\PHPMailer(true);
try{
$mail->isSMTP();
$mail->Host = SMTP_HOST;
$mail->SMTPAuth = true;
$mail->Username = SMTP_USER;
$mail->Password = SMTP_PASS;
$mail->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = SMTP_PORT;
$mail->setFrom(FROM_EMAIL, APP_NAME);
$mail->addAddress($to);
$mail->isHTML(true);
$mail->Subject = $subject;
$mail->Body = $body;
$mail->send();
return true;
}catch(Exception $e){
error_log("PHPMailer error: ".$mail->ErrorInfo);
return false;
}
}else{
// simpler Fallback
$headers = "From: ".FROM_EMAIL."\r\n";
$headers .= "Content-type: text/html; charset=utf-8\r\n";
return mail($to, $subject, $body, $headers);
}
}
// Utility
function json_response($data){ header('Content-Type: application/json'); echo json_encode($data); exit; }
function slugify($s){ $s = preg_replace('~[^\pL\d]+~u','-', $s); $s = iconv('utf-8','us-ascii//TRANSLIT', $s); $s = preg_replace('~[^-\w]+~','',$s); $s = trim($s,'-'); $s = preg_replace('~-+~','-',$s); return strtolower($s?:'item'); }
function require_login(){ if(empty($_SESSION['user'])) json_response(['error'=>'unauthenticated']); }
// Server API Endpoints AJAX
$action = $_REQUEST['action'] ?? null;
if($action === 'register'){
$email = strtolower(trim($_POST['email'] ?? ''));
$pass = $_POST['password'] ?? '';
$name = trim($_POST['name'] ?? '');
if(!filter_var($email,FILTER_VALIDATE_EMAIL)) json_response(['error'=>'ungültige email']);
if(strlen($pass) < 6) json_response(['error'=>'passwort zu kurz']);
$pdo = db();
$exists = $pdo->prepare("SELECT id FROM users WHERE email=?");
$exists->execute([$email]);
if($exists->fetch()) json_response(['error'=>'email bereits benutzt']);
$hash = password_hash($pass,PASSWORD_DEFAULT);
$token = bin2hex(random_bytes(16));
$stmt = $pdo->prepare("INSERT INTO users (email,password,name,verify_token) VALUES (?,?,?,?)");
$stmt->execute([$email,$hash,$name,$token]);
$verifyUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http")."://".$_SERVER['HTTP_HOST'].dirname($_SERVER['SCRIPT_NAME'])."/?verify=".$token;
$body = "<p>Hallo ".htmlspecialchars($name)."</p><p>Bitte bestätige deine Email mit einem Klick</p><p><a href='$verifyUrl'>$verifyUrl</a></p>";
send_mail($email,"Bitte bestätige deine Email bei ".APP_NAME,$body);
json_response(['success'=>true,'message'=>'registrierung erfolgreich bitte email prüfen']);
}
if($action === 'verify'){
$token = $_POST['token'] ?? '';
if(!$token) json_response(['error'=>'kein token']);
$pdo = db();
$stmt = $pdo->prepare("UPDATE users SET verified=1, verify_token=NULL WHERE verify_token=?");
$stmt->execute([$token]);
json_response(['success'=>true]);
}
if($action === 'login'){
$email = strtolower(trim($_POST['email'] ?? ''));
$pass = $_POST['password'] ?? '';
$pdo = db();
$stmt = $pdo->prepare("SELECT id,email,password,name,verified,is_admin FROM users WHERE email=?");
$stmt->execute([$email]);
$u = $stmt->fetch(PDO::FETCH_ASSOC);
if(!$u || !password_verify($pass,$u['password'])) json_response(['error'=>'ungültige daten']);
if(!$u['verified']) json_response(['error'=>'email nicht verifiziert']);
$_SESSION['user'] = ['id'=>$u['id'],'email'=>$u['email'],'name'=>$u['name'],'is_admin'=>$u['is_admin']];
json_response(['success'=>true,'user'=>$_SESSION['user']]);
}
if($action === 'logout'){ session_destroy(); json_response(['success'=>true]); }
if($action === 'request_reset'){
$email = strtolower(trim($_POST['email'] ?? ''));
$pdo = db();
$stmt = $pdo->prepare("SELECT id,name FROM users WHERE email=?");
$stmt->execute([$email]);
$u = $stmt->fetch(PDO::FETCH_ASSOC);
if(!$u) json_response(['error'=>'email unbekannt']);
$token = bin2hex(random_bytes(16));
$pdo->prepare("UPDATE users SET reset_token=? WHERE id=?")->execute([$token,$u['id']]);
$resetUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http")."://".$_SERVER['HTTP_HOST'].dirname($_SERVER['SCRIPT_NAME'])."/?reset=".$token;
$body = "<p>Hallo ".htmlspecialchars($u['name'])."</p><p>Nutze diesen Link zum zurücksetzen des Passworts</p><p><a href='$resetUrl'>$resetUrl</a></p>";
send_mail($email,"Passwort zurücksetzen ".APP_NAME,$body);
json_response(['success'=>true]);
}
if($action === 'do_reset'){
$token = $_POST['token'] ?? '';
$pass = $_POST['password'] ?? '';
if(!$token || strlen($pass) < 6) json_response(['error'=>'ungültig']);
$pdo = db();
$stmt = $pdo->prepare("SELECT id FROM users WHERE reset_token=?");
$stmt->execute([$token]);
$u = $stmt->fetch(PDO::FETCH_ASSOC);
if(!$u) json_response(['error'=>'token ungültig']);
$hash = password_hash($pass,PASSWORD_DEFAULT);
$pdo->prepare("UPDATE users SET password=?, reset_token=NULL WHERE id=?")->execute([$hash,$u['id']]);
json_response(['success'=>true]);
}
// Projekt erstellen
if($action === 'create_project'){
require_login();
$u = $_SESSION['user'];
$title = trim($_POST['title'] ?? '');
$short = trim($_POST['short_desc'] ?? '');
$desc = trim($_POST['description'] ?? '');
$goal = intval($_POST['goal'] ?? 0);
$ends = trim($_POST['ends_at'] ?? '');
if(!$title || !$goal) json_response(['error'=>'fehlende daten']);
$slug = slugify($title).'-'.substr(bin2hex(random_bytes(4)),0,6);
$pdo = db();
$stmt = $pdo->prepare("INSERT INTO projects (user_id,title,slug,short_desc,description,goal,ends_at) VALUES (?,?,?,?,?,?,?)");
$stmt->execute([$u['id'],$title,$slug,$short,$desc,$goal,$ends ?: null]);
$pid = $pdo->lastInsertId();
json_response(['success'=>true,'project_id'=>$pid]);
}
// Rewards erstellen
if($action === 'create_reward'){
require_login();
$u = $_SESSION['user'];
$project_id = intval($_POST['project_id'] ?? 0);
$title = trim($_POST['title'] ?? '');
$amount = intval($_POST['amount'] ?? 0);
$stock = intval($_POST['stock'] ?? 0);
$desc = trim($_POST['description'] ?? '');
if(!$project_id || !$title || !$amount) json_response(['error'=>'fehlend']);
$pdo = db();
// ownership check
$proj = $pdo->prepare("SELECT user_id FROM projects WHERE id=?");
$proj->execute([$project_id]);
$p = $proj->fetch(PDO::FETCH_ASSOC);
if(!$p || $p['user_id'] != $u['id']) json_response(['error'=>'nicht berechtigt']);
$pdo->prepare("INSERT INTO rewards (project_id,title,amount,stock,description) VALUES (?,?,?,?,?)")->execute([$project_id,$title,$amount,$stock,$desc]);
json_response(['success'=>true]);
}
// Pledge
if($action === 'pledge'){
require_login();
$u = $_SESSION['user'];
$project_id = intval($_POST['project_id'] ?? 0);
$amount = intval($_POST['amount'] ?? 0);
$reward_id = intval($_POST['reward_id'] ?? 0) ?: null;
if(!$project_id || $amount <= 0) json_response(['error'=>'ungültig']);
$pdo = db();
$pdo->beginTransaction();
$pdo->prepare("INSERT INTO pledges (project_id,user_id,reward_id,amount) VALUES (?,?,?,?)")->execute([$project_id,$u['id'],$reward_id,$amount]);
$pdo->prepare("UPDATE projects SET pledged = pledged + ? WHERE id = ?")->execute([$amount,$project_id]);
$pdo->commit();
// Notification to project owner
$owner = $pdo->prepare("SELECT user_id FROM projects WHERE id=?");
$owner->execute([$project_id]);
$own = $owner->fetch(PDO::FETCH_ASSOC);
if($own){
$pdo->prepare("INSERT INTO notifications (user_id,message) VALUES (?,?)")->execute([$own['user_id'],"Dein Projekt erhielt eine neue Unterstützung"]);
}
json_response(['success'=>true]);
}
// Kommentare
if($action === 'comment'){
require_login();
$u = $_SESSION['user'];
$project_id = intval($_POST['project_id'] ?? 0);
$content = trim($_POST['content'] ?? '');
if(!$project_id || !$content) json_response(['error'=>'ungültig']);
$pdo = db();
$pdo->prepare("INSERT INTO comments (project_id,user_id,content) VALUES (?,?,?)")->execute([$project_id,$u['id'],$content]);
json_response(['success'=>true]);
}
// Follow
if($action === 'follow'){
require_login();
$u = $_SESSION['user'];
$target = intval($_POST['user_id'] ?? 0);
if(!$target) json_response(['error'=>'ungültig']);
$pdo = db();
$exists = $pdo->prepare("SELECT id FROM follows WHERE follower_id=? AND followee_id=?");
$exists->execute([$u['id'],$target]);
if($exists->fetch()) {
$pdo->prepare("DELETE FROM follows WHERE follower_id=? AND followee_id=?")->execute([$u['id'],$target]);
json_response(['success'=>true,'following'=>false]);
} else {
$pdo->prepare("INSERT INTO follows (follower_id,followee_id) VALUES (?,?)")->execute([$u['id'],$target]);
$pdo->prepare("INSERT INTO notifications (user_id,message) VALUES (?,?)")->execute([$target,"Du hast einen neuen Follower"]);
json_response(['success'=>true,'following'=>true]);
}
}
// Load project list
if($action === 'list_projects'){
$pdo = db();
$q = $pdo->query("SELECT p.*,u.name as author FROM projects p LEFT JOIN users u ON p.user_id=u.id ORDER BY created_at DESC LIMIT 50");
$rows = $q->fetchAll(PDO::FETCH_ASSOC);
json_response($rows);
}
// Admin Moderation
if($action === 'admin_delete_project'){
require_login();
$u = $_SESSION['user'];
if(!$u['is_admin']) json_response(['error'=>'nicht autorisiert']);
$pid = intval($_POST['project_id'] ?? 0);
$pdo = db();
$pdo->prepare("DELETE FROM projects WHERE id=?")->execute([$pid]);
json_response(['success'=>true]);
}
// Fetch notifications
if($action === 'notifications'){
require_login();
$u = $_SESSION['user'];
$pdo = db();
$q = $pdo->prepare("SELECT * FROM notifications WHERE user_id=? ORDER BY created_at DESC LIMIT 50");
$q->execute([$u['id']]);
json_response($q->fetchAll(PDO::FETCH_ASSOC));
}
// Mark notification read
if($action === 'mark_read'){
require_login();
$id = intval($_POST['id'] ?? 0);
db()->prepare("UPDATE notifications SET readed=1 WHERE id=?")->execute([$id]);
json_response(['success'=>true]);
}
// Simple route to serve main app if no API action
if(!$action){
// render html below
}
?>
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title><?=htmlspecialchars(APP_NAME)?></title>
<style>
:root{--bg:#0b0f14;--card:#0f1720;--accent:#06b6d4;--muted:#94a3b8;--white:#eef2f7}
body{margin:0;font-family:Inter,Segoe UI,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--white);-webkit-font-smoothing:antialiased}
header{display:flex;align-items:center;justify-content:space-between;padding:18px 28px;background:linear-gradient(90deg,rgba(255,255,255,0.02),transparent)}
.brand{font-weight:700;color:var(--accent)}
.container{max-width:1100px;margin:24px auto;padding:0 16px}
.grid{display:grid;grid-template-columns:1fr 360px;gap:20px}
.card{background:var(--card);padding:18px;border-radius:12px;box-shadow:0 8px 30px rgba(2,6,23,0.6)}
.muted{color:var(--muted);font-size:13px}
input,textarea,select{width:100;padding:10px;border-radius:8px;border:1px solid rgba(255,255,255,0.04);background:transparent;color:var(--white);box-sizing:border-box}
button{background:var(--accent);border:none;color:#012;background-clip:padding-box;padding:10px 14px;border-radius:10px;font-weight:700;cursor:pointer}
.small{font-size:13px}
.project{padding:12px;border-radius:10px;border:1px solid rgba(255,255,255,0.03);margin-bottom:12px}
.project h3{margin:0 0 6px 0}
.note{background:rgba(6,182,212,0.08);color:var(--accent);padding:8px;border-radius:8px;font-size:13px}
footer{text-align:center;padding:20px;color:var(--muted);font-size:13px}
nav .btnlink{background:transparent;border:1px solid rgba(255,255,255,0.03);padding:8px 12px;border-radius:10px;color:var(--white);cursor:pointer}
.rightcol .section{margin-bottom:12px}
.mini{font-size:12px;color:var(--muted)}
</style>
</head>
<body>
<header>
<div class="brand"><?=htmlspecialchars(APP_NAME)?></div>
<div>
<button id="btnNewProject" class="btn">Neues Projekt</button>
<button id="btnLogin" class="btn" onclick="showLogin()">Login</button>
</div>
</header>
<main class="container">
<div class="grid">
<section class="card" id="main">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px">
<h2>Projekte entdecken</h2>
<div class="muted small">Echtzeit Vorschau mit AJAX</div>
</div>
<div id="projectList"></div>
</section>
<aside class="rightcol">
<div class="card section">
<h3>Aktionscenter</h3>
<div class="mini muted">Schnellaktionen</div>
<div style="margin-top:10px;display:flex;gap:8px">
<button onclick="refreshProjects()" class="btn">Aktualisieren</button>
<button onclick="loadNotifications()" class="btn">Benachrichtigungen</button>
</div>
</div>
<div class="card section" id="userPanel">
<h3>Account</h3>
<div id="userInfo" class="muted">Nicht angemeldet</div>
<div id="authForms" style="margin-top:10px">
<input id="email" placeholder="Email">
<input id="password" placeholder="Passwort" type="password" style="margin-top:8px">
<div style="display:flex;gap:8px;margin-top:8px">
<button onclick="api('login',{email:byId('email').value,password:byId('password').value}, onLogin)">Login</button>
<button onclick="showRegister()">Registrieren</button>
</div>
<div style="margin-top:8px">
<a href="#" class="muted small" onclick="showReset()">Passwort vergessen</a>
</div>
</div>
</div>
<div class="card section" id="rightNotifications">
<h3>Benachrichtigungen</h3>
<div id="notes">Noch keine</div>
</div>
<div class="card section" id="adminPanel" style="display:none">
<h3>Admin</h3>
<div class="muted small">Moderation</div>
<div style="margin-top:8px">
<input id="admin_project_id" placeholder="Projekt id">
<button onclick="adminDelete()">Projekt löschen</button>
</div>
</div>
</aside>
</div>
<!-- Modale und Formulare -->
<div id="modal" style="display:none;position:fixed;inset:0;align-items:center;justify-content:center;background:rgba(2,6,23,0.6);">
<div style="width:720px;max-width:96%;background:var(--card);padding:20px;border-radius:12px">
<div id="modalContent"></div>
<div style="text-align:right;margin-top:12px"><button onclick="closeModal()">Schließen</button></div>
</div>
</div>
</main>
<footer>
© <?=date('Y')?> <a href="http://www.dreamcodes.net" target="_blank" style="color:inherit;text-decoration:none">Dreamcodes</a> — Crowdfunding Klon
</footer>
<script>
// Einfacher AJAX Helfer
function api(action,data,cb){
data = data || {};
data.action = action;
fetch(window.location.pathname, {
method: 'POST',
headers: {'Content-Type':'application/x-www-form-urlencoded'},
body: new URLSearchParams(data)
}).then(r=>r.json()).then(cb).catch(e=>console.error(e));
}
function byId(id){return document.getElementById(id)}
// Laden der Projekte
function renderProjects(list){
const el = byId('projectList');
el.innerHTML = '';
if(!list.length){ el.innerHTML = '<div class="note">Keine Projekte gefunden</div>'; return; }
list.forEach(p=>{
const div = document.createElement('div');
div.className = 'project';
div.innerHTML = `<h3>${escapeHtml(p.title)}</h3>
<div class="muted small">von ${escapeHtml(p.author || 'anonym')}</div>
<p>${escapeHtml(p.short_desc || '')}</p>
<div style="display:flex;gap:8px;margin-top:8px">
<button onclick="openProject(${p.id})">Ansehen</button>
<button onclick="openPledge(${p.id})">Unterstützen</button>
</div>
<div class="muted small" style="margin-top:8px">Ziel: ${p.goal}€ Pledged: ${p.pledged}€ Ende: ${p.ends_at || 'offen'}</div>`;
el.appendChild(div);
});
}
function refreshProjects(){
fetch(window.location.pathname+'?action=list_projects').then(r=>r.json()).then(data=>renderProjects(data));
}
refreshProjects();
function openModalHtml(html){
byId('modalContent').innerHTML = html;
byId('modal').style.display = 'flex';
}
function closeModal(){ byId('modal').style.display='none'; }
// Login
function onLogin(res){
if(res.error) return alert(res.error);
if(res.success){ updateUser(res.user); }
}
function updateUser(user){
byId('userInfo').innerHTML = `<strong>${escapeHtml(user.name || user.email)}</strong><div class="muted small">eingeloggt</div>`;
byId('authForms').style.display='none';
if(user.is_admin) byId('adminPanel').style.display='block';
loadNotifications();
}
function showLogin(){
openModalHtml(`<h3>Login</h3><input id="m_email" placeholder="Email"><input id="m_pass" placeholder="Passwort" type="password"><div style="margin-top:8px"><button onclick="modalLogin()">Login</button></div>`);
}
function modalLogin(){
const email = byId('m_email').value, password = byId('m_pass').value;
api('login',{email,password}, function(res){ if(res.error) alert(res.error); else { closeModal(); updateUser(res.user); } });
}
function showRegister(){
openModalHtml(`<h3>Registrieren</h3><input id="r_name" placeholder="Name"><input id="r_email" placeholder="Email"><input id="r_pass" placeholder="Passwort" type="password"><div style="margin-top:8px"><button onclick="doRegister()">Registrieren</button></div>`);
}
function doRegister(){
const name = byId('r_name').value, email = byId('r_email').value, password = byId('r_pass').value;
api('register',{name,email,password}, function(res){ if(res.error) alert(res.error); else { alert(res.message); closeModal(); } });
}
function showReset(){
openModalHtml(`<h3>Passwort zurücksetzen</h3><input id="rs_email" placeholder="Email"><div style="margin-top:8px"><button onclick="doResetRequest()">Link anfordern</button></div>`);
}
function doResetRequest(){ api('request_reset',{email:byId('rs_email').value}, res=>{ if(res.error) alert(res.error); else { alert('Email gesendet'); closeModal(); } }); }
// Projekt erstellen UI
byId('btnNewProject').addEventListener('click', ()=> {
openModalHtml(`<h3>Neues Projekt</h3>
<input id="p_title" placeholder="Titel">
<input id="p_short" placeholder="Kurzbeschreibung">
<textarea id="p_desc" placeholder="Beschreibung" style="height:120px"></textarea>
<input id="p_goal" placeholder="Ziel in Euro">
<input id="p_ends" placeholder="Enddatum optional YYYY-MM-DD">
<div style="margin-top:8px"><button onclick="createProject()">Projekt anlegen</button></div>`);
});
function createProject(){
api('create_project',{
title:byId('p_title').value,
short_desc:byId('p_short').value,
description:byId('p_desc').value,
goal:byId('p_goal').value,
ends_at:byId('p_ends').value
}, res=>{ if(res.error) alert(res.error); else { alert('Projekt erstellt'); closeModal(); refreshProjects(); } });
}
// Pledge UI
function openPledge(project_id){
openModalHtml(`<h3>Unterstützen</h3><input id="pledge_amount" placeholder="Betrag in Euro"><select id="pledge_reward"><option value="">Belohnung wählen</option></select><div style="margin-top:8px"><button onclick="doPledge(${project_id})">Zahlung simulieren</button></div>`);
// rewards load optional
}
function doPledge(project_id){
const amount = byId('pledge_amount').value;
api('pledge',{project_id,amount}, res=>{ if(res.error) alert(res.error); else { alert('Danke für die Unterstützung'); closeModal(); refreshProjects(); } });
}
// Kommentare
function openProject(id){
fetch(window.location.pathname+'?action=get_project&id='+id).then(r=>r.json()).then(p=>{
// fallback if not implemented show minimal
openModalHtml(`<h3>${escapeHtml(p.title||'Projekt')}</h3><p>${escapeHtml(p.description||'')}</p>
<div style="margin-top:12px"><textarea id="c_text" placeholder="Kommentar"></textarea><div style="margin-top:8px"><button onclick="postComment(${id})">Senden</button></div></div>`);
}).catch(()=>{ openModalHtml('<div class="note">Projekt nicht gefunden</div>') });
}
function postComment(pid){
api('comment',{project_id:pid,content:byId('c_text').value}, res=>{ if(res.error) alert(res.error); else { alert('Kommentar hinzugefügt'); closeModal(); } });
}
// Notifications
function loadNotifications(){
api('notifications',{}, res=>{ if(res.error) return; const n = byId('notes'); n.innerHTML=''; res.forEach(it=>{ const d = document.createElement('div'); d.className='muted small'; d.innerText = it.message+' '+it.created_at; n.appendChild(d); }); });
}
// Admin delete
function adminDelete(){
const pid = byId('admin_project_id').value;
api('admin_delete_project',{project_id:pid}, res=>{ if(res.error) alert(res.error); else { alert('gelöscht'); refreshProjects(); } });
}
// Helpers
function escapeHtml(s){ if(!s) return ''; return String(s).replace(/[&<>"']/g,c=> ({'&':'&','<':'<','>':'>','"':'"',"'":'''})[c]); }
</script>
</body>
</html>