Files
intranet/web/intranet_repo/projects/dashboard/dashboard.js
T

237 lines
13 KiB
JavaScript

(() => {
'use strict';
const CONTENT_REFRESH_MS = 30_000;
const INDOOR_REFRESH_MS = 10_000;
const OUTDOOR_REFRESH_MS = 30_000;
const VERTICAL_SPEED_PX_PER_SECOND = 7;
const HORIZONTAL_SPEED_PX_PER_SECOND = 10;
const WEATHER_ICONS = {
cloudy: `<svg xmlns="http://www.w3.org/2000/svg" width="49" height="49" viewBox="6 0 50 50" aria-hidden="true"><defs><filter id="weather-blur" x="-20%" y="-20%" width="140%" height="140%"><feGaussianBlur in="SourceAlpha" stdDeviation="2.5"/><feOffset dx="0" dy="2" result="off"/><feComponentTransfer><feFuncA type="linear" slope="0.15"/></feComponentTransfer><feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge></filter></defs><g filter="url(#weather-blur)"><path d="M22 46h26a10 10 0 0 0 0-20c-.8 0-1.6.1-2.4.3A13 13 0 0 0 20 29a9 9 0 0 0 2 17z" fill="#57A0EE" stroke="#fff" stroke-width="2" stroke-linejoin="round"/><path d="M18 46h22a9 9 0 0 0 0-18c-.7 0-1.4.1-2 .2A11 11 0 0 0 17 31a8 8 0 0 0 1 15z" fill="#91C0F8" stroke="#fff" stroke-width="2" stroke-linejoin="round" opacity="0.95"/></g></svg>`,
sun: `<svg xmlns="http://www.w3.org/2000/svg" width="49" height="49" viewBox="6 0 50 50" aria-hidden="true"><g fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round"><circle cx="32" cy="32" r="10" fill="#FFD34D" stroke="#fff"/><path d="M32 6v8M32 50v8M6 32h8M50 32h8M13.5 13.5l5.7 5.7M44.8 44.8l5.7 5.7M50.5 13.5l-5.7 5.7M19.2 44.8l-5.7 5.7"/></g></svg>`,
moon: `<svg xmlns="http://www.w3.org/2000/svg" width="49" height="49" viewBox="6 0 50 50" aria-hidden="true"><path d="M41 8a22 22 0 1 0 15 35A18 18 0 0 1 41 8z" fill="#DDE7FF" stroke="#fff" stroke-width="2" stroke-linejoin="round"/></svg>`,
cloudy_day: `<svg xmlns="http://www.w3.org/2000/svg" width="49" height="49" viewBox="6 0 50 50" aria-hidden="true"><g><g transform="translate(4,0)"><circle cx="22" cy="22" r="9" fill="#FFD34D" stroke="#fff" stroke-width="2"/><g fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round"><path d="M22 6v6M22 32v6M6 22h6M32 22h6M10.5 10.5l4.2 4.2M29.3 29.3l4.2 4.2M33.5 10.5l-4.2 4.2M14.7 29.3l-4.2 4.2"/></g></g><g transform="translate(8,10)"><path d="M18 36h26a10 10 0 0 0 0-20c-.8 0-1.6.1-2.4.3A13 13 0 0 0 16 19a9 9 0 0 0 2 17z" fill="#91C0F8" stroke="#fff" stroke-width="2" stroke-linejoin="round"/><path d="M22 36h22a9 9 0 0 0 0-18c-.7 0-1.4.1-2 .2A11 11 0 0 0 21 21a8 8 0 0 0 1 15z" fill="#57A0EE" stroke="#fff" stroke-width="2" stroke-linejoin="round" opacity="0.95"/></g></g></svg>`,
cloudy_night: `<svg xmlns="http://www.w3.org/2000/svg" width="49" height="49" viewBox="6 0 50 50" aria-hidden="true"><g><path d="M38 10a16 16 0 1 0 11 26A13 13 0 0 1 38 10z" fill="#DDE7FF" stroke="#fff" stroke-width="2" stroke-linejoin="round"/><g transform="translate(10,18)"><path d="M14 28h26a10 10 0 0 0 0-20c-.8 0-1.6.1-2.4.3A13 13 0 0 0 12 11a9 9 0 0 0 2 17z" fill="#91C0F8" stroke="#fff" stroke-width="2" stroke-linejoin="round"/><path d="M18 28h22a9 9 0 0 0 0-18c-.7 0-1.4.1-2 .2A11 11 0 0 0 17 13a8 8 0 0 0 1 15z" fill="#57A0EE" stroke="#fff" stroke-width="2" stroke-linejoin="round" opacity="0.95"/></g></g></svg>`,
rain_shower: `<svg xmlns="http://www.w3.org/2000/svg" width="49" height="49" viewBox="6 0 50 50" aria-hidden="true"><g><path d="M20 36h26a10 10 0 0 0 0-20c-.8 0-1.6.1-2.4.3A13 13 0 0 0 18 19a9 9 0 0 0 2 17z" fill="#91C0F8" stroke="#fff" stroke-width="2" stroke-linejoin="round"/><g stroke="#fff" stroke-width="2" stroke-linecap="round"><path d="M24 42l-3 6"/><path d="M34 42l-3 6"/><path d="M44 42l-3 6"/></g></g></svg>`,
rain: `<svg xmlns="http://www.w3.org/2000/svg" width="49" height="49" viewBox="6 0 50 50" aria-hidden="true"><g><path d="M20 34h26a10 10 0 0 0 0-20c-.8 0-1.6.1-2.4.3A13 13 0 0 0 18 17a9 9 0 0 0 2 17z" fill="#57A0EE" stroke="#fff" stroke-width="2" stroke-linejoin="round"/><g stroke="#fff" stroke-width="2" stroke-linecap="round"><path d="M22 40l-3 8"/><path d="M32 40l-3 8"/><path d="M42 40l-3 8"/><path d="M52 40l-3 8"/></g></g></svg>`,
thunder: `<svg xmlns="http://www.w3.org/2000/svg" width="49" height="49" viewBox="6 0 50 50" aria-hidden="true"><g><path d="M18 34h28a10 10 0 0 0 0-20c-.8 0-1.6.1-2.4.3A13 13 0 0 0 16 17a9 9 0 0 0 2 17z" fill="#91C0F8" stroke="#fff" stroke-width="2" stroke-linejoin="round"/><path d="M32 36l-6 12h7l-3 10 12-16h-7l3-6z" fill="#FFD34D" stroke="#fff" stroke-width="2" stroke-linejoin="round"/></g></svg>`,
snowy: `<svg xmlns="http://www.w3.org/2000/svg" width="49" height="49" viewBox="6 0 50 50" aria-hidden="true"><g><path d="M20 34h26a10 10 0 0 0 0-20c-.8 0-1.6.1-2.4.3A13 13 0 0 0 18 17a9 9 0 0 0 2 17z" fill="#91C0F8" stroke="#fff" stroke-width="2" stroke-linejoin="round"/><g fill="#fff"><circle cx="24" cy="44" r="2"/><circle cx="34" cy="44" r="2"/><circle cx="44" cy="44" r="2"/><circle cx="29" cy="52" r="2"/><circle cx="39" cy="52" r="2"/></g></g></svg>`,
mist: `<svg xmlns="http://www.w3.org/2000/svg" width="49" height="49" viewBox="6 0 50 50" aria-hidden="true"><g fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" opacity="0.95"><path d="M14 24h36"/><path d="M10 32h44"/><path d="M14 40h36"/><path d="M18 48h28"/></g></svg>`,
};
function setText(selector, value) {
const element = document.querySelector(selector);
if (element) {
element.textContent = value ?? '';
}
}
function updateClock() {
const now = new Date();
const clock = document.getElementById('uhrzeit');
if (clock) {
clock.textContent = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
}
}
function renderAnnouncements(announcements) {
const container = document.querySelector('.news-scroll');
if (!container || !Array.isArray(announcements)) {
return;
}
container.replaceChildren();
announcements.forEach((announcement) => {
const box = document.createElement('div');
box.className = 'news-box';
const date = document.createElement('div');
date.className = 'news-date';
date.append(document.createTextNode(announcement.date_label || ''));
if (announcement.end_label) {
date.append(document.createElement('br'), document.createTextNode(announcement.end_label));
}
const body = document.createElement('div');
body.append(document.createTextNode(announcement.title || ''));
if (announcement.subtitle) {
const subtitle = document.createElement('div');
subtitle.className = 'news-subtitle';
subtitle.textContent = announcement.subtitle;
body.append(subtitle);
}
if (announcement.meta) {
const meta = document.createElement('div');
meta.className = 'news-subtitle';
const italic = document.createElement('i');
italic.textContent = announcement.meta;
meta.append(italic);
body.append(meta);
}
box.append(date, body);
container.append(box);
});
}
function renderPosters(posters) {
const container = document.querySelector('.aushang-flex');
if (!container || !Array.isArray(posters)) {
return;
}
container.replaceChildren();
if (posters.length === 0) {
const empty = document.createElement('p');
empty.style.textAlign = 'left';
empty.textContent = 'Keine Aushänge vorgeplant.';
container.append(empty);
return;
}
posters.forEach((poster) => {
const image = document.createElement('img');
image.src = poster.url;
image.alt = poster.alt || 'Aushang';
image.style.height = '95%';
image.addEventListener('load', () => scheduleScrollingSetup(0), { once: true });
container.append(image);
});
}
function renderMonthImages(images) {
if (!Array.isArray(images)) {
return;
}
images.forEach((image) => {
const element = document.querySelector(`[data-month-page="${image.page}"]`);
if (element && image.url && element.getAttribute('src') !== image.url) {
element.src = image.url;
}
});
}
function renderIndoor(indoor) {
if (indoor) {
setText('#temperature_txt', `${indoor.value}${indoor.unit || '°C'}`);
}
}
function renderOutdoor(outdoor) {
const weather = document.getElementById('wetter');
if (!weather || !outdoor) {
return;
}
const icon = WEATHER_ICONS[outdoor.icon] || WEATHER_ICONS.cloudy;
weather.replaceChildren();
const iconContainer = document.createElement('div');
iconContainer.innerHTML = icon;
const temperature = document.createElement('div');
temperature.className = 'text-outdoor';
temperature.textContent = `${outdoor.temperature}°C`;
weather.append(iconContainer, temperature);
}
function renderContent(data) {
setText('[data-dashboard-date]', data.date_label);
if (data.service) {
setText('.bible', data.service.bibelwort);
setText('.verse', data.service.bibelvers);
setText('.GB', data.service.eingang_nummer);
setText('.GB-title', data.service.eingang_titel);
}
renderAnnouncements(data.announcements);
renderPosters(data.posters);
renderMonthImages(data.month_images);
scheduleScrollingSetup();
}
function startPolling(resource, interval, render) {
const refresh = async () => {
try {
const response = await fetch(`data.php?resource=${encodeURIComponent(resource)}`, { cache: 'no-store' });
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
if (data.error) {
throw new Error(data.error);
}
render(data);
} catch (error) {
console.error(`Dashboard-Aktualisierung (${resource}) fehlgeschlagen:`, error);
} finally {
window.setTimeout(refresh, interval);
}
};
refresh();
}
function applyAnimation(element, distance, speed, animationName) {
element.style.animation = 'none';
if (distance <= 0) {
return;
}
const variable = animationName === 'scroll-h' ? '--scroll-distance-h' : '--scroll-distance-v';
element.style.setProperty(variable, `-${distance}px`);
element.style.animationName = animationName;
element.style.animationDuration = `${Math.max(1, distance / speed)}s`;
element.style.animationTimingFunction = 'linear';
element.style.animationIterationCount = 'infinite';
}
function setupScrolling() {
const posterContainer = document.querySelector('.aushang-flex');
if (posterContainer) {
const images = Array.from(posterContainer.querySelectorAll('img'));
const totalWidth = images.reduce((sum, image) => {
const styles = window.getComputedStyle(image);
return sum + image.offsetWidth + (parseFloat(styles.marginRight) || 0);
}, 0);
applyAnimation(
posterContainer,
Math.max(0, totalWidth - posterContainer.offsetWidth),
HORIZONTAL_SPEED_PX_PER_SECOND,
'scroll-h'
);
}
const newsContainer = document.querySelector('.news-container');
const news = document.querySelector('.news-scroll');
if (newsContainer && news) {
applyAnimation(
news,
Math.max(0, news.scrollHeight - newsContainer.offsetHeight),
VERTICAL_SPEED_PX_PER_SECOND,
'scroll-v'
);
}
}
let scrollingTimer = null;
function scheduleScrollingSetup(delay = 80) {
if (scrollingTimer !== null) {
window.clearTimeout(scrollingTimer);
}
scrollingTimer = window.setTimeout(setupScrolling, delay);
}
updateClock();
window.setInterval(updateClock, 1000);
window.addEventListener('load', () => scheduleScrollingSetup(50));
window.addEventListener('resize', () => scheduleScrollingSetup(100));
startPolling('content', CONTENT_REFRESH_MS, renderContent);
startPolling('indoor', INDOOR_REFRESH_MS, (data) => renderIndoor(data.indoor));
startPolling('outdoor', OUTDOOR_REFRESH_MS, (data) => renderOutdoor(data.outdoor));
})();