Dienstag, 20 Januar 2026

Diese Woche am beliebtesten

Vertiefendes Material

Kickstarter Klon

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="https://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=> ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'})[c]); }
</script>

</body>
</html>
Dreamcodes Redaktion
Dreamcodes Redaktion
Qualität als Standard. Verantwortung als Prinzip. Jede Ressource auf Dreamcodes basiert auf geprüften Best Practices und fundierter Praxiserfahrung. Unser Anspruch ist ein belastbares Fundament statt experimenteller Lösungen. Die Integration und Absicherung der Inhalte liegt in Ihrem Ermessen. Wir liefern die fachliche Basis, die Verantwortung für den produktiven Einsatz verbleibt bei Ihnen.
Vorheriges Tutorial
Nächstes Tutorial

Vielleicht einen Blick WERT?