mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 19:19:19 +02:00
tfe page: author above title, interne/externe jury split, rounded images, strip contact protocol
This commit is contained in:
8
TODO.md
8
TODO.md
@@ -1,6 +1,14 @@
|
||||
# TODO
|
||||
|
||||
## Done
|
||||
- [x] Redesign TFE detail page
|
||||
- [x] Author moved above title (smaller body font)
|
||||
- [x] Remove "← Retour" back link
|
||||
- [x] Split jury promoteurs into interne/externe using `getThesisJury()`
|
||||
- [x] Jury fields order: promoteur interne → externe → président → lecteurs
|
||||
- [x] Contact URL strips `http(s)://` prefix for display
|
||||
- [x] Right-column images get `border-radius: 8px`
|
||||
- [x] Remove orphan `.tfe-back-link` CSS rule
|
||||
- [x] Improve À propos page layout
|
||||
- [x] Replace two-column (prose + aside) with sticky-TOC-nav + main-content layout
|
||||
- [x] Sticky left nav with section anchors (À propos, Contacts, Crédits + erg link)
|
||||
|
||||
@@ -32,12 +32,14 @@
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
/* Back link — top of left column */
|
||||
.tfe-back-link {
|
||||
font-size: .88rem;
|
||||
color: var(--text-tertiary);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
/* Author (p) — above title, smaller */
|
||||
.tfe-author {
|
||||
font-family: var(--font-body);
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
color: var(--text-primary);
|
||||
margin: 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* Title (h1) — primary heading, very large */
|
||||
@@ -51,17 +53,6 @@
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
/* Author (p) — large but secondary */
|
||||
.tfe-author {
|
||||
font-family: var(--font-display);
|
||||
font-size: 1.9rem;
|
||||
font-weight: 400;
|
||||
color: var(--text-primary);
|
||||
margin: 0;
|
||||
line-height: 1.15;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
/* Metadata description list — target <dl> directly inside article > header */
|
||||
article dl {
|
||||
display: flex;
|
||||
@@ -125,12 +116,14 @@ article dl dd a {
|
||||
aside figure {
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
aside figure img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
aside figure embed,
|
||||
@@ -188,10 +181,6 @@ aside figcaption {
|
||||
.tfe-title {
|
||||
font-size: 1.7rem;
|
||||
}
|
||||
|
||||
.tfe-author {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
@@ -202,8 +191,4 @@ aside figcaption {
|
||||
.tfe-title {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.tfe-author {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,9 @@ extract($ctrl->handle());
|
||||
|
||||
<!-- LEFT: info — article header -->
|
||||
<header class="tfe-left">
|
||||
<a href="index.php" class="tfe-back-link">← Retour</a>
|
||||
<!-- Author above title -->
|
||||
<p class="tfe-author"><?= htmlspecialchars($data['authors'] ?? 'Auteur inconnu') ?></p>
|
||||
|
||||
<!-- Title is the primary heading; author is metadata -->
|
||||
<h1 class="tfe-title">
|
||||
<?= htmlspecialchars($data['title']) ?>
|
||||
<?php if (!empty($data['subtitle'])): ?>
|
||||
@@ -26,8 +26,6 @@ extract($ctrl->handle());
|
||||
<?php endif; ?>
|
||||
</h1>
|
||||
|
||||
<p class="tfe-author"><?= htmlspecialchars($data['authors'] ?? 'Auteur inconnu') ?></p>
|
||||
|
||||
<dl>
|
||||
<?php if (!empty($data['orientation'])): ?>
|
||||
<div>
|
||||
@@ -78,24 +76,31 @@ extract($ctrl->handle());
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($data['jury_president'])): ?>
|
||||
<?php if (!empty($promoteursInternes)): ?>
|
||||
<div>
|
||||
<dt>Promoteur·ice interne :</dt>
|
||||
<dd><?= htmlspecialchars(implode(', ', $promoteursInternes)) ?></dd>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($promoteursExternes)): ?>
|
||||
<div>
|
||||
<dt>Promoteur·ice externe :</dt>
|
||||
<dd><?= htmlspecialchars(implode(', ', $promoteursExternes)) ?></dd>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($juryPresidents)): ?>
|
||||
<div>
|
||||
<dt>Président·e du jury :</dt>
|
||||
<dd><?= htmlspecialchars($data['jury_president']) ?></dd>
|
||||
<dd><?= htmlspecialchars(implode(', ', $juryPresidents)) ?></dd>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($data['jury_promoteurs'])): ?>
|
||||
<div>
|
||||
<dt>Promoteur·ice :</dt>
|
||||
<dd><?= htmlspecialchars($data['jury_promoteurs']) ?></dd>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($data['jury_lecteurs'])): ?>
|
||||
<?php if (!empty($juryLecteurs)): ?>
|
||||
<div>
|
||||
<dt>Lecteur·ices :</dt>
|
||||
<dd><?= htmlspecialchars($data['jury_lecteurs']) ?></dd>
|
||||
<dd><?= htmlspecialchars(implode(', ', $juryLecteurs)) ?></dd>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -121,11 +126,15 @@ extract($ctrl->handle());
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($data['baiu_link'])): ?>
|
||||
<?php
|
||||
$_contactHref = htmlspecialchars($data['baiu_link']);
|
||||
$_contactLabel = preg_replace('#^https?://#i', '', rtrim($data['baiu_link'], '/'));
|
||||
?>
|
||||
<div>
|
||||
<dt>Contact :</dt>
|
||||
<dd>
|
||||
<a href="<?= htmlspecialchars($data['baiu_link']) ?>" target="_blank" rel="noopener">
|
||||
<?= htmlspecialchars($data['baiu_link']) ?>
|
||||
<a href="<?= $_contactHref ?>" target="_blank" rel="noopener">
|
||||
<?= htmlspecialchars($_contactLabel) ?>
|
||||
<span class="sr-only">(ouvre dans un nouvel onglet)</span>
|
||||
</a>
|
||||
</dd>
|
||||
|
||||
@@ -78,6 +78,10 @@ class TfeController
|
||||
// Caption (WebVTT) files — N-th VTT is paired with the N-th <video>
|
||||
$captionFiles = $this->collectCaptionPaths($data['files'] ?? []);
|
||||
|
||||
// Jury members with interne/externe split
|
||||
$jury = $this->db->getThesisJury($thesisId);
|
||||
$juryByRole = $this->splitJuryByRole($jury);
|
||||
|
||||
// Page meta
|
||||
$metaDescription = $this->buildMetaDescription($data['synopsis'] ?? '');
|
||||
$ogTags = $this->buildOgTags($data, $thesisId, $metaDescription);
|
||||
@@ -92,6 +96,10 @@ class TfeController
|
||||
'accessTypeId' => $accessTypeId,
|
||||
'isInterdit' => $isInterdit,
|
||||
'captionFiles' => $captionFiles,
|
||||
'juryPresidents' => $juryByRole['presidents'],
|
||||
'promoteursInternes' => $juryByRole['internes'],
|
||||
'promoteursExternes' => $juryByRole['externes'],
|
||||
'juryLecteurs' => $juryByRole['lecteurs'],
|
||||
|
||||
// Page meta
|
||||
'pageTitle' => $pageTitle,
|
||||
@@ -169,6 +177,40 @@ class TfeController
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Split jury members by role and internal/external flag.
|
||||
*
|
||||
* @param array<int, array<string, mixed>> $jury
|
||||
* @return array{presidents: list<string>, internes: list<string>, externes: list<string>, lecteurs: list<string>}
|
||||
*/
|
||||
private function splitJuryByRole(array $jury): array
|
||||
{
|
||||
$result = ['presidents' => [], 'internes' => [], 'externes' => [], 'lecteurs' => []];
|
||||
|
||||
foreach ($jury as $member) {
|
||||
$name = $member['name'] ?? '';
|
||||
if ($name === '') continue;
|
||||
|
||||
switch ($member['role']) {
|
||||
case 'president':
|
||||
$result['presidents'][] = $name;
|
||||
break;
|
||||
case 'promoteur':
|
||||
if ((int)$member['is_external'] === 1) {
|
||||
$result['externes'][] = $name;
|
||||
} else {
|
||||
$result['internes'][] = $name;
|
||||
}
|
||||
break;
|
||||
case 'lecteur':
|
||||
$result['lecteurs'][] = $name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an ordered list of VTT caption file paths from the files array.
|
||||
* The N-th entry corresponds to the N-th <video> element in document order.
|
||||
|
||||
Reference in New Issue
Block a user