Montag, 25 August 2025

Top 5 diese Woche

Ähnliche Tutorials

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

</body>
</html>
Vorheriges Tutorial

Hier etwas für dich dabei?