diff --git a/public/index.html b/public/index.html
index 3ea67c8..cff25e9 100644
--- a/public/index.html
+++ b/public/index.html
@@ -516,6 +516,13 @@ window.dispatchEvent(new Event('tiptap-loaded'));
.res-card-cat { font-size: 12px; color: var(--accent); font-weight: 500; margin-bottom: 6px; }
.res-card-desc { font-size: 13px; color: var(--text-muted); line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.res-card-tags { margin-top: 8px; display: flex; gap: 4px; flex-wrap: wrap; }
+ .res-card-link { margin-top: 8px; font-size: 12px; display: flex; align-items: center; gap: 4px; }
+ .res-card-link a { color: var(--accent); text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+ .res-card-link a:hover { text-decoration: underline; }
+ .res-card-link svg { width: 12px; height: 12px; flex-shrink: 0; stroke: var(--accent); fill: none; stroke-width: 2; }
+ .res-modal-link { margin-top: 4px; }
+ .res-modal-link a { color: var(--accent); text-decoration: none; font-size: 14px; word-break: break-all; }
+ .res-modal-link a:hover { text-decoration: underline; }
/* Responsive */
@media (max-width: 768px) {
@@ -829,7 +836,7 @@ window.dispatchEvent(new Event('tiptap-loaded'));
@@ -2304,6 +2311,21 @@ let ressources = [];
let editingResId = null;
let pendingResImage = null; // dataUrl for new upload
+function videoThumbnail(url) {
+ if (!url) return null;
+ // YouTube: various URL formats
+ let m = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([a-zA-Z0-9_-]{11})/);
+ if (m) return `https://img.youtube.com/vi/${m[1]}/hqdefault.jpg`;
+ // Vimeo
+ m = url.match(/vimeo\.com\/(\d+)/);
+ if (m) return `https://vumbnail.com/${m[1]}.jpg`;
+ return null;
+}
+
+function extractDomain(url) {
+ try { return new URL(url).hostname.replace('www.', ''); } catch { return url; }
+}
+
async function loadRessources() {
try {
const res = await fetch('/api/ressources');
@@ -2338,35 +2360,93 @@ function renderRessources() {
ressources.forEach(r => {
if (filterCat && r.Categorie !== filterCat) return;
if (filterSearch) {
- const hay = [r.Titre, r.Description, r.Tags, r.Categorie].filter(Boolean).join(' ').toLowerCase();
+ const hay = [r.Titre, r.Description, r.Tags, r.Categorie, r.Lien].filter(Boolean).join(' ').toLowerCase();
if (!hay.includes(filterSearch)) return;
}
const card = document.createElement('div');
card.className = 'res-card';
card.onclick = () => openResEdit(r.Id);
- const tags = (r.Tags || '').split(',').filter(t => t.trim()).map(t =>
- `
${esc(t.trim())}`
- ).join('');
- let imgHtml = '';
- if (r.Image) {
- imgHtml = `
})
`;
+ // Image: uploaded > video thumbnail > nothing
+ const imgSrc = r.Image || videoThumbnail(r.Lien);
+ if (imgSrc) {
+ const img = document.createElement('img');
+ img.className = 'res-card-img';
+ img.src = imgSrc;
+ img.alt = '';
+ card.appendChild(img);
}
- card.innerHTML = `
- ${imgHtml}
-
-
${esc(r.Titre || '')}
- ${r.Categorie ? `
${esc(r.Categorie)}
` : ''}
- ${r.Description ? `
${esc(r.Description)}
` : ''}
- ${tags ? `
${tags}
` : ''}
-
- `;
+ const body = document.createElement('div');
+ body.className = 'res-card-body';
+
+ const titleEl = document.createElement('div');
+ titleEl.className = 'res-card-title';
+ titleEl.textContent = r.Titre || '';
+ body.appendChild(titleEl);
+
+ if (r.Categorie) {
+ const catEl = document.createElement('div');
+ catEl.className = 'res-card-cat';
+ catEl.textContent = r.Categorie;
+ body.appendChild(catEl);
+ }
+
+ if (r.Description) {
+ const descEl = document.createElement('div');
+ descEl.className = 'res-card-desc';
+ descEl.textContent = r.Description;
+ body.appendChild(descEl);
+ }
+
+ // Lien cliquable
+ if (r.Lien) {
+ const linkDiv = document.createElement('div');
+ linkDiv.className = 'res-card-link';
+ linkDiv.innerHTML = '
';
+ const a = document.createElement('a');
+ a.href = r.Lien;
+ a.target = '_blank';
+ a.rel = 'noopener';
+ a.textContent = extractDomain(r.Lien);
+ a.onclick = (e) => e.stopPropagation();
+ linkDiv.appendChild(a);
+ body.appendChild(linkDiv);
+ }
+
+ const tags = (r.Tags || '').split(',').filter(t => t.trim());
+ if (tags.length) {
+ const tagsEl = document.createElement('div');
+ tagsEl.className = 'res-card-tags';
+ tags.forEach(t => {
+ const badge = document.createElement('span');
+ badge.className = 'card-badge badge-tag';
+ badge.style.cssText = 'font-size:11px;padding:2px 6px;';
+ badge.textContent = t.trim();
+ tagsEl.appendChild(badge);
+ });
+ body.appendChild(tagsEl);
+ }
+
+ card.appendChild(body);
grid.appendChild(card);
});
}
+function updateResUrlLink(url) {
+ const container = document.getElementById('fr-url-link');
+ container.innerHTML = '';
+ if (url && url.startsWith('http')) {
+ const a = document.createElement('a');
+ a.href = url;
+ a.target = '_blank';
+ a.rel = 'noopener';
+ a.textContent = 'Ouvrir le lien';
+ container.appendChild(a);
+ }
+}
+
function openResModal() {
editingResId = null;
pendingResImage = null;
@@ -2378,6 +2458,7 @@ function openResModal() {
document.getElementById('fr-desc').value = '';
document.getElementById('fr-image-input').value = '';
document.getElementById('fr-image-preview').innerHTML = '';
+ updateResUrlLink('');
document.getElementById('btn-delete-res').style.display = 'none';
document.getElementById('res-modal').classList.add('open');
}
@@ -2395,6 +2476,7 @@ function openResEdit(id) {
document.getElementById('fr-desc').value = r.Description || '';
document.getElementById('fr-image-input').value = '';
document.getElementById('fr-image-preview').innerHTML = r.Image ? `
})
` : '';
+ updateResUrlLink(r.Lien || '');
document.getElementById('btn-delete-res').style.display = 'block';
document.getElementById('res-modal').classList.add('open');
}