- Rename "Statistiques" to "Stats" in navbar across all pages - Fix Admin button showing yellow border on click - Fix toggle switch background to blend with dark mode container Co-Authored-By: Claude Opus 4.5 <[email protected]>
1401 lines
65 KiB
HTML
1401 lines
65 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Pool Oscars - Admin</title>
|
||
<script src="./js/app_theme.js"></script>
|
||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
||
<link href="./css/app.css" rel="stylesheet">
|
||
<link rel="preload" as="image" href="img/logo_w.png">
|
||
<script src="./js/app_header_boot.js" defer></script>
|
||
<style>
|
||
.navbar-custom {
|
||
background-color: black;
|
||
color: white;
|
||
}
|
||
.navbar-custom .navbar-brand,
|
||
.navbar-custom .nav-link {
|
||
color: white;
|
||
}
|
||
.navbar-custom .nav-link:hover {
|
||
color: #ddd;
|
||
}
|
||
.navbar-toggler {
|
||
border-color: white;
|
||
}
|
||
.navbar-toggler-icon {
|
||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3E%3Cpath stroke='white' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E");
|
||
}
|
||
|
||
.admin-tabs-content {
|
||
font-size: 0.95rem;
|
||
}
|
||
|
||
.admin-tabs-content .info-line {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
line-height: 1;
|
||
align-self: center;
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.admin-tabs-content .info-line .badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
line-height: 1;
|
||
width: 1.2em;
|
||
height: 1.2em;
|
||
padding: 0;
|
||
}
|
||
|
||
.admin-tabs-content .info-line .info-text {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
line-height: 1;
|
||
}
|
||
|
||
.admin-tabs-content .btn-compact {
|
||
padding: 0.2rem 0.5rem;
|
||
font-size: 0.85rem;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.admin-tabs-content .btn-check-compact {
|
||
padding-left: 0.35rem;
|
||
padding-right: 0.35rem;
|
||
}
|
||
|
||
.admin-tabs-content .app-version-create {
|
||
font-size: 0.95rem;
|
||
}
|
||
|
||
.admin-tabs-content .app-version-create .fw-semibold {
|
||
font-size: 0.95rem;
|
||
}
|
||
|
||
.admin-tabs-content .backup-restore-panel {
|
||
font-size: 0.95rem;
|
||
}
|
||
|
||
.admin-tabs-content .backup-restore-panel .form-label,
|
||
.admin-tabs-content .backup-restore-panel .form-check-label {
|
||
font-size: 0.95rem;
|
||
}
|
||
|
||
.admin-tabs-content .backup-restore-panel .alert {
|
||
font-size: 0.9rem;
|
||
padding: 0.5rem 0.75rem;
|
||
}
|
||
|
||
.admin-tabs-content .btn-check-small {
|
||
height: 1.9rem;
|
||
}
|
||
|
||
.admin-tabs-content .oscar-date-year-select {
|
||
max-width: 220px;
|
||
width: 220px;
|
||
margin-right: 0;
|
||
}
|
||
|
||
.admin-tabs-content .manage-year-select {
|
||
max-width: 110px;
|
||
width: 110px;
|
||
}
|
||
|
||
.admin-tabs-content .oscar-date-input {
|
||
max-width: 220px;
|
||
width: 220px;
|
||
}
|
||
|
||
.admin-tabs-content .points-input-half {
|
||
max-width: 110px;
|
||
width: 110px;
|
||
}
|
||
|
||
.admin-tabs-content .oscar-date-layout {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
.admin-tabs-content .oscar-date-fields {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.admin-tabs-content .oscar-date-fields .form-text {
|
||
margin-top: 0.2rem;
|
||
}
|
||
|
||
.admin-tabs-content .oscar-date-current {
|
||
display: inline-block;
|
||
width: fit-content;
|
||
max-width: 100%;
|
||
padding: 0.4rem 0.65rem;
|
||
min-width: 220px;
|
||
}
|
||
|
||
.admin-tabs-content .btn-check-wide {
|
||
padding-left: 0.55rem;
|
||
padding-right: 0.55rem;
|
||
}
|
||
|
||
.admin-tabs-content h4,
|
||
.admin-tabs-content h5 {
|
||
font-size: 1rem;
|
||
font-weight: 600;
|
||
letter-spacing: 0.01em;
|
||
}
|
||
|
||
.admin-subtabs .nav-link {
|
||
padding: 0.3rem 0.7rem;
|
||
font-size: 0.9rem;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
|
||
html[data-theme="dark"] #adminAddMovieAccordion .accordion-item {
|
||
background-color: var(--app-surface-alt);
|
||
border-color: var(--app-border);
|
||
}
|
||
|
||
html[data-theme="dark"] #adminAddMovieAccordion .accordion-button {
|
||
background-color: var(--app-surface-alt);
|
||
color: var(--app-text);
|
||
box-shadow: none;
|
||
}
|
||
|
||
html[data-theme="dark"] #adminAddMovieAccordion .accordion-button::after {
|
||
filter: invert(1);
|
||
}
|
||
|
||
html[data-theme="dark"] #adminAddMovieAccordion .accordion-button:not(.collapsed) {
|
||
background-color: var(--app-surface);
|
||
color: var(--app-text);
|
||
box-shadow: none;
|
||
}
|
||
|
||
html[data-theme="dark"] #adminAddMovieAccordion .accordion-body {
|
||
background-color: var(--app-surface);
|
||
color: var(--app-text);
|
||
}
|
||
|
||
.admin-movies-add {
|
||
margin-top: 0.5rem;
|
||
}
|
||
|
||
.admin-movies-separator {
|
||
border-top: 1px solid var(--app-border);
|
||
margin: 0.75rem 0;
|
||
}
|
||
|
||
.admin-movies-meta {
|
||
margin: 0 0 0.5rem;
|
||
}
|
||
|
||
.admin-subtabs-tight {
|
||
margin-top: 0;
|
||
}
|
||
|
||
.admin-tabs-content .table,
|
||
.admin-tabs-content .table th,
|
||
.admin-tabs-content .table td {
|
||
font-size: 0.95rem;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body class="bg-light">
|
||
|
||
<nav class="navbar navbar-expand-lg navbar-custom">
|
||
<div class="container">
|
||
<a class="navbar-brand" href="movies.html">
|
||
<img src="img/logo_w.png" width="220" height="70" loading="eager" fetchpriority="high" decoding="async" style="max-width: 220px; height: 70px; object-fit: contain;">
|
||
</a>
|
||
<span class="navbar-text ms-auto me-2 d-lg-none navbar-user-name-mobile" data-user-name></span>
|
||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||
<span class="navbar-toggler-icon"></span>
|
||
</button>
|
||
<div class="collapse navbar-collapse" id="navbarNav">
|
||
<div class="navbar-nav">
|
||
<a href="movies.html" class="nav-link btn btn-outline-light me-2">Films</a>
|
||
<a href="checklist.html" class="nav-link btn btn-outline-light me-2">Checklist</a>
|
||
<a href="user-stat.html" class="nav-link btn btn-outline-light me-2">Stats</a>
|
||
<a href="picks.html" class="nav-link btn btn-outline-light me-2" id="nav-link-picks" style="display: none;">Mes picks</a>
|
||
<a href="control.html" class="nav-link btn btn-outline-light me-2 active">Admin</a>
|
||
</div>
|
||
<div class="d-flex ms-auto mt-2 mt-lg-0 align-items-center gap-2 navbar-user">
|
||
<span id="user-name" class="navbar-text" data-user-name></span>
|
||
<button class="theme-toggle" type="button" data-theme-toggle role="switch" aria-checked="false" aria-label="Basculer le mode sombre">
|
||
<span class="theme-toggle-icon theme-toggle-icon-sun" aria-hidden="true">
|
||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||
<circle cx="12" cy="12" r="4"></circle>
|
||
<path d="M12 2v2"></path>
|
||
<path d="M12 20v2"></path>
|
||
<path d="M4.93 4.93l1.41 1.41"></path>
|
||
<path d="M17.66 17.66l1.41 1.41"></path>
|
||
<path d="M2 12h2"></path>
|
||
<path d="M20 12h2"></path>
|
||
<path d="M4.93 19.07l1.41-1.41"></path>
|
||
<path d="M17.66 6.34l1.41-1.41"></path>
|
||
</svg>
|
||
</span>
|
||
<span class="theme-toggle-thumb" aria-hidden="true"></span>
|
||
<span class="theme-toggle-icon theme-toggle-icon-moon" aria-hidden="true">
|
||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"></path>
|
||
</svg>
|
||
</span>
|
||
</button>
|
||
<button id="log-off" class="btn btn-danger">Déconnexion</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
|
||
<div class="container mt-4 pb-5">
|
||
<div id="response" class="alert d-none" role="alert"></div>
|
||
|
||
<ul class="nav nav-tabs" id="adminTabs" role="tablist">
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link active" id="admin-settings-tab" data-bs-toggle="tab" data-bs-target="#admin-settings" type="button" role="tab" aria-controls="admin-settings" aria-selected="true">
|
||
Réglages
|
||
</button>
|
||
</li>
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link" id="admin-movies-tab" data-bs-toggle="tab" data-bs-target="#admin-movies" type="button" role="tab" aria-controls="admin-movies" aria-selected="false">
|
||
Films
|
||
</button>
|
||
</li>
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link" id="admin-users-tab" data-bs-toggle="tab" data-bs-target="#admin-users" type="button" role="tab" aria-controls="admin-users" aria-selected="false">
|
||
Utilisateurs
|
||
</button>
|
||
</li>
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link" id="admin-modal-100-tab" data-bs-toggle="tab" data-bs-target="#admin-modal-100" type="button" role="tab" aria-controls="admin-modal-100" aria-selected="false">
|
||
Modal 100%
|
||
</button>
|
||
</li>
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link" id="admin-winners-tab" data-bs-toggle="tab" data-bs-target="#admin-winners" type="button" role="tab" aria-controls="admin-winners" aria-selected="false">
|
||
Gagnants
|
||
</button>
|
||
</li>
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link" id="admin-categories-tab" data-bs-toggle="tab" data-bs-target="#admin-categories" type="button" role="tab" aria-controls="admin-categories" aria-selected="false">
|
||
Catégories Oscars
|
||
</button>
|
||
</li>
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link" id="admin-backup-tab" data-bs-toggle="tab" data-bs-target="#admin-backup" type="button" role="tab" aria-controls="admin-backup" aria-selected="false">
|
||
Backup / Restore
|
||
</button>
|
||
</li>
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link" id="admin-version-tab" data-bs-toggle="tab" data-bs-target="#admin-version" type="button" role="tab" aria-controls="admin-version" aria-selected="false">
|
||
Version
|
||
</button>
|
||
</li>
|
||
</ul>
|
||
|
||
<div class="tab-content pt-3 admin-tabs-content">
|
||
<div class="tab-pane fade show active" id="admin-settings" role="tabpanel" aria-labelledby="admin-settings-tab">
|
||
<div class="card p-3">
|
||
<div class="d-flex flex-wrap align-items-end justify-content-between gap-2">
|
||
<div class="info-line small text-muted">
|
||
<span class="badge bg-light text-dark border" aria-hidden="true">i</span>
|
||
<span class="info-text">Paramètres globaux du site (année, dates, options du lecteur).</span>
|
||
</div>
|
||
</div>
|
||
|
||
<ul class="nav nav-pills admin-subtabs mt-3" id="adminSettingsSubTabs" role="tablist">
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link active" id="admin-settings-subtab-oscar-date" data-bs-toggle="tab" data-bs-target="#admin-settings-pane-oscar-date" type="button" role="tab" aria-controls="admin-settings-pane-oscar-date" aria-selected="true">
|
||
Date Oscars
|
||
</button>
|
||
</li>
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link" id="admin-settings-subtab-visibility" data-bs-toggle="tab" data-bs-target="#admin-settings-pane-visibility" type="button" role="tab" aria-controls="admin-settings-pane-visibility" aria-selected="false">
|
||
Visibilité
|
||
</button>
|
||
</li>
|
||
</ul>
|
||
|
||
<div class="tab-content pt-3">
|
||
<!-- Oscar date + points -->
|
||
<div class="tab-pane fade show active" id="admin-settings-pane-oscar-date" role="tabpanel" aria-labelledby="admin-settings-subtab-oscar-date" tabindex="0">
|
||
<div class="row g-3">
|
||
<div class="col-12 col-xxl-6">
|
||
<div class="border rounded p-3 bg-light h-100">
|
||
<div class="text-muted small mb-3">
|
||
Configure la date de cérémonie pour le compte à rebours (Films & Checklist). Défaut: <code>15 mars</code>.
|
||
</div>
|
||
|
||
<div class="oscar-date-layout">
|
||
<div class="oscar-date-fields">
|
||
<div>
|
||
<label for="oscar_date_year" class="form-label visually-hidden">Année</label>
|
||
<div class="input-group align-items-center">
|
||
<select class="form-select oscar-date-year-select" id="oscar_date_year"></select>
|
||
<button type="button" id="save-active-year" class="btn btn-warning btn-compact btn-check-compact btn-check-small btn-check-wide rounded-3 ms-1" aria-label="Appliquer l'année active">✓</button>
|
||
</div>
|
||
<div class="form-text">Basé sur les années présentes dans les films.</div>
|
||
</div>
|
||
<div>
|
||
<label for="oscar_date_value" class="form-label visually-hidden">Date</label>
|
||
<div class="input-group align-items-center">
|
||
<input type="date" class="form-control oscar-date-input" id="oscar_date_value">
|
||
<button type="button" id="save-oscar-date" class="btn btn-warning btn-compact btn-check-compact btn-check-small btn-check-wide rounded-3 ms-1" aria-label="Enregistrer">✓</button>
|
||
</div>
|
||
<div class="form-text">Format: YYYY-MM-DD.</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mt-3">
|
||
<div class="text-muted small mb-1">Dates configurées</div>
|
||
<div id="oscar-date-current" class="border rounded bg-white oscar-date-current">—</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-12 col-xxl-6">
|
||
<div class="border rounded p-3 bg-light h-100">
|
||
<div class="text-muted small mb-3">
|
||
Configure les points attribués pour les films regardés et les bonnes réponses aux Oscars.
|
||
</div>
|
||
|
||
<div id="points-config-alert" class="alert d-none" role="alert"></div>
|
||
|
||
<div class="row g-3">
|
||
<div class="col-12">
|
||
<label for="points-per-movie" class="form-label">Points par film regardé</label>
|
||
<input type="number" class="form-control points-input-half" id="points-per-movie" min="0" step="1" value="1">
|
||
<div class="form-text">Points attribués pour chaque film complété.</div>
|
||
</div>
|
||
<div class="col-12">
|
||
<label for="points-per-correct-pick" class="form-label">Points par bonne réponse</label>
|
||
<input type="number" class="form-control points-input-half" id="points-per-correct-pick" min="0" step="1" value="1">
|
||
<div class="form-text">Points attribués pour chaque bonne réponse aux Oscars.</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mt-3">
|
||
<div class="d-flex gap-2">
|
||
<button type="button" id="points-config-reload" class="btn btn-outline-secondary btn-compact">Recharger</button>
|
||
<button type="button" id="points-config-save" class="btn btn-warning btn-compact">Enregistrer</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mt-3">
|
||
<div class="text-muted small mb-1">Calcul des points</div>
|
||
<div class="border rounded p-2 bg-white">
|
||
<div class="small">
|
||
<strong>Total =</strong> (Films regardés × <span id="points-per-movie-preview">1</span>) + (Bonnes réponses × <span id="points-per-correct-pick-preview">1</span>)
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Visibility settings -->
|
||
<div class="tab-pane fade" id="admin-settings-pane-visibility" role="tabpanel" aria-labelledby="admin-settings-subtab-visibility" tabindex="0">
|
||
<div class="border rounded p-3 bg-light">
|
||
<div id="visibility-config-alert" class="alert d-none mb-3" role="alert"></div>
|
||
|
||
<div class="form-check mb-4">
|
||
<input class="form-check-input" type="checkbox" id="visibility-show-picks-features">
|
||
<label class="form-check-label" for="visibility-show-picks-features">
|
||
Afficher les fonctionnalités "Picks"
|
||
</label>
|
||
<div class="form-text small text-muted">Contrôle l'affichage du bouton "Mes picks" dans la navigation et de la colonne "Bon picks" dans les statistiques.</div>
|
||
</div>
|
||
|
||
<div class="form-check mb-4">
|
||
<input class="form-check-input" type="checkbox" id="player_ui_show_status">
|
||
<label class="form-check-label" for="player_ui_show_status">
|
||
Afficher "Source" + "Progress" (admins)
|
||
</label>
|
||
<div class="form-text small text-muted">Contrôle l'affichage du bloc debug dans <code>player.html</code> (admins uniquement).</div>
|
||
</div>
|
||
|
||
<div class="d-flex gap-2">
|
||
<button type="button" id="visibility-config-save" class="btn btn-warning btn-compact">Enregistrer</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="tab-pane fade" id="admin-version" role="tabpanel" aria-labelledby="admin-version-tab">
|
||
<div class="card p-3">
|
||
<div class="d-flex flex-wrap align-items-end justify-content-between gap-2">
|
||
<div class="info-line small text-muted">
|
||
<span class="badge bg-light text-dark border" aria-hidden="true">i</span>
|
||
<span class="info-text">Contrôle la version affichée dans le footer (stockée en DB). Clique sur ✓ pour activer une version, puis vérifie l’aperçu.</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="app_version_alert" class="alert d-none mt-3 mb-0" role="alert"></div>
|
||
|
||
<div class="pt-3">
|
||
<div class="row g-3">
|
||
<div class="col-12 col-xxl-7">
|
||
<div id="app_version_list" class="border rounded bg-white app-version-list">—</div>
|
||
</div>
|
||
|
||
<div class="col-12 col-xxl-5">
|
||
<div class="border rounded p-3 bg-light app-version-create">
|
||
<div class="fw-semibold mb-1">Créer une nouvelle version</div>
|
||
<div class="text-muted small mb-3">
|
||
Ajoute une entrée dans l’historique (tu peux l’activer immédiatement).
|
||
</div>
|
||
|
||
<div class="row g-3">
|
||
<div class="col-12 col-md-5">
|
||
<label for="app_version_new_version" class="form-label">Version</label>
|
||
<input id="app_version_new_version" type="text" class="form-control" placeholder="ex: 1.0.1" maxlength="80">
|
||
<div class="form-text">Ex: <code>1.2.3</code></div>
|
||
</div>
|
||
<div class="col-12 col-md-7">
|
||
<label for="app_version_new_message" class="form-label">Message (optionnel)</label>
|
||
<input id="app_version_new_message" type="text" class="form-control" placeholder="ex: Hotfix login" maxlength="500">
|
||
<div class="form-text">Court & lisible (affiché dans le footer).</div>
|
||
</div>
|
||
<div class="col-12 d-flex flex-wrap justify-content-end gap-2">
|
||
<button type="button" id="app_version_create" class="btn btn-primary btn-compact">Créer</button>
|
||
<button type="button" id="app_version_create_and_activate" class="btn btn-warning btn-compact">Créer & activer</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="tab-pane fade" id="admin-modal-100" role="tabpanel" aria-labelledby="admin-modal-100-tab">
|
||
<div class="card p-3">
|
||
<div class="d-flex flex-wrap align-items-end justify-content-between gap-2">
|
||
<div class="info-line small text-muted">
|
||
<span class="badge bg-light text-dark border" aria-hidden="true">i</span>
|
||
<span class="info-text">Contenu affiché quand un utilisateur a vu 100% des films.</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="pt-3">
|
||
<!-- Edit + Live preview side-by-side -->
|
||
<div>
|
||
<div class="row g-3">
|
||
<div class="col-12 col-xxl-7">
|
||
<div class="border rounded p-3 bg-light h-100">
|
||
<div class="fw-semibold mb-1">Contenu</div>
|
||
<div class="text-muted small mb-3">
|
||
Modifie le contenu puis enregistre. L’aperçu est live (non sauvegardé tant que tu n’enregistres pas).
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label for="completion_modal_title" class="form-label">Titre</label>
|
||
<input type="text" class="form-control" id="completion_modal_title" maxlength="200" placeholder="Félicitations! ...">
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label for="completion_modal_body_text" class="form-label">Texte (optionnel)</label>
|
||
<textarea class="form-control" id="completion_modal_body_text" rows="4" placeholder="Texte affiché dans le modal (retours à la ligne conservés)"></textarea>
|
||
<div class="form-text">Affiché en texte brut (safe). Laisse vide si tu n’en veux pas.</div>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label for="completion_modal_body_html" class="form-label">HTML (optionnel)</label>
|
||
<textarea class="form-control font-monospace" id="completion_modal_body_html" rows="6" placeholder="<p>…</p>"></textarea>
|
||
<div class="form-text">
|
||
Affiché comme HTML (le serveur retire les <code><script></code> et les handlers <code>on*</code>).
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label for="completion_modal_video_src" class="form-label">Video src (optionnel)</label>
|
||
<input type="text" class="form-control" id="completion_modal_video_src" maxlength="2048" placeholder="video/reward.mp4">
|
||
<div class="form-text">Chemin relatif (ex: <code>video/reward.mp4</code>) ou URL complète. Vide = pas de vidéo.</div>
|
||
</div>
|
||
|
||
<div class="d-flex flex-wrap justify-content-end gap-2">
|
||
<button type="button" id="completion_modal_save" class="btn btn-warning btn-compact">Enregistrer</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-12 col-xxl-5">
|
||
<div class="border rounded p-3 bg-light">
|
||
<div class="d-flex align-items-center justify-content-between gap-2 mb-2">
|
||
<div class="fw-semibold" id="completion_modal_preview_title">—</div>
|
||
</div>
|
||
<div id="completion_modal_preview_custom" class="text-start d-none"></div>
|
||
<div id="completion_modal_preview_text" class="text-start" style="white-space: pre-wrap;"></div>
|
||
|
||
<div class="mt-3">
|
||
<video id="completion_modal_preview_video" width="55%" controls class="d-none">
|
||
<source id="completion_modal_preview_video_source" src="" type="video/mp4">
|
||
Votre navigateur ne supporte pas la vidéo.
|
||
</video>
|
||
</div>
|
||
|
||
<div class="border rounded p-3 bg-white mt-3">
|
||
<ul class="mb-0 text-muted">
|
||
<li><strong>Texte</strong>: affiché tel quel (safe), retours à la ligne conservés.</li>
|
||
<li><strong>HTML</strong>: le serveur retire les <code><script></code> et les attributs <code>on*</code>.</li>
|
||
<li><strong>Vidéo</strong>: accepte un chemin relatif (ex: <code>video/reward.mp4</code>) ou une URL complète.</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="tab-pane fade" id="admin-users" role="tabpanel" aria-labelledby="admin-users-tab">
|
||
<div class="row g-3">
|
||
<div class="col-12">
|
||
<div class="card p-3">
|
||
<div class="d-flex flex-wrap align-items-end justify-content-between gap-2 mb-2">
|
||
<div class="info-line small text-muted">
|
||
<span class="badge bg-light text-dark border" aria-hidden="true">i</span>
|
||
<span class="info-text"><span id="admin-users-count" class="me-1">—</span>utilisateur(s)</span>
|
||
</div>
|
||
<div class="d-flex gap-2">
|
||
<button type="button" id="open-add-user-modal" class="btn btn-warning btn-sm btn-compact">Ajouter un utilisateur</button>
|
||
<button type="button" id="refresh-users" class="btn btn-outline-primary btn-sm btn-compact">Rafraîchir</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="table-responsive">
|
||
<table class="table table-sm align-middle mb-0">
|
||
<thead>
|
||
<tr>
|
||
<th>Nom</th>
|
||
<th>Email</th>
|
||
<th style="width: 1%;">Statut</th>
|
||
<th style="width: 1%;">Actions</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="admin-users-body">
|
||
<tr><td colspan="4" class="text-muted">Charge la liste…</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="tab-pane fade" id="admin-movies" role="tabpanel" aria-labelledby="admin-movies-tab">
|
||
<div class="card p-3">
|
||
<div class="admin-movies-add">
|
||
<div class="info-line small text-muted mb-3">
|
||
<span class="badge bg-light text-dark border" aria-hidden="true">i</span>
|
||
<span class="info-text">Ajout de films : cette section récupère automatiquement les infos du film via l’<code>imdb_id</code> (OMDb).</span>
|
||
</div>
|
||
|
||
<form id="add-movie-form">
|
||
<div class="row g-3">
|
||
<div class="col-12 col-md-4">
|
||
<label for="oscar_year" class="form-label">Oscar year</label>
|
||
<input type="number" class="form-control" id="oscar_year" placeholder="2026" min="1900" max="3000" required>
|
||
<div class="form-text">Sépare films & stats par année.</div>
|
||
</div>
|
||
<div class="col-12 col-md-4">
|
||
<label for="imdb_id" class="form-label">IMDB ID</label>
|
||
<input type="text" class="form-control" id="imdb_id" placeholder="tt1234567" required>
|
||
</div>
|
||
<div class="col-12 col-md-4">
|
||
<label for="category" class="form-label">Catégorie</label>
|
||
<input type="text" class="form-control" id="category" placeholder="Best Picture, etc." required>
|
||
</div>
|
||
<div class="col-12 d-flex flex-wrap justify-content-end gap-2">
|
||
<button type="submit" class="btn btn-primary btn-compact">Ajouter</button>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<div class="admin-movies-separator"></div>
|
||
|
||
<div class="d-flex flex-wrap align-items-end justify-content-between gap-2 admin-movies-meta">
|
||
<div class="text-muted small">
|
||
<span id="admin-movies-table-count"></span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row g-2 align-items-end">
|
||
<div class="col-12 col-md-4 col-xl-3">
|
||
<label for="manage_year" class="form-label visually-hidden">Oscar year</label>
|
||
<select id="manage_year" class="form-select manage-year-select">
|
||
<option value="">Toutes</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="col-12 col-md-8 col-xl-9 d-flex gap-2 justify-content-end">
|
||
<button type="button" id="refresh-movies" class="btn btn-outline-primary btn-compact">Rafraîchir</button>
|
||
<button type="button" id="update-omdb-selected" class="btn btn-outline-warning btn-compact" disabled>Refresh OMDb</button>
|
||
<button type="button" id="delete-selected" class="btn btn-danger btn-compact" disabled>Supprimer</button>
|
||
</div>
|
||
|
||
<div class="col-12">
|
||
<div class="d-flex justify-content-between align-items-center">
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="checkbox" id="select-all-movies">
|
||
<label class="form-check-label" for="select-all-movies">Tout sélectionner</label>
|
||
</div>
|
||
<small class="text-muted"><span id="selected-count">0</span> sélectionné(s)</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="table-responsive mt-2">
|
||
<table class="table table-sm align-middle mb-0 admin-movies-table">
|
||
<thead>
|
||
<tr>
|
||
<th style="width: 1%;">Sel.</th>
|
||
<th style="width: 1%;">ID</th>
|
||
<th>Titre</th>
|
||
<th style="width: 12%;">Player</th>
|
||
<th style="width: 20%;">Catégorie</th>
|
||
<th style="width: 12%;">Année</th>
|
||
<th style="width: 1%;">Actions</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="admin-movies-body">
|
||
<tr><td colspan="7" class="text-muted">Charge la liste…</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="tab-pane fade" id="admin-backup" role="tabpanel" aria-labelledby="admin-backup-tab">
|
||
<div class="card p-3">
|
||
<div class="d-flex flex-wrap align-items-end justify-content-between gap-2">
|
||
<div class="info-line small text-muted">
|
||
<span class="badge bg-light text-dark border" aria-hidden="true">i</span>
|
||
<span class="info-text">Sauvegarde et restauration de la base MongoDB.</span>
|
||
</div>
|
||
</div>
|
||
|
||
<ul class="nav nav-pills admin-subtabs mt-3" id="adminBackupSubTabs" role="tablist">
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link active" id="admin-backup-subtab-backup" data-bs-toggle="tab" data-bs-target="#admin-backup-pane-backup" type="button" role="tab" aria-controls="admin-backup-pane-backup" aria-selected="true">
|
||
Backup
|
||
</button>
|
||
</li>
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link" id="admin-backup-subtab-restore" data-bs-toggle="tab" data-bs-target="#admin-backup-pane-restore" type="button" role="tab" aria-controls="admin-backup-pane-restore" aria-selected="false">
|
||
Restore
|
||
</button>
|
||
</li>
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link" id="admin-backup-subtab-help" data-bs-toggle="tab" data-bs-target="#admin-backup-pane-help" type="button" role="tab" aria-controls="admin-backup-pane-help" aria-selected="false">
|
||
Aide
|
||
</button>
|
||
</li>
|
||
</ul>
|
||
|
||
<div class="tab-content pt-3">
|
||
<!-- Backup -->
|
||
<div class="tab-pane fade show active" id="admin-backup-pane-backup" role="tabpanel" aria-labelledby="admin-backup-subtab-backup" tabindex="0">
|
||
<div class="d-flex flex-wrap align-items-end justify-content-between gap-2 mb-2">
|
||
<div>
|
||
<div class="fw-semibold">Sauvegardes</div>
|
||
<div class="text-muted small">Format <code>.ndjson.gz</code>, stocké sur le serveur.</div>
|
||
</div>
|
||
<div class="d-flex flex-wrap gap-2">
|
||
<button type="button" id="db-backup-create" class="btn btn-warning btn-compact">Créer une sauvegarde</button>
|
||
<button type="button" id="db-backup-refresh" class="btn btn-outline-secondary btn-compact">Rafraîchir</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="table-responsive">
|
||
<table class="table table-sm align-middle mb-0">
|
||
<thead>
|
||
<tr>
|
||
<th>Fichier</th>
|
||
<th class="text-nowrap" style="width: 1%;">Taille</th>
|
||
<th class="text-nowrap" style="width: 1%;">Modifié</th>
|
||
<th class="text-nowrap text-end" style="width: 1%;">Actions</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="db-backup-list">
|
||
<tr><td colspan="4" class="text-muted">Ouvre l’onglet pour charger la liste…</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Restore -->
|
||
<div class="tab-pane fade" id="admin-backup-pane-restore" role="tabpanel" aria-labelledby="admin-backup-subtab-restore" tabindex="0">
|
||
<div class="row g-3">
|
||
<div class="col-12 col-xxl-7">
|
||
<div class="border rounded p-3 bg-light">
|
||
<div class="fw-semibold text-danger mb-1">Restaurer une sauvegarde</div>
|
||
<div class="text-muted small mb-3">
|
||
Restaure un fichier <code>.ndjson.gz</code>. L’option <strong>drop</strong> est recommandée.
|
||
</div>
|
||
|
||
<div class="alert alert-warning mb-3">
|
||
La restauration peut <strong>écraser</strong> les données. Fais un backup juste avant si besoin.
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label for="db-restore-file" class="form-label">Fichier de backup</label>
|
||
<input class="form-control" type="file" id="db-restore-file" accept=".gz,.ndjson.gz,application/gzip">
|
||
</div>
|
||
|
||
<div class="form-check mb-3">
|
||
<input class="form-check-input" type="checkbox" id="db-restore-drop" checked>
|
||
<label class="form-check-label" for="db-restore-drop">
|
||
Drop les collections avant restore (recommandé)
|
||
</label>
|
||
</div>
|
||
|
||
<div class="d-flex flex-wrap justify-content-end gap-2">
|
||
<button type="button" id="db-restore-run" class="btn btn-danger btn-compact">Restaurer</button>
|
||
</div>
|
||
|
||
<div id="db-restore-result" class="small text-muted mt-3"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-12 col-xxl-5">
|
||
<div class="border rounded p-3 bg-white">
|
||
<div class="fw-semibold mb-2">Conseil</div>
|
||
<div class="text-muted">
|
||
Utilise <strong>drop</strong> sauf si tu sais exactement ce que tu fais (sinon risque de doublons/erreurs).
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Help -->
|
||
<div class="tab-pane fade" id="admin-backup-pane-help" role="tabpanel" aria-labelledby="admin-backup-subtab-help" tabindex="0">
|
||
<div class="row g-3">
|
||
<div class="col-12 col-xxl-8">
|
||
<div class="border rounded p-3 bg-white">
|
||
<div class="fw-semibold mb-2">Fonctionnement</div>
|
||
<ul class="mb-0 text-muted">
|
||
<li><strong>Backup</strong>: crée un fichier <code>.ndjson.gz</code> (export MongoDB) et le garde sur le serveur.</li>
|
||
<li><strong>Télécharger</strong>: récupère le fichier localement (utile avant une manipulation risquée).</li>
|
||
<li><strong>Restore</strong>: importe le fichier. Avec <strong>drop</strong>, les collections restaurées sont d’abord supprimées.</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="col-12 col-xxl-4">
|
||
<div class="border rounded p-3 bg-light">
|
||
<div class="fw-semibold mb-2">Check rapide</div>
|
||
<div class="text-muted">
|
||
Après restore, recharge la page et vérifie “Films”, “Checklist” et “Statistiques”.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="tab-pane fade" id="admin-categories" role="tabpanel" aria-labelledby="admin-categories-tab">
|
||
<div class="card p-3">
|
||
<div class="d-flex flex-wrap align-items-end justify-content-between gap-2">
|
||
<div class="info-line small text-muted">
|
||
<span class="badge bg-light text-dark border" aria-hidden="true">i</span>
|
||
<span class="info-text">Créez et gérez les catégories manuellement.</span>
|
||
</div>
|
||
</div>
|
||
|
||
<ul class="nav nav-pills admin-subtabs mt-3" id="adminCategoriesSubTabs" role="tablist">
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link active" id="admin-categories-subtab-manual" data-bs-toggle="tab" data-bs-target="#admin-categories-pane-manual" type="button" role="tab" aria-controls="admin-categories-pane-manual" aria-selected="true">
|
||
Création manuelle
|
||
</button>
|
||
</li>
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link" id="admin-categories-subtab-list" data-bs-toggle="tab" data-bs-target="#admin-categories-pane-list" type="button" role="tab" aria-controls="admin-categories-pane-list" aria-selected="false">
|
||
Liste
|
||
</button>
|
||
</li>
|
||
</ul>
|
||
|
||
<div class="tab-content pt-3">
|
||
<!-- Manual creation -->
|
||
<div class="tab-pane fade show active" id="admin-categories-pane-manual" role="tabpanel" aria-labelledby="admin-categories-subtab-manual" tabindex="0">
|
||
<div class="row g-3">
|
||
<div class="col-12 col-xxl-7">
|
||
<div class="border rounded p-3 bg-light">
|
||
<div class="fw-semibold mb-1">Créer une catégorie manuellement</div>
|
||
<div class="text-muted small mb-3">
|
||
Ajoutez une catégorie et ses nommés un par un.
|
||
</div>
|
||
|
||
<div id="categories-manual-alert" class="alert d-none" role="alert"></div>
|
||
|
||
<form id="create-category-form">
|
||
<div class="mb-3">
|
||
<label for="manual-year" class="form-label">Année</label>
|
||
<select class="form-select" id="manual-year" required>
|
||
<option value="">Chargement...</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label for="category-number" class="form-label">Numéro de catégorie</label>
|
||
<input type="number" class="form-control" id="category-number" placeholder="1" min="1" max="50" required>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label for="category-name" class="form-label">Nom de la catégorie</label>
|
||
<input type="text" class="form-control" id="category-name" placeholder="Best Picture" required>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label for="nominees-list" class="form-label">Nommés (un par ligne)</label>
|
||
<textarea class="form-control" id="nominees-list" rows="10" placeholder="Anora The Brutalist Dune: Part Two" required></textarea>
|
||
<div class="form-text">Entrez un nommé par ligne.</div>
|
||
</div>
|
||
|
||
<div class="d-flex flex-wrap justify-content-end gap-2">
|
||
<button type="submit" class="btn btn-primary">Créer</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-12 col-xxl-5">
|
||
<div class="border rounded p-3 bg-white">
|
||
<div class="fw-semibold mb-2">Note</div>
|
||
<div class="text-muted">
|
||
Vous pouvez créer plusieurs catégories pour la même année.
|
||
Le numéro de catégorie doit être unique par année.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- List -->
|
||
<div class="tab-pane fade" id="admin-categories-pane-list" role="tabpanel" aria-labelledby="admin-categories-subtab-list" tabindex="0">
|
||
<div class="d-flex flex-wrap align-items-end justify-content-between gap-2 mb-2">
|
||
<div>
|
||
<div class="fw-semibold">Catégories importées</div>
|
||
<div class="text-muted small">Gérez les catégories existantes</div>
|
||
</div>
|
||
<div class="d-flex flex-column gap-2">
|
||
<div class="d-flex gap-2">
|
||
<select class="form-select" id="list-year-filter" style="width: 150px;">
|
||
<option value="">Toutes les années</option>
|
||
</select>
|
||
<button type="button" id="refresh-categories-list" class="btn btn-outline-secondary">Rafraîchir</button>
|
||
<button type="button" id="bulk-delete-categories-btn" class="btn btn-sm btn-danger" disabled>Supprimer</button>
|
||
</div>
|
||
<div id="categories-selection-text" class="text-muted small" style="display: none;">
|
||
<span id="bulk-delete-count">0</span> catégorie(s) sélectionnée(s)
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="categories-list-container" class="border rounded bg-white p-3">
|
||
<div class="text-muted">Charge la liste…</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="tab-pane fade" id="admin-winners" role="tabpanel" aria-labelledby="admin-winners-tab">
|
||
<div class="card p-3">
|
||
<div class="d-flex flex-wrap align-items-end justify-content-between gap-2">
|
||
<div class="info-line small text-muted">
|
||
<span class="badge bg-light text-dark border" aria-hidden="true">i</span>
|
||
<span class="info-text">Définis un ou plusieurs gagnants par année (égalité possible).</span>
|
||
</div>
|
||
</div>
|
||
|
||
<ul class="nav nav-pills admin-subtabs mt-3" id="adminWinnersSubTabs" role="tablist">
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link active" id="admin-winners-subtab-set" data-bs-toggle="tab" data-bs-target="#admin-winners-pane-set" type="button" role="tab" aria-controls="admin-winners-pane-set" aria-selected="true">
|
||
Gérer
|
||
</button>
|
||
</li>
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link" id="admin-winners-subtab-help" data-bs-toggle="tab" data-bs-target="#admin-winners-pane-help" type="button" role="tab" aria-controls="admin-winners-pane-help" aria-selected="false">
|
||
Aide
|
||
</button>
|
||
</li>
|
||
</ul>
|
||
|
||
<div class="tab-content pt-3">
|
||
<!-- Set -->
|
||
<div class="tab-pane fade show active" id="admin-winners-pane-set" role="tabpanel" aria-labelledby="admin-winners-subtab-set" tabindex="0">
|
||
<div class="row g-3">
|
||
<div class="col-12 col-xxl-7">
|
||
<div class="border rounded p-3 bg-light">
|
||
<div class="fw-semibold mb-1">Ajouter un gagnant</div>
|
||
<div class="text-muted small mb-3">
|
||
Tu peux ajouter plusieurs gagnants pour une même année (égalité).
|
||
</div>
|
||
|
||
<div class="row g-3">
|
||
<div class="col-12 col-md-4">
|
||
<label for="winner_year" class="form-label">Année</label>
|
||
<select id="winner_year" class="form-select"></select>
|
||
<div class="form-text">Basé sur les années présentes dans les films.</div>
|
||
</div>
|
||
<div class="col-12 col-md-5">
|
||
<label for="winner_user" class="form-label">Utilisateur</label>
|
||
<select id="winner_user" class="form-select">
|
||
<option value="">—</option>
|
||
</select>
|
||
<div class="form-text">Pour retirer: utilise la liste ci-dessous.</div>
|
||
</div>
|
||
<div class="col-12 col-md-3">
|
||
<label for="winner_points" class="form-label">Points (optionnel)</label>
|
||
<input id="winner_points" type="number" class="form-control" placeholder="ex: 42" min="0" step="1">
|
||
<div class="form-text">Laisse vide si non applicable.</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="d-flex flex-wrap justify-content-end gap-2 mt-3">
|
||
<button type="button" id="winner_reload" class="btn btn-outline-secondary btn-compact">Recharger</button>
|
||
<button type="button" id="winner_clear" class="btn btn-outline-danger btn-compact">Tout supprimer</button>
|
||
<button type="button" id="winner_save" class="btn btn-warning btn-compact">Ajouter</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-12 col-xxl-5">
|
||
<div class="border rounded p-3 bg-white h-100">
|
||
<div class="d-flex flex-wrap align-items-end justify-content-between gap-2 mb-2">
|
||
<div>
|
||
<div class="fw-semibold">Gagnants actuels</div>
|
||
<div class="text-muted small">Vue globale (toutes les années).</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="winner_current" class="border rounded p-2 bg-light">—</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Help -->
|
||
<div class="tab-pane fade" id="admin-winners-pane-help" role="tabpanel" aria-labelledby="admin-winners-subtab-help" tabindex="0">
|
||
<div class="row g-3">
|
||
<div class="col-12 col-xxl-8">
|
||
<div class="border rounded p-3 bg-white">
|
||
<div class="fw-semibold mb-2">Comment ça marche</div>
|
||
<ul class="mb-0 text-muted">
|
||
<li>Une année peut avoir <strong>plusieurs gagnants</strong> (égalité).</li>
|
||
<li>Les points sont <strong>optionnels</strong> (affichage uniquement).</li>
|
||
<li>Pour retirer un gagnant, utilise le bouton “Retirer” dans la liste “Gagnants actuels”.</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="col-12 col-xxl-4">
|
||
<div class="border rounded p-3 bg-light">
|
||
<div class="fw-semibold mb-2">Astuce</div>
|
||
<div class="text-muted">
|
||
Si tu changes l’année active du site, vérifie aussi les gagnants de cette année.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Reset user password modal -->
|
||
<div class="modal fade" id="resetUserPasswordModal" tabindex="-1" aria-hidden="true">
|
||
<div class="modal-dialog modal-dialog-scrollable">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title">Reset mot de passe</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="text-muted mb-2">
|
||
Génère un mot de passe temporaire. L'utilisateur devra le changer à sa prochaine connexion.
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<div class="fw-semibold" id="reset_user_label">—</div>
|
||
<div class="text-muted small" id="reset_user_email_label">—</div>
|
||
</div>
|
||
|
||
<div id="reset-user-modal-response" class="alert d-none" role="alert"></div>
|
||
|
||
<div id="reset-user-modal-result" class="border rounded p-2 bg-light d-none">
|
||
<div class="small text-muted mb-1">Mot de passe temporaire (affiché une seule fois)</div>
|
||
<div class="d-flex gap-2 align-items-center">
|
||
<code class="flex-grow-1" id="reset-user-temp-password">—</code>
|
||
<button type="button" class="btn btn-outline-secondary btn-sm" id="copy-temp-password">Copier</button>
|
||
</div>
|
||
<div class="small text-muted mt-2" id="reset-user-expires-at"></div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-outline-secondary btn-compact" data-bs-dismiss="modal">Fermer</button>
|
||
<button type="button" class="btn btn-danger btn-compact" id="confirm-reset-user-password">Générer</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Delete user confirmation modal -->
|
||
<div class="modal fade" id="deleteUserModal" tabindex="-1" aria-hidden="true">
|
||
<div class="modal-dialog">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title text-danger">Supprimer un utilisateur</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="mb-2">
|
||
Tu es sur le point de supprimer:
|
||
<div class="fw-semibold" id="delete_user_label">—</div>
|
||
<div class="text-muted small" id="delete_user_email_label">—</div>
|
||
</div>
|
||
|
||
<div class="alert alert-warning">
|
||
Cette action est <strong>irréversible</strong>.
|
||
</div>
|
||
|
||
<div class="mb-2">
|
||
Pour confirmer, tape l'email exact:
|
||
</div>
|
||
<input type="text" class="form-control" id="delete_user_verify_input" placeholder="[email protected]">
|
||
<div class="form-text">
|
||
Le bouton “Supprimer” s'activera seulement si l'email correspond.
|
||
</div>
|
||
|
||
<div id="delete-user-modal-response" class="alert d-none mt-3" role="alert"></div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-outline-secondary btn-compact" data-bs-dismiss="modal">Annuler</button>
|
||
<button type="button" class="btn btn-danger btn-compact" id="confirm-delete-user" disabled>Supprimer</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Add user modal -->
|
||
<div class="modal fade" id="addUserModal" tabindex="-1" aria-hidden="true">
|
||
<div class="modal-dialog modal-dialog-scrollable">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title">Ajouter un utilisateur</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="text-muted mb-2">
|
||
Crée un compte avec un <strong>mot de passe temporaire</strong> (affiché une seule fois). L’utilisateur devra le changer à sa prochaine connexion.
|
||
</div>
|
||
|
||
<div id="add-user-response" class="alert d-none" role="alert"></div>
|
||
|
||
<form id="add-user-form">
|
||
<div class="mb-3">
|
||
<label for="add_user_name" class="form-label">Nom</label>
|
||
<input type="text" class="form-control" id="add_user_name" placeholder="Prénom / pseudo" required>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label for="add_user_email" class="form-label">Email</label>
|
||
<input type="email" class="form-control" id="add_user_email" placeholder="[email protected]" required>
|
||
</div>
|
||
<div class="form-check mb-3">
|
||
<input class="form-check-input" type="checkbox" id="add_user_admin">
|
||
<label class="form-check-label" for="add_user_admin">Admin</label>
|
||
</div>
|
||
|
||
<div id="add-user-result" class="border rounded p-2 bg-light d-none">
|
||
<div class="small text-muted mb-1">Mot de passe temporaire (affiché une seule fois)</div>
|
||
<div class="d-flex gap-2 align-items-center">
|
||
<code class="flex-grow-1" id="add-user-temp-password">—</code>
|
||
<button type="button" class="btn btn-outline-secondary btn-sm" id="copy-add-user-temp-password">Copier</button>
|
||
</div>
|
||
<div class="small text-muted mt-2" id="add-user-expires-at"></div>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Fermer</button>
|
||
<button type="button" id="add-user-reset" class="btn btn-outline-secondary">Reset</button>
|
||
<button type="submit" form="add-user-form" class="btn btn-warning" id="add-user-submit">Créer</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Edit category modal -->
|
||
<div class="modal fade" id="editCategoryModal" tabindex="-1" aria-hidden="true">
|
||
<div class="modal-dialog modal-dialog-scrollable">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title">Modifier la catégorie</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<input type="hidden" id="edit_category_id">
|
||
|
||
<div class="mb-3">
|
||
<label for="edit_category_number" class="form-label">Numéro de catégorie</label>
|
||
<input type="number" class="form-control" id="edit_category_number" min="1" max="50" required>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label for="edit_category_name" class="form-label">Nom de la catégorie</label>
|
||
<input type="text" class="form-control" id="edit_category_name" required>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label for="edit_nominees_list" class="form-label">Nommés (un par ligne)</label>
|
||
<textarea class="form-control" id="edit_nominees_list" rows="10" required></textarea>
|
||
<div class="form-text">Entrez un nommé par ligne.</div>
|
||
</div>
|
||
|
||
<div id="edit-category-alert" class="alert d-none" role="alert"></div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Annuler</button>
|
||
<button type="button" class="btn btn-primary" id="save-category-changes">Enregistrer</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Edit movie modal -->
|
||
<div class="modal fade" id="editMovieModal" tabindex="-1" aria-hidden="true">
|
||
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title" id="editMovieModalTitle">Modifier le film</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<input type="hidden" id="edit_movie_id">
|
||
|
||
<div class="mb-3">
|
||
<div class="text-muted small mb-1">Movie ID</div>
|
||
<div class="d-flex gap-2 align-items-center">
|
||
<code class="flex-grow-1" id="edit_movie_id_label">—</code>
|
||
<button type="button" class="btn btn-outline-secondary btn-sm" id="copy-movie-id">Copier</button>
|
||
</div>
|
||
</div>
|
||
|
||
<ul class="nav nav-tabs" id="editMovieTabs" role="tablist">
|
||
<li class="nav-item" role="presentation">
|
||
<button
|
||
class="nav-link active"
|
||
id="edit-movie-tab-info"
|
||
data-bs-toggle="tab"
|
||
data-bs-target="#edit-movie-pane-info"
|
||
type="button"
|
||
role="tab"
|
||
aria-controls="edit-movie-pane-info"
|
||
aria-selected="true"
|
||
>
|
||
Info
|
||
</button>
|
||
</li>
|
||
<li class="nav-item" role="presentation">
|
||
<button
|
||
class="nav-link"
|
||
id="edit-movie-tab-video"
|
||
data-bs-toggle="tab"
|
||
data-bs-target="#edit-movie-pane-video"
|
||
type="button"
|
||
role="tab"
|
||
aria-controls="edit-movie-pane-video"
|
||
aria-selected="false"
|
||
>
|
||
Player / Vidéo
|
||
</button>
|
||
</li>
|
||
</ul>
|
||
|
||
<div class="tab-content pt-3" id="editMovieTabsContent">
|
||
<!-- Info tab -->
|
||
<div
|
||
class="tab-pane fade show active"
|
||
id="edit-movie-pane-info"
|
||
role="tabpanel"
|
||
aria-labelledby="edit-movie-tab-info"
|
||
tabindex="0"
|
||
>
|
||
<div class="row g-3">
|
||
<div class="col-md-6">
|
||
<label for="edit_imdb_id" class="form-label">IMDB ID (ex: tt1234567)</label>
|
||
<input type="text" class="form-control" id="edit_imdb_id" placeholder="tt..." required>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<label for="edit_year" class="form-label">Oscar year</label>
|
||
<input type="number" class="form-control" id="edit_year" placeholder="2026" min="1900" max="3000">
|
||
</div>
|
||
|
||
<div class="col-md-6">
|
||
<label for="edit_category" class="form-label">Catégorie</label>
|
||
<input type="text" class="form-control" id="edit_category" placeholder="Best Picture, etc." required>
|
||
</div>
|
||
|
||
<div class="col-md-4">
|
||
<label for="edit_rating" class="form-label">Rating (imdbRating)</label>
|
||
<input type="text" class="form-control" id="edit_rating" placeholder="7.9">
|
||
</div>
|
||
<div class="col-md-4">
|
||
<label for="edit_runtime" class="form-label">Runtime</label>
|
||
<input type="text" class="form-control" id="edit_runtime" placeholder="142 min">
|
||
<div class="form-text">Ex: 142 min</div>
|
||
</div>
|
||
<div class="col-md-4 d-flex align-items-end">
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="checkbox" id="edit_cam_flag">
|
||
<label class="form-check-label" for="edit_cam_flag">Tag CAM</label>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-12">
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="checkbox" id="edit_refresh_omdb">
|
||
<label class="form-check-label" for="edit_refresh_omdb">
|
||
Recharger depuis OMDb (écrase titre / plot / rating / runtime / poster)
|
||
</label>
|
||
</div>
|
||
<div class="form-text">Si coché, le serveur refait un appel OMDb via l’IMDB ID au moment de l’enregistrement.</div>
|
||
</div>
|
||
|
||
<div class="col-12">
|
||
<label for="edit_title" class="form-label">Titre</label>
|
||
<input type="text" class="form-control" id="edit_title" placeholder="Title">
|
||
</div>
|
||
|
||
<div class="col-12">
|
||
<label for="edit_poster" class="form-label">Poster URL</label>
|
||
<input type="url" class="form-control" id="edit_poster" placeholder="https://...">
|
||
</div>
|
||
|
||
<div class="col-12">
|
||
<label for="edit_description" class="form-label">Description (Plot)</label>
|
||
<textarea class="form-control" id="edit_description" rows="4" placeholder="Plot..."></textarea>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Video tab -->
|
||
<div
|
||
class="tab-pane fade"
|
||
id="edit-movie-pane-video"
|
||
role="tabpanel"
|
||
aria-labelledby="edit-movie-tab-video"
|
||
tabindex="0"
|
||
>
|
||
<div class="row g-3">
|
||
<div class="col-12">
|
||
<label class="form-label">Lecteur (source)</label>
|
||
<div class="row g-2">
|
||
<div class="col-12 col-lg-4">
|
||
<label for="edit_player_mode" class="form-label">Mode</label>
|
||
<select id="edit_player_mode" class="form-select">
|
||
<option value="auto">Auto</option>
|
||
<option value="video">Video</option>
|
||
<option value="embed">Embed</option>
|
||
</select>
|
||
<div class="form-text">Auto = video_src → embed_src → vod_link.</div>
|
||
</div>
|
||
<div class="col-12 col-lg-8">
|
||
<label for="edit_video_src" class="form-label">Video URL (mp4/webm/m3u8)</label>
|
||
<input type="text" class="form-control" id="edit_video_src" placeholder="https://.../movie.mp4 or .../playlist.m3u8">
|
||
<div class="form-text">If you use a server file (below) and leave this empty, it will auto-use <code>/api/video/<movieId></code>.</div>
|
||
</div>
|
||
<div class="col-12">
|
||
<label for="edit_video_file" class="form-label">Server video file (optional)</label>
|
||
<input type="text" class="form-control font-monospace" id="edit_video_file" placeholder="my_movie.mp4 or 2026/my_movie.mp4">
|
||
<div class="form-text">Relative path under <code>VIDEO_FILES_DIR</code> (default: <code>public/video</code>).</div>
|
||
</div>
|
||
<div class="col-12">
|
||
<label for="edit_video_file_low" class="form-label">Server video file (low quality, optional)</label>
|
||
<input type="text" class="form-control font-monospace" id="edit_video_file_low" placeholder="LOW/my_movie.LOW.mp4">
|
||
<div class="form-text">Relative path under <code>VIDEO_FILES_DIR</code>. Example: <code>LOW/123.LOW.mp4</code>.</div>
|
||
</div>
|
||
<div class="col-12">
|
||
<label for="edit_subtitle_file" class="form-label">Sous-titres (SRT, optionnel)</label>
|
||
<input type="file" class="form-control" id="edit_subtitle_file" accept=".srt,.vtt,text/plain,text/vtt">
|
||
<div class="form-text">Sous-titres existants:</div>
|
||
<div id="edit_subtitle_list" class="d-flex flex-column gap-2 mt-1"></div>
|
||
<div class="form-text">Ajoute un fichier par langue; répète l’upload pour plusieurs sous-titres.</div>
|
||
</div>
|
||
<div class="col-12">
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="checkbox" id="edit_subtitle_remove">
|
||
<label class="form-check-label" for="edit_subtitle_remove">Supprimer tous les sous-titres</label>
|
||
</div>
|
||
<div class="form-text">Efface les fichiers VTT/SRT et réinitialise les champs de langue/label.</div>
|
||
</div>
|
||
<div class="col-12 col-lg-4">
|
||
<label for="edit_subtitle_lang" class="form-label">Langue (ex: fr, en)</label>
|
||
<input type="text" class="form-control" id="edit_subtitle_lang" placeholder="fr">
|
||
</div>
|
||
<div class="col-12 col-lg-4">
|
||
<label for="edit_subtitle_label" class="form-label">Label (affiché)</label>
|
||
<input type="text" class="form-control" id="edit_subtitle_label" placeholder="Français">
|
||
</div>
|
||
<div class="col-12 col-lg-4 d-flex align-items-end">
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="checkbox" id="edit_subtitle_default">
|
||
<label class="form-check-label" for="edit_subtitle_default">Par défaut</label>
|
||
</div>
|
||
</div>
|
||
<div class="col-12">
|
||
<label for="edit_embed_src" class="form-label">Embed URL or iframe code</label>
|
||
<textarea class="form-control font-monospace" id="edit_embed_src" rows="3" placeholder="https://... OR <iframe ...>"></textarea>
|
||
</div>
|
||
<div class="col-12">
|
||
<label for="edit_vod_link" class="form-label">Legacy VOD link (optional)</label>
|
||
<input type="text" class="form-control" id="edit_vod_link" placeholder="https://...">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Annuler</button>
|
||
<button type="button" class="btn btn-primary" id="save-movie-changes">Enregistrer</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Footer Section -->
|
||
<footer>
|
||
<p id="footer-copy">Oscar Pool <span aria-hidden="true">·</span> <a id="copy" href="https://github.com/quoije/oscars-pool">quoije</a></p>
|
||
<p id="footer-text"></p>
|
||
</footer>
|
||
|
||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
|
||
<script src="./js/app_control.js"></script>
|
||
<script src="./js/app_version.js"></script>
|
||
|
||
</body>
|
||
</html>
|
||
|