diff --git a/TODO.md b/TODO.md index de27e71..6ead946 100644 --- a/TODO.md +++ b/TODO.md @@ -1,132 +1,31 @@ -# Post-ERG – Dependency & Refactoring Analysis +# TODO -## Summary +## Styling Redesign (matching design images) -The project has **zero external PHP library dependencies** (no Composer, no vendor/). -All PHP logic relies exclusively on standard PHP extensions: PDO/SQLite, `finfo`, -`session_*`, `password_verify`, `hash_equals`, `random_bytes`, `json_*`, SPL iterators. -There is one vendored CSS file (`modern-normalize.min.css`, 1 file, 8 lines). +- [x] Redesign shared nav bar (purple gradient top, flat, POSTERG / RÉPERTOIRE / À PROPOS) +- [x] Redesign shared search bar (full-width, icon, bottom border only, white bg) +- [x] Rewrite `common.css` (nav + search bar components) +- [x] Rewrite `main.css` (home page — white bg, media card grid, label below) +- [x] Rewrite `search.css` (répertoire index — 4-col ANNÉES/CATÉGORIES/ÉTUDIANTES/MOTS-CLÉS) +- [x] Rewrite `tfe.css` (TFE page — 2-col, large author/title left, media right) +- [x] Add `apropos.css` (À Propos — 2-col, large monospace text) +- [x] Rewrite `admin.css` (dark bg, purple gradient nav, bottom-border-only form inputs) +- [x] Update `templates/nav.php` (new shared nav partial) +- [x] Update `templates/search-bar.php` (new shared search bar partial) +- [x] Rewrite `public/index.php` (home page with new layout) +- [x] Rewrite `public/search.php` (répertoire index view + search results view) +- [x] Rewrite `public/tfe.php` (individual TFE page) +- [x] Create `public/apropos.php` (À Propos page) +- [x] Rewrite `templates/admin/head.php` (admin nav) +- [x] Rewrite `templates/admin/footer.php` (clean close) +- [x] Rewrite `public/admin/add.php` (form with row layout) +- [x] Rewrite `public/admin/index.php` (dark table) +- [x] Rewrite `public/admin/edit.php` (form with row layout) +- [x] Rewrite `public/admin/login.php` (centered dark login box) +- [x] Rewrite `public/admin/thanks.php` (dark info cards) +- [x] Rewrite `public/admin/import.php` (clean dark form) -The only real problems are **internal structural bugs** and **dead code paths**, not -third-party dependencies. The tasks below are ordered from critical to nice-to-have. +## Pending ---- - -## Critical Bugs (broken at runtime) - -- [x] **Fix broken `lib/` require paths in all admin pages** - Admin pages (`add.php`, `edit.php`, `import.php`, `thanks.php`, `login.php`, - `logout.php`, `actions/formulaire.php`, `actions/publish.php`) all require - `../../lib/AdminAuth.php` and `../../lib/Database.php`, but the `lib/` directory - **does not exist**. The actual files live in `src/`. This means the entire admin - panel is broken. Fix: change all `lib/` references to `src/`. - -- [x] **Fix missing `modern-normalize.css` (no `.min` variant)** - `templates/header.php`, `templates/head.php`, and `public/search.php` reference - `assets/modern-normalize.css` (without `.min`), but only `modern-normalize.min.css` - exists. Either rename the file or update the references to be consistent. - -- [x] **Fix `admin/index.php` inconsistency** - `admin/index.php` uses `src/AdminAuth.php` (correct) but then - `../../lib/Database.php` (broken). It should load from `src/` consistently. - ---- - -## Structural / Code-Quality Refactors - -- [ ] **Unify and rename `src/` path references across the entire codebase** - After fixing the `lib/` → `src/` migration, normalise every admin page to load - `src/Database.php` and `src/AdminAuth.php` via `APP_ROOT` (the constant already - defined in `bootstrap.php`), removing the fragile relative-path `../../` chains. - -- [x] **Eliminate the duplicate `searchTheses` / `countSearchResults` condition block** - `Database::searchTheses()` and `Database::countSearchResults()` share identical - WHERE-clause construction logic (~80 lines each). Extract a private - `buildSearchConditions(array $params): array` helper that returns `[$conditions, - $bindings]` and call it from both methods. - -- [ ] **Remove `getConnection()` / `getPDO()` alias duplication** - The `Database` class exposes `getConnection()`, `getPDO()`, and direct transaction - delegation (`beginTransaction`, `commit`, `rollback`) purely because the admin code - accesses raw PDO. Consider removing `getConnection()` (alias of `getPDO()`) and - instead promoting the most-used raw queries into `Database` methods, reducing - direct PDO exposure. - -- [x] **Move inline SQL in `admin/index.php` into `Database`** - `admin/index.php` builds a raw SQL query with dynamic filter conditions directly in - the page. This is the only admin page doing so. Add a `getThesesList(array - $filters): array` method to `Database` to match the pattern used everywhere else. - -- [ ] **Add a `getThesisByIdAdmin(int $id): ?array` method to remove repeated raw queries in admin** - `admin/thanks.php` and `admin/edit.php` each call `$db->getThesis($id)` then - immediately issue further raw PDO queries for related data (`thesis_languages`, - `thesis_formats`, files). Consolidate into a method that returns everything needed - for the admin detail view. - ---- - -## What Can Be Removed / Simplified - -- [x] **Remove `include_template()` helper from `bootstrap.php` — it is never called** - The function `include_template($name)` in `config/bootstrap.php` is dead code; - pages use direct `include APP_ROOT . '/templates/...'` instead. - -- [x] **Remove the Composer autoload stub from `bootstrap.php`** - `bootstrap.php` has `if (file_exists(APP_ROOT . '/vendor/autoload.php'))` — there - is no Composer vendor directory and no plan for one. Remove this dead branch. - -- [x] **Delete `apps/admin/` directory** - `apps/admin/` contains only `data/` (empty with test data) and `error.log` and - `test.db`. It appears to be a leftover from an earlier structure. If confirmed - unused, delete it. - -- [x] **Remove `apps/` directory entirely if it contains only residual artefacts** - Related to the above — verify no active code references `apps/`. - ---- - -## What Needs External Dependencies (nothing — keep it that way) - -- **Authentication**: `password_verify` + `session_*` + `random_bytes` — already - standard PHP. No dependency needed. -- **Database**: PDO + SQLite — already standard PHP. No dependency needed. -- **Rate limiting**: File-based JSON sliding window — already implemented without - deps. Could be replaced by Redis/APCu at scale, but unnecessary for current load. -- **File serving / MIME validation**: `finfo` (fileinfo extension) — standard PHP - bundled extension. -- **CSRF**: `hash_equals` + `random_bytes` — standard PHP. No dependency needed. -- **CSS reset** (`modern-normalize`): The single vendored file (8 lines, minified) - is small enough to keep vendored. No CDN link, no build step. ✓ - ---- - -## Testing Infrastructure - -- [x] **Fix `SearchTest.php` — it calls `searchTheses()` with a string, not an array** - `$db->searchTheses('art')` passes a string, but `searchTheses()` expects - `array $params`. This test would throw a TypeError at runtime. Fix the call to - `$db->searchTheses(['query' => 'art'])`. - -- [ ] **Add a test for the `lib/` → `src/` path fix once it is applied** - After the path fix, add a smoke test that `require`-s each admin page's - dependencies to catch future regressions. - ---- - -## Low Priority / Nice-to-Have - -- [ ] **Normalise `modern-normalize` to a single canonical filename** - Pick either `.min.css` or `.css` and use it everywhere. Prefer `.min.css` since - the file is already minified. - -- [ ] **Consider extracting file-upload logic from `formulaire.php` into `Database`** - File validation, directory creation, and `insertThesisFile()` are scattered across - `formulaire.php`. Wrapping them in a `Database::attachFile()` or a dedicated - `FileUploadHandler` class would make `formulaire.php` much shorter and the upload - logic testable. - -- [ ] **Unify `head.php` vs `header.php` templates** - The public site has both `templates/head.php` (shared `` tag) and - `templates/header.php` (full `` + `
`). `tfe.php` uses - `head.php` and renders its own ``, while `index.php` uses `header.php`. - This split is confusing. Consider making `header.php` the single entry point. +- [ ] Add pagination to répertoire student index (currently capped at 100) +- [ ] Thumbnail generation / cover image support for home grid cards diff --git a/public/admin/add.php b/public/admin/add.php index 58c1bfe..d782f6d 100644 --- a/public/admin/add.php +++ b/public/admin/add.php @@ -1,263 +1,238 @@ getAllOrientations(); - $apPrograms = $db->getAllAPPrograms(); + $apPrograms = $db->getAllAPPrograms(); $finalityTypes = $db->getAllFinalityTypes(); - $languages = $db->getAllLanguages(); - $formatTypes = $db->getAllFormatTypes(); + $languages = $db->getAllLanguages(); + $formatTypes = $db->getAllFormatTypes(); } catch (Exception $e) { error_log("Failed to load form data: " . $e->getMessage()); - die("Erreur lors du chargement du formulaire. Veuillez réessayer plus tard."); + die("Erreur lors du chargement du formulaire."); } -// Get error message and preserved form data from session (if redirected back from error) -$error = isset($_SESSION["form_error"]) ? $_SESSION["form_error"] : null; -$formData = isset($_SESSION["form_data"]) ? $_SESSION["form_data"] : []; +$error = $_SESSION["form_error"] ?? null; +$formData = $_SESSION["form_data"] ?? []; +unset($_SESSION["form_error"], $_SESSION["form_data"]); -// Clear session data after retrieving -unset($_SESSION["form_error"]); -unset($_SESSION["form_data"]); - -// Helper function to get old form value -function old($key, $default = "") -{ +function old($key, $default = "") { global $formData; - return isset($formData[$key]) - ? htmlspecialchars($formData[$key]) - : $default; + return isset($formData[$key]) ? htmlspecialchars($formData[$key]) : $default; } - -// Helper function to check if value was previously selected -function wasSelected($key, $value) -{ +function wasSelected($key, $value) { global $formData; - if (!isset($formData[$key])) { - return false; - } - if (is_array($formData[$key])) { - return in_array($value, $formData[$key]); - } + if (!isset($formData[$key])) return false; + if (is_array($formData[$key])) return in_array($value, $formData[$key]); return $formData[$key] == $value; } ?> -
+ +
+

Ajouter un TFE

+ -
- ⚠️ Erreur: -
+
-
- - "> + + "> + +
+ + +
-
- Informations de base - - " required> -
- - "> -
- - " placeholder="" value="" required> -
+ +
+ + +
+ +
+ + +
-
- Informations académiques - - + + + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
-
- - + - + -
- - + + + + -
- - "> -
- - - -
- À propos du TFE - - " required> - -
- - "> -
- - -
- - -
- -
    - -
  • - -
  • + +
    + +
    + + -
+ + -
- -
    - -
  • - -
  • + +
    + +
    + + -
+ + -
- - "> -
- Séparez les mots-clés par des virgules. Maximum 10 mots-clés. - - "> -
- Indiquez la durée (en minutes) ou le nombre de pages de votre TFE. - - "> -
+ +
+ +
+ +

Séparez par des virgules. Max 10 mots-clés.

+
+
+ +
+ + +
-
- Fichiers - - Formats acceptés : JPG, PNG. Taille max : 10MB. - + +
+ +
+ +

Durée (minutes) ou nombre de pages.

+
+
- - Formats acceptés : PDF, JPG, PNG, MP4, ZIP. Taille max par fichier : 50MB. - Si vous voulez importer un dossier, créez une archive ZIP. - -
+ +
+ + +
-
- + +
+ +
+ +

JPG, PNG. Taille max : 10 MB.

+
+
+ + +
+ +
+ +

PDF, JPG, PNG, MP4, ZIP. Max 50 MB par fichier.

+
+
+ +
+ +
- \ No newline at end of file + diff --git a/public/admin/edit.php b/public/admin/edit.php index 51a9b8b..bdbb05e 100644 --- a/public/admin/edit.php +++ b/public/admin/edit.php @@ -175,147 +175,156 @@ try { ?> -
- -
- ⚠️ Erreur: -
- +
+

Modifier un TFE

- -
- -
- + +
+ + +
+ -
- + + -

Informations de base

+
+ + +
-
- - - Si plusieurs, séparer par des virgules -
+
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
-

Informations académiques

+
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
-
- - - Si plusieurs, séparer par des virgules -
+
+ + +
-

À propos du TFE

+
+ + +
-
- - -
- -
- - -
- -
- - -
- -
- - -
+ + -
- - -
+ + -
- - - Séparer par des virgules -
+
+ +
+ +

Séparer par des virgules. Max 10.

+
+
-
- - -
+
+ + +
-
- - -
+
+ + +
-

Publication

+
+ + +
-
- - Si coché, ce TFE sera visible sur le site public. Sinon, il restera en attente. -
- - - Annuler -
-
+
+ + Annuler +
+ +
diff --git a/public/admin/import.php b/public/admin/import.php index 2a12358..1231882 100644 --- a/public/admin/import.php +++ b/public/admin/import.php @@ -279,72 +279,51 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['csv_file'])) { ?> -
-

Importer des TFE depuis un fichier CSV

+
+

Importer une liste de TFE

-
- ⚠️ Erreurs: -
    - -
  • - -
-
+
+ ⚠ Erreurs : +
    + +
  • + +
+
-
- -
+
-
- + + -
- Sélectionner un fichier CSV +
+ +
+ +
+ Colonnes attendues : Identifiant, Titre, Sous-titre, Auteur·ice(s), Contact, Promoteur·ice(s), Format, Année, AP, Orientation, Finalité, Mots-clés, Synopsis, Contexte, Remarques, Langue, Autorisation, License, taille, Points sur 20, lien BAIU
+ — Deux premières lignes ignorées (en-tête) — Séparateur : virgule — Encodage : UTF-8 +
+
+
-

Format attendu:

-
    -
  • Colonnes: Identifiant, Titre, Sous-titre, Auteur·ice(s), Contact, Promoteur·ice(s), Format, Année, AP, Orientation, Finalité, Mots-clés, Synopsis, Contexte, Remarques, Langue, Autorisation, License, taille, Points sur 20, lien BAIU
  • -
  • Les deux premières lignes seront ignorées (entête)
  • -
  • Séparateur: virgule
  • -
  • Encodage: UTF-8
  • -
- - - - - -
+
+ +
-

Résultats de l'import

+
+

Résultats de l'import

-
+
+
- -
- -

Notes importantes

-
    -
  • Codes orientation: SC (Sculpture), VI (Vidéographie), CA (Cinéma d'animation), IP (Installation-Performance), etc.
  • -
  • Codes AP: DPM, LIENS, APS (comme dans la base)
  • -
  • Auteurs multiples: Séparer par des virgules
  • -
  • Mots-clés: Maximum 10, séparés par des virgules
  • -
  • Formats: Séparer par des virgules
  • -
  • Les lignes avec erreurs seront ignorées et loggées
  • -
- -

Exemple de fichier CSV

-

Voir: ../db/Database_TFE_test.csv

\ No newline at end of file diff --git a/public/admin/index.php b/public/admin/index.php index 8bf48c0..ac31078 100644 --- a/public/admin/index.php +++ b/public/admin/index.php @@ -1,254 +1,196 @@ getThesesList($filters); - $years = $db->getAllYears(); + $theses = $db->getThesesList($filters); + $years = $db->getAllYears(); $orientations = $db->getAllOrientations(); } catch (Exception $e) { error_log("Error loading theses list: " . $e->getMessage()); die("Erreur lors du chargement de la liste."); } ?> - + -
+
+

Liste des TFE

+ -
- ⚠️ Erreur: -
+
- -
- -
+
-
+ +
+
+
+
TFE total
+
+
+
$t['is_published'])) ?>
+
Publiés
+
+
+
!$t['is_published'])) ?>
+
En attente
+
+
+ + +
+ + + + + + Réinitialiser + +
+ + +
0 TFE(s) sélectionné(s) -
- - +
+ +
- +
-
-
-
-
TFE total
-
-
-
$t['is_published'])); ?>
-
Publiés
-
-
-
!$t['is_published'])); ?>
-
En attente
-
-
- -
-
-
- - -
- -
- - -
- -
- - -
- - - - Réinitialiser - -
-
- + -

Aucun TFE trouvé.

+

Aucun TFE trouvé.

- - - - - - - - - - - - - - - - - - - - - - - - - + + + +
IDTitreAuteur(s)AnnéeOrientationAPStatutActions
-
- -
- -
+ + + + + + + + + + + + + + + + + + + + + + + + + + - - - - -
IDTitreAuteur(s)AnnéeOrientationAPStatutActions
+
+ +
+ +
+ + Publié + + En attente + + +
+ Voir + Éditer +
+ + - Publié + + - En attente + + -
-
- Voir - Éditer - - - - - - - - - - - -
-
+ + +
- \ No newline at end of file + diff --git a/public/admin/login.php b/public/admin/login.php index fc54227..fbecf15 100644 --- a/public/admin/login.php +++ b/public/admin/login.php @@ -2,13 +2,10 @@ require_once __DIR__ . '/../../config/bootstrap.php'; require_once __DIR__ . '/../../src/AdminAuth.php'; -// If no password is configured, nothing to log into — go straight to admin. if (!defined('ADMIN_PASSWORD_HASH')) { header('Location: /admin/'); exit; } - -// Already authenticated — redirect to admin. if (AdminAuth::isAuthenticated()) { header('Location: /admin/'); exit; @@ -21,7 +18,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { header('Location: /admin/'); exit; } - // Intentionally vague error — avoid user-enumeration. $error = 'Mot de passe incorrect.'; } @@ -31,30 +27,32 @@ $pageTitle = 'Connexion'; - - <?php echo htmlspecialchars($pageTitle); ?> — Post-ERG Admin + + Connexion – Posterg Admin - - -
-

-
-
- -
- ⚠️ -
- -
-
- Authentification admin - - - -
-
-
+ + + + diff --git a/public/admin/thanks.php b/public/admin/thanks.php index 81c0c8f..bd37dce 100644 --- a/public/admin/thanks.php +++ b/public/admin/thanks.php @@ -69,133 +69,93 @@ function formatFileSize($bytes) { } // Set page title for header -$pageTitle = "Merci"; +$pageTitle = "Récapitulatif TFE"; ?> -
- -
-

⚠️

-

Retour au formulaire

-
+
+

Récapitulatif TFE

- -

d'avoir soumis votre TFE. Les informations ont été enregistrées et sont en attente de traitement.

+ +
+

Retour au formulaire

-
-

Récapitulatif de votre soumission

- -

Informations de base

-
-
Identifiant:
-
- -
Titre:
-
- - -
Sous-titre:
-
- - -
Auteur·ice(s):
-
- -
Année:
-
-
- -

Détails académiques

-
-
Orientation:
-
- -
Atelier Pratique:
-
- -
Finalité:
-
- - -
Promoteur·ice(s):
-
- -
- -

Contenu

-
- -
Synopsis:
-
- - - -
Langue(s):
-
- - - -
Format(s):
-
- - - -
Mots-clés:
-
- - - -
Durée/Taille:
-
- - - -
Lien:
-
- -
- -
- - -

Fichiers téléversés

- - - - - - - - - - - - - - - - - - - -
TypeNom du fichierTailleDate
+ +
+

Informations de base

+
+
Identifiant
+
Titre
+ +
Sous-titre
+
Auteur·ice(s)
+
Année
+
+
-

Statut de publication

-

⏳ En attente - Votre TFE ne sera publié qu'après la soutenance et l'ajout éventuel d'une note contextuelle par le jury.

+
+

Détails académiques

+
+
Orientation
+
Atelier pratique
+
Finalité
+ +
Promoteur·ice(s)
+ +
+
- -
+
+

Contenu

+
+ +
Langue(s)
+ + +
Format(s)
+ + +
Mots-clés
+ + +
Durée / Taille
+ + +
Lien
+ +
+
-

Soumettre un autre TFE

- - -

Aucune donnée à afficher.

-

Retour au formulaire

+ +
+

Fichiers

+ + + + + + + + + + + + +
TypeFichierTailleDate
+
-
+ + + + +

Aucune donnée à afficher.

+

Retour au formulaire

+ +
diff --git a/public/apropos.php b/public/apropos.php new file mode 100644 index 0000000..896a432 --- /dev/null +++ b/public/apropos.php @@ -0,0 +1,92 @@ + + + + + + + À Propos – Posterg + + + + + + + + + + + + +
+
+ + +
+
+

Ce site POSTERG a été créé pour répertorier et valoriser les mémoires de l'erg – École de Recherches Graphique de Bruxelles.

+

L'objectif est à la fois d'offrir une vitrine aux projets des anciens étudiantes et de mettre en lumière la diversité des disciplines et des parcours qui façonnent l'histoire de l'école à travers les âges, depuis près de 100 ans.

+
+ +
+

Licences

+

Les contenus de ce site sont publiés avec l'accord des auteur·ices et de l'erg. La reproduction des œuvres est soumise à l'autorisation de leurs auteur·ices respectif·ives.

+
+
+ + +
+ +
+

+ Site de l'erg +

+
+ +
+

Contacts

+ +
+ Laurent Leprince + Bibliothèque d'architecture, d'ingénierie architecturale, d'urbanisme (BAIU) : + laurent.leprince@uclouvain.be +
+ +
+ Xavier Gorgol + Responsable des mémoires de l'ERG : + xavier.gorgol@erg.be +
+ +
+ Brigitte Ledune + Cours de suivi de mémoire : + brigitte.ledune@erg.be +
+
+ +
+

Crédits

+

+ Design & développement : Olivia Marly, Théophile Gerveau-Mercie & Théo Hennequin +

+

+ Typographies : Ductus (Amélie Dumont) & BBB DM Sans +

+
+ +
+ +
+
+ + + diff --git a/public/assets/admin.css b/public/assets/admin.css index d66b4d8..ed8d6a6 100644 --- a/public/assets/admin.css +++ b/public/assets/admin.css @@ -1,304 +1,473 @@ +/* ============================================================ + ADMIN SECTION + ============================================================ */ + :root { - --background-body: #3c856bff; - --background: #161f27; - --background-alt: #1a242f; - --selection: #1c76c5; - --text-main: #dbdbdb; - --text-bright: #fff; - --text-muted: #a9b1ba; - --links: #41adff; - --focus: #0096bfab; - --border: ; - --code: #ffbe85; - --animation-duration: 0.1s; - --button-base: #0c151c; - --button-hover: #040a0f; - --scrollbar-thumb: var(--button-hover); - --scrollbar-thumb-hover: color-mod(var(--scrollbar-thumb) lightness(-30%)); - --form-placeholder: #a9a9a9; - --form-text: #fff; - --variable: #d941e2; - --highlight: #efdb43; - --select-arrow: svg-load("./assets/select-arrow.svg", fill: #efefef); + --admin-bg: #1a1a1a; + --admin-bg-alt: #242424; + --admin-border: #333; + --admin-text: #e8e8e8; + --admin-text-muted: #888; + --admin-purple: #9557b5; + --admin-input-bg: transparent; } -/* @media (prefers-color-scheme: dark) { */ -/* --background-body: #202b38; */ -/* --background: #161f27; */ -/* --background-alt: #1a242f; */ -/* --selection: #1c76c5; */ -/* --text-main: #dbdbdb; */ -/* --text-bright: #fff; */ -/* --text-muted: #a9b1ba; */ -/* --links: #41adff; */ -/* --focus: #0096bfab; */ -/* --border: #526980; */ -/* --code: #ffbe85; */ -/* --animation-duration: 0.1s; */ -/* --button-base: #0c151c; */ -/* --button-hover: #040a0f; */ -/* --scrollbar-thumb: var(--button-hover); */ -/* --scrollbar-thumb-hover: color-mod(var(--scrollbar-thumb) lightness(-30%)); */ -/* --form-placeholder: #a9a9a9; */ -/* --form-text: #fff; */ -/* --variable: #d941e2; */ -/* --highlight: #efdb43; */ -/* --select-arrow: svg-load("./assets/select-arrow.svg", fill: #efefef); */ -/* } */ - -/* Base Styles */ -body { - margin: auto; - padding: 0; - background-color: var(--background-body); - color: var(--text-main); - font-size: 1.2em; -} - -header, main, footer { - margin: auto 2ch; -} - -header, footer { - text-align: center; -} - -main { - padding: 2ch; - max-width: 80vw; - margin: auto 1.2rem; - max-width: 800px; - margin: 20px auto; - padding: 0 10px; - position: relative; -} -table { - table-layout: auto; - margin: auto; - width: 95vw; - margin-left: calc(50% - 45vw); - /* margin: auto; */ -} -table { - width: max-content; - min-width: 95vw; /* optional fallback */ - table-layout: auto; -} - -/* https://stackoverflow.com/questions/3084261/alternate-table-row-color-using-css */ - -tr:nth-child(even) { - background-color: #204639; -} - -tr:nth-child(odd) { - background-color: #2f6a55; -} - -nav { - margin-top: 1rem; -} - -fieldset { - border: 1px solid; - border-radius: 6px; +html, body { margin: 0; - margin-bottom: 12px; - padding: 1em; + padding: 0; + height: 100%; } -legend { - font-size: 0.9em; +.admin-body { + display: flex; + flex-direction: column; + min-height: 100vh; + background: var(--admin-bg); + color: var(--admin-text); + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, + "Helvetica Neue", Arial, sans-serif; +} + +/* Admin nav (dark version of site-nav) */ +.admin-nav { + background: linear-gradient(to bottom, var(--admin-purple) 0%, rgba(149, 87, 181, 0.0) 100%); + padding: 0.55rem 1.5rem; + display: flex; + align-items: center; + gap: 2.5rem; + flex-shrink: 0; +} + +.admin-nav__logo { + font-size: 0.88rem; + letter-spacing: 0.12em; + text-transform: uppercase; + color: rgba(255, 255, 255, 0.8); + text-decoration: none; + font-weight: 400; +} + +.admin-nav__link { + font-size: 0.85rem; + letter-spacing: 0.1em; + text-transform: uppercase; + color: rgba(255, 255, 255, 0.9); + text-decoration: none; + font-weight: 400; + transition: opacity 0.15s; +} + +.admin-nav__link:hover, +.admin-nav__link.active { + opacity: 1; + color: var(--white); +} + +/* Main content area */ +.admin-main { + flex: 1; + padding: 2.5rem 2rem 4rem; + max-width: 1100px; + width: 100%; +} + +.admin-page-title { + font-size: 1.8rem; font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--admin-text); + margin: 0 0 2.5rem 0; } -label { - vertical-align: middle; - margin-bottom: 1em; - display: inline-block; +/* ---- Forms ---- */ +.admin-form { + display: flex; + flex-direction: column; + gap: 0; } -button, -select, -input[type="submit"], -input[type="reset"], -input[type="button"], -input[type="checkbox"], -input[type="range"], -input[type="radio"] { +.admin-form-row { + display: grid; + grid-template-columns: 260px 1fr; + align-items: start; + border-top: 1px solid var(--admin-border); + padding: 0.75rem 0; + gap: 1rem; +} + +.admin-form-row:last-of-type { + border-bottom: 1px solid var(--admin-border); +} + +.admin-label { + font-size: 0.92rem; + color: var(--admin-text); + padding-top: 0.5rem; + font-weight: 400; +} + +.admin-input, +.admin-select, +.admin-textarea { + width: 100%; + background: var(--admin-input-bg); + border: none; + border-bottom: 1px solid var(--admin-border); + color: var(--admin-text); + font-size: 0.92rem; + font-family: inherit; + padding: 0.4rem 0; + outline: none; + border-radius: 0; + transition: border-color 0.15s; + -webkit-appearance: none; + appearance: none; +} + +.admin-input:focus, +.admin-select:focus, +.admin-textarea:focus { + border-bottom-color: var(--admin-purple); +} + +.admin-input::placeholder, +.admin-textarea::placeholder { + color: var(--admin-text-muted); + font-size: 0.88rem; +} + +.admin-textarea { + resize: vertical; + min-height: 100px; + line-height: 1.5; +} + +/* Select custom arrow */ +.admin-select { + cursor: pointer; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%23888' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 0 center; + padding-right: 1.2rem; +} + +.admin-select option { + background: var(--admin-bg); + color: var(--admin-text); +} + +/* File inputs */ +.admin-file-input { + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.admin-file-input input[type="file"] { + font-size: 0.85rem; + color: var(--admin-text-muted); + background: transparent; + border: 1px dashed var(--admin-border); + padding: 0.4rem 0.6rem; + border-radius: 3px; + cursor: pointer; + font-family: inherit; +} + +.admin-file-input input[type="file"]:hover { + border-color: var(--admin-purple); +} + +.admin-hint { + font-size: 0.78rem; + color: var(--admin-text-muted); + margin-top: 0.15rem; +} + +/* Checkboxes & radios */ +.admin-checkbox-list { + display: flex; + flex-direction: column; + gap: 0.35rem; + padding-top: 0.3rem; +} + +.admin-checkbox-label { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.9rem; + color: var(--admin-text); cursor: pointer; } -input, -select { - display: block; +.admin-checkbox-label input[type="checkbox"] { + accent-color: var(--admin-purple); + width: 14px; + height: 14px; + cursor: pointer; } -input, -button, -textarea, -select { - color: var(--form-text); - background-color: var(--background); - font-family: inherit; - font-size: inherit; - margin-right: 6px; - margin-bottom: 6px; - padding: 10px; +/* Submit button */ +.admin-submit-wrap { + margin-top: 2rem; + padding-top: 1.5rem; +} + +.admin-btn { + padding: 0.65rem 2.5rem; + background: var(--admin-purple); + color: #fff; border: none; - border-radius: 6px; - outline: none; + border-radius: 3px; + font-size: 0.92rem; + font-family: inherit; + cursor: pointer; + letter-spacing: 0.04em; + transition: background 0.15s; } -button, -input[type="submit"], -input[type="reset"], -input[type="button"] { - background-color: var(--button-base); - padding-right: 30px; - padding-left: 30px; +.admin-btn:hover { + background: #7b3fa0; } -/* Alert Messages */ -.error-message, -.alert-error { - background: #fee; - border: 2px solid #c00; - padding: 1rem; - margin-bottom: 1rem; - border-radius: 4px; - color: #c00; +.admin-btn-secondary { + padding: 0.5rem 1.5rem; + background: transparent; + color: var(--admin-text-muted); + border: 1px solid var(--admin-border); + border-radius: 3px; + font-size: 0.88rem; + font-family: inherit; + cursor: pointer; + letter-spacing: 0.04em; + text-decoration: none; + display: inline-block; + transition: all 0.15s; } -.success-message, -.alert-success { - background: #efe; - border: 2px solid #0a0; - padding: 1rem; - margin-bottom: 1rem; - border-radius: 4px; - color: #0a0; +.admin-btn-secondary:hover { + border-color: var(--admin-text-muted); + color: var(--admin-text); } -.info-message { - background: #f5f5f5; - padding: 1rem; - border-radius: 4px; - max-height: 400px; - overflow-y: auto; +/* ---- Alert Messages ---- */ +.admin-alert { + padding: 0.75rem 1rem; + border-radius: 3px; + font-size: 0.9rem; + margin-bottom: 1.5rem; + border-left: 3px solid; } -.info-message pre { - margin: 0; - font-size: 0.9em; +.admin-alert--error { + background: rgba(200, 0, 0, 0.1); + border-color: #c00; + color: #ff6b6b; } -/* Lists */ -/* ul.no-style { */ -/* list-style: none; */ -/* } */ - -/* Filters */ -.filters { - padding: 1rem; - margin-bottom: 2rem; - border-radius: 4px; +.admin-alert--success { + background: rgba(0, 150, 80, 0.1); + border-color: #0a0; + color: #4caf50; } -.filters form { +/* ---- Stats cards ---- */ +.admin-stats { display: flex; gap: 1rem; + margin-bottom: 2rem; flex-wrap: wrap; - align-items: end; } -.filters fieldset { - margin: 0; - padding: 0; +.admin-stat { + background: var(--admin-bg-alt); + border: 1px solid var(--admin-border); + border-radius: 4px; + padding: 1rem 1.5rem; + min-width: 140px; +} + +.admin-stat__number { + font-size: 2rem; + font-weight: 700; + color: var(--admin-purple); + line-height: 1; +} + +.admin-stat__label { + font-size: 0.82rem; + color: var(--admin-text-muted); + margin-top: 0.25rem; +} + +/* ---- Filters bar ---- */ +.admin-filters { + display: flex; + gap: 0.75rem; + margin-bottom: 1.5rem; + flex-wrap: wrap; + align-items: flex-end; +} + +.admin-filters input[type="text"], +.admin-filters select { + background: var(--admin-bg-alt); + border: 1px solid var(--admin-border); + border-radius: 3px; + color: var(--admin-text); + font-size: 0.88rem; + font-family: inherit; + padding: 0.45rem 0.75rem; + outline: none; + cursor: pointer; +} + +.admin-filters input[type="text"]:focus, +.admin-filters select:focus { + border-color: var(--admin-purple); +} + +.admin-filters-btn { + padding: 0.45rem 1rem; + background: var(--admin-purple); + color: #fff; border: none; - min-width: 200px; + border-radius: 3px; + font-size: 0.88rem; + font-family: inherit; + cursor: pointer; } -/* Tables */ -.thesis-table { +.admin-filters-reset { + font-size: 0.88rem; + color: var(--admin-text-muted); + text-decoration: underline; + cursor: pointer; +} + +/* ---- Table ---- */ +.admin-table { width: 100%; border-collapse: collapse; + font-size: 0.88rem; } -.thesis-table th, -.thesis-table td { - padding: 0.75rem; +.admin-table th { text-align: left; + font-size: 0.75rem; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--admin-text-muted); + padding: 0.5rem 0.75rem; + border-bottom: 1px solid var(--admin-border); + font-weight: 400; + white-space: nowrap; } -.thesis-table th { - font-weight: bold; +.admin-table td { + padding: 0.65rem 0.75rem; + border-bottom: 1px solid var(--admin-border); + color: var(--admin-text); + vertical-align: top; } -.thesis-title { - font-weight: bold; +.admin-table tr:hover td { + background: var(--admin-bg-alt); } -.thesis-subtitle { +.admin-table .thesis-title { + font-weight: 500; + color: var(--admin-text); +} + +.admin-table .thesis-subtitle { + font-size: 0.82rem; + color: var(--admin-text-muted); font-style: italic; - color: #666; - font-size: 0.9em; } -/* Status Badges */ +/* Status badges */ .status-badge { display: inline-block; - padding: 0.25rem 0.5rem; + padding: 0.2rem 0.5rem; border-radius: 3px; - font-size: 0.85em; -} - -.status-pending { - background: #ffd700; - color: #000; + font-size: 0.78rem; + font-weight: 500; + letter-spacing: 0.04em; } .status-published { - background: #90ee90; - color: #000; + background: rgba(0, 150, 80, 0.15); + color: #4caf50; } -/* Buttons */ -.actions { +.status-pending { + background: rgba(255, 200, 0, 0.12); + color: #ffc107; +} + +/* Action buttons in table */ +.admin-actions { display: flex; - gap: 0.5rem; + gap: 0.4rem; + flex-wrap: wrap; } -.btn { - padding: 0.35rem 0.75rem; +.admin-btn-sm { + padding: 0.25rem 0.6rem; border-radius: 3px; + font-size: 0.78rem; + font-family: inherit; + cursor: pointer; text-decoration: none; - font-size: 0.9em; display: inline-block; + border: 1px solid transparent; + transition: all 0.15s; + white-space: nowrap; } -.btn-view { - background: #4a90e2; - color: white; +.admin-btn-view { + background: rgba(65, 173, 255, 0.15); + color: #41adff; + border-color: rgba(65, 173, 255, 0.3); } -.btn-edit { - background: #f39c12; - color: white; +.admin-btn-view:hover { + background: rgba(65, 173, 255, 0.25); } -.btn-publish { - background: #27ae60; - color: white; - border: none; - cursor: pointer; +.admin-btn-edit { + background: rgba(243, 156, 18, 0.15); + color: #f39c12; + border-color: rgba(243, 156, 18, 0.3); } -.btn-unpublish { - background: #95a5a6; - color: white; - border: none; - cursor: pointer; +.admin-btn-edit:hover { + background: rgba(243, 156, 18, 0.25); +} + +.admin-btn-publish { + background: rgba(0, 150, 80, 0.15); + color: #4caf50; + border-color: rgba(0, 150, 80, 0.3); + border: 1px solid rgba(0, 150, 80, 0.3); +} + +.admin-btn-publish:hover { + background: rgba(0, 150, 80, 0.25); +} + +.admin-btn-unpublish { + background: rgba(149, 165, 166, 0.15); + color: #95a5a6; + border-color: rgba(149, 165, 166, 0.3); + border: 1px solid rgba(149, 165, 166, 0.3); +} + +.admin-btn-unpublish:hover { + background: rgba(149, 165, 166, 0.25); } .publish-form { @@ -306,162 +475,112 @@ input[type="button"] { margin: 0; } -/* Statistics */ -.stats { - display: flex; - gap: 2rem; - margin-bottom: 2rem; - flex-wrap: wrap; -} - -.stat-card { - background: darkslateblue; - padding: 1rem; - border-radius: 4px; - min-width: 150px; -} - -.stat-number { - font-size: 2em; - font-weight: bold; -} - -.stat-label { - font-size: 0.9em; -} - -/* Bulk Actions */ -.bulk-actions { - background: #f5f5f5; - padding: 1rem; - margin-bottom: 1rem; - border-radius: 4px; - display: flex; - gap: 1rem; +/* Bulk actions */ +.admin-bulk-actions { + display: none; align-items: center; + gap: 1rem; + padding: 0.6rem 1rem; + background: var(--admin-bg-alt); + border: 1px solid var(--admin-border); + border-radius: 4px; + margin-bottom: 1rem; + font-size: 0.88rem; } -.bulk-actions-buttons { +.admin-bulk-btns { display: flex; gap: 0.5rem; } -.btn-bulk-publish { - background: #27ae60; - color: white; - border: none; - cursor: pointer; - padding: 0.5rem 1rem; - border-radius: 3px; -} - -.btn-bulk-unpublish { - background: #95a5a6; - color: white; - border: none; - cursor: pointer; - padding: 0.5rem 1rem; - border-radius: 3px; -} - -.select-checkbox { - cursor: pointer; -} - -.select-all-checkbox { - cursor: pointer; -} - -#bulk-actions { - display: none; -} - -#bulk-form { - display: none; -} - -/* Thesis Info (Thanks page) */ -.thesis-info { - /* background: #f5f5f5; */ - border: 1px white solid; - padding: 2rem; - border-radius: 8px; - margin: 2rem 0; -} - -.thesis-info h2 { - margin-top: 0; - border-bottom: 2px solid #333; - padding-bottom: 0.5rem; -} - -.thesis-info h3 { - margin-top: 2rem; - margin-bottom: 1rem; - /* color: #555; */ -} - -.thesis-info dl { - display: grid; - grid-template-columns: 200px 1fr; - gap: 0.5rem 1rem; +/* Thesis info (thanks page) */ +.admin-thesis-info { + border: 1px solid var(--admin-border); + border-radius: 6px; + padding: 1.5rem; margin-bottom: 1.5rem; } -.thesis-info dt { - font-weight: bold; - /* color: #666; */ +.admin-thesis-info h2 { + margin: 0 0 1rem; + font-size: 1.2rem; + border-bottom: 1px solid var(--admin-border); + padding-bottom: 0.5rem; } -.thesis-info dd { +.admin-thesis-info dl { + display: grid; + grid-template-columns: 180px 1fr; + gap: 0.4rem 1rem; +} + +.admin-thesis-info dt { + font-weight: 600; + font-size: 0.88rem; + color: var(--admin-text-muted); +} + +.admin-thesis-info dd { margin: 0; + font-size: 0.9rem; } -.thesis-info table { - width: 100%; - margin-top: 1rem; +/* Info/error messages */ +.info-message { + background: var(--admin-bg-alt); + border: 1px solid var(--admin-border); + border-radius: 4px; + padding: 1rem; + font-size: 0.9rem; } -.thesis-info table th { - text-align: left; - background: #ddd; - padding: 0.5rem; +.info-message pre { + margin: 0; + font-size: 0.85rem; + white-space: pre-wrap; + word-break: break-word; } -.thesis-info table td { - padding: 0.5rem; - border-bottom: 1px solid #ddd; -} - -.submitted-date { - margin-top: 2rem; - font-style: italic; - color: #666; -} - -.error { - background: #fee; - border: 2px solid #c00; - padding: 1.5rem; - border-radius: 8px; - color: #c00; -} - -/* Form Elements */ -label.checkbox-label { +/* Login page */ +.admin-login-wrap { display: flex; align-items: center; - gap: 0.5rem; + justify-content: center; + min-height: calc(100vh - 60px); } -/* Responsive */ -@media (max-width: 768px) { - .thesis-info dl { - grid-template-columns: 1fr; - gap: 0.25rem; - } - - .thesis-info dt { - margin-top: 1rem; - } +.admin-login-box { + background: var(--admin-bg-alt); + border: 1px solid var(--admin-border); + border-radius: 6px; + padding: 2rem; + width: 100%; + max-width: 380px; +} + +.admin-login-box h2 { + margin: 0 0 1.5rem; + font-size: 1.1rem; + font-weight: 500; + text-align: center; +} + +.admin-login-box .admin-form-row { + grid-template-columns: 1fr; + border: none; + padding: 0.4rem 0; +} + +.admin-login-box .admin-label { + font-size: 0.82rem; + color: var(--admin-text-muted); + padding: 0; + margin-bottom: 0.2rem; +} + +/* Import page */ +.admin-import-area { + display: flex; + flex-direction: column; + gap: 1.5rem; } diff --git a/public/assets/apropos.css b/public/assets/apropos.css new file mode 100644 index 0000000..e32c2a3 --- /dev/null +++ b/public/assets/apropos.css @@ -0,0 +1,139 @@ +/* ============================================================ + À PROPOS PAGE (apropos.php) + ============================================================ */ + +html, body { + margin: 0; + padding: 0; + height: 100%; +} + +.apropos-body { + display: flex; + flex-direction: column; + min-height: 100vh; + background: var(--white); +} + +.apropos-main { + flex: 1; + overflow-y: auto; + padding: 2.5rem 1.5rem 4rem; +} + +/* Two-column layout */ +.apropos-layout { + display: grid; + grid-template-columns: 1.4fr 1fr; + gap: 4rem; + max-width: 1200px; +} + +/* Left col — main description text in big monospace */ +.apropos-left {} + +.apropos-description { + font-family: "Courier New", Courier, monospace; + font-size: 1.55rem; + line-height: 1.45; + color: var(--black); + font-weight: 400; + margin: 0 0 2rem 0; +} + +.apropos-description p { + margin: 0 0 1.2em 0; +} + +/* Right col — links, contacts, credits */ +.apropos-right { + display: flex; + flex-direction: column; + gap: 2rem; +} + +.apropos-section-title { + font-family: "Courier New", Courier, monospace; + font-size: 1.55rem; + font-weight: 400; + color: var(--black); + margin: 0 0 0.5rem 0; + line-height: 1.2; +} + +.apropos-section-title a { + color: inherit; + text-decoration: underline; + text-underline-offset: 3px; +} + +.apropos-contact { + margin-bottom: 1rem; +} + +.apropos-contact-name { + font-weight: 700; + font-size: 0.95rem; + color: var(--black); + display: block; + margin-bottom: 0.15rem; +} + +.apropos-contact-role, +.apropos-contact-email { + font-size: 0.9rem; + color: var(--black); + line-height: 1.4; + display: block; +} + +.apropos-credits-text { + font-size: 0.9rem; + color: var(--black); + line-height: 1.6; +} + +/* Licences section */ +.apropos-licences { + margin-top: 2rem; +} + +.apropos-licences h2 { + font-family: "Courier New", Courier, monospace; + font-size: 1.55rem; + font-weight: 400; + margin: 0 0 0.75rem 0; +} + +.apropos-licences p { + font-size: 0.9rem; + color: var(--black); + line-height: 1.6; + margin: 0 0 0.75rem 0; +} + +/* Responsive */ +@media (max-width: 900px) { + .apropos-layout { + grid-template-columns: 1fr; + gap: 2rem; + } + + .apropos-description { + font-size: 1.2rem; + } + + .apropos-section-title { + font-size: 1.2rem; + } +} + +@media (max-width: 600px) { + .apropos-main { + padding: 1.5rem 1rem 3rem; + } + + .apropos-description { + font-size: 1rem; + } +} diff --git a/public/assets/common.css b/public/assets/common.css index 556a162..1104d3d 100644 --- a/public/assets/common.css +++ b/public/assets/common.css @@ -1,104 +1,135 @@ @font-face { - font-family: police1; + font-family: "police1"; src: url("./fonts/Combinedd.otf"); } -/* Dark theme */ -/* UTILE POUR FORCER UN MODE LIGHT */ -/* @media (prefers-color-scheme: dark) { */ -/* :root, */ -/* ::backdrop { */ -/* --bg: #fff; */ -/* --accent-bg: #f5f7ff; */ -/* --text: #212121; */ -/* --text-light: #585858; */ -/* --border: #898ea4; */ -/* --accent: #0d47a1; */ -/* --code: #d81b60; */ -/* --preformatted: #444; */ -/* --marked: #ffdd33; */ -/* --disabled: #efefef; */ -/* } */ -/* } */ +/* ============================================================ + SHARED VARIABLES & RESET + ============================================================ */ +:root { + --purple: #9557b5; + --purple-dark: #7b3fa0; + --purple-light: rgba(149, 87, 181, 0.12); + --black: #111; + --white: #fff; + --grey-light: #f5f5f5; + --border-color: #ddd; + --text-muted: #666; +} + +*, +*::before, +*::after { + box-sizing: border-box; +} body { margin: 0; -} - -/* FORMULAIRE */ -form label { - font-family: police1; - font-size: 1rem; -} -form input, -select, -textarea { - border-color: #c104fc; - overflow: visible; - outline: none; - background-color: white; -} - -form input:focus, -select:focus { - border: 3px solid rgba(77, 168, 112, 1); -} - -label { - margin-top: 2rem; -} - -input { - /* font-family: police1; */ - /* font-weight: bold; */ - background-color: none; - color: rgb(193, 4, 252); - border: 1px solid rgb(193, 4, 252); + padding: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, + "Helvetica Neue", Arial, sans-serif; + background: var(--white); + color: var(--black); } a { - color: rgb(193, 4, 252); + color: inherit; + text-decoration: none; } a:hover { text-decoration: none; } -a, a:visited { - color: rgb(193, 4, 252); + +/* ============================================================ + NAV BAR (shared across all public pages) + ============================================================ */ +.site-nav { + background: linear-gradient(to bottom, var(--purple) 0%, rgba(149, 87, 181, 0.0) 100%); + padding: 0.55rem 1.5rem; + display: flex; + align-items: center; + justify-content: space-between; + flex-shrink: 0; } -input:active { - border-color: rgba(77, 168, 112, 1); +.site-nav__logo { + font-family: "police1", sans-serif; + font-size: 0.95rem; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--white); + text-decoration: none; + font-weight: 400; } -button, -[role="button"], -input[type="submit"], -input[type="reset"], -input[type="button"], -label[type="button"] { - background-color: white; - margin-top: 2rem; - font-size: 16px; - border-radius: 10px; - padding: 1rem; - margin: 1rem; - a { - color: black; - text-decoration: none; - } +.site-nav__links { + display: flex; + gap: 3rem; + align-items: center; } -button {} - -/* For Google Chrome, Safari, and newer versions of Opera */ -::placeholder { - /* color: rgb(213, 73, 255); */ - font-size: 0.8rem; +.site-nav__link { + font-size: 0.85rem; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--white); + text-decoration: none; + font-weight: 400; + opacity: 0.92; + transition: opacity 0.15s; } -/* For Mozilla Firefox */ -::-moz-placeholder { - /* color: rgb(213, 73, 255); */ - font-size: 0.8rem; +.site-nav__link:hover { + opacity: 1; +} + +.site-nav__right { + font-size: 0.85rem; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--white); + text-decoration: none; + font-weight: 400; + opacity: 0.92; + transition: opacity 0.15s; +} + +.site-nav__right:hover { + opacity: 1; +} + +/* ============================================================ + SEARCH BAR (shared) + ============================================================ */ +.site-search { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 1.5rem; + border-bottom: 1px solid var(--border-color); + background: var(--white); + flex-shrink: 0; +} + +.site-search__icon { + color: var(--text-muted); + flex-shrink: 0; + width: 16px; + height: 16px; +} + +.site-search__input { + flex: 1; + border: none; + outline: none; + font-size: 0.95rem; + color: var(--black); + background: transparent; + padding: 0.15rem 0; + font-family: inherit; +} + +.site-search__input::placeholder { + color: #aaa; } diff --git a/public/assets/main.css b/public/assets/main.css index 98d7a50..93111e9 100644 --- a/public/assets/main.css +++ b/public/assets/main.css @@ -1,350 +1,152 @@ -body { +/* ============================================================ + HOME PAGE (index.php) + ============================================================ */ + +html, body { margin: 0; + padding: 0; + height: 100%; +} + +.home-body { + display: flex; + flex-direction: column; height: 100vh; overflow: hidden; - display: flex; - flex-direction: column; - font-family: - -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", - Arial, sans-serif; + background: var(--white); } -/* Layout ratios: header 2/5, main 3/5, footer 1/5 */ -header { - flex: 2; - min-height: 0; -} - -main { - flex: 7; - min-height: 0; -} - -footer { +/* Cards grid — scrollable main area */ +.home-main { flex: 1; - min-height: 0; -} - -header, main, footer { - padding: 1rem; - margin: 0; - border-radius: 40px; -} - -header { - font-family: "police1", sans-serif; - background: #9557b5ff; - color: white; - display: flex; - gap: 4%; - padding: 1.5rem 3rem; - align-items: center; overflow-y: auto; - box-sizing: border-box; -} - -header .title { - color: white; - font-size: 1.75rem; - font-weight: 700; - text-decoration: none; - white-space: nowrap; -} - -header section { - flex: 1; - font-size: 0.9rem; - line-height: 1.4; -} - -header section p { - margin: 0.5rem 0; -} - -header section p:first-child { - font-size: 1rem; - font-weight: 600; -} - -header section p:not(:first-child) { - font-size: 0.85rem; - opacity: 0.95; -} - -header nav { - display: flex; - gap: 0.75rem; -} - -header nav button { - background: rgba(255, 255, 255, 0.15); - border: 1px solid rgba(255, 255, 255, 0.3); - color: white; - padding: 0.5rem 1rem; - border-radius: 20px; - cursor: pointer; - transition: all 0.2s; - font-size: 0.9rem; - font-family: inherit; -} - -header nav button:hover { - background: rgba(255, 255, 255, 0.25); - transform: translateY(-1px); -} - -header nav a { - color: white; - text-decoration: none; -} - -/* Filter info banner */ -.filter-info { - background: rgba(149, 87, 181, 0.9); - color: white; - padding: 0.5rem 1rem; - text-align: center; - font-size: 0.9rem; - display: flex; - align-items: center; - justify-content: center; - gap: 1rem; -} - -.clear-filter { - color: white; - text-decoration: none; - padding: 0.25rem 0.75rem; - background: rgba(0, 0, 0, 0.2); - border-radius: 15px; - font-size: 0.85rem; - transition: background 0.2s; -} - -.clear-filter:hover { - background: rgba(0, 0, 0, 0.4); -} - -main { - background: #3c856bff; - position: relative; - padding: 1rem; - box-sizing: border-box; - overflow: hidden; - display: flex; - flex-direction: column; + overflow-x: hidden; + padding: 0; } .cards-container { display: grid; - gap: 0.75rem; - flex: 1; - min-height: 0; - padding: 0.5rem; - overflow: hidden; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 0; } -/* Default: 3 rows × 4 columns = 12 items */ -.cards-container { - grid-template-rows: repeat(3, 1fr); - grid-template-columns: repeat(4, 1fr); - grid-auto-flow: row; -} - -/* Small screens: 2 rows × 3 columns = 6 items */ -@media (max-width: 1200px) { +@media (min-width: 1400px) { .cards-container { - grid-template-rows: repeat(2, 1fr); - grid-template-columns: repeat(3, 1fr); + grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); } } -/* Medium screens: 3 rows × 4 columns = 12 items */ -@media (min-width: 1201px) and (max-width: 1500px) { - .cards-container { - grid-template-rows: repeat(3, 1fr); - grid-template-columns: repeat(4, 1fr); - } -} - -/* Large screens: 3 rows × 6 columns = 18 items */ -@media (min-width: 1701px) { - .cards-container { - grid-template-rows: repeat(3, 1fr); - grid-template-columns: repeat(6, 1fr); - } -} - -/* Extra large screens: 4 rows × 6 columns = 24 items */ -@media (min-width: 2000px) { - .cards-container { - grid-template-rows: repeat(4, 1fr); - grid-template-columns: repeat(6, 1fr); - } -} - -/* Mobile placeholder (will adjust later) */ @media (max-width: 768px) { .cards-container { - grid-template-rows: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr); } } +/* Each card = media thumbnail + text below */ .card-link { text-decoration: none; color: inherit; display: block; - height: 100%; } .card { - background: #eee; - border-radius: 8px; - padding: 0.5rem; - height: 100%; + display: flex; + flex-direction: column; + cursor: pointer; + border: none; + background: var(--white); + overflow: hidden; +} + +.card__media { width: 100%; - box-sizing: border-box; - transition: transform 0.2s, box-shadow 0.2s; - display: flex; - flex-direction: column; + aspect-ratio: 4/3; overflow: hidden; - min-width: 0; - min-height: 0; + background: #e8e8e8; + position: relative; } -.card:hover { - transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); -} - -.card-content { - display: flex; - flex-direction: column; - gap: 0.3rem; +.card__media img, +.card__media video { + width: 100%; height: 100%; - overflow: hidden; + object-fit: cover; + display: block; + transition: transform 0.3s ease; } -.card .title { - font-size: 0.75rem; - font-weight: 600; +.card:hover .card__media img, +.card:hover .card__media video { + transform: scale(1.02); +} + +.card__media--placeholder { + width: 100%; + height: 100%; + background: linear-gradient(135deg, #e8e8e8, #d0d0d0); + display: flex; + align-items: center; + justify-content: center; + color: #aaa; + font-size: 2rem; +} + +.card__info { + padding: 0.55rem 0.5rem 0.65rem; + font-size: 0.88rem; + line-height: 1.35; + color: var(--black); +} + +.card__info .authors { margin: 0; - line-height: 1.15; - color: #333; + font-weight: 400; +} + +.card__info .title { + margin: 0; + font-weight: 400; + color: var(--text-muted); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; - text-overflow: ellipsis; } -.card .authors { - font-size: 0.65rem; - margin: 0; - color: #666; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.card .year { - font-size: 0.65rem; - margin: 0; - color: #9557b5; - font-weight: 600; -} - -.card .tags { - display: flex; - flex-wrap: wrap; - gap: 0.2rem; - margin-top: auto; - padding-top: 0.2rem; -} - -.card .tag { - font-size: 0.55rem; - padding: 0.15rem 0.25rem; - background: rgba(149, 87, 181, 0.15); - color: #7a3d95; - border-radius: 2px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 100%; - font-weight: 500; -} - -footer { - background: #222222ff; - overflow: hidden; - box-sizing: border-box; -} - -/* Years navigation in footer */ -.years-nav { - height: 100%; +/* Filter info */ +.filter-info { + background: var(--purple-light); + color: var(--purple); + padding: 0.4rem 1.5rem; + font-size: 0.85rem; display: flex; align-items: center; - padding: 0 1rem; + gap: 1rem; + flex-shrink: 0; } -.years-scroll { - display: flex; - gap: 0.75rem; - overflow-x: auto; - overflow-y: hidden; - scroll-behavior: smooth; - padding: 0.75rem 0; - width: 100%; -} - -/* Hide scrollbar but keep functionality */ -.years-scroll::-webkit-scrollbar { - height: 4px; -} - -.years-scroll::-webkit-scrollbar-track { - background: transparent; -} - -.years-scroll::-webkit-scrollbar-thumb { - background: rgba(255, 255, 255, 0.3); - border-radius: 2px; -} - -.year-link { - color: white; +.clear-filter { + color: var(--purple); text-decoration: none; - padding: 0.5rem 1rem; - border-radius: 20px; - white-space: nowrap; - transition: background 0.2s; - font-size: 1rem; + padding: 0.15rem 0.6rem; + background: rgba(149, 87, 181, 0.12); + border-radius: 3px; + font-size: 0.82rem; } -.year-link:hover { - background: rgba(255, 255, 255, 0.1); +.clear-filter:hover { + background: rgba(149, 87, 181, 0.22); } -.year-link.active { - background: #9557b5ff; - font-weight: bold; -} - -/* Pagination controls */ -.pagination { +/* Pagination */ +.pagination-wrap { display: flex; - align-items: center; justify-content: center; + align-items: center; gap: 0.5rem; - padding: 0.75rem; - background: rgba(255, 255, 255, 0.1); - border-radius: 10px; - margin-top: 1rem; - width: fit-content; - align-self: center; + padding: 1rem; + border-top: 1px solid var(--border-color); + background: var(--white); flex-shrink: 0; } @@ -352,24 +154,20 @@ footer { display: inline-flex; align-items: center; justify-content: center; - min-width: 2.5rem; - height: 2.5rem; - padding: 0 0.75rem; - background: rgba(255, 255, 255, 0.9); - color: #3c856b; + min-width: 2rem; + height: 2rem; + padding: 0 0.5rem; + border: 1px solid var(--border-color); + border-radius: 3px; + color: var(--black); + font-size: 0.9rem; text-decoration: none; - border-radius: 6px; - font-size: 1.2rem; - font-weight: 600; - transition: all 0.2s; - border: 2px solid transparent; + transition: all 0.15s; } .pagination-btn:hover:not(.disabled) { - background: white; - border-color: #9557b5; - transform: translateY(-1px); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + border-color: var(--purple); + color: var(--purple); } .pagination-btn.disabled { @@ -379,32 +177,12 @@ footer { } .pagination-info { - display: flex; - align-items: center; - gap: 0.25rem; - padding: 0 1rem; - color: white; - font-size: 1rem; - font-weight: 500; + font-size: 0.9rem; + color: var(--text-muted); + padding: 0 0.5rem; } .page-current { - font-size: 1.25rem; - font-weight: 700; - color: #9557b5; - background: white; - padding: 0.25rem 0.75rem; - border-radius: 6px; - min-width: 2rem; - text-align: center; -} - -.page-separator { - font-weight: 300; - opacity: 0.6; - padding: 0 0.25rem; -} - -.page-total { - opacity: 0.8; + font-weight: 600; + color: var(--black); } diff --git a/public/assets/search.css b/public/assets/search.css index bc5216a..1d08c1a 100644 --- a/public/assets/search.css +++ b/public/assets/search.css @@ -1,452 +1,289 @@ -/* Search page - horizontal layout */ -body { +/* ============================================================ + RÉPERTOIRE / SEARCH PAGE (search.php) + ============================================================ */ + +html, body { margin: 0; + padding: 0; + height: 100%; +} + +.search-body { + display: flex; + flex-direction: column; height: 100vh; overflow: hidden; - display: flex; - flex-direction: row; + background: var(--white); +} + +.search-main { + flex: 1; + overflow-y: auto; + overflow-x: hidden; +} + +/* ---- 4-column index layout ---- */ +.repertoire-index { + display: grid; + grid-template-columns: 1fr 2fr 2fr 1.5fr; gap: 0; - font-family: - -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", - Arial, sans-serif; + padding: 0 1.5rem; + min-height: 100%; } -/* Layout: header sidebar, main+footer wrapper takes rest */ -header { - flex: 0 0 250px; - min-width: 0; - max-width: 250px; - height: 100vh; - font-family: "police1", sans-serif; - background: #9557b5ff; - color: white; - display: flex; - flex-direction: column; - gap: 0.4rem; - padding: 0.75rem 0.6rem; - margin: 0; - overflow-y: auto; - overflow-x: hidden; - box-sizing: border-box; - border-radius: 40px; +.repertoire-col { + padding: 0.75rem 0.5rem 2rem; + border-right: 1px solid var(--border-color); } -.main-wrapper { - flex: 1; - min-width: 0; - height: 100vh; - display: flex; - flex-direction: column; - overflow: hidden; +.repertoire-col:last-child { + border-right: none; } -main { - flex: 1; - min-width: 0; - background: #3c856bff; - position: relative; - padding: 1rem; - margin: 0; - box-sizing: border-box; - overflow: hidden; - display: flex; - flex-direction: column; - border-radius: 40px; +.repertoire-col__header { + font-size: 0.72rem; + letter-spacing: 0.1em; + text-transform: uppercase; + color: var(--text-muted); + font-weight: 400; + margin: 0 0 0.5rem 0; + padding-bottom: 0.4rem; + border-bottom: 1px solid var(--border-color); } -footer { - background: #222222ff; - padding: 0.75rem 1rem; - margin: 0; - box-sizing: border-box; - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - border-radius: 40px; -} - -/* Search header */ -.search-header { - display: flex; - flex-direction: column; - gap: 0.4rem; - width: 100%; - flex-shrink: 0; -} - -.back-button { - background: rgba(255, 255, 255, 0.15); - border: 1px solid rgba(255, 255, 255, 0.3); - color: white; - padding: 0.35rem 0.6rem; - border-radius: 10px; - cursor: pointer; - text-decoration: none; - font-size: 0.8rem; - transition: all 0.2s; - white-space: nowrap; - display: inline-flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - max-width: 100%; - overflow: hidden; - text-overflow: ellipsis; -} - -.back-button:hover { - background: rgba(255, 255, 255, 0.25); - transform: translateY(-1px); -} - -.search-form { - display: flex; - flex-direction: column; - gap: 0.35rem; - min-width: 0; -} - -.search-input { - width: 100%; - padding: 0.35rem 0.6rem; - border: 1px solid rgba(255, 255, 255, 0.3); - border-radius: 10px; - font-size: 0.8rem; - background: rgba(255, 255, 255, 0.9); - color: #333; - box-sizing: border-box; -} - -.search-input::placeholder { - color: #999; - font-size: 0.75rem; -} - -.search-actions { - display: flex; - gap: 0.35rem; -} - -.search-button, -.filter-button { - background: rgba(255, 255, 255, 0.15); - border: 1px solid rgba(255, 255, 255, 0.3); - color: white; - padding: 0.35rem 0.5rem; - border-radius: 10px; - cursor: pointer; - font-size: 0.8rem; - transition: all 0.2s; - white-space: nowrap; - flex: 1; - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; -} - -.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 */ -.filters-panel { - display: none; - background: rgba(255, 255, 255, 0.08); - padding: 0.4rem; - border-radius: 8px; - flex-shrink: 0; - overflow: visible; -} - -.filters-panel.show { +/* Years column - big bold numbers */ +.year-index-item { display: block; -} - -.filters-grid { - display: grid; - grid-template-columns: 1fr; - gap: 0.3rem; -} - -.filter-group { - display: flex; - flex-direction: column; - gap: 0.15rem; - min-width: 0; -} - -.filter-label { - color: white; - font-weight: 600; - font-size: 0.65rem; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.filter-select { - padding: 0.3rem 0.35rem; - border-radius: 6px; - border: 1px solid rgba(255, 255, 255, 0.3); - background: rgba(255, 255, 255, 0.9); - font-size: 0.7rem; - min-width: 0; - width: 100%; - box-sizing: border-box; -} - -.filter-actions { - display: flex; - flex-direction: column; - gap: 0.3rem; - margin-top: 0.3rem; - 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.3rem 0.5rem; - border-radius: 8px; - cursor: pointer; - font-size: 0.7rem; - text-decoration: none; - display: inline-block; - white-space: nowrap; - text-align: center; -} - -.reset-button:hover { - background: rgba(255, 255, 255, 0.25); -} - -.error-message { - background: rgba(255, 100, 100, 0.2); - color: white; - padding: 0.35rem 0.5rem; - border-radius: 8px; - font-size: 0.75rem; - flex-shrink: 0; -} - -/* Main content - results grid */ -.cards-container { - display: grid; - gap: 1rem; - flex: 1; - min-height: 0; - padding: 0.5rem; - overflow-y: auto; - overflow-x: hidden; - grid-template-rows: repeat(3, 1fr); - grid-template-columns: repeat(3, 1fr); - grid-auto-flow: row; -} - -@media (min-width: 1400px) and (max-width: 1700px) { - .cards-container { - grid-template-rows: repeat(3, 1fr); - grid-template-columns: repeat(4, 1fr); - } -} - -@media (min-width: 1701px) { - .cards-container { - grid-template-rows: repeat(4, 1fr); - grid-template-columns: repeat(4, 1fr); - } -} - -@media (max-width: 1399px) { - .cards-container { - grid-template-rows: repeat(3, 1fr); - grid-template-columns: repeat(2, 1fr); - } -} - -.card-link { - text-decoration: none; - color: inherit; - display: block; - height: 100%; -} - -.card { - background: #eee; - border-radius: 12px; - padding: 0.75rem; - height: 100%; - width: 100%; - box-sizing: border-box; - transition: transform 0.2s, box-shadow 0.2s; - display: flex; - flex-direction: column; - overflow: hidden; - min-width: 0; - min-height: 0; -} - -.card:hover { - transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); -} - -.card-content { - display: flex; - flex-direction: column; - gap: 0.4rem; - height: 100%; - overflow: hidden; -} - -.card .title { - font-size: 0.85rem; - font-weight: 600; - margin: 0; - line-height: 1.2; - color: #333; - display: -webkit-box; - -webkit-line-clamp: 3; - -webkit-box-orient: vertical; - overflow: hidden; - text-overflow: ellipsis; -} - -.card .authors { - font-size: 0.75rem; - margin: 0; - color: #666; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.card .year { - font-size: 0.75rem; - margin: 0; - color: #9557b5; - font-weight: 600; -} - -.card .tags { - display: flex; - flex-wrap: wrap; - gap: 0.25rem; - margin-top: auto; - padding-top: 0.3rem; -} - -.card .tag { - font-size: 0.65rem; - padding: 0.2rem 0.4rem; - background: rgba(149, 87, 181, 0.15); - color: #7a3d95; - border-radius: 4px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 100%; - font-weight: 500; -} - -/* Pagination */ -.pagination { - display: flex; - align-items: center; - justify-content: center; - gap: 0.5rem; - padding: 0.5rem 0.75rem; - background: rgba(255, 255, 255, 0.15); - border-radius: 8px; - margin-top: 0.75rem; - width: fit-content; - align-self: center; - flex-shrink: 0; -} - -.pagination-btn { - display: inline-flex; - align-items: center; - justify-content: center; - min-width: 2rem; - height: 2rem; - padding: 0 0.5rem; - background: rgba(255, 255, 255, 0.9); - color: #3c856b; - text-decoration: none; - border-radius: 6px; - font-size: 1rem; - font-weight: 600; - transition: all 0.2s; - border: 2px solid transparent; -} - -.pagination-btn:hover:not(.disabled) { - background: white; - border-color: #9557b5; - transform: translateY(-1px); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); -} - -.pagination-btn.disabled { - opacity: 0.3; - cursor: not-allowed; - pointer-events: none; -} - -.pagination-info { - display: flex; - align-items: center; - gap: 0.25rem; - padding: 0 0.5rem; - color: white; - font-size: 0.9rem; - font-weight: 500; -} - -.page-current { - font-size: 1.1rem; + font-size: 2.2rem; font-weight: 700; - color: #9557b5; - background: white; - padding: 0.2rem 0.6rem; - border-radius: 6px; - min-width: 1.5rem; - text-align: center; -} - -.page-separator { - font-weight: 300; - opacity: 0.6; - padding: 0 0.15rem; -} - -.page-total { - opacity: 0.8; -} - -/* Footer - results count */ -.results-footer { - display: flex; - align-items: center; - justify-content: center; - color: white; - font-size: 1rem; - gap: 0.5rem; - text-align: center; -} - -.results-count { - font-weight: 700; - font-size: 1.5rem; + line-height: 1.1; + color: var(--black); + text-decoration: none; + padding: 0.1rem 0; + transition: color 0.15s; letter-spacing: -0.02em; } + +.year-index-item:hover, +.year-index-item.active { + color: var(--purple); +} + +/* Categories column */ +.cat-index-group { + margin-bottom: 0.6rem; +} + +.cat-index-label { + font-size: 0.72rem; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--text-muted); + font-weight: 400; + display: block; + margin-bottom: 0.15rem; + margin-top: 0.75rem; +} + +.cat-index-item { + display: block; + font-size: 0.95rem; + color: var(--black); + text-decoration: none; + padding: 0.1rem 0; + line-height: 1.4; + transition: color 0.15s; +} + +.cat-index-item:hover, +.cat-index-item.active { + color: var(--purple); +} + +/* Students column */ +.student-index-item { + display: block; + font-size: 0.95rem; + color: var(--black); + text-decoration: none; + padding: 0.1rem 0; + line-height: 1.4; + transition: color 0.15s; +} + +.student-index-item:hover { + color: var(--purple); +} + +/* Keywords column */ +.keyword-index-item { + display: block; + font-size: 0.95rem; + color: var(--black); + text-decoration: none; + padding: 0.1rem 0; + line-height: 1.4; + transition: color 0.15s; +} + +.keyword-index-item:hover, +.keyword-index-item.active { + color: var(--purple); +} + +/* ---- Search results view (grid) ---- */ +.search-results-view { + padding: 1rem 1.5rem; +} + +.search-results-header { + font-size: 0.85rem; + color: var(--text-muted); + margin-bottom: 1rem; +} + +.results-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 1.5rem; +} + +.result-card { + text-decoration: none; + color: inherit; + display: flex; + flex-direction: column; + gap: 0.4rem; +} + +.result-card__authors { + font-size: 0.9rem; + font-weight: 500; + color: var(--black); +} + +.result-card__title { + font-size: 0.85rem; + color: var(--text-muted); + line-height: 1.35; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.result-card__meta { + font-size: 0.78rem; + color: var(--purple); +} + +/* Toggle button (index/results) */ +.view-toggle { + display: flex; + gap: 0; + border: 1px solid var(--border-color); + border-radius: 3px; + overflow: hidden; + flex-shrink: 0; +} + +.view-toggle__btn { + padding: 0.25rem 0.75rem; + font-size: 0.78rem; + background: var(--white); + color: var(--text-muted); + border: none; + cursor: pointer; + text-decoration: none; + display: inline-flex; + align-items: center; + transition: all 0.15s; +} + +.view-toggle__btn.active, +.view-toggle__btn:hover { + background: var(--purple); + color: var(--white); +} + +/* Search controls bar */ +.search-controls { + display: flex; + align-items: center; + gap: 1rem; + padding: 0.4rem 1.5rem; + border-bottom: 1px solid var(--border-color); + flex-shrink: 0; + flex-wrap: wrap; +} + +.search-filter-group { + display: flex; + align-items: center; + gap: 0.4rem; +} + +.search-filter-label { + font-size: 0.78rem; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 0.06em; + white-space: nowrap; +} + +.search-filter-select { + font-size: 0.82rem; + border: 1px solid var(--border-color); + border-radius: 3px; + padding: 0.2rem 0.5rem; + background: var(--white); + color: var(--black); + outline: none; + font-family: inherit; + cursor: pointer; +} + +.search-filter-select:focus { + border-color: var(--purple); +} + +.search-apply-btn { + font-size: 0.82rem; + padding: 0.2rem 0.8rem; + background: var(--purple); + color: var(--white); + border: none; + border-radius: 3px; + cursor: pointer; + font-family: inherit; + transition: background 0.15s; +} + +.search-apply-btn:hover { + background: var(--purple-dark); +} + +.search-reset-link { + font-size: 0.82rem; + color: var(--text-muted); + text-decoration: underline; + cursor: pointer; +} + +.search-empty { + padding: 3rem 1.5rem; + color: var(--text-muted); + font-size: 1rem; + text-align: center; +} + +/* Error message */ +.search-error { + background: #fff0f0; + border-left: 3px solid #c00; + color: #c00; + padding: 0.5rem 1rem; + font-size: 0.88rem; + margin: 0.5rem 1.5rem; + flex-shrink: 0; +} diff --git a/public/assets/tfe.css b/public/assets/tfe.css index 656b29e..8a1f121 100644 --- a/public/assets/tfe.css +++ b/public/assets/tfe.css @@ -1,389 +1,172 @@ -/* TFE (Thesis) Page Styling */ +/* ============================================================ + TFE INDIVIDUAL PAGE (tfe.php) + ============================================================ */ + +html, body { + margin: 0; + padding: 0; + height: 100%; +} .tfe-body { - margin: 0; - height: 100vh; - overflow: hidden; - display: flex; - flex-direction: column; + display: flex; + flex-direction: column; + min-height: 100vh; + background: var(--white); } -/* Header */ -.tfe-header { - flex: 2; - min-height: 0; - background: #9557b5; - color: white; - padding: 1.5rem 2rem; - margin: 0; - border-radius: 40px; - display: flex; - align-items: center; - box-sizing: border-box; - overflow-y: auto; -} - -.header-content { - width: 100%; -} - -.tfe-title { - font-family: "police1", sans-serif; - font-size: 1.75rem; - font-weight: 700; - margin: 0 0 0.5rem 0; - line-height: 1.2; - color: white; -} - -.tfe-subtitle { - font-size: 1.15rem; - margin: 0 0 1rem 0; - opacity: 0.9; - font-style: italic; -} - -.header-metadata { - display: flex; - flex-direction: column; - gap: 0.5rem; -} - -.meta-group { - display: flex; - align-items: center; - gap: 0.5rem; - flex-wrap: wrap; - font-size: 0.95rem; - opacity: 0.95; -} - -.meta-group.keywords { - margin-top: 0.25rem; -} - -.meta-group .label { - font-weight: 600; - opacity: 0.85; -} - -.meta-group .author { - font-weight: 500; -} - -.meta-group .separator { - opacity: 0.6; -} - -.meta-group .year { - font-weight: 600; -} - -/* Main Content */ .tfe-main { - flex: 7; - min-height: 0; - background: #3c856b; - padding: 2rem; - margin: 0; - border-radius: 40px; - box-sizing: border-box; - overflow-y: auto; + flex: 1; + overflow-y: auto; + padding: 2rem 1.5rem 3rem; } -.tfe-container { - max-width: 900px; - margin: 0 auto; - display: flex; - flex-direction: column; +/* Two-column layout */ +.tfe-layout { + display: grid; + grid-template-columns: 1fr 1.4fr; + gap: 3rem; + max-width: 1200px; +} + +/* Left column */ +.tfe-left { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +/* Student name — large */ +.tfe-author { + font-size: 1.9rem; + font-weight: 400; + color: var(--black); + margin: 0; + line-height: 1.15; + letter-spacing: -0.01em; +} + +/* Title — very large, slightly spaced */ +.tfe-title { + font-size: 2.2rem; + font-weight: 400; + color: var(--black); + margin: 0; + line-height: 1.15; + letter-spacing: -0.01em; +} + +/* Metadata list */ +.tfe-meta-list { + display: flex; + flex-direction: column; + gap: 0.45rem; + font-size: 0.95rem; + line-height: 1.4; +} + +.tfe-meta-item { + display: flex; + gap: 0.4rem; + flex-wrap: wrap; +} + +.tfe-meta-item .label { + color: var(--black); + font-weight: 400; + flex-shrink: 0; +} + +.tfe-meta-item .value { + color: var(--black); + font-weight: 700; +} + +.tfe-meta-item .value a { + color: inherit; + text-decoration: underline; + text-underline-offset: 2px; +} + +/* Synopsis text */ +.tfe-synopsis-text { + font-size: 0.95rem; + line-height: 1.7; + color: var(--black); + margin-top: 0.5rem; +} + +/* Right column — media */ +.tfe-right { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.tfe-media-block { + overflow: hidden; +} + +.tfe-media-block img { + width: 100%; + height: auto; + display: block; +} + +.tfe-media-block embed, +.tfe-media-block video { + width: 100%; + display: block; + border: none; +} + +.tfe-media-block video { + max-height: 500px; +} + +.tfe-media-block embed { + height: 700px; +} + +.tfe-file-caption { + font-size: 0.8rem; + color: var(--text-muted); + margin: 0.3rem 0 0; + font-style: italic; +} + +.tfe-no-files { + font-size: 0.95rem; + color: var(--text-muted); + padding: 1rem 0; +} + +/* Responsive */ +@media (max-width: 900px) { + .tfe-layout { + grid-template-columns: 1fr; gap: 2rem; -} + } -/* Metadata Section */ -.tfe-metadata { - background: white; - border-radius: 12px; - padding: 2rem; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); -} - -.subtitle { - font-size: 1.25rem; - color: #666; - margin: 0 0 1.5rem 0; - font-style: italic; -} - -.metadata-grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 2rem 3rem; - margin-bottom: 1rem; -} - -.metadata-column { - display: flex; - flex-direction: column; - gap: 1rem; -} - -.meta-item { - display: flex; - flex-direction: column; - gap: 0.25rem; -} - -.meta-item strong { - color: #9557b5; - font-size: 0.875rem; - text-transform: uppercase; - letter-spacing: 0.05em; -} - -.meta-item span { - color: #333; - font-size: 1rem; - line-height: 1.5; -} - -.context-note { - margin-top: 1.5rem; - padding: 1rem; - background: #f8f8f8; - border-left: 4px solid #9557b5; - border-radius: 4px; -} - -.context-note em { - color: #555; - font-size: 0.95rem; - line-height: 1.6; -} - -/* Synopsis Section */ -.tfe-synopsis { - background: white; - border-radius: 12px; - padding: 2rem; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); -} - -.tfe-synopsis h3 { + .tfe-author { font-size: 1.5rem; - color: #9557b5; - margin: 0 0 1rem 0; - font-weight: 700; + } + + .tfe-title { + font-size: 1.7rem; + } } -.synopsis-content { - color: #333; - font-size: 1rem; - line-height: 1.7; -} - -/* Files Section */ -.tfe-files { - display: flex; - flex-direction: column; - gap: 2rem; -} - -.file-block { - background: white; - border-radius: 12px; - padding: 1.5rem; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); - overflow: hidden; -} - -.file-block embed, -.file-block video { - border-radius: 8px; - display: block; -} - -.image-figure { - margin: 0; - text-align: center; -} - -.image-figure img { - max-width: 100%; - height: auto; - border-radius: 8px; - display: block; - margin: 0 auto; -} - -.file-description { - margin: 1rem 0 0 0; - color: #666; - font-size: 0.9rem; - font-style: italic; - text-align: center; -} - -.no-files { - background: #fff3cd; - color: #856404; - padding: 1.5rem; - border-radius: 8px; - text-align: center; - border: 1px solid #ffeaa7; -} - -/* Footer */ -.tfe-footer { - flex: 1; - min-height: 0; - background: #222; - color: white; - padding: 1rem 2rem; - margin: 0; - border-radius: 40px; - box-sizing: border-box; - overflow: hidden; -} - -.footer-content { - height: 100%; - display: flex; - justify-content: space-between; - align-items: center; - flex-wrap: wrap; - gap: 1rem; -} - -.back-button { - display: flex; - align-items: center; - justify-content: center; - width: 48px; - height: 48px; - background: rgba(255, 255, 255, 0.15); - border-radius: 50%; - color: white; - text-decoration: none; - transition: all 0.2s ease; - flex-shrink: 0; -} - -.back-button:hover { - background: rgba(255, 255, 255, 0.25); - transform: translateX(-2px); -} - -.back-button svg { - display: block; -} - -.footer-meta { - display: flex; - align-items: center; - gap: 0.75rem; - font-size: 0.9rem; - opacity: 0.9; -} - -.footer-meta .separator { - opacity: 0.6; -} - -/* Responsive Design */ -@media (max-width: 768px) { - .tfe-header { - padding: 1rem; - } - - .tfe-title { - font-size: 1.35rem; - } - - .tfe-subtitle { - font-size: 1rem; - } - - .meta-group { - font-size: 0.85rem; - } - - .back-button { - width: 40px; - height: 40px; - } - - .back-button svg { - width: 20px; - height: 20px; - } - - .tfe-main { - padding: 1.5rem; - } - - .tfe-container { - gap: 1.5rem; - } - - .tfe-metadata, - .tfe-synopsis, - .file-block { - padding: 1.5rem; - } - - .metadata-grid { - grid-template-columns: 1fr; - gap: 1.5rem; - } - - .footer-content { - flex-direction: column; - text-align: center; - justify-content: center; - } - - .tfe-footer { - padding: 1rem; - } -} - -@media (max-width: 480px) { - .tfe-title { - font-size: 1.15rem; - } - - .tfe-subtitle { - font-size: 0.9rem; - } - - .meta-group { - font-size: 0.8rem; - gap: 0.4rem; - } - - .back-button { - width: 36px; - height: 36px; - } - - .back-button svg { - width: 18px; - height: 18px; - } - - .tfe-synopsis h3 { - font-size: 1.25rem; - } - - .file-block embed { - height: 500px; - } - - .tfe-main { - padding: 1rem; - } - - .tfe-container { - gap: 1rem; - } +@media (max-width: 600px) { + .tfe-main { + padding: 1.5rem 1rem 2rem; + } + + .tfe-author { + font-size: 1.25rem; + } + + .tfe-title { + font-size: 1.4rem; + } } diff --git a/public/index.php b/public/index.php index 741f793..58f10ab 100644 --- a/public/index.php +++ b/public/index.php @@ -3,20 +3,15 @@ require_once __DIR__ . '/../config/bootstrap.php'; require_once APP_ROOT . '/src/Database.php'; -$pageTitle = "Liste des TFE"; $page = isset($_GET["page"]) ? intval($_GET["page"]) : 1; $year = isset($_GET["year"]) ? intval($_GET["year"]) : null; -// Default to 12 items (4 cols × 3 rows) -$itemsPerPage = 12; +$itemsPerPage = 24; // bigger grid try { $db = Database::getInstance(); $offset = ($page - 1) * $itemsPerPage; - - // Get available years for footer $availableYears = $db->getAvailableYears(); - - // Filter by year if specified + if ($year) { $itemsToLoad = $db->searchTheses(['year' => $year], $itemsPerPage, $offset); $totalItems = $db->countSearchResults(['year' => $year]); @@ -24,91 +19,109 @@ try { $itemsToLoad = $db->getPublishedTheses($itemsPerPage, $offset); $totalItems = $db->countPublishedTheses(); } - + $totalPages = ceil($totalItems / $itemsPerPage); } catch (Exception $e) { error_log("Error loading theses: " . $e->getMessage()); $itemsToLoad = []; $totalPages = 0; $availableYears = []; + $totalItems = 0; } -include APP_ROOT . '/templates/header.php'; +$currentNav = ''; ?> - - -
- Année : - ✕ Réinitialiser -
- - -
-
- - " class="card-link"> -
-
-

-

-

- -
- - - -
- -
-
-
- - - -

Aucun mémoire trouvé.

- -
- - 1): ?> - + + + + + + Posterg + + + + + -
+ + - + + + + +
+ Année : + ✕ Réinitialiser +
+ + +
+
+ + " class="card-link"> +
+
+ + + <?= htmlspecialchars($item['title']) ?> + +
+ +
+
+

+
+
+
+ + + +

Aucun mémoire trouvé.

+ +
+ + 1): ?> +
+ + « + + + / + + + » +
+ +
+ + + diff --git a/public/search.php b/public/search.php index f0e3ea4..4be9b18 100644 --- a/public/search.php +++ b/public/search.php @@ -1,356 +1,254 @@ check()) { http_response_code(429); header('Retry-After: ' . $rateLimit->getResetTime()); - $rateLimit->sendHeaders(); - - // Simple error page - echo 'Rate Limit'; - echo '

Trop de requêtes

'; - echo '

Vous avez dépassé la limite de 30 recherches par minute. Veuillez réessayer dans ' . $rateLimit->getResetTime() . ' secondes.

'; - echo ''; + echo '

Trop de requêtes

Réessayez dans ' . $rateLimit->getResetTime() . ' secondes.

'; exit; } - $rateLimit->sendHeaders(); +if (rand(1, 100) === 1) $rateLimit->cleanup(); -if (rand(1, 100) === 1) { - $rateLimit->cleanup(); -} - -// Pagination - adjust to grid -$page = isset($_GET['page']) ? intval($_GET['page']) : 1; -$itemsPerPage = 9; // Default grid size (3 rows × 3 columns) - -// Collect search parameters +// Collect search/filter params $searchParams = []; -if (!empty($_GET['query'])) { - $searchParams['query'] = trim($_GET['query']); -} -if (!empty($_GET['year'])) { - $searchParams['year'] = intval($_GET['year']); -} -if (!empty($_GET['orientation'])) { - $searchParams['orientation'] = $_GET['orientation']; -} -if (!empty($_GET['ap_program'])) { - $searchParams['ap_program'] = $_GET['ap_program']; -} -if (!empty($_GET['finality'])) { - $searchParams['finality'] = $_GET['finality']; -} -if (!empty($_GET['keyword'])) { - $searchParams['keyword'] = $_GET['keyword']; -} -if (!empty($_GET['format'])) { - $searchParams['format'] = $_GET['format']; -} -if (!empty($_GET['language'])) { - $searchParams['language'] = $_GET['language']; -} -if (isset($_GET['is_doctoral'])) { - $searchParams['is_doctoral'] = $_GET['is_doctoral'] === '1'; -} +if (!empty($_GET['query'])) $searchParams['query'] = trim($_GET['query']); +if (!empty($_GET['year'])) $searchParams['year'] = intval($_GET['year']); +if (!empty($_GET['orientation'])) $searchParams['orientation'] = $_GET['orientation']; +if (!empty($_GET['ap_program'])) $searchParams['ap_program'] = $_GET['ap_program']; +if (!empty($_GET['keyword'])) $searchParams['keyword'] = $_GET['keyword']; +$hasSearch = !empty($searchParams); + +$page = isset($_GET['page']) ? intval($_GET['page']) : 1; +$itemsPerPage = 30; $validationError = null; -$showFilters = isset($_GET['filters']) && $_GET['filters'] === 'show'; try { $db = Database::getInstance(); - - // Get search results $offset = ($page - 1) * $itemsPerPage; - $results = $db->searchTheses($searchParams, $itemsPerPage, $offset); - $totalItems = $db->countSearchResults($searchParams); - $totalPages = ceil($totalItems / $itemsPerPage); - // Get filter options - $years = $db->getAvailableYears(); + if ($hasSearch) { + $results = $db->searchTheses($searchParams, $itemsPerPage, $offset); + $totalItems = $db->countSearchResults($searchParams); + $totalPages = ceil($totalItems / $itemsPerPage); + } else { + $results = []; + $totalItems = 0; + $totalPages = 0; + } + + $years = $db->getAvailableYears(); $orientations = $db->getOrientations(); - $apPrograms = $db->getApPrograms(); - $finalityTypes = $db->getFinalityTypes(); - $keywords = $db->getUsedKeywords(); - $formats = $db->getFormatTypes(); - $languages = $db->getLanguages(); - + $apPrograms = $db->getApPrograms(); + $keywords = $db->getUsedKeywords(); + // Get all published theses for student index (multiple pages if needed) + $students = $db->searchTheses([], 100, 0); // max 100 per DB limit } catch (InvalidArgumentException $e) { - error_log("Search validation error: " . $e->getMessage()); $validationError = $e->getMessage(); - $results = []; - $totalPages = 0; - $totalItems = 0; - $years = []; - $orientations = []; - $apPrograms = []; - $finalityTypes = []; - $keywords = []; - $formats = []; - $languages = []; + $results = []; $totalItems = 0; $totalPages = 0; + $years = []; $orientations = []; $apPrograms = []; $keywords = []; $students = []; } catch (Exception $e) { - error_log("Error in search: " . $e->getMessage()); - $validationError = "Une erreur est survenue lors de la recherche."; - $results = []; - $totalPages = 0; - $totalItems = 0; - $years = []; - $orientations = []; - $apPrograms = []; - $finalityTypes = []; - $keywords = []; - $formats = []; - $languages = []; + error_log("Search error: " . $e->getMessage()); + $validationError = "Une erreur est survenue."; + $results = []; $totalItems = 0; $totalPages = 0; + $years = []; $orientations = []; $apPrograms = []; $keywords = []; $students = []; } + +$currentNav = 'repertoire'; +$searchBarValue = $_GET['query'] ?? ''; ?> - Recherche - Posterg + Répertoire – Posterg + + + - -
-
- ← Retour - -
- - -
- - -
- - - - - - - - - + + + + + + +
+ + + + + + + + + +
+ Année +
- - -
- ⚠ -
- - -
- - - - - - - -
-
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
-
- -
- - Réinitialiser -
- + +
+ Orientation +
-
- -
-
-
- 0): ?> + +
+ AP + +
+ + + Réinitialiser + + +
+
+

résultat 1 ? 's' : '' ?>

+ + +
- -
-
-

-

-

- -
- -
- -
-
-
+ + + + · + - -
- Aucun résultat trouvé pour cette recherche. -
+
+ + 1): ?> +
+ + / + +
+ + -
- Utilisez la barre de recherche pour trouver des mémoires. -
+

Aucun résultat pour cette recherche.

- - 1): ?> -
+ + + +
+
+ + +
+

Années

+ + + + +
- -
- -
-
-
- - + + +
+

Étudiantes

+ $id): ?> + + + + +
+ + +
+

Mots-clés

+ + + + + +
+ +
+
+ + diff --git a/public/tfe.php b/public/tfe.php index 0b1ad7d..17a6b0c 100644 --- a/public/tfe.php +++ b/public/tfe.php @@ -1,202 +1,172 @@ getThesisById($thesisId); - - if (!$data) { - // Thesis not found or not published - header('Location: index.php'); - exit; - } + if (!$data) { header('Location: index.php'); exit; } } catch (Exception $e) { error_log("Error loading thesis: " . $e->getMessage()); - header('Location: index.php'); - exit; + header('Location: index.php'); exit; } } else { - // Redirect to the index page if no id parameter is provided - header('Location: index.php'); - exit; + header('Location: index.php'); exit; } -// Set page title and additional CSS -$pageTitle = $data['title']; -$additionalCSS = ['assets/tfe.css']; - -// Include shared head template -include APP_ROOT . '/templates/head.php'; +$currentNav = ''; ?> + + + + + + <?= htmlspecialchars($data['title']) ?> – Posterg + + + + + + + -
-
-

- -

- - -
-
+ + +
-
- - - - - -
-

Synopsis

-
- -
-
- + +
- -
- 0): ?> + +
+ -
+
- - -
- <?= htmlspecialchars($file['file_name']); ?> -
+ + + <?= htmlspecialchars($file['file_name']) ?> -

+

-

Aucun fichier disponible pour ce TFE.

+

Aucun fichier disponible pour ce TFE.

-
+
- diff --git a/src/cache/rate_limit/f528764d624db129b32c21fbca0cb8d6.json b/src/cache/rate_limit/f528764d624db129b32c21fbca0cb8d6.json index cea1698..a86dea0 100644 --- a/src/cache/rate_limit/f528764d624db129b32c21fbca0cb8d6.json +++ b/src/cache/rate_limit/f528764d624db129b32c21fbca0cb8d6.json @@ -1 +1 @@ -{"10":1770899252,"11":1770899274,"12":1770899301} \ No newline at end of file +[1771972436,1771972448] \ No newline at end of file diff --git a/templates/admin/footer.php b/templates/admin/footer.php index 5678470..308b1d0 100644 --- a/templates/admin/footer.php +++ b/templates/admin/footer.php @@ -1,6 +1,2 @@ -
-

Formulaire fait avec ❤ en PHP et Water.css.

-
- - \ No newline at end of file + diff --git a/templates/admin/head.php b/templates/admin/head.php index 5754111..7cb7d0f 100644 --- a/templates/admin/head.php +++ b/templates/admin/head.php @@ -1,71 +1,35 @@ - - - - <?php echo htmlspecialchars($pageTitle ?? 'Admin'); ?> - Post-ERG + + <?= htmlspecialchars($pageTitle ?? 'Admin') ?> – Posterg - - - + - - -
-

- -
\ No newline at end of file + + diff --git a/templates/nav.php b/templates/nav.php new file mode 100644 index 0000000..3b8b77c --- /dev/null +++ b/templates/nav.php @@ -0,0 +1,15 @@ + + diff --git a/templates/search-bar.php b/templates/search-bar.php new file mode 100644 index 0000000..86a2f01 --- /dev/null +++ b/templates/search-bar.php @@ -0,0 +1,19 @@ + +