Add cookie notice with settings, jfif preview support, and share page title from link name

This commit is contained in:
dev
2026-09-12 10:05:02 +03:00
parent 192de5e690
commit fd753c1318
11 changed files with 80 additions and 36 deletions
+1 -1
View File
@@ -21,7 +21,7 @@ function setMode(m) {
}
function isImageFile(name) {
return /\.(jpe?g|png|gif|webp|bmp|avif|svg|ico)$/i.test(name || '');
return /\.(jpe?g|jfif|png|gif|webp|bmp|avif|svg|ico)$/i.test(name || '');
}
function humanSize(b) {
+1 -1
View File
@@ -21,7 +21,7 @@ async function loadLinkSettings() {
}
function isImageFile(name) {
return /\.(jpe?g|png|gif|webp|bmp|avif|svg|ico)$/i.test(name || '');
return /\.(jpe?g|jfif|png|gif|webp|bmp|avif|svg|ico)$/i.test(name || '');
}
function mainPhotoPath(e) {
+3 -1
View File
@@ -1,4 +1,4 @@
const DIRTY_FIELDS = ['spamInterval', 'footerLeft', 'footerRight', 'aiPrompt', 'aiAutoCheck', 'shareShowMessage', 'shareShowDate', 'shareShowNames', 'shareShowPhotos'];
const DIRTY_FIELDS = ['spamInterval', 'footerLeft', 'footerRight', 'aiPrompt', 'aiAutoCheck', 'shareShowMessage', 'shareShowDate', 'shareShowNames', 'shareShowPhotos', 'cookieNoticeText'];
function fieldValue(el) {
return el.type === 'checkbox' ? String(el.checked) : el.value;
@@ -51,6 +51,7 @@ async function loadSettings() {
document.getElementById('shareShowDate').checked = s.share_show_entry_date === 'true';
document.getElementById('shareShowNames').checked = s.share_show_student_names !== 'false';
document.getElementById('shareShowPhotos').checked = s.share_show_group_photos !== 'false';
document.getElementById('cookieNoticeText').value = s.cookie_notice_text || '';
snapshotDirty();
loadAiQueue();
if (stRes.ok) {
@@ -166,6 +167,7 @@ async function saveSettings() {
share_show_entry_date: document.getElementById('shareShowDate').checked ? 'true' : 'false',
share_show_student_names: document.getElementById('shareShowNames').checked ? 'true' : 'false',
share_show_group_photos: document.getElementById('shareShowPhotos').checked ? 'true' : 'false',
cookie_notice_text: document.getElementById('cookieNoticeText').value.trim(),
} })
});
if (res.ok) {
+33 -11
View File
@@ -1,7 +1,7 @@
function esc(s){return String(s==null?'':s).replace(/&/g,'&').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 isImageFile(name){return /\.(jpe?g|jfif|png|gif|webp|bmp|avif|svg|ico)$/i.test(name||'')}
function thumbSrc(p){
if(!p)return p;
if(p.startsWith('/uploads/'))return '/uploads/thumb/'+p.slice('/uploads/'.length);
@@ -84,7 +84,7 @@ function startCountdown(expiresAt) {
}
function fileType(name){
if(/\.(jpe?g|png|gif|webp|bmp|avif|svg|ico)$/i.test(name||'')) return 'image';
if(/\.(jpe?g|jfif|png|gif|webp|bmp|avif|svg|ico)$/i.test(name||'')) return 'image';
if(/\.(mp3|wav|ogg|flac|m4a|aac|opus)$/i.test(name||'')) return 'music';
if(/\.(pptx?|odp|key)$/i.test(name||'')) return 'presentation';
if(/\.(pdf|docx?|xlsx?|txt|rtf|odt|ods|csv|md)$/i.test(name||'')) return 'document';
@@ -185,7 +185,7 @@ function showError(msg) {
}
function renderShare(data, password) {
const heading = data.group_name || data.name || 'Записи занятий';
const heading = (data.name && String(data.name).trim()) || data.group_name || 'Записи занятий';
document.title = heading;
document.getElementById('title').textContent = heading;
const chips = [];
@@ -239,22 +239,25 @@ function renderShare(data, password) {
const isAnonymized = !data.show_student_names;
const showStudentMessage = !!data.show_student_message;
const showEntryDate = !!data.show_entry_date;
grid.innerHTML = data.entries.map(e => `
grid.innerHTML = data.entries.map(e => {
const body = [
!isAnonymized ? `<div class="name">${esc(e.student_name)}</div>` : '',
!isAnonymized && e.group_name ? `<div><span class="group">${esc(e.group_name)}</span></div>` : '',
showStudentMessage && e.description ? `<div class="desc">${esc(e.description)}</div>` : '',
filesHTML(e.files, password, currentToken)
].join('');
return `
<div class="card">
<div class="photo">
${e.photo_path
? `<img src="${thumbSrc(e.photo_path)}" data-img="${esc(e.photo_path)}" loading="lazy" decoding="async" alt="">`
: `<div class="no-photo"><svg viewBox="0 0 240 160" preserveAspectRatio="xMidYMid meet" aria-hidden="true"><circle cx="105" cy="48" r="15"/><path d="M74 140 C74 106 88 92 105 92 C122 92 136 106 136 140 Z"/><circle cx="145" cy="58" r="18"/><path d="M108 144 C108 104 124 88 145 88 C166 88 182 104 182 144 Z"/></svg></div>`}
</div>
<div class="body">
${!isAnonymized ? `<div class="name">${esc(e.student_name)}</div>` : ''}
${!isAnonymized && e.group_name ? `<div><span class="group">${esc(e.group_name)}</span></div>` : ''}
${showStudentMessage && e.description ? `<div class="desc">${esc(e.description)}</div>` : ''}
${filesHTML(e.files, password, currentToken)}
</div>
${body ? `<div class="body">${body}</div>` : ''}
${showEntryDate ? `<div class="foot">${new Date(e.created_at).toLocaleString('ru',{day:'2-digit',month:'2-digit',year:'numeric',hour:'2-digit',minute:'2-digit'})}</div>` : ''}
</div>
`).join('');
`;
}).join('');
}
document.getElementById('lightbox').addEventListener('click', () => document.getElementById('lightbox').classList.remove('open'));
@@ -268,3 +271,22 @@ document.addEventListener('click', e => {
(async()=>{
await loadShare();
})();
(async function(){
const notice = document.getElementById('cookieNotice');
const acceptBtn = document.getElementById('cookieAcceptBtn');
const COOKIE_KEY = 'whatido_cookie_consent';
if (!notice || !acceptBtn || localStorage.getItem(COOKIE_KEY)) return;
try {
const res = await fetch('/api/public-settings');
const s = await res.json();
const text = (s.cookie_notice_text || '').trim();
if (!text) return;
notice.querySelector('p').textContent = text;
notice.classList.remove('hidden');
} catch (e) { return; }
acceptBtn.addEventListener('click', () => {
localStorage.setItem(COOKIE_KEY, '1');
notice.classList.add('hidden');
});
})();
+1 -1
View File
@@ -4,7 +4,7 @@ let page = 1;
let totalEntries = 0;
function isImageFile(name) {
return /\.(jpe?g|png|gif|webp|bmp|avif|svg|ico)$/i.test(name || '');
return /\.(jpe?g|jfif|png|gif|webp|bmp|avif|svg|ico)$/i.test(name || '');
}
function filesHTML(files) {
+6
View File
@@ -81,6 +81,12 @@
</label>
</div>
</div>
<div class="settings-row">
<div class="settings-field" style="flex:1">
<label for="cookieNoticeText">Уведомление о cookie</label>
<textarea id="cookieNoticeText" class="settings-input" rows="3" style="min-height:80px"></textarea>
</div>
</div>
</div>
<div class="settings-card">
+10
View File
@@ -75,6 +75,12 @@ body{font-family:'Segoe UI',system-ui,-apple-system,Roboto,sans-serif;background
.countdown.warn .cd-text strong{color:#E65100}
.countdown.urgent{background:#FFEBEE}
.countdown.urgent .cd-text strong{color:#C62828}
.cookie-notice{position:fixed;bottom:24px;right:24px;z-index:150;max-width:340px;background:var(--card-bg);border-radius:16px;padding:18px 20px;box-shadow:0 8px 30px rgba(20,30,40,.18);font-size:.9rem;line-height:1.5;color:#3a3f4a}
.cookie-notice.hidden{display:none}
.cookie-notice p{margin-bottom:14px}
.cookie-notice button{width:100%;padding:10px 14px;background:var(--yellow);color:var(--dark);border:none;border-radius:12px;font-size:.9rem;font-weight:700;cursor:pointer}
.cookie-notice button:hover{opacity:.9}
@media(max-width:480px){.cookie-notice{left:24px;right:24px;max-width:none}}
</style>
</head>
<body>
@@ -93,6 +99,10 @@ body{font-family:'Segoe UI',system-ui,-apple-system,Roboto,sans-serif;background
</main>
<div class="lightbox" id="lightbox"><img id="lightboxImg" alt=""></div>
<div id="countdown" class="countdown"><span class="cd-icon">⏳</span><span class="cd-text"></span></div>
<div class="cookie-notice hidden" id="cookieNotice">
<p>Этот сайт использует cookie-файлы для корректной работы. Продолжая просмотр, вы соглашаетесь с их использованием.</p>
<button type="button" id="cookieAcceptBtn">Принять</button>
</div>
<div class="password-modal" id="passwordModal">
<div class="password-box">