feat(backoffice): exportação CSV nas listagens, com audit
Novo helper csv_export() (config/functions.php): CSV Excel-friendly (BOM UTF-8 + ';') e cada exportação fica no audit_log (RGPD: exportar dados pessoais tem de ser rastreável). Botão "Exportar CSV" em todas as listagens do backoffice: equipa, eventos, galerias, ginásios, horário, mensalidades, modalidades, horário de funcionamento e lixeira (pedidos de inscrição e aulas experimentais já tinham). Fecha a metade "exportação com permissões e audit" do item 6.9 (a única parte que o relatório considera ganho real à escala atual). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
33158381bb
commit
116817e2de
10 files changed
+122
-4
No files matched your search
@@ -15,6 +15,14 @@ if (isset($_POST['delete']) && is_numeric($_POST['delete'])) {
|
||||
|
||||
$items = db_events_list($pdo, true); // include soft-deleted so they can be restored
|
||||
|
||||
if (isset($_GET['csv'])) {
|
||||
$rows = [];
|
||||
foreach ($items as $item) {
|
||||
$rows[] = [$item['title'], $item['summary'] ?? '', format_date_pt((string) $item['publish_from']), format_date_pt((string) $item['publish_until']), events_status($item), db_event_leads_count($pdo, (int) $item['e_id'])];
|
||||
}
|
||||
csv_export('event', 'eventos', 'Exportação CSV de eventos', ['Evento', 'Resumo', 'Início', 'Fim', 'Estado', 'Leads'], $rows);
|
||||
}
|
||||
|
||||
// Status badge styling + label (PT).
|
||||
$badge = [
|
||||
'active' => ['bg-success', 'Ativo'],
|
||||
@@ -30,7 +38,10 @@ include __DIR__ . '/../../../includes/header.php';
|
||||
<div class="container">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 class="h3 mb-0">Eventos</h1>
|
||||
<a class="btn btn-primary" href="<?= base_url('views/admin/events/create.php') ?>"><i class="bi bi-plus-lg me-1"></i>Adicionar Evento</a>
|
||||
<div class="d-flex gap-2">
|
||||
<a class="btn btn-outline-secondary" href="?csv=1"><i class="bi bi-filetype-csv me-1"></i>Exportar CSV</a>
|
||||
<a class="btn btn-primary" href="<?= base_url('views/admin/events/create.php') ?>"><i class="bi bi-plus-lg me-1"></i>Adicionar Evento</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm"><div class="card-body p-0">
|
||||
|
||||
@@ -26,6 +26,15 @@ if (isset($_POST['optimize_all'])) {
|
||||
}
|
||||
|
||||
$galleries = db_gallery_list($pdo);
|
||||
|
||||
if (isset($_GET['csv'])) {
|
||||
$rows = [];
|
||||
foreach ($galleries as $g) {
|
||||
$rows[] = [$g['name']];
|
||||
}
|
||||
csv_export('gallery', 'galerias', 'Exportação CSV de galerias', ['Nome'], $rows);
|
||||
}
|
||||
|
||||
$page_title = 'Galerias';
|
||||
include __DIR__ . '/../../../includes/header.php';
|
||||
?>
|
||||
@@ -33,6 +42,7 @@ include __DIR__ . '/../../../includes/header.php';
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 class="h3 mb-0">Galerias</h1>
|
||||
<div class="d-flex gap-2">
|
||||
<a class="btn btn-outline-secondary" href="?csv=1"><i class="bi bi-filetype-csv me-1"></i>Exportar CSV</a>
|
||||
<form method="POST" class="mb-0">
|
||||
<?= csrf_field() ?>
|
||||
<button type="submit" name="optimize_all" value="1" class="btn btn-outline-success"
|
||||
|
||||
@@ -17,6 +17,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
|
||||
$gyms = db_gyms_list($pdo, true);
|
||||
|
||||
if (isset($_GET['csv'])) {
|
||||
$rows = [];
|
||||
foreach ($gyms as $g) {
|
||||
$rows[] = [$g['name'], $g['locality'] ?? '', $g['slug'] ?? '', $g['deleted_at'] ? 'Apagado' : ($g['is_published'] ? 'Publicado' : 'Não publicado')];
|
||||
}
|
||||
csv_export('gym', 'ginasios', 'Exportação CSV de ginásios', ['Ginásio', 'Localidade', 'Slug', 'Estado'], $rows);
|
||||
}
|
||||
|
||||
$editing = admin_gym_id();
|
||||
$page_title = 'Ginásios';
|
||||
include __DIR__ . '/../../../includes/header.php';
|
||||
@@ -24,7 +33,10 @@ include __DIR__ . '/../../../includes/header.php';
|
||||
<div class="container">
|
||||
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-3">
|
||||
<h1 class="h3 mb-0">Ginásios</h1>
|
||||
<a class="btn btn-primary" href="<?= base_url('views/admin/gyms/create.php') ?>"><i class="bi bi-plus-lg me-1"></i>Adicionar ginásio</a>
|
||||
<div class="d-flex gap-2">
|
||||
<a class="btn btn-outline-secondary" href="?csv=1"><i class="bi bi-filetype-csv me-1"></i>Exportar CSV</a>
|
||||
<a class="btn btn-primary" href="<?= base_url('views/admin/gyms/create.php') ?>"><i class="bi bi-plus-lg me-1"></i>Adicionar ginásio</a>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-muted small">Cada ginásio usa as mesmas secções do site com o seu próprio conteúdo (equipa, modalidades e horário, mensalidades, contactos…). O conteúdo que editas nas outras páginas é o do ginásio escolhido no topo («A editar»).</p>
|
||||
|
||||
|
||||
@@ -14,6 +14,15 @@ if (isset($_POST['delete']) && is_numeric($_POST['delete'])) {
|
||||
}
|
||||
|
||||
$items = db_horario_list($pdo);
|
||||
|
||||
if (isset($_GET['csv'])) {
|
||||
$rows = [];
|
||||
foreach ($items as $item) {
|
||||
$rows[] = [$item['modalidade_nome'], $item['dia_semana'], $item['hora_inicio_fmt'], $item['hora_fim_fmt'], $item['local'] ?? ''];
|
||||
}
|
||||
csv_export('horario', 'horario', 'Exportação CSV do horário de aulas', ['Modalidade', 'Dia', 'Início', 'Fim', 'Sala'], $rows);
|
||||
}
|
||||
|
||||
$page_title = 'Horário de Aulas';
|
||||
include __DIR__ . '/../../../includes/header.php';
|
||||
|
||||
@@ -23,6 +32,7 @@ $dias = ['segunda'=>'Segunda','terça'=>'Terça','quarta'=>'Quarta','quinta'=>'Q
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 class="h3 mb-0">Horário de Aulas</h1>
|
||||
<div class="d-flex gap-2">
|
||||
<a class="btn btn-outline-secondary" href="?csv=1"><i class="bi bi-filetype-csv me-1"></i>Exportar CSV</a>
|
||||
<a class="btn btn-outline-secondary" href="<?= base_url('views/admin/horario/instagram.php') ?>"><i class="bi bi-instagram me-1"></i>Instagram</a>
|
||||
<a class="btn btn-outline-secondary" href="<?= base_url('views/admin/horario/calendar.php') ?>"><i class="bi bi-calendar3 me-1"></i>Calendário</a>
|
||||
<a class="btn btn-primary" href="<?= base_url('views/admin/horario/create.php') ?>"><i class="bi bi-plus-lg me-1"></i>Adicionar Aula</a>
|
||||
|
||||
@@ -18,6 +18,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
|
||||
$items = db_memberships_list($pdo, true);
|
||||
|
||||
if (isset($_GET['csv'])) {
|
||||
$rows = [];
|
||||
foreach ($items as $m) {
|
||||
$rows[] = [$m['name'], $m['subtitle'] ?? '', membership_price_label($m), $m['deleted_at'] ? 'Apagada' : ($m['is_active'] ? 'Visível' : 'Oculta'), $m['item_order']];
|
||||
}
|
||||
csv_export('membership', 'mensalidades', 'Exportação CSV de mensalidades', ['Nome', 'Subtítulo', 'Preço', 'Estado', 'Ordem'], $rows);
|
||||
}
|
||||
|
||||
$new_leads = db_membership_leads_count_new($pdo);
|
||||
$page_title = 'Mensalidades';
|
||||
include __DIR__ . '/../../../includes/header.php';
|
||||
@@ -26,6 +35,7 @@ include __DIR__ . '/../../../includes/header.php';
|
||||
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-4">
|
||||
<h1 class="h3 mb-0">Mensalidades</h1>
|
||||
<div class="d-flex gap-2">
|
||||
<a class="btn btn-outline-secondary" href="?csv=1"><i class="bi bi-filetype-csv me-1"></i>Exportar CSV</a>
|
||||
<a class="btn btn-outline-primary" href="<?= base_url('views/admin/memberships/leads.php') ?>">
|
||||
<i class="bi bi-inbox me-1"></i>Pedidos de inscrição<?php if ($new_leads): ?> <span class="badge bg-danger"><?= $new_leads ?></span><?php endif; ?>
|
||||
</a>
|
||||
|
||||
@@ -25,6 +25,15 @@ if (isset($_POST['optimize'])) {
|
||||
}
|
||||
|
||||
$items = db_modalidades_list($pdo);
|
||||
|
||||
if (isset($_GET['csv'])) {
|
||||
$rows = [];
|
||||
foreach ($items as $item) {
|
||||
$rows[] = [$item['nome'], $item['cor'] ?? '', $item['item_order']];
|
||||
}
|
||||
csv_export('modalidade', 'modalidades', 'Exportação CSV de modalidades', ['Nome', 'Cor', 'Ordem'], $rows);
|
||||
}
|
||||
|
||||
$page_title = 'Modalidades';
|
||||
include __DIR__ . '/../../../includes/header.php';
|
||||
?>
|
||||
@@ -32,6 +41,7 @@ include __DIR__ . '/../../../includes/header.php';
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 class="h3 mb-0">Modalidades</h1>
|
||||
<div class="d-flex gap-2">
|
||||
<a class="btn btn-outline-secondary" href="?csv=1"><i class="bi bi-filetype-csv me-1"></i>Exportar CSV</a>
|
||||
<form method="POST" class="mb-0">
|
||||
<?= csrf_field() ?>
|
||||
<button type="submit" name="optimize" value="1" class="btn btn-outline-success"
|
||||
|
||||
@@ -23,6 +23,15 @@ if (isset($_POST['optimize'])) {
|
||||
}
|
||||
|
||||
$items = db_team_list($pdo);
|
||||
|
||||
if (isset($_GET['csv'])) {
|
||||
$rows = [];
|
||||
foreach ($items as $item) {
|
||||
$rows[] = [$item['name'], $item['title'] ?? '', $item['at'] ?? '', $item['status']];
|
||||
}
|
||||
csv_export('team', 'equipa', 'Exportação CSV da equipa', ['Nome', 'Cargo', 'Área', 'Estado'], $rows);
|
||||
}
|
||||
|
||||
$page_title = 'Equipa';
|
||||
include __DIR__ . '/../../../includes/header.php';
|
||||
?>
|
||||
@@ -30,6 +39,7 @@ include __DIR__ . '/../../../includes/header.php';
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 class="h3 mb-0">Equipa</h1>
|
||||
<div class="d-flex gap-2">
|
||||
<a class="btn btn-outline-secondary" href="?csv=1"><i class="bi bi-filetype-csv me-1"></i>Exportar CSV</a>
|
||||
<form method="POST" class="mb-0">
|
||||
<?= csrf_field() ?>
|
||||
<button type="submit" name="optimize" value="1" class="btn btn-outline-success"
|
||||
|
||||
@@ -29,13 +29,25 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
|
||||
$items = trash_list($pdo);
|
||||
|
||||
if (isset($_GET['csv'])) {
|
||||
$rows = [];
|
||||
foreach ($items as $it) {
|
||||
$rows[] = [$it['label'], $it['title'], $it['deleted_at'], $it['deleted_by_name'] ?? ''];
|
||||
}
|
||||
csv_export('trash', 'lixeira', 'Exportação CSV da lixeira', ['Tipo', 'Registo', 'Apagado em', 'Por'], $rows);
|
||||
}
|
||||
|
||||
$page_title = 'Lixeira';
|
||||
include __DIR__ . '/../../../includes/header.php';
|
||||
?>
|
||||
<div class="container">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<h1 class="h3 mb-0">Lixeira</h1>
|
||||
<span class="badge bg-secondary"><?= count($items) ?></span>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span class="badge bg-secondary"><?= count($items) ?></span>
|
||||
<a class="btn btn-outline-secondary btn-sm" href="?csv=1"><i class="bi bi-filetype-csv me-1"></i>Exportar CSV</a>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-muted small mb-4">
|
||||
O que se apaga no backoffice fica aqui. Pode ser restaurado a qualquer momento;
|
||||
|
||||
@@ -14,6 +14,15 @@ if (isset($_POST['delete']) && is_numeric($_POST['delete'])) {
|
||||
}
|
||||
|
||||
$items = db_whours_list($pdo);
|
||||
|
||||
if (isset($_GET['csv'])) {
|
||||
$rows = [];
|
||||
foreach ($items as $item) {
|
||||
$rows[] = [$item['week_day'], $item['open'], $item['close']];
|
||||
}
|
||||
csv_export('work_hour', 'horario-funcionamento', 'Exportação CSV do horário de funcionamento', ['Dia', 'Abertura', 'Fecho'], $rows);
|
||||
}
|
||||
|
||||
$page_title = 'Horário de Funcionamento';
|
||||
include __DIR__ . '/../../../includes/header.php';
|
||||
|
||||
@@ -22,7 +31,10 @@ $dias = ['segunda'=>'Segunda','terça'=>'Terça','quarta'=>'Quarta','quinta'=>'Q
|
||||
<div class="container">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 class="h3 mb-0">Horário de Funcionamento</h1>
|
||||
<a class="btn btn-primary" href="<?= base_url('views/admin/work_hours/create.php') ?>"><i class="bi bi-plus-lg me-1"></i>Adicionar Dia</a>
|
||||
<div class="d-flex gap-2">
|
||||
<a class="btn btn-outline-secondary" href="?csv=1"><i class="bi bi-filetype-csv me-1"></i>Exportar CSV</a>
|
||||
<a class="btn btn-primary" href="<?= base_url('views/admin/work_hours/create.php') ?>"><i class="bi bi-plus-lg me-1"></i>Adicionar Dia</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm"><div class="card-body p-0">
|
||||
|
||||
Reference in new issue
Block a user