PageSourceSearch

https://ffcvcalendar.netlify.app/admin

html ffcvcalendar.netlify.app collected 2026-10-03 10:43:50 UTC 17,740 bytes, 351 lines download raw bytes

1<!DOCTYPE html>
2<html lang="fr">
3<head>
4  <meta charset="UTF-8" />
5<!-- This site is hosted on Netlify. Anyone can build and deploy a site
6     like this one for free: https://netlify.new/?utm_campaign=loops&utm_source=ai-legible&utm_medium=owned&utm_content=comment&utm_id=e4b63396-6d96-494c-9aa4-18feb85b095c
7     Netlify hosting facts for this site: static/SSR served via Netlify Edge. -->
8  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
9  <title>Administration — Calendrier des courses FFCV</title>
10  <link rel="icon" type="image/png" href="img/logo-ffcv.png" />
11  <link rel="preconnect" href="https://fonts.googleapis.com" />
12  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
13  <link href="https://fonts.googleapis.com/css2?family=Chivo:wght@400;700;900&family=Roboto:wght@400;500;700&display=swap" rel="stylesheet" />
14  <link rel="stylesheet" href="css/styles.css" />
15</head>
16<body class="page-admin">
17  <header class="site-header">
18    <div class="wrap">
19      <div class="logo"><img src="img/logo-ffcv.png" alt="Fédération Française de Char à Voile" /></div>
20      <div>
21        <h1>Administration du calendrier</h1>
22        <p>Validation des propositions et gestion des courses</p>
23      </div>
24      <div class="header-actions">
25        <a class='btn btn-ghost masque' href='/documentation' id='lien-doc'>📖 Documentation</a>
26        <a class='btn btn-ghost' href='/'>← Voir le calendrier public</a>
27        <button class="btn btn-secondary masque" id="btn-deconnexion">Se déconnecter</button>
28      </div>
29    </div>
30  </header>
31
32  <main>
33    <div id="bandeau-config" class="bandeau-config masque">
34      ⚠️ <strong>Configuration requise.</strong> Renseignez votre URL et clé Supabase dans <code>js/config.js</code>.
35    </div>
36
37    <!-- Écran de connexion -->
38    <section id="ecran-connexion" class="connexion-wrap masque">
39      <div class="carte-connexion">
40        <h2>Connexion</h2>
41        <div id="conn-alerte"></div>
42        <form id="form-connexion">
43          <div class="champ">
44            <label for="email">Adresse email</label>
45            <input type="email" id="email" required autocomplete="username" />
46          </div>
47          <div class="champ">
48            <label for="mdp">Mot de passe</label>
49            <input type="password" id="mdp" required autocomplete="current-password" />
50          </div>
51          <button class="btn btn-primary" type="submit" id="btn-connexion" style="width:100%">Se connecter</button>
52        </form>
53        <p style="margin-top:14px;text-align:center">
54          <a href="#" id="lien-oubli" style="font-size:.9rem">Mot de passe oublié ou première connexion</a>
55        </p>
56
57        <!-- Formulaire mot de passe oublié -->
58        <form id="form-oubli" class="masque" style="margin-top:6px">
59          <div id="oubli-alerte"></div>
60          <p class="aide">Saisissez votre email : vous recevrez un lien pour définir un nouveau mot de passe.</p>
61          <div class="champ">
62            <label for="oubli-email">Adresse email</label>
63            <input type="email" id="oubli-email" required autocomplete="username" />
64          </div>
65          <button class="btn btn-primary" type="submit" id="btn-oubli" style="width:100%">Envoyer le lien</button>
66          <p style="margin-top:12px;text-align:center">
67            <a href="#" id="lien-retour-connexion" style="font-size:.9rem">← Retour à la connexion</a>
68          </p>
69        </form>
70      </div>
71    </section>
72
73    <!-- Tableau de bord -->
74    <section id="ecran-admin" class="masque">
75      <div class="admin-barre">
76        <div class="onglets">
77          <button class="onglet actif" data-onglet="propositions">
78            Propositions <span class="compteur" id="cpt-prop">0</span>
79          </button>
80          <button class="onglet" data-onglet="toutes">Toutes les courses</button>
81          <button class="onglet" data-onglet="parametres">⚙️ Paramètres</button>
82        </div>
83        <div class="admin-actions">
84          <button class="btn btn-secondary" id="btn-export-xlsx" title="Exporter le tableau filtré au format Excel">⬇ Exporter (Excel)</button>
85          <button class="btn btn-primary" id="btn-nouvelle">+ Nouvelle course</button>
86        </div>
87      </div>
88
89      <div id="admin-alerte"></div>
90
91      <div class="filtres" id="admin-filtres">
92        <div class="champ">
93          <label for="a-recherche">Recherche</label>
94          <input type="search" id="a-recherche" placeholder="Lieu, nom, type…" />
95        </div>
96        <div class="champ">
97          <label for="a-saison">Saison</label>
98          <select id="a-saison"><option value="">Toutes</option></select>
99        </div>
100        <div class="champ champ-reset">
101          <label>&nbsp;</label>
102          <button type="button" class="btn btn-secondary" id="btn-reset-filtres" title="Effacer tous les filtres de colonne">✖ Réinitialiser les filtres</button>
103        </div>
104      </div>
105
106      <div class="tableau-wrap">
107        <table class="courses">
108          <thead>
109            <tr>
110              <th>Modifier</th>
111              <th>Dates</th><th>Événement</th><th>Club</th><th>Ligue</th>
112              <th>Lieu</th><th>Type</th><th>Classes</th>
113              <th>Dir. de course</th><th>Prés. de jury</th>
114              <th title="Avis de course (ERS) renseigné">ERC</th>
115              <th title="Lien d'inscription renseigné">Inscr.</th>
116              <th title="Lien des résultats renseigné">Résultat</th>
117              <th title="Lettre de mission — Directeur de course">LM DC</th>
118              <th title="Lettre de mission — Président de jury">LM PJ</th>
119              <th title="Note de frais — Directeur de course">NDF DC</th>
120              <th title="Note de frais — Président de jury">NDF PJ</th>
121              <th>Statut</th><th>Actions</th>
122            </tr>
123            <tr class="filtres-col" id="ligne-filtres">
124              <th></th>
125              <th><input type="search" data-filtre="dates" placeholder="Filtrer…" /></th>
126              <th><input type="search" data-filtre="nom" placeholder="Filtrer…" /></th>
127              <th><select data-filtre="club"><option value="">Tous</option></select></th>
128              <th><select data-filtre="ligue"><option value="">Toutes</option></select></th>
129              <th><input type="search" data-filtre="lieu" placeholder="Filtrer…" /></th>
130              <th><select data-filtre="type_evenement"><option value="">Tous</option></select></th>
131              <th><select data-filtre="classes"><option value="">Toutes</option></select></th>
132              <th><input type="search" data-filtre="directeur_course" placeholder="Filtrer…" /></th>
133              <th><input type="search" data-filtre="directeur_jury" placeholder="Filtrer…" /></th>
134              <th><select data-oui-non="ers_url"><option value="">Tous</option><option value="oui">Oui</option><option value="non">Non</option></select></th>
135              <th><select data-oui-non="inscriptions_url"><option value="">Tous</option><option value="oui">Oui</option><option value="non">Non</option></select></th>
136              <th><select data-oui-non="resultat_url"><option value="">Tous</option><option value="oui">Oui</option><option value="non">Non</option></select></th>
137              <th><select data-oui-non="lm_directeur_course"><option value="">Tous</option><option value="oui">Oui</option><option value="non">Non</option></select></th>
138              <th><select data-oui-non="lm_president_jury"><option value="">Tous</option><option value="oui">Oui</option><option value="non">Non</option></select></th>
139              <th><select data-oui-non="ndf_directeur_course"><option value="">Tous</option><option value="oui">Oui</option><option value="non">Non</option></select></th>
140              <th><select data-oui-non="ndf_president_jury"><option value="">Tous</option><option value="oui">Oui</option><option value="non">Non</option></select></th>
141              <th><select id="a-statut"><option value="">Tous</option></select></th>
142              <th></th>
143            </tr>
144          </thead>
145          <tbody id="corps-tableau"></tbody>
146        </table>
147      </div>
148      <div id="tableau-vide" class="message-vide masque"><span class="emoji">📭</span>Aucune course.</div>
149
150      <!-- Écran des paramètres (types d'événement & statuts) -->
151      <div id="zone-parametres" class="masque">
152        <div class="param-bloc">
153          <div class="param-tete">
154            <h3>Types d'événement</h3>
155            <button class="btn btn-secondary btn-sm" id="ajout-type">+ Ajouter un type</button>
156          </div>
157          <p class="aide">La couleur définit la teinte de la tuile de course dans le calendrier.</p>
158          <div id="liste-param-types" class="param-liste"></div>
159        </div>
160
161        <div class="param-bloc">
162          <div class="param-tete">
163            <h3>Statuts</h3>
164            <button class="btn btn-secondary btn-sm" id="ajout-statut">+ Ajouter un statut</button>
165          </div>
166          <p class="aide">
167            « Public » = une course avec ce statut est visible dans le calendrier public.
168            Les statuts « système » ne sont pas supprimables (« À venir » devient
169            automatiquement « En cours » puis « Passée » selon les dates).
170          </p>
171          <div id="liste-param-statuts" class="param-liste"></div>
172        </div>
173
174        <div class="param-bloc">
175          <div class="param-tete">
176            <h3>Utilisateurs &amp; rôles</h3>
177            <a class='btn btn-secondary btn-sm' href='/arbitre'>Ouvrir l'espace Arbitre →</a>
178          </div>
179          <p class="aide">
180            Cochez les rôles de chaque personne — un même utilisateur peut être
181            <strong>Administrateur</strong> et/ou <strong>Arbitre</strong>.
182            Pour ajouter quelqu'un : email + mot de passe initial (ou mot de passe vide
183            pour un compte existant), puis cochez les rôles. « Arbitre » donne accès à
184            la page de saisie des affectations (directeur de course / président de jury).
185          </p>
186          <div id="utilisateurs-alerte"></div>
187          <div id="liste-utilisateurs" class="param-liste"></div>
188          <form id="form-utilisateur" class="param-ligne" style="margin-top:12px;flex-wrap:wrap" autocomplete="off">
189            <input type="email" id="u-email" placeholder="[email protected]" required style="flex:1;min-width:180px" />
190            <input type="password" id="u-mdp" placeholder="Mot de passe initial (facultatif si compte existant)" style="flex:1;min-width:180px" autocomplete="new-password" />
191            <label class="param-check"><input type="checkbox" id="u-admin" /> Admin</label>
192            <label class="param-check"><input type="checkbox" id="u-arbitre" /> Arbitre</label>
193            <button class="btn btn-primary btn-sm" type="submit" id="bt
193n-ajout-utilisateur">Ajouter</button>
194          </form>
195        </div>
196
197        <div class="param-bloc">
198          <div class="param-tete">
199            <h3>Journal de connexions</h3>
200            <button class="btn btn-secondary btn-sm" id="btn-rafraichir-connexions">↻ Rafraîchir</button>
201          </div>
202          <p class="aide">
203            Enregistre chaque <strong>connexion</strong> et <strong>déconnexion</strong> d'un utilisateur
204            identifié (administrateur ou arbitre). Les 200 dernières entrées sont affichées.
205          </p>
206          <div class="conn-barre">
207            <div class="champ">
208              <label for="conn-filtre-user">Utilisateur</label>
209              <select id="conn-filtre-user"><option value="">Tous</option></select>
210            </div>
211            <div class="champ">
212              <label for="conn-filtre-evt">Événement</label>
213              <select id="conn-filtre-evt">
214                <option value="">Tous</option>
215                <option value="connexion">Connexions</option>
216                <option value="deconnexion">Déconnexions</option>
217              </select>
218            </div>
219          </div>
220          <div id="connexions-alerte"></div>
221          <div class="tableau-wrap">
222            <table class="courses" id="table-connexions">
223              <thead>
224                <tr><th>Date &amp; heure</th><th>Événement</th><th>Utilisateur</th><th>Rôles</th><th>Navigateur</th></tr>
225              </thead>
226              <tbody id="corps-connexions"></tbody>
227            </table>
228          </div>
229          <div class="conn-purge">
230            <label for="conn-purge-periode">Purger les entrées de plus de</label>
231            <select id="conn-purge-periode">
232              <option value="30">30 jours</option>
233              <option value="90">90 jours</option>
234              <option value="180">180 jours</option>
235              <option value="365" selected>1 an</option>
236              <option value="0">tout le journal</option>
237            </select>
238            <button class="btn btn-danger btn-sm" id="btn-purger-connexions">Purger</button>
239          </div>
240        </div>
241      </div>
242    </section>
243  </main>
244
245  <!-- Modale d'édition / création -->
246  <div class="modale-fond" id="modale-edition">
247    <div class="modale" role="dialog" aria-modal="true">
248      <div class="modale-tete">
249        <h2 id="titre-edition">Modifier la course</h2>
250        <button class="fermer" data-fermer aria-label="Fermer">&times;</button>
251      </div>
252      <div class="modale-corps">
253        <div id="edit-alerte"></div>
254        <form id="form-edition" class="form-grille">
255          <input type="hidden" name="id" />
256          <div class="pleine">
257            <label>Nom de l'événement</label>
258            <input name="nom" />
259          </div>
260          <div>
261            <label>Club organisateur</label>
262            <input name="club" list="liste-clubs" placeholder="Ex. Club de char à voile de…" />
263            <datalist id="liste-clubs"></datalist>
264          </div>
265          <div>
266            <label>Ligue de rattachement</label>
267            <input name="ligue" list="liste-ligues" placeholder="Ex. Ligue Hauts-de-France" />
268            <datalist id="liste-ligues"></datalist>
269          </div>
270          <div>
271            <label>Lieu</label>
272            <input name="lieu" />
273          </div>
274          <div>
275            <label>Type d'événement</label>
276            <input name="type_evenement" list="liste-types" />
277            <datalist id="liste-types"></datalist>
278          </div>
279          <div>
280            <label>Date de début</label>
281            <input type="date" name="date_debut" />
282          </div>
283          <div>
284            <label>Date de fin</label>
285            <input type="date" name="date_fin" />
286          </div>
287          <div>
288            <label>Saison</label>
289            <input name="saison" placeholder="2026/2027" />
290          </div>
291          <div>
292            <label>Statut</label>
293            <select name="statut" id="edit-statut"></select>
294            <p class="aide">« À venir » devient seul « En cours » puis « Passée » selon les dates.</p>
295          </div>
296          <div class="pleine">
297            <label>Classes concernées</label>
298            <div class="cases-classes" id="edit-classes"></div>
299          </div>
300          <div>
301            <label>Directeur de course</label>
302            <input name="directeur_course" />
303          </div>
304          <div>
305            <label>Président de jury</label>
306            <input name="directeur_jury" />
307          </div>
308          <div>
309            <label>Lien d'inscription</label>
310            <input name="inscriptions_url" type="url" placeholder="https://…" />
311          </div>
312          <div class="pleine">
313            <label>Avis de course / ERS (lien)</label>
314            <input name="ers_url" type="url" placeholder="https://…" />
315          </div>
316          <div class="pleine">
317            <label>Suivi administratif</label>
318            <div class="cases-classes">
319              <label class="case-classe"><input type="checkbox" name="lm_directeur_course" /> Lettre mission — Directeur de course</label>
320              <label class="case-classe"><input type="checkbox" name="lm_president_jury" /> Lettre mission — Président de jury</label>
321              <label class="case-classe"><input type="checkbox" name="ndf_directeur_course" /> Note de frais — Directeur de course</label>
322              <label class="case-classe"><input type="checkbox" name="ndf_president_jury" /> Note de frais — Président de jury</label>
323            </div>
324          </div>
325          <div class="pleine">
326            <label>Résultats (lien)</label>
327            <input name="resultat_url" type="url" placeholder="https://…" />
328          </div>
329          <div class="pleine" id="bloc-proposeur" style="background:var(--sable);padding:12px;border-radius:9px">
330            <label>Informations du proposeur</label>
331            <p class="aide" id="txt-proposeur" style="margin:0"></p>
332          </div>
333        </form>
334      </div>
335      <div class="modale-pied">
336        <button class="btn btn-danger" id="btn-supprimer" style="margin-right:auto">Supprimer</button>
337        <button class="btn btn-outline" data-fermer>Annuler</button>
338        <button class="btn btn-primary" id="btn-enregistrer" form="form-edition" type="submit">Enregistrer</button>
339      </div>
340    </div>
341  </div>
342
343  <footer class="pied-page">Espace réservé aux administrateurs.</footer>
344
345  
345<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
345
346  
346<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/xlsx.full.min.js"></script>
346
347  
347<script src="js/config.js"></script>
347
348  
348<script src="js/common.js"></script>
348
349  
349<script src="js/admin.js"></script>
349
350</body>
351</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.