Files
softspm_www/assets/schedule.js
T
2026-09-03 21:57:48 +02:00

209 lines
7.6 KiB
JavaScript

/* ============================================================================
SoftSPM - harmonogram z Akademii Wodociągowca
Podmienia wiersze tabeli terminów danymi z api/schedule.php.
Gdy API nie odpowiada albo nie ma pozycji, statyczne wiersze z HTML-a
zostają nietknięte - strona nigdy nie pokazuje pustej tabeli.
========================================================================= */
(function () {
'use strict';
var DICT = {
pl: {
dateHead: 'Data',
webinar: 'Webinar',
training: 'Szkolenie',
online: 'Online',
elearning: 'Online · e-learning',
onDemand: 'Na żądanie',
signUp: 'Zapisz się →',
viewCourse: 'Zobacz kurs →',
soldOut: 'Brak miejsc',
soon: 'Zapisy wkrótce',
note: 'Terminy pobierane na bieżąco z platformy kursowej Akademia Wodociągowca. Zapisy prowadzi Akademia.',
plateTitle: 'Terminy i kursy otwarte',
heading: 'Najbliższe szkolenia i webinaria',
intro: 'Poniżej pozycje otwarte w tej chwili na naszej platformie kursowej. Zapisy prowadzi Akademia Wodociągowca.',
locale: 'pl-PL',
levels: { BASIC: 'podstawowy', INTERMEDIATE: 'średni', ADVANCED: 'zaawansowany' }
},
en: {
dateHead: 'Date',
webinar: 'Webinar',
training: 'Training',
online: 'Online',
elearning: 'Online · e-learning',
onDemand: 'On demand',
signUp: 'Sign up →',
viewCourse: 'View course →',
soldOut: 'Fully booked',
soon: 'Registration soon',
note: 'Dates are pulled live from our Akademia Wodociagowca course platform, which also handles registration.',
plateTitle: 'Open dates and courses',
heading: 'Upcoming training and webinars',
intro: 'Everything currently open on our course platform. Registration is handled by Akademia Wodociagowca.',
locale: 'en-GB',
levels: { BASIC: 'basic', INTERMEDIATE: 'intermediate', ADVANCED: 'advanced' }
}
};
var lang = (document.documentElement.getAttribute('lang') || 'pl').slice(0, 2).toLowerCase();
var t = DICT[lang] || DICT.pl;
function el(tag, className, text) {
var node = document.createElement(tag);
if (className) { node.className = className; }
if (text !== undefined && text !== null) { node.textContent = text; }
return node;
}
function formatDate(iso, timezone) {
var date = new Date(iso);
if (isNaN(date.getTime())) { return null; }
var zone = timezone || 'Europe/Warsaw';
var opts = { day: '2-digit', month: '2-digit', year: 'numeric', timeZone: zone };
var timeOpts = { hour: '2-digit', minute: '2-digit', timeZone: zone };
try {
return {
day: new Intl.DateTimeFormat(t.locale, opts).format(date),
hour: new Intl.DateTimeFormat(t.locale, timeOpts).format(date)
};
} catch (e) {
// Nieznana strefa albo stary silnik - pokazujemy datę bez strefy.
return { day: date.toLocaleDateString(t.locale), hour: date.toLocaleTimeString(t.locale) };
}
}
function actionCell(item) {
var cell = el('td', 's-act');
if (item.kind === 'webinar' && item.soldOut) {
cell.appendChild(el('span', 'text-muted', t.soldOut));
return cell;
}
if (item.kind === 'webinar' && item.open === false) {
cell.appendChild(el('span', 'text-muted', t.soon));
return cell;
}
if (!item.url) {
cell.appendChild(el('span', 'text-muted', item.kind === 'webinar' ? t.soon : ''));
return cell;
}
var link = el('a', 'sign', item.kind === 'webinar' ? t.signUp : t.viewCourse);
link.href = item.url;
link.target = '_blank';
link.rel = 'noopener';
cell.appendChild(link);
return cell;
}
function buildRow(item) {
var row = document.createElement('tr');
var date = el('td', 's-date');
if (item.kind === 'webinar') {
var parts = formatDate(item.startsAt, item.timezone);
if (parts) {
date.appendChild(document.createTextNode(parts.day));
date.appendChild(el('span', 's-hour', parts.hour));
} else {
date.appendChild(el('span', 'tag tag-soon', t.onDemand));
}
} else {
date.appendChild(el('span', 'tag tag-soon', t.onDemand));
}
row.appendChild(date);
// textContent, nie innerHTML - tytuły przychodzą z innego systemu.
row.appendChild(el('td', 's-topic', item.title));
var form = el('td', 's-form');
form.appendChild(el(
'span',
item.kind === 'webinar' ? 'tag tag-outline' : 'tag tag-accent',
item.kind === 'webinar' ? t.webinar : t.training
));
row.appendChild(form);
var place = item.kind === 'webinar' ? t.online : t.elearning;
if (item.kind === 'course' && item.level && t.levels[item.level]) {
place = t.elearning + ' · ' + t.levels[item.level];
}
row.appendChild(el('td', 's-place', place));
row.appendChild(actionCell(item));
return row;
}
function render(table, items) {
var body = table.querySelector('tbody');
if (!body) { return; }
var fresh = document.createDocumentFragment();
for (var i = 0; i < items.length; i++) {
fresh.appendChild(buildRow(items[i]));
}
body.innerHTML = '';
body.appendChild(fresh);
// Mamy realne daty, więc nagłówek kolumny wraca z "Termin" na "Data".
var head = table.querySelector('[data-schedule-datehead]');
if (head) { head.textContent = t.dateHead; }
// Przy realnych terminach nagłówki mówiące "ustalamy terminy" przestają
// być prawdziwe, więc lecą razem z wierszami.
var section = table.closest ? table.closest('section') : null;
if (section) {
var heading = section.querySelector('[data-schedule-heading]');
if (heading) { heading.textContent = t.heading; }
var intro = section.querySelector('[data-schedule-intro]');
if (intro) { intro.textContent = t.intro; }
}
var plate = table.closest ? table.closest('.plate') : null;
var plateTitle = plate ? plate.querySelector('[data-schedule-title]') : null;
if (plateTitle) { plateTitle.textContent = t.plateTitle; }
var note = plate ? plate.querySelector('[data-schedule-note]') : null;
if (note) {
note.textContent = t.note + ' ';
var link = el('a', null, 'akademiawodociagowca.pl');
link.href = 'https://akademiawodociagowca.pl/';
link.target = '_blank';
link.rel = 'noopener';
note.appendChild(link);
}
table.setAttribute('data-schedule-state', 'live');
}
function boot() {
var tables = document.querySelectorAll('table[data-schedule]');
if (!tables.length) { return; }
var limit = 0;
for (var i = 0; i < tables.length; i++) {
limit = Math.max(limit, parseInt(tables[i].getAttribute('data-schedule-limit'), 10) || 12);
}
fetch('api/schedule.php?limit=' + limit, {
credentials: 'same-origin',
headers: { 'Accept': 'application/json' }
})
.then(function (response) { return response.json(); })
.then(function (data) {
if (!data || data.source !== 'api' || !data.items || !data.items.length) { return; }
for (var i = 0; i < tables.length; i++) {
var own = parseInt(tables[i].getAttribute('data-schedule-limit'), 10) || data.items.length;
render(tables[i], data.items.slice(0, own));
}
})
.catch(function () {
// Cisza jest właściwą reakcją: w tabeli zostają statyczne wiersze.
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', boot);
} else {
boot();
}
})();