From ac1c856c42dfa6022b8b8a96d7c1d955e95e1ec9 Mon Sep 17 00:00:00 2001 From: dev Date: Sun, 6 Sep 2026 11:50:02 +0300 Subject: [PATCH] show project files in share page cards with image preview and file download --- public/share.html | 13 +++++++++++++ server.js | 9 +++++++++ 2 files changed, 22 insertions(+) diff --git a/public/share.html b/public/share.html index e5d6ba3..1bc90bd 100644 --- a/public/share.html +++ b/public/share.html @@ -26,6 +26,10 @@ body{font-family:'Segoe UI',system-ui,-apple-system,Roboto,sans-serif;background .card .name{font-size:1.02rem;font-weight:700;color:var(--dark);letter-spacing:-.01em} .card .group{display:inline-block;background:var(--yellow);color:var(--dark);font-size:.72rem;font-weight:700;padding:4px 11px;border-radius:999px;align-self:flex-start} .card .desc{font-size:.9rem;line-height:1.45;color:#4a4f5a;white-space:pre-wrap;word-break:break-word} +.card .files{display:flex;flex-wrap:wrap;gap:6px;margin-top:2px} +.card .files a{display:inline-flex;align-items:center;gap:4px;background:#F2F2F2;border:1px solid #E6E6E6;color:#3a3f4a;text-decoration:none;border-radius:999px;padding:5px 12px;font-size:.75rem;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;transition:border .15s,color .15s} +.card .files a:hover{border-color:var(--yellow);color:var(--dark)} +.card .files a::before{content:'📎'} .card .foot{margin-top:auto;padding:8px 6px 2px;font-size:.78rem;color:var(--gray)} .empty{grid-column:1/-1;background:#fff;border-radius:var(--radius);padding:48px 24px;text-align:center;color:var(--gray);box-shadow:0 1px 3px rgba(20,30,40,.06)} .empty .big{font-size:2rem;margin-bottom:10px} @@ -52,6 +56,14 @@ body{font-family:'Segoe UI',system-ui,-apple-system,Roboto,sans-serif;background function esc(s){return String(s==null?'':s).replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"')} function fmt(d){return d?d.split('-').reverse().join('.'):'весь период'} function showImg(src){document.getElementById('lightboxImg').src=src;document.getElementById('lightbox').classList.add('open')} +function isImageFile(name){return /\.(jpe?g|png|gif|webp|bmp|avif|svg|ico)$/i.test(name||'')} +function filesHTML(files){ + if(!files||!files.length)return ''; + return `
${files.map(f=>isImageFile(f.name) + ?`${esc(f.name)}` + :`${esc(f.name)}` + ).join('')}
`; +} (async()=>{ const token = location.pathname.split('/').filter(Boolean).pop() || ''; @@ -83,6 +95,7 @@ function showImg(src){document.getElementById('lightboxImg').src=src;document.ge
${esc(e.student_name)}
${e.group_name ? `${esc(e.group_name)}` : ''}
${esc(e.description)}
+ ${filesHTML(e.files)}
${new Date(e.created_at).toLocaleString('ru',{day:'2-digit',month:'2-digit',year:'numeric',hour:'2-digit',minute:'2-digit'})}
diff --git a/server.js b/server.js index 89438fb..cd3007f 100644 --- a/server.js +++ b/server.js @@ -303,6 +303,15 @@ app.get('/api/share/:token', async (req, res) => { JOIN groups g ON g.id = e.group_id${where} ORDER BY e.created_at DESC`, params ); + let files = {}; + if (entries.length) { + const fRes = await pool.query( + 'SELECT entry_id, token, name FROM project_files WHERE entry_id = ANY($1) ORDER BY id', + [entries.map(r => r.id)] + ); + fRes.rows.forEach(f => { (files[f.entry_id] = files[f.entry_id] || []).push({ token: f.token, name: f.name }); }); + } + entries.forEach(r => { r.files = files[r.id] || []; }); res.json({ name: l.name, group_name: l.group_name,