mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 11:09:18 +02:00
Improve search page with denser header and filter layout
- Transform header into compact search bar with back button - Move filters panel underneath search bar (collapsible) - Display results in grid layout matching main.css style - Add pagination controls in main section - Show result count in footer - Prevent overflow with responsive design and proper flex constraints - Reduce padding and font sizes for denser layout
This commit is contained in:
@@ -4,43 +4,32 @@ require_once __DIR__ . '/../config/bootstrap.php';
|
|||||||
require_once APP_ROOT . '/src/Database.php';
|
require_once APP_ROOT . '/src/Database.php';
|
||||||
require_once APP_ROOT . '/src/RateLimit.php';
|
require_once APP_ROOT . '/src/RateLimit.php';
|
||||||
|
|
||||||
|
|
||||||
// Rate limiting: 30 requests per minute
|
// Rate limiting: 30 requests per minute
|
||||||
$rateLimit = new RateLimit(30, 60);
|
$rateLimit = new RateLimit(30, 60);
|
||||||
|
|
||||||
// Check rate limit
|
// Check rate limit
|
||||||
if (!$rateLimit->check()) {
|
if (!$rateLimit->check()) {
|
||||||
// Send rate limit headers
|
|
||||||
http_response_code(429);
|
http_response_code(429);
|
||||||
header('Retry-After: ' . $rateLimit->getResetTime());
|
header('Retry-After: ' . $rateLimit->getResetTime());
|
||||||
$rateLimit->sendHeaders();
|
$rateLimit->sendHeaders();
|
||||||
|
|
||||||
// Display error page
|
// Simple error page
|
||||||
include APP_ROOT . '/templates/header.php';
|
echo '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Rate Limit</title></head><body>';
|
||||||
echo '<section class="section">';
|
echo '<h1>Trop de requêtes</h1>';
|
||||||
echo ' <div class="container">';
|
echo '<p>Vous avez dépassé la limite de 30 recherches par minute. Veuillez réessayer dans ' . $rateLimit->getResetTime() . ' secondes.</p>';
|
||||||
echo ' <div class="notification is-danger">';
|
echo '</body></html>';
|
||||||
echo ' <strong>Trop de requêtes</strong><br>';
|
|
||||||
echo ' Vous avez dépassé la limite de ' . 30 . ' recherches par minute.';
|
|
||||||
echo ' <br>Veuillez réessayer dans ' . $rateLimit->getResetTime() . ' secondes.';
|
|
||||||
echo ' </div>';
|
|
||||||
echo ' </div>';
|
|
||||||
echo '</section>';
|
|
||||||
include APP_ROOT . '/templates/footer.php';
|
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send rate limit headers for successful requests
|
|
||||||
$rateLimit->sendHeaders();
|
$rateLimit->sendHeaders();
|
||||||
|
|
||||||
// Periodic cleanup (1% chance)
|
|
||||||
if (rand(1, 100) === 1) {
|
if (rand(1, 100) === 1) {
|
||||||
$rateLimit->cleanup();
|
$rateLimit->cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pagination (max 100 per page)
|
// Pagination - adjust to grid
|
||||||
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
|
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
|
||||||
$itemsPerPage = min(100, isset($_GET['per_page']) ? intval($_GET['per_page']) : 20);
|
$itemsPerPage = 12; // Default grid size (3 rows × 4 columns)
|
||||||
|
|
||||||
// Collect search parameters
|
// Collect search parameters
|
||||||
$searchParams = [];
|
$searchParams = [];
|
||||||
@@ -73,6 +62,7 @@ if (isset($_GET['is_doctoral'])) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$validationError = null;
|
$validationError = null;
|
||||||
|
$showFilters = isset($_GET['filters']) && $_GET['filters'] === 'show';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$db = Database::getInstance();
|
$db = Database::getInstance();
|
||||||
@@ -93,7 +83,6 @@ try {
|
|||||||
$languages = $db->getLanguages();
|
$languages = $db->getLanguages();
|
||||||
|
|
||||||
} catch (InvalidArgumentException $e) {
|
} catch (InvalidArgumentException $e) {
|
||||||
// Input validation error
|
|
||||||
error_log("Search validation error: " . $e->getMessage());
|
error_log("Search validation error: " . $e->getMessage());
|
||||||
$validationError = $e->getMessage();
|
$validationError = $e->getMessage();
|
||||||
$results = [];
|
$results = [];
|
||||||
@@ -107,7 +96,6 @@ try {
|
|||||||
$formats = [];
|
$formats = [];
|
||||||
$languages = [];
|
$languages = [];
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
// Database or other error
|
|
||||||
error_log("Error in search: " . $e->getMessage());
|
error_log("Error in search: " . $e->getMessage());
|
||||||
$validationError = "Une erreur est survenue lors de la recherche.";
|
$validationError = "Une erreur est survenue lors de la recherche.";
|
||||||
$results = [];
|
$results = [];
|
||||||
@@ -121,44 +109,255 @@ try {
|
|||||||
$formats = [];
|
$formats = [];
|
||||||
$languages = [];
|
$languages = [];
|
||||||
}
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Recherche - Posterg</title>
|
||||||
|
<link rel="stylesheet" href="assets/modern-normalize.css">
|
||||||
|
<link rel="stylesheet" href="assets/common.css">
|
||||||
|
<link rel="stylesheet" href="assets/main.css">
|
||||||
|
<style>
|
||||||
|
header {
|
||||||
|
padding: 1rem 2rem !important;
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
gap: 0.75rem !important;
|
||||||
|
overflow-y: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
include APP_ROOT . '/templates/header.php'; ?>
|
.search-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
width: 100%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
<section class="section">
|
.back-button {
|
||||||
<div class="container">
|
background: rgba(255, 255, 255, 0.15);
|
||||||
<h1 class="title">Rechercher un mémoire</h1>
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
|
color: white;
|
||||||
|
padding: 0.5rem 0.9rem;
|
||||||
|
border-radius: 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
transition: all 0.2s;
|
||||||
|
white-space: nowrap;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-button:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.25);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
flex: 1;
|
||||||
|
padding: 0.5rem 0.9rem;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
|
border-radius: 15px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
color: #333;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input::placeholder {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-button, .filter-button {
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
|
color: white;
|
||||||
|
padding: 0.5rem 0.9rem;
|
||||||
|
border-radius: 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
transition: all 0.2s;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-button:hover, .filter-button:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.25);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-button.active {
|
||||||
|
background: rgba(255, 255, 255, 0.3);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters-panel {
|
||||||
|
display: none;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
padding: 0.75rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters-panel.show {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1400px) {
|
||||||
|
.filters-grid {
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1000px) {
|
||||||
|
.filters-grid {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.25rem;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-label {
|
||||||
|
color: white;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-select {
|
||||||
|
padding: 0.4rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reset-button {
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
|
color: white;
|
||||||
|
padding: 0.4rem 0.8rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reset-button:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
background: rgba(255, 100, 100, 0.2);
|
||||||
|
color: white;
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.results-footer {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: white;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
height: 100%;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.results-count {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<div class="search-header">
|
||||||
|
<a href="index.php" class="back-button">← Retour</a>
|
||||||
|
|
||||||
|
<form method="GET" action="search.php" class="search-form">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="query"
|
||||||
|
class="search-input"
|
||||||
|
placeholder="Rechercher par titre, auteur, mots-clés..."
|
||||||
|
value="<?= htmlspecialchars($_GET['query'] ?? ''); ?>"
|
||||||
|
autofocus
|
||||||
|
>
|
||||||
|
<button type="submit" class="search-button">Rechercher</button>
|
||||||
|
<button type="button" class="filter-button <?= $showFilters ? 'active' : ''; ?>" onclick="toggleFilters()">
|
||||||
|
Filtres
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Hidden field to maintain filter panel state -->
|
||||||
|
<input type="hidden" name="filters" id="filters-state" value="<?= $showFilters ? 'show' : 'hide'; ?>">
|
||||||
|
|
||||||
|
<!-- Preserve other filter values as hidden fields when searching -->
|
||||||
|
<?php foreach (['year', 'orientation', 'ap_program', 'finality', 'keyword', 'format', 'language', 'is_doctoral'] as $field): ?>
|
||||||
|
<?php if (!empty($_GET[$field])): ?>
|
||||||
|
<input type="hidden" name="<?= $field; ?>" value="<?= htmlspecialchars($_GET[$field]); ?>">
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Display validation errors -->
|
|
||||||
<?php if ($validationError): ?>
|
<?php if ($validationError): ?>
|
||||||
<div class="notification is-danger">
|
<div class="error-message">
|
||||||
<strong>Erreur de validation :</strong> <?= htmlspecialchars($validationError); ?>
|
⚠ <?= htmlspecialchars($validationError); ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<!-- Search Form -->
|
<div class="filters-panel <?= $showFilters ? 'show' : ''; ?>" id="filters-panel">
|
||||||
<form method="GET" action="search.php">
|
<form method="GET" action="search.php">
|
||||||
<div class="box">
|
<!-- Preserve query when using filters -->
|
||||||
<!-- Main search query -->
|
<?php if (!empty($_GET['query'])): ?>
|
||||||
<div class="field">
|
<input type="hidden" name="query" value="<?= htmlspecialchars($_GET['query']); ?>">
|
||||||
<label class="label">Recherche libre</label>
|
<?php endif; ?>
|
||||||
<div class="control">
|
<input type="hidden" name="filters" value="show">
|
||||||
<input class="input" type="text" name="query"
|
|
||||||
placeholder="Titre, auteur, mots-clés, synopsis..."
|
|
||||||
value="<?= htmlspecialchars($_GET['query'] ?? ''); ?>">
|
|
||||||
</div>
|
|
||||||
<p class="help">Recherche dans le titre, sous-titre, synopsis, auteurs, promoteurs et mots-clés</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Advanced filters in columns -->
|
<div class="filters-grid">
|
||||||
<div class="columns is-multiline">
|
<div class="filter-group">
|
||||||
<!-- Year filter -->
|
<label class="filter-label">Année</label>
|
||||||
<div class="column is-half">
|
<select name="year" class="filter-select">
|
||||||
<div class="field">
|
<option value="">Toutes</option>
|
||||||
<label class="label">Année</label>
|
|
||||||
<div class="control">
|
|
||||||
<div class="select is-fullwidth">
|
|
||||||
<select name="year">
|
|
||||||
<option value="">Toutes les années</option>
|
|
||||||
<?php foreach ($years as $year): ?>
|
<?php foreach ($years as $year): ?>
|
||||||
<option value="<?= (int)$year; ?>" <?= (isset($_GET['year']) && $_GET['year'] == $year) ? 'selected' : ''; ?>>
|
<option value="<?= (int)$year; ?>" <?= (isset($_GET['year']) && $_GET['year'] == $year) ? 'selected' : ''; ?>>
|
||||||
<?= (int)$year; ?>
|
<?= (int)$year; ?>
|
||||||
@@ -166,18 +365,11 @@ include APP_ROOT . '/templates/header.php'; ?>
|
|||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Orientation filter -->
|
<div class="filter-group">
|
||||||
<div class="column is-half">
|
<label class="filter-label">Orientation</label>
|
||||||
<div class="field">
|
<select name="orientation" class="filter-select">
|
||||||
<label class="label">Orientation</label>
|
<option value="">Toutes</option>
|
||||||
<div class="control">
|
|
||||||
<div class="select is-fullwidth">
|
|
||||||
<select name="orientation">
|
|
||||||
<option value="">Toutes les orientations</option>
|
|
||||||
<?php foreach ($orientations as $orientation): ?>
|
<?php foreach ($orientations as $orientation): ?>
|
||||||
<option value="<?= htmlspecialchars($orientation['name']); ?>"
|
<option value="<?= htmlspecialchars($orientation['name']); ?>"
|
||||||
<?= (isset($_GET['orientation']) && $_GET['orientation'] == $orientation['name']) ? 'selected' : ''; ?>>
|
<?= (isset($_GET['orientation']) && $_GET['orientation'] == $orientation['name']) ? 'selected' : ''; ?>>
|
||||||
@@ -186,18 +378,11 @@ include APP_ROOT . '/templates/header.php'; ?>
|
|||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- AP Program filter -->
|
<div class="filter-group">
|
||||||
<div class="column is-half">
|
<label class="filter-label">AP</label>
|
||||||
<div class="field">
|
<select name="ap_program" class="filter-select">
|
||||||
<label class="label">Atelier Pratique (AP)</label>
|
<option value="">Tous</option>
|
||||||
<div class="control">
|
|
||||||
<div class="select is-fullwidth">
|
|
||||||
<select name="ap_program">
|
|
||||||
<option value="">Tous les AP</option>
|
|
||||||
<?php foreach ($apPrograms as $ap): ?>
|
<?php foreach ($apPrograms as $ap): ?>
|
||||||
<option value="<?= htmlspecialchars($ap['name']); ?>"
|
<option value="<?= htmlspecialchars($ap['name']); ?>"
|
||||||
<?= (isset($_GET['ap_program']) && $_GET['ap_program'] == $ap['name']) ? 'selected' : ''; ?>>
|
<?= (isset($_GET['ap_program']) && $_GET['ap_program'] == $ap['name']) ? 'selected' : ''; ?>>
|
||||||
@@ -206,18 +391,11 @@ include APP_ROOT . '/templates/header.php'; ?>
|
|||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Finality filter -->
|
<div class="filter-group">
|
||||||
<div class="column is-half">
|
<label class="filter-label">Finalité</label>
|
||||||
<div class="field">
|
<select name="finality" class="filter-select">
|
||||||
<label class="label">Finalité</label>
|
<option value="">Toutes</option>
|
||||||
<div class="control">
|
|
||||||
<div class="select is-fullwidth">
|
|
||||||
<select name="finality">
|
|
||||||
<option value="">Toutes les finalités</option>
|
|
||||||
<?php foreach ($finalityTypes as $finality): ?>
|
<?php foreach ($finalityTypes as $finality): ?>
|
||||||
<option value="<?= htmlspecialchars($finality['name']); ?>"
|
<option value="<?= htmlspecialchars($finality['name']); ?>"
|
||||||
<?= (isset($_GET['finality']) && $_GET['finality'] == $finality['name']) ? 'selected' : ''; ?>>
|
<?= (isset($_GET['finality']) && $_GET['finality'] == $finality['name']) ? 'selected' : ''; ?>>
|
||||||
@@ -226,18 +404,11 @@ include APP_ROOT . '/templates/header.php'; ?>
|
|||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Format filter -->
|
<div class="filter-group">
|
||||||
<div class="column is-half">
|
<label class="filter-label">Format</label>
|
||||||
<div class="field">
|
<select name="format" class="filter-select">
|
||||||
<label class="label">Format</label>
|
<option value="">Tous</option>
|
||||||
<div class="control">
|
|
||||||
<div class="select is-fullwidth">
|
|
||||||
<select name="format">
|
|
||||||
<option value="">Tous les formats</option>
|
|
||||||
<?php foreach ($formats as $format): ?>
|
<?php foreach ($formats as $format): ?>
|
||||||
<option value="<?= htmlspecialchars($format['name']); ?>"
|
<option value="<?= htmlspecialchars($format['name']); ?>"
|
||||||
<?= (isset($_GET['format']) && $_GET['format'] == $format['name']) ? 'selected' : ''; ?>>
|
<?= (isset($_GET['format']) && $_GET['format'] == $format['name']) ? 'selected' : ''; ?>>
|
||||||
@@ -246,18 +417,11 @@ include APP_ROOT . '/templates/header.php'; ?>
|
|||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Language filter -->
|
<div class="filter-group">
|
||||||
<div class="column is-half">
|
<label class="filter-label">Langue</label>
|
||||||
<div class="field">
|
<select name="language" class="filter-select">
|
||||||
<label class="label">Langue</label>
|
<option value="">Toutes</option>
|
||||||
<div class="control">
|
|
||||||
<div class="select is-fullwidth">
|
|
||||||
<select name="language">
|
|
||||||
<option value="">Toutes les langues</option>
|
|
||||||
<?php foreach ($languages as $language): ?>
|
<?php foreach ($languages as $language): ?>
|
||||||
<option value="<?= htmlspecialchars($language['name']); ?>"
|
<option value="<?= htmlspecialchars($language['name']); ?>"
|
||||||
<?= (isset($_GET['language']) && $_GET['language'] == $language['name']) ? 'selected' : ''; ?>>
|
<?= (isset($_GET['language']) && $_GET['language'] == $language['name']) ? 'selected' : ''; ?>>
|
||||||
@@ -266,18 +430,11 @@ include APP_ROOT . '/templates/header.php'; ?>
|
|||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Keyword filter -->
|
<div class="filter-group">
|
||||||
<div class="column is-full">
|
<label class="filter-label">Mot-clé</label>
|
||||||
<div class="field">
|
<select name="keyword" class="filter-select">
|
||||||
<label class="label">Mot-clé</label>
|
<option value="">Tous</option>
|
||||||
<div class="control">
|
|
||||||
<div class="select is-fullwidth">
|
|
||||||
<select name="keyword">
|
|
||||||
<option value="">Tous les mots-clés</option>
|
|
||||||
<?php foreach ($keywords as $keyword): ?>
|
<?php foreach ($keywords as $keyword): ?>
|
||||||
<option value="<?= htmlspecialchars($keyword['keyword']); ?>"
|
<option value="<?= htmlspecialchars($keyword['keyword']); ?>"
|
||||||
<?= (isset($_GET['keyword']) && $_GET['keyword'] == $keyword['keyword']) ? 'selected' : ''; ?>>
|
<?= (isset($_GET['keyword']) && $_GET['keyword'] == $keyword['keyword']) ? 'selected' : ''; ?>>
|
||||||
@@ -286,133 +443,97 @@ include APP_ROOT . '/templates/header.php'; ?>
|
|||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Thesis type filter -->
|
<div class="filter-group">
|
||||||
<div class="column is-full">
|
<label class="filter-label">Type</label>
|
||||||
<div class="field">
|
<select name="is_doctoral" class="filter-select">
|
||||||
<label class="label">Type</label>
|
<option value="">Tous</option>
|
||||||
<div class="control">
|
|
||||||
<div class="select is-fullwidth">
|
|
||||||
<select name="is_doctoral">
|
|
||||||
<option value="">TFE et Thèses doctorales</option>
|
|
||||||
<option value="0" <?= (isset($_GET['is_doctoral']) && $_GET['is_doctoral'] == '0') ? 'selected' : ''; ?>>
|
<option value="0" <?= (isset($_GET['is_doctoral']) && $_GET['is_doctoral'] == '0') ? 'selected' : ''; ?>>
|
||||||
TFE uniquement
|
TFE uniquement
|
||||||
</option>
|
</option>
|
||||||
<option value="1" <?= (isset($_GET['is_doctoral']) && $_GET['is_doctoral'] == '1') ? 'selected' : ''; ?>>
|
<option value="1" <?= (isset($_GET['is_doctoral']) && $_GET['is_doctoral'] == '1') ? 'selected' : ''; ?>>
|
||||||
Thèses doctorales uniquement
|
Thèses doctorales
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Action buttons -->
|
<div class="filter-actions">
|
||||||
<div class="field is-grouped">
|
<button type="submit" class="search-button">Appliquer</button>
|
||||||
<div class="control">
|
<a href="search.php?filters=show" class="reset-button">Réinitialiser</a>
|
||||||
<button type="submit" class="button is-link">Rechercher</button>
|
|
||||||
</div>
|
|
||||||
<div class="control">
|
|
||||||
<a href="search.php" class="button is-light">Réinitialiser</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<!-- Search results -->
|
|
||||||
<?php if (!empty($searchParams)): ?>
|
|
||||||
<div class="notification is-info is-light">
|
|
||||||
<strong><?= (int)$totalItems; ?></strong> résultat<?= $totalItems > 1 ? 's' : ''; ?> trouvé<?= $totalItems > 1 ? 's' : ''; ?>
|
|
||||||
</div>
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div class="cards-container">
|
||||||
<?php if (count($results) > 0): ?>
|
<?php if (count($results) > 0): ?>
|
||||||
<div class="columns is-multiline">
|
|
||||||
<?php foreach ($results as $item): ?>
|
<?php foreach ($results as $item): ?>
|
||||||
<div class="column is-one-fifth">
|
|
||||||
<a href="tfe.php?id=<?= (int)$item['id']; ?>" class="card-link">
|
<a href="tfe.php?id=<?= (int)$item['id']; ?>" class="card-link">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<?php
|
|
||||||
// Get cover image from thesis_files if available
|
|
||||||
$coverImage = null;
|
|
||||||
if (!empty($item['id'])) {
|
|
||||||
$files = $db->getThesisFiles($item['id']);
|
|
||||||
foreach ($files as $file) {
|
|
||||||
// Look for dedicated cover file (type = 'cover')
|
|
||||||
if ($file['file_type'] === 'cover') {
|
|
||||||
$coverImage = '/media.php?path=' . urlencode($file['file_path']);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
<?php if ($coverImage): ?>
|
|
||||||
<div class="card-image">
|
|
||||||
<figure class="image">
|
|
||||||
<img src="<?= htmlspecialchars($coverImage); ?>" alt="Image preview">
|
|
||||||
</figure>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<h4 class="title is-4">
|
<h3 class="title"><?= htmlspecialchars($item['title']); ?></h3>
|
||||||
<?= htmlspecialchars($item['title']); ?>
|
<p class="authors"><?= htmlspecialchars($item['authors'] ?? 'Auteur inconnu'); ?></p>
|
||||||
</h4>
|
<p class="year"><?= htmlspecialchars($item['year']); ?></p>
|
||||||
<h2 class="subtitle">
|
<?php if (!empty($item['orientation'])): ?>
|
||||||
<?= htmlspecialchars($item['authors'] ?? 'Auteur inconnu'); ?>
|
<div class="tags">
|
||||||
</h2>
|
<span class="tag"><?= htmlspecialchars($item['orientation']); ?></span>
|
||||||
<h3 class="tag title is-6 is-link is-light">
|
</div>
|
||||||
<?= htmlspecialchars($item['year']); ?>
|
<?php endif; ?>
|
||||||
</h3>
|
|
||||||
<p class="block content">
|
|
||||||
<?php
|
|
||||||
$excerpt_length = 150;
|
|
||||||
$synopsis = $item['synopsis'] ?? '';
|
|
||||||
$description_excerpt = strlen($synopsis) > $excerpt_length
|
|
||||||
? substr($synopsis, 0, $excerpt_length) . '...'
|
|
||||||
: $synopsis;
|
|
||||||
?>
|
|
||||||
<?= htmlspecialchars($description_excerpt); ?>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
<?php elseif (!empty($searchParams)): ?>
|
||||||
|
<div style="grid-column: 1 / -1; display: flex; align-items: center; justify-content: center; color: white; font-size: 1.2rem;">
|
||||||
|
Aucun résultat trouvé pour cette recherche.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Pagination -->
|
|
||||||
<?php if ($totalPages > 1): ?>
|
|
||||||
<nav class="pagination is-centered" role="navigation" aria-label="pagination">
|
|
||||||
<?php if ($page > 1): ?>
|
|
||||||
<a href="?<?= http_build_query(array_merge($_GET, ['page' => $page - 1])); ?>" class="pagination-previous">Précédent</a>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if ($page < $totalPages): ?>
|
|
||||||
<a href="?<?= http_build_query(array_merge($_GET, ['page' => $page + 1])); ?>" class="pagination-next">Suivant</a>
|
|
||||||
<?php endif; ?>
|
|
||||||
<ul class="pagination-list">
|
|
||||||
<?php for ($i = 1; $i <= $totalPages; $i++): ?>
|
|
||||||
<li>
|
|
||||||
<a href="?<?= http_build_query(array_merge($_GET, ['page' => $i])); ?>"
|
|
||||||
class="pagination-link <?= $i === $page ? 'is-current' : ''; ?>">
|
|
||||||
<?= (int)$i; ?>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<?php endfor; ?>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<div class="notification">
|
<div style="grid-column: 1 / -1; display: flex; align-items: center; justify-content: center; color: white; font-size: 1.2rem;">
|
||||||
Utilisez le formulaire ci-dessus pour rechercher des mémoires.
|
Utilisez la barre de recherche pour trouver des mémoires.
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
|
||||||
|
|
||||||
<?php include APP_ROOT . '/templates/footer.php'; ?>
|
<?php if ($totalPages > 1): ?>
|
||||||
|
<div class="pagination">
|
||||||
|
<a href="?<?= http_build_query(array_merge($_GET, ['page' => max(1, $page - 1)])); ?>"
|
||||||
|
class="pagination-btn <?= $page <= 1 ? 'disabled' : ''; ?>">
|
||||||
|
←
|
||||||
|
</a>
|
||||||
|
<div class="pagination-info">
|
||||||
|
<span class="page-current"><?= $page; ?></span>
|
||||||
|
<span class="page-separator">/</span>
|
||||||
|
<span class="page-total"><?= $totalPages; ?></span>
|
||||||
|
</div>
|
||||||
|
<a href="?<?= http_build_query(array_merge($_GET, ['page' => min($totalPages, $page + 1)])); ?>"
|
||||||
|
class="pagination-btn <?= $page >= $totalPages ? 'disabled' : ''; ?>">
|
||||||
|
→
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<div class="results-footer">
|
||||||
|
<span class="results-count"><?= (int)$totalItems; ?></span>
|
||||||
|
<span>résultat<?= $totalItems > 1 ? 's' : ''; ?> trouvé<?= $totalItems > 1 ? 's' : ''; ?></span>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function toggleFilters() {
|
||||||
|
const panel = document.getElementById('filters-panel');
|
||||||
|
const button = document.querySelector('.filter-button');
|
||||||
|
const state = document.getElementById('filters-state');
|
||||||
|
|
||||||
|
panel.classList.toggle('show');
|
||||||
|
button.classList.toggle('active');
|
||||||
|
|
||||||
|
// Update hidden field
|
||||||
|
state.value = panel.classList.contains('show') ? 'show' : 'hide';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
[1770895784,1770895787]
|
[1770898865]
|
||||||
Reference in New Issue
Block a user