feat(datetime): timezone/time_format в настройках + единый хелпер дат
Часовой пояс и формат времени (24h/12h) перенесены из браузера в настройки приложения: валидация IANA-зоны, сид в db/init.sql + db/migration.sql, отдача в GET /api/public-settings, раздел #sec-datetime в настройках с живым превью. Границы суток в SQL переведены на tzDayStart/tzDayEnd + bindTz ($TZ$) — раньше $n::date по TIMESTAMPTZ считал дни в UTC (у контейнера TimeZone=UTC) и молча сдвигал выборку на день; хардкод Europe/Moscow вычищен, now() заменён на tzWall(). Новый public/js/datetime.js: три семейства хелперов (instant / чистая DATE-строка без Date() / чистая TIME-строка), подключён на всех страницах включая публичные share/report/index, initDateTime() встроен в checkAuth(). Прямые toLocale*/getFullYear/toISOString().slice(0,10) в public/ выпилены. Попутно: bindTz добавляет параметр только при наличии $TZ$ в SQL (иначе запрос виснет вечно без global error handler) — регрессия закрыта в api.smoketest.js; починены off-by-one месяца в report.js и группировка по дате в share.js.
This commit is contained in:
@@ -124,7 +124,7 @@ function gridEntryHTML(e) {
|
||||
<div class="group">${esc(e.group_name)}${e.module_name ? ' · ' + esc(e.module_name) : ''}</div>
|
||||
<div class="desc">${esc(e.description)}${aiBadge(e)}</div>
|
||||
${filesHTML(e.files)}
|
||||
<div class="time">${new Date(e.created_at).toLocaleString('ru')}</div>
|
||||
<div class="time">${fmtFull(e.created_at)}</div>
|
||||
</div>
|
||||
<button class="edit" data-edit="${e.id}" title="Редактировать">✎</button>
|
||||
${mp ? `<button class="edit" data-enhance="${e.id}" title="Улучшить фото">✦</button>` : ''}
|
||||
@@ -148,7 +148,7 @@ function cardEntryHTML(e) {
|
||||
${filesHTML(e.files)}
|
||||
</div>
|
||||
<div class="foot">
|
||||
<span>${new Date(e.created_at).toLocaleString('ru')}</span>
|
||||
<span>${fmtFull(e.created_at)}</span>
|
||||
<span>
|
||||
<button class="edit" data-edit="${e.id}" title="Редактировать">✎</button>
|
||||
${mp ? `<button class="edit" data-enhance="${e.id}" title="Улучшить фото">✦</button>` : ''}
|
||||
@@ -220,7 +220,7 @@ function setView(v) {
|
||||
}
|
||||
|
||||
function setDefaultDates() {
|
||||
const today = new Date().toISOString().slice(0, 10);
|
||||
const today = todayIso();
|
||||
document.getElementById('dateFrom').value = today;
|
||||
document.getElementById('dateTo').value = today;
|
||||
}
|
||||
@@ -895,7 +895,7 @@ async function loadPhotoHistory() {
|
||||
</div>
|
||||
<div style="flex:1;min-width:0">
|
||||
<div style="font-size:.85rem">${esc(PHOTO_JOB_ACTION_LABELS[r.action] || r.action)}
|
||||
<span style="color:var(--muted);font-size:.75rem;margin-left:6px">${esc(PHOTO_JOB_STATUS_LABELS[r.status] || r.status)} · ${esc(new Date(time).toLocaleString('ru'))}</span>
|
||||
<span style="color:var(--muted);font-size:.75rem;margin-left:6px">${esc(PHOTO_JOB_STATUS_LABELS[r.status] || r.status)} · ${esc(fmtFull(time))}</span>
|
||||
</div>
|
||||
${err}
|
||||
</div>
|
||||
@@ -1229,7 +1229,7 @@ function renderEditAiStatus(e) {
|
||||
el.style.display = 'block';
|
||||
const changed = e.description_ai && e.description_original && e.description_ai !== e.description_original;
|
||||
const parts = [AI_STATUS_LABELS[e.ai_status] || `ИИ: ${e.ai_status}`];
|
||||
if (e.ai_checked_at) parts.push(new Date(e.ai_checked_at).toLocaleString('ru'));
|
||||
if (e.ai_checked_at) parts.push(fmtFull(e.ai_checked_at));
|
||||
let html = `<span>${esc(parts.join(' · '))}</span>`;
|
||||
if (changed) html += ` <button type="button" id="aiRevertBtn" class="ai-btn" style="font-size:.75rem" title="Вернуть исходный текст (до ИИ)">↩ Оригинал</button>`;
|
||||
html += ` <button type="button" id="aiRecheckBtn" class="ai-btn" style="font-size:.75rem" title="Проверить заново">⟳ Проверить</button>`;
|
||||
@@ -1416,7 +1416,7 @@ async function exportCSV() {
|
||||
e.group_name,
|
||||
e.module_name || '',
|
||||
e.description.replace(/\n/g, ' '),
|
||||
new Date(e.created_at).toLocaleString('ru'),
|
||||
fmtFull(e.created_at),
|
||||
e.photo_path ? `${API}${e.photo_path}` : ''
|
||||
]);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user