Initial Release: Lütte Planer mit Google-Login-Vorbereitung
- Login-System (Mock für Tests, Google OAuth vorbereitet) - Reisen erstellen mit Emoji, Ziel und Datum - Ablauf (Timeline), Packliste, Ideen, Notizen, Dokumente - Team-System: Personen einladen per E-Mail + Einladungslinks - Berechtigungen: Nur lesen / Bearbeiten / Admin - E-Mail-Benachrichtigungen beim Einladen Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/config.php';
|
||||
$tripId = $_GET['trip'] ?? '';
|
||||
[$trip, $user] = requireTripAccess($tripId);
|
||||
$isAdmin = userIsAdmin($tripId, $user['id']);
|
||||
|
||||
// Person einladen
|
||||
if ($isAdmin && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['einladen'])) {
|
||||
$email = strtolower(trim($_POST['email'] ?? ''));
|
||||
$access = in_array($_POST['access'] ?? '', ['see','edit']) ? $_POST['access'] : 'see';
|
||||
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
// Prüfen ob schon Mitglied
|
||||
$existing = dbOne('SELECT u.id FROM users u JOIN reise_zugriff rz ON rz.user_id = u.id WHERE u.email = ? AND rz.trip_id = ?', [$email, $tripId]);
|
||||
if (!$existing) {
|
||||
// Prüfen ob User schon existiert
|
||||
$invitedUser = dbOne('SELECT * FROM users WHERE email = ?', [$email]);
|
||||
if ($invitedUser) {
|
||||
// Direkt hinzufügen
|
||||
$alreadyZugriff = dbOne('SELECT 1 FROM reise_zugriff WHERE trip_id = ? AND user_id = ?', [$tripId, $invitedUser['id']]);
|
||||
if (!$alreadyZugriff) {
|
||||
dbInsert('reise_zugriff', ['trip_id' => $tripId, 'user_id' => $invitedUser['id'], 'access' => $access, 'invited_by' => $user['id']]);
|
||||
$tripUrl = (isset($_SERVER['HTTPS']) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'] . '/Develop/PlanerGoogel/ablauf.php?trip=' . $tripId;
|
||||
sendAddedToTripMail($invitedUser['email'], $invitedUser['name'], $trip['name'], $tripUrl, $user['name']);
|
||||
}
|
||||
$msg = h($invitedUser['name']) . ' wurde zur Reise hinzugefügt und per E-Mail informiert.';
|
||||
} else {
|
||||
// Einladungslink erstellen
|
||||
$token = bin2hex(random_bytes(16));
|
||||
// Alte offene Einladung für diese E-Mail löschen
|
||||
dbExec('DELETE FROM reise_einladungen WHERE trip_id = ? AND email = ? AND used_at IS NULL', [$tripId, $email]);
|
||||
dbInsert('reise_einladungen', ['trip_id' => $tripId, 'email' => $email, 'token' => $token, 'access' => $access, 'invited_by' => $user['id']]);
|
||||
$inviteLink = (isset($_SERVER['HTTPS']) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'] . '/Develop/PlanerGoogel/login.php?invite=' . $token;
|
||||
$mailSent = sendInviteMail($email, $trip['name'], $inviteLink, $user['name']);
|
||||
$msg = $mailSent
|
||||
? 'Einladungsmail an <strong>' . h($email) . '</strong> wurde verschickt!'
|
||||
: 'Einladungslink erstellt (E-Mail konnte nicht gesendet werden).<br><code style="font-size:.75rem;word-break:break-all;">' . h($inviteLink) . '</code>';
|
||||
}
|
||||
} else {
|
||||
$msg = 'Diese Person ist bereits Mitglied.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Zugriff ändern
|
||||
if ($isAdmin && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['change_access'])) {
|
||||
$memberId = (int)$_POST['member_id'];
|
||||
$newAccess = $_POST['new_access'] ?? '';
|
||||
if ($memberId !== $user['id'] && in_array($newAccess, ['see','edit','admin'])) {
|
||||
dbExec('UPDATE reise_zugriff SET access = ? WHERE trip_id = ? AND user_id = ?', [$newAccess, $tripId, $memberId]);
|
||||
}
|
||||
header('Location: /Develop/PlanerGoogel/team.php?trip=' . $tripId); exit;
|
||||
}
|
||||
|
||||
// Person entfernen
|
||||
if ($isAdmin && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['remove'])) {
|
||||
$memberId = (int)$_POST['remove'];
|
||||
if ($memberId !== $user['id']) {
|
||||
dbExec('DELETE FROM reise_zugriff WHERE trip_id = ? AND user_id = ?', [$tripId, $memberId]);
|
||||
}
|
||||
header('Location: /Develop/PlanerGoogel/team.php?trip=' . $tripId); exit;
|
||||
}
|
||||
|
||||
// Reise-Einstellungen speichern
|
||||
if ($isAdmin && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['save_trip'])) {
|
||||
dbExec('UPDATE reisen SET name = ?, ziel = ?, start_datum = ?, end_datum = ?, cover_emoji = ?, beschreibung = ? WHERE id = ?', [
|
||||
trim($_POST['name'] ?? $trip['name']),
|
||||
trim($_POST['ziel'] ?? ''),
|
||||
$_POST['start_datum'] ?? '',
|
||||
$_POST['end_datum'] ?? '',
|
||||
trim($_POST['cover_emoji'] ?? '✈️') ?: '✈️',
|
||||
trim($_POST['beschreibung'] ?? ''),
|
||||
$tripId,
|
||||
]);
|
||||
header('Location: /Develop/PlanerGoogel/team.php?trip=' . $tripId); exit;
|
||||
}
|
||||
|
||||
// Reise löschen
|
||||
if ($isAdmin && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_trip'])) {
|
||||
dbExec('DELETE FROM reisen WHERE id = ?', [$tripId]);
|
||||
header('Location: /Develop/PlanerGoogel/'); exit;
|
||||
}
|
||||
|
||||
$members = getTripMembers($tripId);
|
||||
$invites = dbAll('SELECT * FROM reise_einladungen WHERE trip_id = ? AND used_at IS NULL ORDER BY created_at DESC', [$tripId]);
|
||||
$baseUrl = (isset($_SERVER['HTTPS']) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'];
|
||||
|
||||
$pageTitle = 'Team & Einstellungen';
|
||||
$activeTab = 'team';
|
||||
include __DIR__ . '/includes/header.php';
|
||||
?>
|
||||
|
||||
<main class="main">
|
||||
<h1 class="section-head">Team & Einstellungen</h1>
|
||||
|
||||
<?php if (!empty($msg)): ?>
|
||||
<div class="alert-success"><?= $msg ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Mitglieder -->
|
||||
<div class="settings-section">
|
||||
<h2 class="col-title">Mitglieder</h2>
|
||||
<div class="member-list">
|
||||
<?php foreach ($members as $m): ?>
|
||||
<div class="member-item">
|
||||
<?= avatarHtml($m, '38px') ?>
|
||||
<div class="member-info">
|
||||
<div class="member-name"><?= h($m['name']) ?></div>
|
||||
<div class="member-email"><?= h($m['email']) ?></div>
|
||||
</div>
|
||||
<span class="access-badge access-<?= $m['access'] ?>"><?= ucfirst($m['access']) ?></span>
|
||||
<?php if ($isAdmin && $m['id'] != $user['id']): ?>
|
||||
<form method="POST" style="display:flex;gap:6px;align-items:center;">
|
||||
<input type="hidden" name="member_id" value="<?= $m['id'] ?>">
|
||||
<select name="new_access" style="font-size:.78rem;padding:4px 8px;border:1.5px solid var(--border);border-radius:6px;">
|
||||
<option value="see" <?= $m['access'] === 'see' ? 'selected' : '' ?>>Nur lesen</option>
|
||||
<option value="edit" <?= $m['access'] === 'edit' ? 'selected' : '' ?>>Bearbeiten</option>
|
||||
<option value="admin" <?= $m['access'] === 'admin' ? 'selected' : '' ?>>Admin</option>
|
||||
</select>
|
||||
<button type="submit" name="change_access" class="btn-edit">Ändern</button>
|
||||
</form>
|
||||
<form method="POST" onsubmit="return confirm('Person entfernen?')">
|
||||
<button type="submit" name="remove" value="<?= $m['id'] ?>" class="btn-delete">✕</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<?php if ($isAdmin): ?>
|
||||
<div class="invite-form">
|
||||
<h3 style="font-size:.88rem;font-weight:600;margin-bottom:10px;color:var(--ink);">Person einladen</h3>
|
||||
<form method="POST" style="display:flex;gap:8px;flex-wrap:wrap;">
|
||||
<input type="email" name="email" placeholder="E-Mail-Adresse" required style="flex:1;min-width:200px;padding:8px 12px;border:1.5px solid var(--border);border-radius:8px;font-size:.85rem;">
|
||||
<select name="access" style="padding:8px 12px;border:1.5px solid var(--border);border-radius:8px;font-size:.85rem;">
|
||||
<option value="see">Nur lesen</option>
|
||||
<option value="edit">Bearbeiten</option>
|
||||
</select>
|
||||
<button type="submit" name="einladen" class="btn btn-primary">Einladen</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($invites)): ?>
|
||||
<div style="margin-top:16px;">
|
||||
<h3 style="font-size:.82rem;font-weight:600;color:var(--muted);margin-bottom:8px;">Offene Einladungen</h3>
|
||||
<?php foreach ($invites as $inv): ?>
|
||||
<div style="display:flex;align-items:center;gap:10px;padding:8px 12px;background:var(--sky);border-radius:8px;margin-bottom:6px;font-size:.8rem;">
|
||||
<span>✉️ <?= h($inv['email']) ?></span>
|
||||
<span class="access-badge access-<?= $inv['access'] ?>"><?= ucfirst($inv['access']) ?></span>
|
||||
<code style="flex:1;font-size:.68rem;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;"><?= $baseUrl ?>/Develop/PlanerGoogel/login.php?invite=<?= $inv['token'] ?></code>
|
||||
<button onclick="navigator.clipboard.writeText('<?= $baseUrl ?>/Develop/PlanerGoogel/login.php?invite=<?= $inv['token'] ?>').then(()=>alert('Link kopiert!'))" class="btn-edit" type="button">📋 Kopieren</button>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Reise-Einstellungen -->
|
||||
<?php if ($isAdmin): ?>
|
||||
<div class="settings-section">
|
||||
<h2 class="col-title">Reise bearbeiten</h2>
|
||||
<form method="POST" style="display:flex;flex-direction:column;gap:12px;max-width:500px;">
|
||||
<div>
|
||||
<label style="font-size:.78rem;font-weight:500;color:var(--muted);display:block;margin-bottom:4px;">Name</label>
|
||||
<input type="text" name="name" value="<?= h($trip['name']) ?>" required style="width:100%;padding:9px 12px;border:1.5px solid var(--border);border-radius:8px;font-size:.88rem;">
|
||||
</div>
|
||||
<div style="display:flex;gap:8px;">
|
||||
<div style="flex:1;">
|
||||
<label style="font-size:.78rem;font-weight:500;color:var(--muted);display:block;margin-bottom:4px;">Ziel</label>
|
||||
<input type="text" name="ziel" value="<?= h($trip['ziel'] ?? '') ?>" style="width:100%;padding:9px 12px;border:1.5px solid var(--border);border-radius:8px;font-size:.88rem;">
|
||||
</div>
|
||||
<div style="max-width:80px;">
|
||||
<label style="font-size:.78rem;font-weight:500;color:var(--muted);display:block;margin-bottom:4px;">Emoji</label>
|
||||
<input type="text" name="cover_emoji" value="<?= h($trip['cover_emoji']) ?>" style="width:100%;padding:9px 12px;border:1.5px solid var(--border);border-radius:8px;font-size:.88rem;text-align:center;">
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex;gap:8px;">
|
||||
<div style="flex:1;">
|
||||
<label style="font-size:.78rem;font-weight:500;color:var(--muted);display:block;margin-bottom:4px;">Start</label>
|
||||
<input type="date" name="start_datum" value="<?= h($trip['start_datum'] ?? '') ?>" style="width:100%;padding:9px 12px;border:1.5px solid var(--border);border-radius:8px;font-size:.88rem;">
|
||||
</div>
|
||||
<div style="flex:1;">
|
||||
<label style="font-size:.78rem;font-weight:500;color:var(--muted);display:block;margin-bottom:4px;">Ende</label>
|
||||
<input type="date" name="end_datum" value="<?= h($trip['end_datum'] ?? '') ?>" style="width:100%;padding:9px 12px;border:1.5px solid var(--border);border-radius:8px;font-size:.88rem;">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" name="save_trip" class="btn btn-primary">Speichern</button>
|
||||
</form>
|
||||
|
||||
<div style="margin-top:32px;padding-top:20px;border-top:1.5px solid #fca5a5;">
|
||||
<h3 style="font-size:.88rem;font-weight:600;color:#dc2626;margin-bottom:8px;">Gefahrenzone</h3>
|
||||
<form method="POST" onsubmit="return confirm('Reise wirklich löschen? Alle Daten gehen verloren!')">
|
||||
<button type="submit" name="delete_trip" class="btn" style="background:#dc2626;color:#fff;padding:8px 20px;">Reise löschen</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</main>
|
||||
|
||||
<?php include __DIR__ . '/includes/footer.php'; ?>
|
||||
Reference in New Issue
Block a user