264 lines
14 KiB
PHP
264 lines
14 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
require_once __DIR__ . '/app.php';
|
|
|
|
$type = normalize_entry_type((string) ($_GET['type'] ?? 'program'));
|
|
$mode = (string) ($_GET['mode'] ?? 'detail');
|
|
$selectedId = isset($_GET['id']) ? (int) $_GET['id'] : null;
|
|
$entry = $selectedId ? get_entry($selectedId) : null;
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'save_entry') {
|
|
$postedType = normalize_entry_type((string) ($_POST['entry_type'] ?? 'program'));
|
|
$saveId = isset($_POST['id']) && $_POST['id'] !== '' ? (int) $_POST['id'] : null;
|
|
|
|
try {
|
|
$savedId = save_entry([
|
|
'entry_type' => $postedType,
|
|
'title' => $_POST['title'] ?? '',
|
|
'subtitle' => $_POST['subtitle'] ?? '',
|
|
'body' => $_POST['body'] ?? '',
|
|
'meta_url' => $_POST['meta_url'] ?? '',
|
|
'meta_value' => $_POST['meta_value'] ?? '',
|
|
'weekday' => $_POST['weekday'] ?? null,
|
|
'start_time' => $_POST['start_time'] ?? null,
|
|
'end_time' => $_POST['end_time'] ?? null,
|
|
'status' => $_POST['status'] ?? 'published',
|
|
'sort_order' => $_POST['sort_order'] ?? 0,
|
|
], $saveId);
|
|
set_flash('success', $saveId ? 'Contenido actualizado.' : 'Nuevo contenido creado.');
|
|
header('Location: /admin.php?type=' . urlencode($postedType) . '&id=' . $savedId);
|
|
exit;
|
|
} catch (Throwable $e) {
|
|
set_flash('danger', $e->getMessage());
|
|
header('Location: /admin.php?type=' . urlencode($postedType) . ($saveId ? '&id=' . $saveId : '&mode=new'));
|
|
exit;
|
|
}
|
|
}
|
|
|
|
$flash = pull_flash();
|
|
$counts = get_entry_counts();
|
|
$programs = get_entries('program');
|
|
$djs = get_entries('dj');
|
|
$socials = get_entries('social');
|
|
$messages = get_entries('message');
|
|
|
|
if (!$entry && $selectedId) {
|
|
$type = 'program';
|
|
}
|
|
|
|
$entry = $entry && $entry['entry_type'] === $type ? $entry : ($mode === 'new' ? null : $entry);
|
|
$assetVersion = (string) max(@filemtime(__DIR__ . '/assets/css/custom.css') ?: time(), @filemtime(__DIR__ . '/assets/js/main.js') ?: time());
|
|
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? project_description();
|
|
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
|
$logoPath = project_logo_path();
|
|
$logoVersion = (string) (@filemtime(__DIR__ . $logoPath) ?: time());
|
|
?>
|
|
<!doctype html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Admin · <?= h(project_name()) ?></title>
|
|
<meta name="description" content="<?= h($projectDescription) ?>">
|
|
<?php if ($projectDescription): ?>
|
|
<meta property="og:description" content="<?= h($projectDescription) ?>">
|
|
<meta property="twitter:description" content="<?= h($projectDescription) ?>">
|
|
<?php endif; ?>
|
|
<?php if ($projectImageUrl): ?>
|
|
<meta property="og:image" content="<?= h($projectImageUrl) ?>">
|
|
<meta property="twitter:image" content="<?= h($projectImageUrl) ?>">
|
|
<?php endif; ?>
|
|
<link rel="icon" type="image/png" sizes="512x512" href="<?= h($logoPath) ?>?v=<?= h($logoVersion) ?>">
|
|
<link rel="apple-touch-icon" href="<?= h($logoPath) ?>?v=<?= h($logoVersion) ?>">
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
|
<link rel="stylesheet" href="/assets/css/custom.css?v=<?= h($assetVersion) ?>">
|
|
</head>
|
|
<body class="admin-body">
|
|
<nav class="navbar navbar-expand-lg navbar-dark sticky-top radio-nav border-bottom border-secondary-subtle">
|
|
<div class="container">
|
|
<a class="navbar-brand d-flex align-items-center gap-2" href="/">
|
|
<span class="brand-logo">
|
|
<img src="<?= h($logoPath) ?>" alt="Logo de Lili Records Radio" width="44" height="44">
|
|
</span>
|
|
<span>
|
|
<span class="d-block fw-semibold brand-title">Lili Records Radio</span>
|
|
<span class="brand-subtitle">Admin</span>
|
|
</span>
|
|
</a>
|
|
<div class="d-flex gap-2 ms-auto">
|
|
<a class="btn btn-sm btn-outline-light" href="/">Ver sitio</a>
|
|
<a class="btn btn-sm btn-light" href="/admin.php?type=program&mode=new">Nuevo programa</a>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<main class="section-block admin-shell">
|
|
<div class="container">
|
|
<div class="section-heading d-flex flex-wrap align-items-end justify-content-between gap-3 mb-4">
|
|
<div>
|
|
<span class="section-label">Panel simple</span>
|
|
<h1 class="section-title mb-0">Gestiona shows, DJs, redes y mensajes</h1>
|
|
</div>
|
|
<p class="section-copy">Primera versión funcional: listados por tipo, detalle editable y revisión básica de mensajes recibidos.</p>
|
|
</div>
|
|
|
|
<div class="row g-4 mb-4">
|
|
<div class="col-6 col-lg-3"><div class="mini-stat"><span class="mini-label">Programas</span><strong><?= h((string) $counts['program']) ?></strong></div></div>
|
|
<div class="col-6 col-lg-3"><div class="mini-stat"><span class="mini-label">DJs</span><strong><?= h((string) $counts['dj']) ?></strong></div></div>
|
|
<div class="col-6 col-lg-3"><div class="mini-stat"><span class="mini-label">Redes</span><strong><?= h((string) $counts['social']) ?></strong></div></div>
|
|
<div class="col-6 col-lg-3"><div class="mini-stat"><span class="mini-label">Mensajes</span><strong><?= h((string) $counts['message']) ?></strong></div></div>
|
|
</div>
|
|
|
|
<div class="row g-4">
|
|
<div class="col-xl-5">
|
|
<div class="panel p-4 mb-4">
|
|
<div class="d-flex flex-wrap gap-2 mb-3">
|
|
<a class="btn btn-sm <?= $type === 'program' ? 'btn-light' : 'btn-outline-light' ?>" href="/admin.php?type=program">Programas</a>
|
|
<a class="btn btn-sm <?= $type === 'dj' ? 'btn-light' : 'btn-outline-light' ?>" href="/admin.php?type=dj">DJs</a>
|
|
<a class="btn btn-sm <?= $type === 'social' ? 'btn-light' : 'btn-outline-light' ?>" href="/admin.php?type=social">Redes</a>
|
|
<a class="btn btn-sm <?= $type === 'message' ? 'btn-light' : 'btn-outline-light' ?>" href="/admin.php?type=message">Mensajes</a>
|
|
</div>
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h2 class="h5 text-white mb-0">Listado</h2>
|
|
<?php if ($type !== 'message'): ?>
|
|
<a class="btn btn-sm btn-outline-light" href="/admin.php?type=<?= h($type) ?>&mode=new">Crear nuevo</a>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<?php $source = $type === 'program' ? $programs : ($type === 'dj' ? $djs : ($type === 'social' ? $socials : $messages)); ?>
|
|
<?php if ($source === []): ?>
|
|
<div class="empty-card">Sin registros todavía.</div>
|
|
<?php else: ?>
|
|
<div class="admin-list">
|
|
<?php foreach ($source as $item): ?>
|
|
<a class="admin-item <?= $selectedId === (int) $item['id'] ? 'active' : '' ?>" href="/admin.php?type=<?= h($type) ?>&id=<?= h((string) $item['id']) ?>">
|
|
<div class="d-flex justify-content-between gap-3 align-items-start">
|
|
<div>
|
|
<strong class="text-white d-block"><?= h($item['title']) ?></strong>
|
|
<span class="small text-secondary"><?= h($item['subtitle'] ?: ($type === 'program' ? weekday_name((int) $item['weekday']) . ' · ' . time_label($item['start_time']) : 'Sin subtítulo')) ?></span>
|
|
</div>
|
|
<span class="soft-badge"><?= h($item['status']) ?></span>
|
|
</div>
|
|
<?php if (!empty($item['meta_value']) && $type === 'message'): ?>
|
|
<p class="small text-secondary-emphasis mb-0 mt-2">Pedido: <?= h($item['meta_value']) ?></p>
|
|
<?php elseif (!empty($item['body'])): ?>
|
|
<p class="small text-secondary-emphasis mb-0 mt-2"><?= h(strlen($item['body']) > 120 ? substr($item['body'], 0, 117) . '…' : $item['body']) ?></p>
|
|
<?php endif; ?>
|
|
</a>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-xl-7">
|
|
<div class="panel p-4 p-lg-5">
|
|
<div class="d-flex justify-content-between align-items-start gap-3 mb-4">
|
|
<div>
|
|
<span class="section-label">Detalle</span>
|
|
<h2 class="h3 text-white mb-1"><?= $type === 'message' ? 'Revisar mensaje' : (($mode === 'new' || !$entry) ? 'Crear contenido' : 'Editar contenido') ?></h2>
|
|
<p class="text-secondary mb-0"><?= $type === 'message' ? 'Marca el estado del mensaje y consulta el pedido.' : 'Completa los campos mínimos para actualizar la experiencia pública.' ?></p>
|
|
</div>
|
|
</div>
|
|
|
|
<form method="post" class="row g-3">
|
|
<input type="hidden" name="action" value="save_entry">
|
|
<input type="hidden" name="entry_type" value="<?= h($type) ?>">
|
|
<input type="hidden" name="id" value="<?= h((string) ($entry['id'] ?? '')) ?>">
|
|
|
|
<div class="col-md-7">
|
|
<label class="form-label" for="title">Título</label>
|
|
<input class="form-control" id="title" name="title" maxlength="160" required value="<?= h($entry['title'] ?? '') ?>" <?= $type === 'message' ? '' : '' ?>>
|
|
</div>
|
|
<div class="col-md-5">
|
|
<label class="form-label" for="status">Estado</label>
|
|
<select class="form-select" id="status" name="status">
|
|
<?php foreach ($type === 'message' ? ['new', 'reviewed'] : ['published', 'draft'] as $status): ?>
|
|
<option value="<?= h($status) ?>" <?= ($entry['status'] ?? ($type === 'message' ? 'new' : 'published')) === $status ? 'selected' : '' ?>><?= h($status) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<label class="form-label" for="subtitle"><?= $type === 'message' ? 'Email' : 'Subtítulo' ?></label>
|
|
<input class="form-control" id="subtitle" name="subtitle" maxlength="160" value="<?= h($entry['subtitle'] ?? '') ?>">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label" for="meta_value"><?= $type === 'message' ? 'Pedido musical' : 'Etiqueta interna' ?></label>
|
|
<input class="form-control" id="meta_value" name="meta_value" maxlength="255" value="<?= h($entry['meta_value'] ?? '') ?>">
|
|
</div>
|
|
|
|
<?php if ($type === 'program'): ?>
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="weekday">Día</label>
|
|
<select class="form-select" id="weekday" name="weekday" required>
|
|
<option value="">Selecciona</option>
|
|
<?php for ($day = 1; $day <= 7; $day++): ?>
|
|
<option value="<?= $day ?>" <?= (string) ($entry['weekday'] ?? '') === (string) $day ? 'selected' : '' ?>><?= h(weekday_name($day)) ?></option>
|
|
<?php endfor; ?>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="start_time">Inicio</label>
|
|
<input class="form-control" id="start_time" name="start_time" type="time" value="<?= h(!empty($entry['start_time']) ? substr((string) $entry['start_time'], 0, 5) : '') ?>" required>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="end_time">Fin</label>
|
|
<input class="form-control" id="end_time" name="end_time" type="time" value="<?= h(!empty($entry['end_time']) ? substr((string) $entry['end_time'], 0, 5) : '') ?>" required>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($type !== 'message'): ?>
|
|
<div class="col-md-8">
|
|
<label class="form-label" for="meta_url">URL principal</label>
|
|
<input class="form-control" id="meta_url" name="meta_url" maxlength="255" placeholder="https://..." value="<?= h($entry['meta_url'] ?? '') ?>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="sort_order">Orden</label>
|
|
<input class="form-control" id="sort_order" name="sort_order" type="number" value="<?= h((string) ($entry['sort_order'] ?? 0)) ?>">
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="col-md-12">
|
|
<label class="form-label" for="sort_order">Orden</label>
|
|
<input class="form-control" id="sort_order" name="sort_order" type="number" value="<?= h((string) ($entry['sort_order'] ?? 0)) ?>">
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div class="col-12">
|
|
<label class="form-label" for="body"><?= $type === 'message' ? 'Mensaje completo' : 'Descripción' ?></label>
|
|
<textarea class="form-control" id="body" name="body" rows="6"><?= h($entry['body'] ?? '') ?></textarea>
|
|
</div>
|
|
|
|
<div class="col-12 d-flex flex-wrap gap-3 pt-2">
|
|
<button class="btn btn-light" type="submit"><?= $type === 'message' ? 'Guardar revisión' : 'Guardar cambios' ?></button>
|
|
<a class="btn btn-outline-light" href="/admin.php?type=<?= h($type) ?>&mode=new">Limpiar formulario</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<?php if ($flash): ?>
|
|
<div class="toast-container position-fixed bottom-0 end-0 p-3">
|
|
<div class="toast radio-toast align-items-center text-bg-dark border-0" id="statusToast" role="status" aria-live="polite" aria-atomic="true" data-autoshow="true">
|
|
<div class="d-flex">
|
|
<div class="toast-body">
|
|
<span class="toast-indicator <?= h($flash['type']) ?>"></span>
|
|
<?= h($flash['message']) ?>
|
|
</div>
|
|
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Cerrar"></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
|
|
<script src="/assets/js/main.js?v=<?= h($assetVersion) ?>"></script>
|
|
</body>
|
|
</html>
|