mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 19:19:19 +02:00
This commit introduces a complete thesis management interface and migrates the system from YAML-based storage to SQLite: Core Changes: - Add Database.php helper class with PDO connection and entity management - Add list.php for viewing all theses with filtering and sorting - Add edit.php for modifying existing thesis records - Add import.php for migrating legacy YAML data to SQLite - Add justfile with development tasks (serve, init-test-db, etc.) Documentation: - Add MIGRATION.md with complete migration guide and architecture docs - Update README.md with database setup and Just recipe instructions - Update .gitignore to exclude test databases and error logs Modified Forms: - Enhanced formulaire.php with transaction-based SQLite processing - Updated index.php with database-driven form options - Improved thanks.php to read from database views The new architecture provides: - Normalized database schema (19 tables, 2 views) - Transaction safety and referential integrity - CRUD operations for thesis management - Filtering by year, orientation, AP program, publication status - Secure file handling with metadata tracking 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
296 lines
10 KiB
PHP
296 lines
10 KiB
PHP
<?php
|
|
// List all theses in the database
|
|
session_start();
|
|
|
|
require_once __DIR__ . '/Database.php';
|
|
|
|
try {
|
|
$db = new Database();
|
|
$pdo = $db->getPDO();
|
|
|
|
// Get filter parameters
|
|
$searchQuery = isset($_GET['search']) ? trim($_GET['search']) : '';
|
|
$yearFilter = isset($_GET['year']) ? intval($_GET['year']) : null;
|
|
$orientationFilter = isset($_GET['orientation']) ? intval($_GET['orientation']) : null;
|
|
|
|
// Build query
|
|
$sql = "SELECT
|
|
t.id, t.identifier, t.title, t.subtitle, t.year,
|
|
o.name as orientation,
|
|
ap.name as ap_program,
|
|
GROUP_CONCAT(DISTINCT a.name) as authors,
|
|
t.submitted_at,
|
|
t.is_published
|
|
FROM theses t
|
|
LEFT JOIN orientations o ON t.orientation_id = o.id
|
|
LEFT JOIN ap_programs ap ON t.ap_program_id = ap.id
|
|
LEFT JOIN thesis_authors ta ON t.id = ta.thesis_id
|
|
LEFT JOIN authors a ON ta.author_id = a.id
|
|
WHERE 1=1";
|
|
|
|
$params = [];
|
|
|
|
if ($searchQuery) {
|
|
$sql .= " AND (t.title LIKE ? OR t.subtitle LIKE ? OR a.name LIKE ?)";
|
|
$searchParam = "%$searchQuery%";
|
|
$params[] = $searchParam;
|
|
$params[] = $searchParam;
|
|
$params[] = $searchParam;
|
|
}
|
|
|
|
if ($yearFilter) {
|
|
$sql .= " AND t.year = ?";
|
|
$params[] = $yearFilter;
|
|
}
|
|
|
|
if ($orientationFilter) {
|
|
$sql .= " AND t.orientation_id = ?";
|
|
$params[] = $orientationFilter;
|
|
}
|
|
|
|
$sql .= " GROUP BY t.id ORDER BY t.year DESC, t.submitted_at DESC";
|
|
|
|
$stmt = $pdo->prepare($sql);
|
|
$stmt->execute($params);
|
|
$theses = $stmt->fetchAll();
|
|
|
|
// Get unique years for filter
|
|
$yearsStmt = $pdo->query("SELECT DISTINCT year FROM theses ORDER BY year DESC");
|
|
$years = $yearsStmt->fetchAll(PDO::FETCH_COLUMN);
|
|
|
|
// Get orientations for filter
|
|
$orientations = $db->getAllOrientations();
|
|
|
|
} catch (Exception $e) {
|
|
error_log("Error loading theses list: " . $e->getMessage());
|
|
die("Erreur lors du chargement de la liste.");
|
|
}
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Liste des TFE - Post-ERG</title>
|
|
<link rel="stylesheet" href="assets/normalize.css">
|
|
<link rel="stylesheet" href="https://raw.githack.com/waldyrious/downstyler/master/downstyler.css" />
|
|
<link rel="shortcut icon" href="assets/icon.svg" type="image/svg">
|
|
<style>
|
|
.filters {
|
|
background: #f5f5f5;
|
|
padding: 1rem;
|
|
margin-bottom: 2rem;
|
|
border-radius: 4px;
|
|
}
|
|
.filters form {
|
|
display: flex;
|
|
gap: 1rem;
|
|
flex-wrap: wrap;
|
|
align-items: end;
|
|
}
|
|
.filters fieldset {
|
|
margin: 0;
|
|
padding: 0;
|
|
border: none;
|
|
min-width: 200px;
|
|
}
|
|
.thesis-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
}
|
|
.thesis-table th,
|
|
.thesis-table td {
|
|
padding: 0.75rem;
|
|
text-align: left;
|
|
border-bottom: 1px solid #ddd;
|
|
}
|
|
.thesis-table th {
|
|
background: #f0f0f0;
|
|
font-weight: bold;
|
|
}
|
|
.thesis-table tr:hover {
|
|
background: #f9f9f9;
|
|
}
|
|
.thesis-title {
|
|
font-weight: bold;
|
|
}
|
|
.thesis-subtitle {
|
|
font-style: italic;
|
|
color: #666;
|
|
font-size: 0.9em;
|
|
}
|
|
.status-badge {
|
|
display: inline-block;
|
|
padding: 0.25rem 0.5rem;
|
|
border-radius: 3px;
|
|
font-size: 0.85em;
|
|
}
|
|
.status-pending {
|
|
background: #ffd700;
|
|
color: #000;
|
|
}
|
|
.status-published {
|
|
background: #90ee90;
|
|
color: #000;
|
|
}
|
|
.actions {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
}
|
|
.btn {
|
|
padding: 0.35rem 0.75rem;
|
|
border-radius: 3px;
|
|
text-decoration: none;
|
|
font-size: 0.9em;
|
|
display: inline-block;
|
|
}
|
|
.btn-view {
|
|
background: #4a90e2;
|
|
color: white;
|
|
}
|
|
.btn-edit {
|
|
background: #f39c12;
|
|
color: white;
|
|
}
|
|
.stats {
|
|
display: flex;
|
|
gap: 2rem;
|
|
margin-bottom: 2rem;
|
|
flex-wrap: wrap;
|
|
}
|
|
.stat-card {
|
|
background: #f5f5f5;
|
|
padding: 1rem;
|
|
border-radius: 4px;
|
|
min-width: 150px;
|
|
}
|
|
.stat-number {
|
|
font-size: 2em;
|
|
font-weight: bold;
|
|
color: #4a90e2;
|
|
}
|
|
.stat-label {
|
|
color: #666;
|
|
font-size: 0.9em;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<header>
|
|
<h1>Liste des TFE</h1>
|
|
<nav>
|
|
<a href="index.php">← Nouveau TFE</a> |
|
|
<a href="import.php">📥 Importer CSV</a>
|
|
</nav>
|
|
</header>
|
|
|
|
<main>
|
|
<div class="stats">
|
|
<div class="stat-card">
|
|
<div class="stat-number"><?php echo count($theses); ?></div>
|
|
<div class="stat-label">TFE total</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-number"><?php echo count(array_filter($theses, fn($t) => $t['is_published'])); ?></div>
|
|
<div class="stat-label">Publiés</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-number"><?php echo count(array_filter($theses, fn($t) => !$t['is_published'])); ?></div>
|
|
<div class="stat-label">En attente</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="filters">
|
|
<form method="get" action="list.php">
|
|
<fieldset>
|
|
<label for="search">Rechercher</label>
|
|
<input type="text" id="search" name="search" placeholder="Titre, auteur..." value="<?php echo htmlspecialchars($searchQuery); ?>">
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<label for="year">Année</label>
|
|
<select id="year" name="year">
|
|
<option value="">Toutes</option>
|
|
<?php foreach ($years as $year): ?>
|
|
<option value="<?php echo $year; ?>" <?php echo $yearFilter == $year ? 'selected' : ''; ?>>
|
|
<?php echo $year; ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<label for="orientation">Orientation</label>
|
|
<select id="orientation" name="orientation">
|
|
<option value="">Toutes</option>
|
|
<?php foreach ($orientations as $orientation): ?>
|
|
<option value="<?php echo $orientation['id']; ?>" <?php echo $orientationFilter == $orientation['id'] ? 'selected' : ''; ?>>
|
|
<?php echo htmlspecialchars($orientation['name']); ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</fieldset>
|
|
|
|
<button type="submit">Filtrer</button>
|
|
<?php if ($searchQuery || $yearFilter || $orientationFilter): ?>
|
|
<a href="list.php">Réinitialiser</a>
|
|
<?php endif; ?>
|
|
</form>
|
|
</div>
|
|
|
|
<?php if (empty($theses)): ?>
|
|
<p>Aucun TFE trouvé.</p>
|
|
<?php else: ?>
|
|
<table class="thesis-table">
|
|
<thead>
|
|
<tr>
|
|
<th>ID</th>
|
|
<th>Titre</th>
|
|
<th>Auteur(s)</th>
|
|
<th>Année</th>
|
|
<th>Orientation</th>
|
|
<th>AP</th>
|
|
<th>Statut</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($theses as $thesis): ?>
|
|
<tr>
|
|
<td><?php echo htmlspecialchars($thesis['identifier'] ?? $thesis['id']); ?></td>
|
|
<td>
|
|
<div class="thesis-title"><?php echo htmlspecialchars($thesis['title']); ?></div>
|
|
<?php if ($thesis['subtitle']): ?>
|
|
<div class="thesis-subtitle"><?php echo htmlspecialchars($thesis['subtitle']); ?></div>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td><?php echo htmlspecialchars($thesis['authors'] ?? 'N/A'); ?></td>
|
|
<td><?php echo $thesis['year']; ?></td>
|
|
<td><?php echo htmlspecialchars($thesis['orientation'] ?? 'N/A'); ?></td>
|
|
<td><?php echo htmlspecialchars($thesis['ap_program'] ?? 'N/A'); ?></td>
|
|
<td>
|
|
<?php if ($thesis['is_published']): ?>
|
|
<span class="status-badge status-published">Publié</span>
|
|
<?php else: ?>
|
|
<span class="status-badge status-pending">En attente</span>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td>
|
|
<div class="actions">
|
|
<a href="thanks.php?id=<?php echo $thesis['id']; ?>" class="btn btn-view">Voir</a>
|
|
<a href="edit.php?id=<?php echo $thesis['id']; ?>" class="btn btn-edit">Éditer</a>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
<?php endif; ?>
|
|
</main>
|
|
|
|
<footer>
|
|
<p>Post-ERG - <?php echo count($theses); ?> TFE dans la base de données</p>
|
|
</footer>
|
|
</body>
|
|
</html>
|