Page title and player UI #47
+84
-29
@@ -688,6 +688,46 @@
|
|||||||
</div>
|
</div>
|
||||||
</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="row g-3">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label for="edit_imdb_id" class="form-label">IMDB ID (ex: tt1234567)</label>
|
<label for="edit_imdb_id" class="form-label">IMDB ID (ex: tt1234567)</label>
|
||||||
@@ -702,10 +742,52 @@
|
|||||||
<label for="edit_category" class="form-label">Catégorie</label>
|
<label for="edit_category" class="form-label">Catégorie</label>
|
||||||
<input type="text" class="form-control" id="edit_category" placeholder="Best Picture, etc." required>
|
<input type="text" class="form-control" id="edit_category" placeholder="Best Picture, etc." required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
|
<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-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 / 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>
|
<label class="form-label">Lecteur (source)</label>
|
||||||
<div class="row g-2">
|
<div class="row g-2">
|
||||||
<div class="col-12">
|
<div class="col-12 col-lg-4">
|
||||||
<label for="edit_player_mode" class="form-label">Mode</label>
|
<label for="edit_player_mode" class="form-label">Mode</label>
|
||||||
<select id="edit_player_mode" class="form-select">
|
<select id="edit_player_mode" class="form-select">
|
||||||
<option value="auto">Auto</option>
|
<option value="auto">Auto</option>
|
||||||
@@ -714,7 +796,7 @@
|
|||||||
</select>
|
</select>
|
||||||
<div class="form-text">Auto = video_src → embed_src → vod_link.</div>
|
<div class="form-text">Auto = video_src → embed_src → vod_link.</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12">
|
<div class="col-12 col-lg-8">
|
||||||
<label for="edit_video_src" class="form-label">Video URL (mp4/webm/m3u8)</label>
|
<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">
|
<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 class="form-text">If you use a server file (below) and leave this empty, it will auto-use <code>/api/video/<movieId></code>.</div>
|
||||||
@@ -734,34 +816,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</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 / poster)
|
|
||||||
</label>
|
|
||||||
</div>
|
</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-md-6">
|
|
||||||
<label for="edit_title" class="form-label">Titre</label>
|
|
||||||
<input type="text" class="form-control" id="edit_title" placeholder="Title">
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<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-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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,7 +7,27 @@ function isTokenExpired(decoded) {
|
|||||||
return !!decoded.exp && decoded.exp < currentTime;
|
return !!decoded.exp && decoded.exp < currentTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function applyActiveOscarYearToTitle() {
|
||||||
|
const base = 'Pool Oscars';
|
||||||
|
const current = String(document.title || base);
|
||||||
|
const sep = ' - ';
|
||||||
|
const idx = current.indexOf(sep);
|
||||||
|
const suffix = idx >= 0 ? current.slice(idx) : '';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/settings/year', { method: 'GET' });
|
||||||
|
const data = res.ok ? await res.json().catch(() => null) : null;
|
||||||
|
const year = Number(data?.year);
|
||||||
|
document.title = Number.isInteger(year) ? `${base} (${year})${suffix}` : `${base}${suffix}`;
|
||||||
|
} catch (_) {
|
||||||
|
document.title = `${base}${suffix}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
window.onload = function () {
|
window.onload = function () {
|
||||||
|
// Best-effort: update page title with active year.
|
||||||
|
applyActiveOscarYearToTitle();
|
||||||
|
|
||||||
const token = localStorage.getItem('auth_token');
|
const token = localStorage.getItem('auth_token');
|
||||||
if (!token) {
|
if (!token) {
|
||||||
window.location.href = '/';
|
window.location.href = '/';
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ window.addEventListener('DOMContentLoaded', async function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const activeYear = await fetchActiveYear();
|
const activeYear = await fetchActiveYear();
|
||||||
document.title = `Pool Oscars ${activeYear} - Checklist`;
|
document.title = `Pool Oscars (${activeYear}) - Checklist`;
|
||||||
const oscarYearEl = document.getElementById('oscar-year');
|
const oscarYearEl = document.getElementById('oscar-year');
|
||||||
if (oscarYearEl) oscarYearEl.textContent = String(activeYear);
|
if (oscarYearEl) oscarYearEl.textContent = String(activeYear);
|
||||||
|
|
||||||
|
|||||||
@@ -2332,7 +2332,7 @@ window.onload = async function () {
|
|||||||
// Initial load
|
// Initial load
|
||||||
activeYear = await fetchActiveYear();
|
activeYear = await fetchActiveYear();
|
||||||
if (activeYear) {
|
if (activeYear) {
|
||||||
document.title = `Pool Oscars ${activeYear} - Admin`;
|
document.title = `Pool Oscars (${activeYear}) - Admin`;
|
||||||
}
|
}
|
||||||
|
|
||||||
hookModal100Inputs();
|
hookModal100Inputs();
|
||||||
@@ -2352,7 +2352,7 @@ window.onload = async function () {
|
|||||||
activeYear = await setActiveYear(y);
|
activeYear = await setActiveYear(y);
|
||||||
activeYearInput.value = String(activeYear);
|
activeYearInput.value = String(activeYear);
|
||||||
yearInput.value = String(activeYear);
|
yearInput.value = String(activeYear);
|
||||||
document.title = `Pool Oscars ${activeYear} - Admin`;
|
document.title = `Pool Oscars (${activeYear}) - Admin`;
|
||||||
showResponse('success', `Année active appliquée: ${activeYear}`);
|
showResponse('success', `Année active appliquée: ${activeYear}`);
|
||||||
await refreshYears();
|
await refreshYears();
|
||||||
await loadMoviesForManagement();
|
await loadMoviesForManagement();
|
||||||
|
|||||||
@@ -2,6 +2,26 @@ function decodeJwt(token) {
|
|||||||
return JSON.parse(atob(token.split('.')[1]));
|
return JSON.parse(atob(token.split('.')[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function applyActiveOscarYearToTitle() {
|
||||||
|
const base = 'Pool Oscars';
|
||||||
|
const current = String(document.title || base);
|
||||||
|
const sep = ' - ';
|
||||||
|
const idx = current.indexOf(sep);
|
||||||
|
const suffix = idx >= 0 ? current.slice(idx) : '';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/settings/year', { method: 'GET' });
|
||||||
|
const data = res.ok ? await res.json().catch(() => null) : null;
|
||||||
|
const year = Number(data?.year);
|
||||||
|
document.title = Number.isInteger(year) ? `${base} (${year})${suffix}` : `${base}${suffix}`;
|
||||||
|
} catch (_) {
|
||||||
|
document.title = `${base}${suffix}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fire and forget.
|
||||||
|
applyActiveOscarYearToTitle();
|
||||||
|
|
||||||
// Check if the user is already logged in by looking for auth_token in localStorage
|
// Check if the user is already logged in by looking for auth_token in localStorage
|
||||||
const existingToken = localStorage.getItem('auth_token');
|
const existingToken = localStorage.getItem('auth_token');
|
||||||
if (existingToken) {
|
if (existingToken) {
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ window.addEventListener('DOMContentLoaded', async function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const activeYear = await fetchActiveYear();
|
const activeYear = await fetchActiveYear();
|
||||||
document.title = `Pool Oscars ${activeYear} - Films`;
|
document.title = `Pool Oscars (${activeYear}) - Films`;
|
||||||
const oscarYearEl = document.getElementById('oscar-year');
|
const oscarYearEl = document.getElementById('oscar-year');
|
||||||
if (oscarYearEl) oscarYearEl.textContent = String(activeYear);
|
if (oscarYearEl) oscarYearEl.textContent = String(activeYear);
|
||||||
|
|
||||||
|
|||||||
+25
-23
@@ -23,6 +23,18 @@ function normalizeUserId(raw) {
|
|||||||
return /^[a-f0-9]{24}$/i.test(s) ? s : null;
|
return /^[a-f0-9]{24}$/i.test(s) ? s : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fetchActiveOscarYear() {
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/settings/year', { method: 'GET' });
|
||||||
|
if (!res.ok) return null;
|
||||||
|
const data = await res.json().catch(() => null);
|
||||||
|
const year = Number(data?.year);
|
||||||
|
return Number.isInteger(year) ? year : null;
|
||||||
|
} catch (_) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchPlaybackProgress(movieId, token) {
|
async function fetchPlaybackProgress(movieId, token) {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/movies/${encodeURIComponent(movieId)}/progress`, {
|
const res = await fetch(`/api/movies/${encodeURIComponent(movieId)}/progress`, {
|
||||||
@@ -341,12 +353,15 @@ function formatClock(seconds) {
|
|||||||
return `${m}:${String(sec).padStart(2, '0')}`;
|
return `${m}:${String(sec).padStart(2, '0')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setHeader(movie) {
|
function setHeader(movie, { activeYear } = {}) {
|
||||||
const titleEl = document.getElementById('movie-title');
|
const titleEl = document.getElementById('movie-title');
|
||||||
const categoryEl = document.getElementById('movie-category');
|
const categoryEl = document.getElementById('movie-category');
|
||||||
const ratingEl = document.getElementById('movie-rating');
|
const ratingEl = document.getElementById('movie-rating');
|
||||||
|
|
||||||
if (titleEl) titleEl.textContent = movie?.title || 'Player';
|
const year = Number.isInteger(activeYear) ? activeYear : null;
|
||||||
|
const titlePrefix = year ? `Pool Oscars (${year})` : 'Pool Oscars';
|
||||||
|
|
||||||
|
if (titleEl) titleEl.textContent = movie?.title || titlePrefix;
|
||||||
|
|
||||||
if (categoryEl) {
|
if (categoryEl) {
|
||||||
const cat = movie?.category ? String(movie.category) : '';
|
const cat = movie?.category ? String(movie.category) : '';
|
||||||
@@ -358,7 +373,7 @@ function setHeader(movie) {
|
|||||||
ratingEl.textContent = r ? `⭐ ${r}` : '';
|
ratingEl.textContent = r ? `⭐ ${r}` : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
document.title = movie?.title ? `Player - ${movie.title}` : 'Player';
|
document.title = movie?.title ? `${titlePrefix} - ${movie.title}` : titlePrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeVodLink(raw) {
|
function normalizeVodLink(raw) {
|
||||||
@@ -475,18 +490,6 @@ function hideAllPlayers() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setOpenOriginal(raw) {
|
|
||||||
const openOriginal = document.getElementById('open-original');
|
|
||||||
if (!openOriginal) return;
|
|
||||||
if (raw) {
|
|
||||||
openOriginal.href = raw;
|
|
||||||
openOriginal.classList.remove('d-none');
|
|
||||||
} else {
|
|
||||||
openOriginal.removeAttribute('href');
|
|
||||||
openOriginal.classList.add('d-none');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isApiVideoUrl(raw) {
|
function isApiVideoUrl(raw) {
|
||||||
const s = String(raw || '').trim();
|
const s = String(raw || '').trim();
|
||||||
if (!s) return false;
|
if (!s) return false;
|
||||||
@@ -597,9 +600,6 @@ async function playVodLink(vodLink, { token } = {}) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// "Open original" button
|
|
||||||
setOpenOriginal(raw);
|
|
||||||
|
|
||||||
// Prefer known embed providers
|
// Prefer known embed providers
|
||||||
const yt = toYoutubeEmbed(raw);
|
const yt = toYoutubeEmbed(raw);
|
||||||
if (yt) {
|
if (yt) {
|
||||||
@@ -721,6 +721,9 @@ window.onload = async function () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load the active Oscar year in parallel with the movie fetch.
|
||||||
|
const activeYearPromise = fetchActiveOscarYear();
|
||||||
|
|
||||||
let decoded = null;
|
let decoded = null;
|
||||||
try {
|
try {
|
||||||
decoded = decodeJwt(token);
|
decoded = decodeJwt(token);
|
||||||
@@ -771,11 +774,11 @@ window.onload = async function () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setHeader(movie);
|
const activeYear = await activeYearPromise;
|
||||||
|
setHeader(movie, { activeYear });
|
||||||
|
|
||||||
const resolved = resolveMovieSource(movie);
|
const resolved = resolveMovieSource(movie);
|
||||||
if (!resolved?.src) {
|
if (!resolved?.src) {
|
||||||
setOpenOriginal(null);
|
|
||||||
setSourceLabel('—');
|
setSourceLabel('—');
|
||||||
hideAllPlayers();
|
hideAllPlayers();
|
||||||
showAlert('No playable source configured for this movie.');
|
showAlert('No playable source configured for this movie.');
|
||||||
@@ -786,10 +789,9 @@ window.onload = async function () {
|
|||||||
|
|
||||||
if (resolved.isLegacy) {
|
if (resolved.isLegacy) {
|
||||||
// Legacy source: never embed it in the player.
|
// Legacy source: never embed it in the player.
|
||||||
setOpenOriginal(resolved.src);
|
setSourceLabel('Legacy link');
|
||||||
setSourceLabel('Legacy link (opens in new tab)');
|
|
||||||
hideAllPlayers();
|
hideAllPlayers();
|
||||||
showAlertVariant('Legacy source: use “Open original” to watch in a new tab.', 'warning');
|
showAlertVariant('Legacy source: this link is not playable in the in-app player.', 'warning');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,26 @@ function decodeJwt(token) {
|
|||||||
return JSON.parse(atob(token.split('.')[1]));
|
return JSON.parse(atob(token.split('.')[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function applyActiveOscarYearToTitle() {
|
||||||
|
const base = 'Pool Oscars';
|
||||||
|
const current = String(document.title || base);
|
||||||
|
const sep = ' - ';
|
||||||
|
const idx = current.indexOf(sep);
|
||||||
|
const suffix = idx >= 0 ? current.slice(idx) : '';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/settings/year', { method: 'GET' });
|
||||||
|
const data = res.ok ? await res.json().catch(() => null) : null;
|
||||||
|
const year = Number(data?.year);
|
||||||
|
document.title = Number.isInteger(year) ? `${base} (${year})${suffix}` : `${base}${suffix}`;
|
||||||
|
} catch (_) {
|
||||||
|
document.title = `${base}${suffix}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fire and forget.
|
||||||
|
applyActiveOscarYearToTitle();
|
||||||
|
|
||||||
// Check if the user is already logged in by looking for auth_token in localStorage
|
// Check if the user is already logged in by looking for auth_token in localStorage
|
||||||
const existingToken = localStorage.getItem('auth_token');
|
const existingToken = localStorage.getItem('auth_token');
|
||||||
if (existingToken) {
|
if (existingToken) {
|
||||||
|
|||||||
@@ -365,7 +365,7 @@ window.addEventListener('DOMContentLoaded', async function () {
|
|||||||
pageLoader.setProgress(20);
|
pageLoader.setProgress(20);
|
||||||
const activeYear = await fetchActiveYear();
|
const activeYear = await fetchActiveYear();
|
||||||
if (activeYear) {
|
if (activeYear) {
|
||||||
document.title = `Pool Oscars ${activeYear} - Statistiques des utilisateurs`;
|
document.title = `Pool Oscars (${activeYear}) - Statistiques des utilisateurs`;
|
||||||
const h2 = document.querySelector('h2');
|
const h2 = document.querySelector('h2');
|
||||||
if (h2) h2.textContent = `Statistiques des utilisateurs (${activeYear})`;
|
if (h2) h2.textContent = `Statistiques des utilisateurs (${activeYear})`;
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -5,7 +5,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<!-- IMPORTANT: the player may use ?token=... for cross-origin video streaming -->
|
<!-- IMPORTANT: the player may use ?token=... for cross-origin video streaming -->
|
||||||
<meta name="referrer" content="no-referrer" />
|
<meta name="referrer" content="no-referrer" />
|
||||||
<title>Player</title>
|
<title>Pool Oscars</title>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link href="./css/app.css" rel="stylesheet">
|
<link href="./css/app.css" rel="stylesheet">
|
||||||
<link rel="preload" as="image" href="img/logo_w.png">
|
<link rel="preload" as="image" href="img/logo_w.png">
|
||||||
@@ -120,9 +120,8 @@
|
|||||||
<a id="admin-control-link" href="control.html" class="nav-link btn btn-warning me-2 d-none">Admin</a>
|
<a id="admin-control-link" href="control.html" class="nav-link btn btn-warning me-2 d-none">Admin</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex ms-auto mt-2 mt-lg-0 align-items-center gap-2">
|
<div class="d-flex ms-auto mt-2 mt-lg-0 align-items-center gap-2">
|
||||||
<a id="open-original" class="btn btn-outline-light btn-sm d-none" target="_blank" rel="noopener noreferrer">Open original</a>
|
|
||||||
<span id="user-name" class="navbar-text me-1"></span>
|
<span id="user-name" class="navbar-text me-1"></span>
|
||||||
<button id="log-off" class="btn btn-danger btn-sm">Déconnexion</button>
|
<button id="log-off" class="btn btn-danger">Déconnexion</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user