Interface locale : rtg interface (voyages, sources, journées, récit, génération)

Serveur web local sans dépendance nouvelle (http.server, Jinja2, htmx et
Leaflet fournis). Écrans : Mes voyages, nouveau voyage, un voyage (journées
avec carte, rattachement des traces sans heures à un jour avec suggestion
d'après les photos, sources avec choix de dossier, infos et récit,
génération avec avancement et arrêt), réglages du site et vérification
des outils.

Chaîne : jours imposés ([dates_traces] dans voyage.toml), arrêt propre de
la compression, suivi des étapes, état structuré (site.tableau_etat),
titre du voyage repris de voyage.toml sur l'accueil.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mf2kATMtqVABHSYdxJMnim
This commit is contained in:
FabriceandClaude Opus 5.5 committed 2026-10-09 14:07:47 +00:00
1 parent 608334b9d8
commit 06153a227e
37 files changed
+2618 -57

No files matched your search

+94
View File
@@ -0,0 +1,94 @@
<div class="analyse">
{% if not a.details_traces %}
<p class="vide">Aucune trace GPS. Indiquez où elles se trouvent dans l'onglet <a href="#sources">Sources</a>.</p>
{% else %}
<div class="carte" data-couches='{{ couches|tojson }}'></div>
{% if non_datees %}
<div class="encart attention">
<h3>{{ non_datees|length }} trace{{ 's' if non_datees|length > 1 }} à rattacher à un jour</h3>
<p class="aide">Ces traces n'ont pas d'heures (itinéraire préparé, par exemple) et leur nom ne contient pas de date au format AAAA-MM-JJ.
Choisissez le jour de chacune ; les traces en pointillé sur la carte sont celles-ci.</p>
<table>
<tbody>
{% for nom in non_datees %}
{% set d = a.details_traces|selectattr("nom", "equalto", nom)|first %}
<tr>
<td><code>{{ nom }}</code> <span class="discret">{{ d.km|round|int }} km</span></td>
<td>
<form class="en-ligne" hx-post="/voyage/date" hx-target="#analyse">
<input type="hidden" name="v" value="{{ v.chemin }}"><input type="hidden" name="fichier" value="{{ nom }}">
<input type="date" name="jour" required><button>OK</button>
</form>
</td>
<td>
{% set s = suggestions.get(nom) %}
{% if s and s[0] %}
<button class="petit" hx-post="/voyage/date" hx-target="#analyse"
hx-vals='{{ {"v": v.chemin, "fichier": nom, "jour": s[0].isoformat()}|tojson }}'
title="{{ s[1] }} photo(s) ou vidéo(s) de ce jour prises le long de la trace">Le {{ s[0]|date_longue }} ?</button>
{% else %}<span class="discret">pas de suggestion</span>{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if non_datees|length > 1 %}
<form class="en-ligne" hx-post="/voyage/dates-suite" hx-target="#analyse">
<input type="hidden" name="v" value="{{ v.chemin }}">
Ou bien, dans l'ordre des noms, un jour par trace à partir du <input type="date" name="debut" required>
<button>Répartir</button>
</form>
{% endif %}
</div>
{% endif %}
<h3>{{ jours|length }} journée{{ 's' if jours|length > 1 }} · {{ total_km|round|int }} km</h3>
<table class="jours">
<thead><tr><th>Jour</th><th class="num">km</th><th>Traces</th></tr></thead>
<tbody>
{% for j in jours %}
<tr><td><span class="puce" style="background: {{ j.couleur }}"></span> {{ j.date|date_longue }}</td>
<td class="num">{{ j.km|round|int }}</td>
<td class="discret">{{ j.traces|join(", ") }}</td></tr>
{% endfor %}
</tbody>
</table>
<details>
<summary>Les {{ a.details_traces|length }} fichier{{ 's' if a.details_traces|length > 1 }} de traces et leur jour</summary>
<p class="aide">Un jour imposé ici remplace celui que RTG déduit des heures ou du nom du fichier.</p>
<table>
<thead><tr><th>Fichier</th><th>Jour(s) retenu(s)</th><th>Jour imposé</th></tr></thead>
<tbody>
{% for d in a.details_traces %}
<tr>
<td><code title="{{ d.fichier }}">{{ d.nom }}</code>{% if d.erreur %} <span class="pastille attention" title="{{ d.erreur }}">illisible</span>{% endif %}</td>
<td>{% for j in d.jours %}{{ j|date_courte }}{{ ", " if not loop.last }}{% else %}<span class="discret">—</span>{% endfor %}</td>
<td>
<form class="en-ligne" hx-post="/voyage/date" hx-target="#analyse">
<input type="hidden" name="v" value="{{ v.chemin }}"><input type="hidden" name="fichier" value="{{ d.nom }}">
<input type="date" name="jour" value="{{ d.impose.isoformat() if d.impose else '' }}">
<button class="petit">{{ "Changer" if d.impose else "Imposer" }}</button>
</form>
{% if d.impose %}
<button class="petit" hx-post="/voyage/date" hx-target="#analyse"
hx-vals='{{ {"v": v.chemin, "fichier": d.nom, "jour": ""}|tojson }}'>Annuler</button>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</details>
{% endif %}
{% set autres = a.a_verifier|reject("commence_par", "Trace à rattacher")|list %}
{% if autres %}
<div class="encart attention">
<h3>À vérifier</h3>
<ul>{% for m in autres %}<li>{{ m }}</li>{% endfor %}</ul>
</div>
{% endif %}
<p class="discret">Photos trouvées : {{ a.photos|length }} · Vidéos trouvées : {{ a.videos|length }}</p>
</div>
+5
View File
@@ -0,0 +1,5 @@
{% if tache and tache.en_cours %}
<a class="bandeau en-cours" href="/tache">⏳ {{ tache.libelle }} · {{ tache.etape }}{% if tache.etape_voyage %} ({{ tache.etape_voyage }}){% endif %}{% if tache.video and tache.video.rang %} · vidéo {{ tache.video.rang[0] }}/{{ tache.video.rang[1] }} ({{ tache.video.pct|int }} %){% endif %}</a>
{% elif tache %}
<a class="bandeau {{ 'ok' if tache.ok else 'echec' }}" href="/tache">{{ "✓" if tache.ok else "⚠️" }} {{ tache.libelle }} : {{ tache.etape|lower }}</a>
{% endif %}
+27
View File
@@ -0,0 +1,27 @@
<div class="fenetre-fond" data-fermer-fond>
<div class="fenetre" role="dialog" aria-label="Choisir un dossier">
<h3>Choisir un dossier de {{ libelle }}</h3>
<div class="raccourcis">
{% for nom, c in raccourcis %}
<button class="petit" hx-get="/dossiers?cle={{ cle }}&v={{ v|url }}&chemin={{ c|url }}" hx-target="#fenetre">{{ nom }}</button>
{% endfor %}
</div>
<p class="chemin"><code>{{ chemin }}</code></p>
<ul class="liste-dossiers">
{% if parent %}<li><a href="#" hx-get="/dossiers?cle={{ cle }}&v={{ v|url }}&chemin={{ parent|url }}" hx-target="#fenetre">⬆︎ Dossier parent</a></li>{% endif %}
{% for d in sous %}
<li><a href="#" hx-get="/dossiers?cle={{ cle }}&v={{ v|url }}&chemin={{ (chemin.rstrip(sep) ~ sep ~ d)|url }}" hx-target="#fenetre">📁 {{ d }}</a></li>
{% else %}
<li class="vide">Pas de sous-dossier</li>
{% endfor %}
</ul>
<div class="pied">
<span class="{{ 'ok' if nb else 'discret' }}">{{ nb }} fichier{{ 's' if nb > 1 }} de {{ libelle }} ici</span>
<span>
<button data-fermer>Annuler</button>
<button class="principal" data-fermer hx-post="/voyage/sources" hx-target="#sources" hx-swap="outerHTML"
hx-vals='{{ {"v": v, "cle": cle, "action": "ajouter", "chemin": chemin}|tojson }}'>Choisir ce dossier</button>
</span>
</div>
</div>
</div>
+12
View File
@@ -0,0 +1,12 @@
<div class="infos">
<div><span class="etiquette">Rushs (sources)</span><strong>{{ i.nb_rushs }}</strong> vidéo{{ 's' if i.nb_rushs > 1 }} · {{ i.rushs|taille }}</div>
<div><span class="etiquette">Reste à encoder</span>
{% if i.a_encoder %}<strong>{{ i.a_encoder }}</strong> · {{ i.a_encoder_octets|taille }}{% else %}<strong>rien</strong>{% endif %}</div>
<div><span class="etiquette">Vidéos web</span>{{ i.videos_web|taille }}{% if i.apercus %} <span class="discret">+ aperçus {{ i.apercus|taille }}</span>{% endif %}</div>
<div><span class="etiquette">Photos</span>{{ i.photos|taille }} <span class="discret">→ web {{ i.photos_web|taille }}</span></div>
<div><span class="etiquette">Espace libre (site)</span>{{ i.libre|taille }}</div>
<div><span class="etiquette">Encodage</span>
{{ noms_qualites.get(cfg.video.qualite, cfg.video.qualite) }} · {{ "matériel" if i.encodage.encodeur == "vt" else "processeur" }}
<span class="discret">(HEVC CRF {{ i.encodage.crf_hevc }} / H.264 CRF {{ i.encodage.crf_h264 }})</span>
<a href="/reglages#video">changer</a></div>
</div>
+31
View File
@@ -0,0 +1,31 @@
<div id="sources" class="sources">
{% for cle, s in sources.items() %}
<div class="bloc-source">
<h3>{{ s.libelle }}</h3>
<ul>
{% for l in s.lignes %}
<li class="{{ '' if l.existe else 'manque' }}">
<code title="{{ l.absolu }}">{{ l.source }}</code>
{% if l.existe %}<span class="pastille {{ 'ok' if l.nb else 'neutre' }}">{{ l.nb }} fichier{{ 's' if l.nb > 1 }}</span>
{% elif l.lien_casse %}<span class="pastille attention">disque débranché ?</span>
{% else %}<span class="pastille attention">introuvable</span>{% endif %}
<button class="petit" hx-post="/voyage/sources" hx-target="#sources" hx-swap="outerHTML"
hx-vals='{{ {"v": v.chemin, "cle": cle, "action": "retirer", "chemin": l.source}|tojson }}'>Retirer</button>
</li>
{% else %}
<li class="vide">Aucune source</li>
{% endfor %}
</ul>
<div class="ajout">
<button hx-get="/dossiers?cle={{ cle }}&v={{ v.chemin|url }}" hx-target="#fenetre">Choisir un dossier…</button>
<form hx-post="/voyage/sources" hx-target="#sources" hx-swap="outerHTML" class="en-ligne">
<input type="hidden" name="v" value="{{ v.chemin }}">
<input type="hidden" name="cle" value="{{ cle }}">
<input type="hidden" name="action" value="ajouter">
<input name="chemin" placeholder="ou collez un chemin (dossier ou fichier)">
<button>Ajouter</button>
</form>
</div>
</div>
{% endfor %}
</div>
+2
View File
@@ -0,0 +1,2 @@
<pre class="journal court">{{ lignes|join("\n") }}</pre>
<p class="discret">Disque du site : {{ libre|taille }} libres sur {{ total|taille }} · <code>{{ racine }}</code></p>
+24
View File
@@ -0,0 +1,24 @@
{% if message %}<div class="erreur">{{ message }}</div>{% endif %}
{% if tache %}
<div class="tache {{ 'en-cours' if tache.en_cours else ('ok' if tache.ok else 'echec') }}"
{% if tache.en_cours %}hx-get="/tache/fragment{{ '?complet=1' if complet }}" hx-trigger="every 1s" hx-swap="outerHTML"{% endif %}>
<div class="tache-tete">
<strong>{{ tache.libelle }}</strong>
<span class="etape">{{ "⏳ " if tache.en_cours }}{{ tache.etape }}{% if tache.etape_voyage %} — {{ tache.etape_voyage }}{% endif %}</span>
<span class="discret">{{ tache.duree }}</span>
{% if tache.en_cours %}
<button class="danger petit" hx-post="/tache/arreter" hx-target="closest .tache" hx-swap="outerHTML">Arrêter</button>
{% endif %}
</div>
{% if tache.video and tache.video.rang %}
{% set i, n = tache.video.rang %}
<div class="video-cours">
<div>🎬 Vidéo {{ i }}/{{ n }} · <code>{{ tache.video.nom }}</code> · {{ tache.video.pct|int }} %</div>
<progress max="100" value="{{ tache.video.pct }}"></progress>
<progress class="global" max="{{ n }}" value="{{ i - 1 + tache.video.pct / 100 }}" title="Ensemble des vidéos"></progress>
</div>
{% endif %}
{% set lignes = tache.lignes|list %}
<pre class="journal">{{ lignes[-(1000 if complet else 30):]|join("\n") }}</pre>
</div>
{% endif %}
+31
View File
@@ -0,0 +1,31 @@
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block titre %}{% endblock %} · RTG</title>
<link rel="stylesheet" href="/static/rtg.css">
{% block tete %}{% endblock %}
<script src="/static/htmx.min.js"></script>
</head>
<body>
<header class="barre">
<a class="logo" href="/">RTG</a>
<nav>
<a href="/">Mes voyages</a>
<a href="/reglages">Réglages</a>
<a href="/tache">Traitement</a>
<a href="/site/" target="_blank">Voir le site ↗</a>
</nav>
<div id="bandeau" hx-get="/tache/bandeau" hx-trigger="load, every 2s"></div>
</header>
<main>
{% block contenu %}{% endblock %}
</main>
<div id="messages" aria-live="polite"></div>
<div id="fenetre"></div>
<footer>RoadTripsGenerator {{ version }} · <code>{{ racine }}</code></footer>
<script src="/static/rtg.js"></script>
{% block fin %}{% endblock %}
</body>
</html>
+28
View File
@@ -0,0 +1,28 @@
{% extends "base.html" %}
{% block titre %}Nouveau voyage{% endblock %}
{% block contenu %}
<h1>Nouveau voyage</h1>
<form id="formulaire" class="formulaire" hx-post="/nouveau" hx-target="#formulaire" hx-select="#formulaire" hx-swap="outerHTML">
{% if erreur %}<div class="erreur">{{ erreur }}</div>{% endif %}
<label>Titre
<input name="titre" value="{{ form.titre[0] if form and form.titre else '' }}" placeholder="Été 2027 en Écosse" autofocus>
</label>
<label>Nom du dossier
<input name="dossier" required value="{{ form.dossier[0] if form and form.dossier else '' }}" placeholder="Ete-2027">
<span class="aide">Il apparaît dans l'adresse des pages : évitez espaces et accents.</span>
</label>
<label>Moto
<input name="moto" value="{{ form.moto[0] if form and form.moto else '' }}" placeholder="facultatif">
</label>
<fieldset>
<legend>Rubrique</legend>
<label class="choix"><input type="radio" name="groupe" value="" checked> Voyages <span class="aide">(dossier à la racine du site, affiché sur la carte globale)</span></label>
{% for g in groupes %}
<label class="choix"><input type="radio" name="groupe" value="{{ g }}"> {{ g }}</label>
{% endfor %}
<label class="choix">Nouvelle rubrique : <input name="groupe_nouveau" placeholder="Balades"></label>
</fieldset>
<p><button class="principal">Créer le voyage</button> <a href="/">Annuler</a></p>
<p class="aide">Le dossier est créé avec un <code>voyage.toml</code>. Vous indiquerez ensuite où sont les traces, les vidéos et les photos : elles restent où elles sont.</p>
</form>
{% endblock %}
+68
View File
@@ -0,0 +1,68 @@
{% extends "base.html" %}
{% block titre %}Réglages{% endblock %}
{% block contenu %}
<h1>Réglages du site</h1>
<p class="aide">Enregistrés dans <code>site.toml</code>. Ils valent pour tous les voyages.</p>
<form class="formulaire" hx-post="/reglages" hx-target="#retour-reglages">
<fieldset>
<legend>Site</legend>
<label>Titre <input name="titre" value="{{ cfg.titre }}"></label>
<label>Adresse publique <input name="adresse_publique" value="{{ cfg.adresse_publique }}" placeholder="https://…/">
<span class="aide">Adresse où le site est consulté (liens de partage).</span></label>
<label>Voyages récents par rubrique sur l'accueil <input type="number" min="1" max="12" name="accueil_nb_recents" value="{{ cfg.accueil_nb_recents }}"></label>
</fieldset>
<fieldset id="video">
<legend>Vidéos</legend>
<p class="aide">Chaque rush donne une version 1080p HEVC (H.265) et une version 720p H.264, lue par défaut et partout.</p>
{% set descriptions = {"econome": "fichiers plus légers, légère perte visible sur les scènes chargées",
"equilibree": "réglage d'origine, très bon compromis",
"haute": "presque indiscernable de l'original, fichiers nettement plus gros"} %}
{% for q, p in qualites.items() %}
<label class="choix"><input type="radio" name="qualite" value="{{ q }}" {{ 'checked' if cfg.video.qualite == q }}>
<strong>{{ {"econome": "Économe", "equilibree": "Équilibrée", "haute": "Haute"}[q] }}</strong>
<span class="aide">{{ descriptions[q] }} (CRF {{ p.crf_hevc }} / {{ p.crf_h264 }})</span></label>
{% endfor %}
<label>Encodeur
<select name="encodeur">
<option value="processeur" {{ 'selected' if cfg.video.encodeur != 'materiel' }}>Processeur (x265 / x264) : meilleure qualité à taille égale</option>
<option value="materiel" {{ 'selected' if cfg.video.encodeur == 'materiel' }}>Matériel (VideoToolbox, Mac) : bien plus rapide, fichiers plus gros</option>
</select></label>
<label>Vitesse de l'encodeur processeur
<select name="preset">
{% for p in ["ultrafast", "superfast", "veryfast", "faster", "fast", "medium", "slow", "slower"] %}
<option {{ 'selected' if cfg.video.preset == p }}>{{ p }}</option>
{% endfor %}
</select>
<span class="aide">« medium » par défaut ; plus lent = un peu plus léger à qualité égale.</span></label>
<label>Vidéos encodées en parallèle <input type="number" min="0" max="16" name="jobs" value="{{ cfg.video.jobs }}">
<span class="aide">0 = automatique selon le nombre de cœurs.</span></label>
</fieldset>
<fieldset>
<legend>Photos</legend>
<label>Plus grand côté des photos web (pixels) <input type="number" min="640" max="6000" step="10" name="grand_cote" value="{{ cfg.photos.grand_cote }}"></label>
<label>Qualité JPEG <input type="number" min="40" max="100" name="qualite_photos" value="{{ cfg.photos.qualite }}"></label>
<label class="choix"><input type="checkbox" name="positions_deduites" {{ 'checked' if cfg.photos.positions_deduites }}>
Placer les photos sans GPS d'après l'heure et la trace (à 5 minutes près)</label>
</fieldset>
<fieldset>
<legend>Journées</legend>
<label>Fuseau horaire <input name="fuseau" value="{{ cfg.journees.fuseau }}" placeholder="Europe/Paris"></label>
<label>Heure de changement de jour <input type="number" min="0" max="12" name="coupure_heure" value="{{ cfg.journees.coupure_heure }}">
<span class="aide">Une étape finie après minuit mais avant cette heure reste sur la veille.</span></label>
</fieldset>
<fieldset>
<legend>Carte globale</legend>
<label>Cartes Google My Maps à reprendre (une adresse par ligne)
<textarea name="mymaps" rows="3">{{ cfg.carte_globale.mymaps|join("\n") }}</textarea>
<span class="aide">Leurs tracés sont récupérés une fois et ajoutés à la carte de l'accueil, sur fond OpenStreetMap.</span></label>
</fieldset>
<p><button class="principal">Enregistrer</button> <span id="retour-reglages"></span></p>
</form>
<h2>Système</h2>
<div hx-get="/systeme" hx-trigger="load"><p class="chargement">Vérification des outils…</p></div>
{% endblock %}
+11
View File
@@ -0,0 +1,11 @@
{% extends "base.html" %}
{% block titre %}Traitement{% endblock %}
{% block contenu %}
<h1>Traitement</h1>
{% if tache %}
{% if tache.voyage %}<p><a href="/voyage?v={{ tache.voyage|url }}">← {{ tache.voyage }}</a></p>{% endif %}
{% with complet = True %}{% include "_tache.html" %}{% endwith %}
{% else %}
<p class="vide">Aucun traitement lancé depuis l'ouverture de l'interface.</p>
{% endif %}
{% endblock %}
+68
View File
@@ -0,0 +1,68 @@
{% extends "base.html" %}
{% block titre %}{{ v.titre }}{% endblock %}
{% block tete %}<link rel="stylesheet" href="/static/leaflet.css"><script src="/static/leaflet.js"></script>{% endblock %}
{% block contenu %}
<div class="entete-page">
<div>
<p class="fil"><a href="/">Mes voyages</a> › {{ v.rubrique }}</p>
<h1>{{ v.titre }}</h1>
<p class="discret">{{ [periode(v.debut, v.fin), v.moto]|select|join(" · ") }} <code>{{ v.chemin }}</code></p>
</div>
<div class="actions">
{% if v.genere %}<a class="bouton" href="/site/{{ v.chemin }}/index.html" target="_blank">Voir la page ↗</a>{% endif %}
</div>
</div>
<nav class="onglets">
<a href="#journees">Journées</a>
<a href="#sources">Sources</a>
<a href="#infos">Infos et récit</a>
<a href="#generer">Générer</a>
</nav>
<section class="onglet" id="onglet-journees">
<div id="analyse" hx-get="/voyage/analyse?v={{ v.chemin|url }}" hx-trigger="load, sources-modifiees from:body, tache-finie from:body">
<p class="chargement">Lecture des traces…</p>
</div>
</section>
<section class="onglet" id="onglet-sources">
<p class="aide">Indiquez où se trouvent les fichiers d'origine : un ou plusieurs dossiers (ou fichiers) par type, sur n'importe quel disque.
RTG ne les modifie jamais : les traces sont recopiées par journée dans <code>routes/</code>, les photos dans <code>photos/</code> (HEIC converties en JPEG),
et les vidéos sont lues directement pour l'encodage.</p>
{% include "_sources.html" %}
</section>
<section class="onglet" id="onglet-infos">
<form class="formulaire" hx-post="/voyage/infos" hx-target="find .retour">
<input type="hidden" name="v" value="{{ v.chemin }}">
<label>Titre <input name="titre" value="{{ cfg.titre }}"></label>
<label>Moto <input name="moto" value="{{ cfg.moto }}" placeholder="facultatif : affichée dans la légende de la carte globale"></label>
<p class="aide">Rubrique : <strong>{{ v.rubrique }}</strong> (elle vient du dossier où est rangé le voyage).</p>
<p><button class="principal">Enregistrer</button> <span class="retour"></span></p>
</form>
<form class="formulaire large" hx-post="/voyage/recit" hx-target="find .retour">
<input type="hidden" name="v" value="{{ v.chemin }}">
<label>Récit (<code>voyage.md</code>, en Markdown)
<textarea name="recit" rows="18" spellcheck="true">{{ recit }}</textarea>
</label>
<p class="aide">Liens spéciaux : <code>[Carte du jour](jour:2026-08-01)</code> ouvre la carte d'un jour,
<code>[Le col](media:photos/IMG_4831.jpeg)</code> un média du voyage.</p>
<p><button class="principal">Enregistrer le récit</button> <span class="retour"></span></p>
</form>
</section>
<section class="onglet" id="onglet-generer">
<div hx-get="/voyage/infos-utiles?v={{ v.chemin|url }}" hx-trigger="load, sources-modifiees from:body, tache-finie from:body"><p class="chargement">Calcul des tailles…</p></div>
<form class="formulaire" hx-post="/tache/lancer" hx-target="#tache-zone">
<input type="hidden" name="v" value="{{ v.chemin }}">
<label class="choix"><input type="checkbox" name="sans_compression"> Sans encoder les vidéos <span class="aide">(photos, positions et pages seulement)</span></label>
<label class="choix"><input type="checkbox" name="force"> Tout refaire, même ce qui est déjà à jour</label>
<p>
<button class="principal" name="quoi" value="generer">Générer le voyage</button>
<button name="quoi" value="compresser">Encoder les vidéos seulement</button>
</p>
</form>
<div id="tache-zone">{% if tache and tache.voyage == v.chemin %}{% include "_tache.html" %}{% endif %}</div>
</section>
{% endblock %}
+63
View File
@@ -0,0 +1,63 @@
{% extends "base.html" %}
{% block titre %}Mes voyages{% endblock %}
{% block contenu %}
{% if not site_existe %}
<div class="encart">
<p><strong>Ce dossier n'est pas encore un site RTG.</strong> Il n'y a pas de <code>site.toml</code> dans <code>{{ racine }}</code>.</p>
<p><button class="principal" hx-post="/site/creer">Créer le site dans ce dossier</button></p>
<p class="aide">Pour un autre dossier, relancez avec <code>rtg interface --racine CHEMIN</code>.</p>
</div>
{% endif %}
<div class="entete-page">
<h1>Mes voyages <span class="compte">{{ nb }}</span></h1>
<div class="actions">
<a class="bouton principal" href="/nouveau">+ Nouveau voyage</a>
<form hx-post="/tache/lancer" hx-target="#tache-zone">
<input type="hidden" name="quoi" value="accueil">
<button title="index.html, pages des rubriques et carte globale">Refaire l'accueil</button>
</form>
</div>
</div>
<div id="tache-zone"></div>
{% for g, liste in groupes %}
<section>
<h2>{{ g }} <span class="compte">{{ liste|length }}</span></h2>
<table class="voyages">
<thead><tr><th>Voyage</th><th>Dates</th><th class="num">Jours</th><th>Vidéos web</th><th>Photos web</th><th>Pages</th><th></th></tr></thead>
<tbody>
{% for v in liste %}
<tr>
<td><a href="/voyage?v={{ v.chemin|url }}"><strong>{{ v.titre }}</strong></a>
{% if v.moto %}<div class="discret">{{ v.moto }}</div>{% endif %}</td>
<td>{{ periode(v.debut, v.fin) }}</td>
<td class="num">{{ v.jours or "—" }}</td>
<td>
{% if v.videos_sources %}
<span class="pastille {{ 'ok' if v.videos_faites == v.videos_sources else 'attention' }}">{{ v.videos_faites }}/{{ v.videos_sources }}</span>
{% elif v.videos_web %}
<span class="pastille neutre" title="Les rushs ne sont pas accessibles (disque débranché ?)">{{ v.videos_web }} · sources absentes</span>
{% else %}<span class="discret">—</span>{% endif %}
</td>
<td>
{% if v.photos %}
<span class="pastille {{ 'ok' if v.photos_web >= v.photos else 'attention' }}">{{ v.photos_web }}/{{ v.photos }}</span>
{% else %}<span class="discret">—</span>{% endif %}
</td>
<td>
{% if not v.genere %}<span class="pastille neutre">jamais générées</span>
{% elif v.pages_a_jour %}<span class="pastille ok">à jour</span>
{% else %}<span class="pastille attention">à refaire</span>{% endif %}
{% if v.a_verifier %}<span class="pastille attention" title="{{ v.a_verifier|join('\n') }}">⚠️ {{ v.a_verifier|length }}</span>{% endif %}
</td>
<td class="droite">{% if v.genere %}<a href="/site/{{ v.chemin }}/index.html" target="_blank">Voir ↗</a>{% endif %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</section>
{% else %}
<p class="vide">Aucun voyage pour l'instant. Commencez par « Nouveau voyage ».</p>
{% endfor %}
{% endblock %}