560 lines
21 KiB
JavaScript
560 lines
21 KiB
JavaScript
// Vanilla JS Migration of anmeld.js (jQuery removed)
|
|
|
|
async function ladeAbsagegruende() {
|
|
const res = await fetch('data/absagegruende.txt');
|
|
const text = await res.text();
|
|
return text
|
|
.split('\n')
|
|
.map(line => line.trim())
|
|
.filter(line => line.length > 0);
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', async () => {
|
|
|
|
// URL-Parameter lesen
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const query = {
|
|
typ: urlParams.get('typ') || 'regular',
|
|
storno: urlParams.get('storno') === 'true',
|
|
double: urlParams.get('double') === 'true',
|
|
name: urlParams.get('name') || 'Null',
|
|
datum: urlParams.get('datum') === 'true'
|
|
};
|
|
const absagegrundListe = await ladeAbsagegruende();
|
|
const fuehrung = query.typ === 'sonnen' ? 'Sonnenführung' : 'Sternführung';
|
|
let absagegrund = "";
|
|
const useDatum = query.datum
|
|
|
|
// Unified API endpoint
|
|
const ajaxURL = "../../DB4js_all.php";
|
|
const ANZAHL_DATES = query.typ === 'sonnen' ? 25 : 50;
|
|
bodytext = ""
|
|
betreff = ""
|
|
const TEXTE = {
|
|
absagebutton: (abg) => `Absage ${abg ? `wurde gesendet am ${abg}` : `senden`}`,
|
|
absagetext: "Absage an alle angemeldeten Besucher senden.",
|
|
bittegrund: "Die Führung wird abgesagt wegen:",
|
|
schonabgesagt: "Absage schon gesendet. Nochmal senden?",
|
|
subject: (useDatum, date) => `Absage der ${useDatum ? `${fuehrung} am ${date}` : `heutigen ${fuehrung}`}`,
|
|
bodytext: (useDatum, date) => `Sehr geehrte Dame, sehr geehrter Herr,
|
|
|
|
Sie haben sich über unsere Webseite www.sternwarte-welzheim.de zur ${useDatum ? `${fuehrung} am ${date}`: `heutigen ${fuehrung}`} angemeldet.
|
|
Aufgrund {absagegrund} können wir ${useDatum ? `am ${date}` : 'heute'} keine ${fuehrung} anbieten.
|
|
Bitte melden Sie sich für einen anderen Termin neu an.
|
|
|
|
Mit freundlichen Grüßen
|
|
Beobachtergruppe Sternwarte Welzheim`
|
|
,
|
|
};
|
|
|
|
const liste = {
|
|
emails: [],
|
|
ids: []
|
|
};
|
|
|
|
let abgesagt = null
|
|
let actualdate;
|
|
let isSmallScreen = false
|
|
const DateTime = luxon.DateTime
|
|
|
|
|
|
function $(selector) {
|
|
return document.querySelector(selector);
|
|
}
|
|
|
|
function $all(selector) {
|
|
return document.querySelectorAll(selector);
|
|
}
|
|
|
|
async function fetchFromDbase(body) {
|
|
body.typ = query.typ;
|
|
const response = await fetch(ajaxURL, {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify(body)
|
|
});
|
|
return await response.json();
|
|
}
|
|
|
|
async function putToDbase(body) {
|
|
body.typ = query.typ;
|
|
const response = await fetch(ajaxURL, {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify(body)
|
|
});
|
|
return await response.json();
|
|
}
|
|
|
|
function clearElement(el) {
|
|
el.innerHTML = '';
|
|
}
|
|
|
|
function appendHtml(el, html) {
|
|
el.insertAdjacentHTML('beforeend', html);
|
|
}
|
|
|
|
function setVisibility(id, visible) {
|
|
$(id).style.visibility = visible ? 'visible' : 'hidden';
|
|
}
|
|
|
|
async function storeAbsage(ids) {
|
|
if (!DateTime || typeof DateTime.now !== 'function') {
|
|
console.error('Luxon DateTime ist nicht verfügbar');
|
|
throw new Error('Datum kann nicht erstellt werden - Luxon nicht geladen');
|
|
}
|
|
const dt = DateTime.now()
|
|
const jetzt = dt.toFormat('yyyy-LL-dd HH:mm:ss')
|
|
if (!jetzt || jetzt === 'undefined' || jetzt.includes('undefined')) {
|
|
console.error('Ungültiges Datum generiert:', jetzt);
|
|
throw new Error('Ungültiges Datum erstellt');
|
|
}
|
|
const update = { cmd: 'UPDATE_TLN_BULK', field: 'abgesagt', ids: ids, values: [jetzt] };
|
|
await putToDbase(update);
|
|
abgesagt = jetzt
|
|
}
|
|
|
|
async function getDetailText(id) {
|
|
const nbr = parseInt(id);
|
|
const holit = await fetchFromDbase({cmd:'GET_TEILN_ID', id:nbr});
|
|
const entry = holit[0];
|
|
return `
|
|
<div class="det">${entry.name} ${entry.vorname}<br /></div>
|
|
<div class="det"><a href="mailto:${entry.email}">${entry.email}</a><br /></div>
|
|
<div class="det">${entry.plz} ${entry.stadt}<br /></div>
|
|
<div class="det">${entry.strasse}<br /></div>
|
|
<div class="det">${entry.telefon}<br /></div>
|
|
<div class="det">${entry.remarks}<br /></div>`;
|
|
}
|
|
|
|
|
|
// Teilnehmer aus 'anmeldungen' austragen und den count in 'fdatum1' anpassen
|
|
const austragen = async (teilnehmer) => {
|
|
let update = {cmd: 'DELETEONE', id: parseInt(teilnehmer.id)}
|
|
const erg1 = await putToDbase(update)
|
|
update = {cmd: 'UPDATECOUNT', date: parseInt(teilnehmer.fdatum), anzahl: parseInt(teilnehmer.anzahl)}
|
|
const erg2 = await putToDbase(update)
|
|
console.log("Storno Ergebisse: ",erg1,erg2)
|
|
showAktAnmeldungen(actualdate)
|
|
}
|
|
|
|
|
|
|
|
async function showAktAnmeldungen(date) {
|
|
actualdate = date;
|
|
liste.emails = [];
|
|
liste.ids = [];
|
|
let column = query.storno ? "col-2" : "col-3";
|
|
const anmeldungen = await fetchFromDbase({cmd:'GET_ANMELD', id:date});
|
|
let besucher = 0;
|
|
|
|
clearElement($('#tabAnmeld tbody'));
|
|
|
|
for (let e of anmeldungen) {
|
|
besucher += parseInt(e.anzahl);
|
|
liste.emails.push(e.email);
|
|
liste.ids.push(e.id);
|
|
abgesagt = e.abgesagt ? e.abgesagt.slice(0,16) : null
|
|
abgesagt = (abgesagt === '1900-01-01 00:00') ? null : abgesagt
|
|
|
|
// const selected = e.teilgenommen === "1" ? "checked" : "";
|
|
const row = document.createElement('tr');
|
|
row.classList.add('d-flex');
|
|
|
|
row.innerHTML = `
|
|
<td class="tdname col-6">${e.name} ${e.vorname}</td>
|
|
<td class=${column}>${e.anzahl}</td>`
|
|
if (query.storno) {
|
|
row.innerHTML += `<td class="col-2 tdstorno"><input type="checkbox" value="${e.id}" </td>`
|
|
}
|
|
|
|
row.querySelector('.tdname').addEventListener('click', async () => {
|
|
const detail = await getDetailText(e.id);
|
|
$('#detailtext').innerHTML = detail;
|
|
$('#detaildialog').showModal();
|
|
});
|
|
|
|
if(query.storno) {
|
|
row.querySelector('.tdstorno').addEventListener('click', function() {
|
|
const tr = this.closest("tr") // Finds the closest row <tr>
|
|
const index = tr.rowIndex-1
|
|
austragen(anmeldungen[index])
|
|
});
|
|
}
|
|
|
|
$('#tabAnmeld tbody').appendChild(row);
|
|
}
|
|
$('#absagen').innerHTML = TEXTE.absagebutton(abgesagt)
|
|
|
|
if (besucher !== 0) {
|
|
const sumRow = document.createElement('tr');
|
|
sumRow.id = 'summe';
|
|
sumRow.classList.add('d-flex');
|
|
sumRow.innerHTML = `
|
|
<td class="col-6">Summe der Anmeldungen</td>`
|
|
if (query.storno) {
|
|
sumRow.innerHTML += `<td style="text-align: center;" class="col-4">${besucher}</td>`;
|
|
} else {
|
|
sumRow.innerHTML += `<td class="col-3">${besucher}</td>`;
|
|
}
|
|
$('#tabAnmeld tbody').appendChild(sumRow);
|
|
}
|
|
|
|
// Add group participants line below table
|
|
let groupInfoDiv = $('#groupInfo');
|
|
if (!groupInfoDiv) {
|
|
groupInfoDiv = document.createElement('div');
|
|
groupInfoDiv.id = 'groupInfo';
|
|
groupInfoDiv.style.fontSize = '75%';
|
|
groupInfoDiv.style.textAlign = 'center';
|
|
groupInfoDiv.style.marginTop = '10px';
|
|
$('#tabAnmeld').parentNode.insertBefore(groupInfoDiv, $('#tabAnmeld').nextSibling);
|
|
}
|
|
groupInfoDiv.innerHTML = '';
|
|
|
|
if (query.typ === 'regular' && besucher !== 0) {
|
|
const groupName = await getGroupForDate(date);
|
|
if (groupName) {
|
|
const participants = await getGroupParticipants(groupName);
|
|
if (participants.length > 0) {
|
|
const participantNames = participants.map(p => `${p.vorname} `).join(', ');
|
|
groupInfoDiv.innerHTML = `${groupName} -> ${participantNames}`;
|
|
}
|
|
}
|
|
}
|
|
bodytext = TEXTE.bodytext(useDatum, DateTime.fromISO(date).setLocale("de").toFormat("cccc, 'den' d. LLLL yyyy"))
|
|
betreff = TEXTE.subject(useDatum, DateTime.fromISO(date).setLocale("de").toFormat("cccc, 'den' d. LLLL yyyy"))
|
|
console.log(betreff)
|
|
}
|
|
|
|
async function getGroupForDate(date) {
|
|
const dateInfo = await fetchFromDbase({cmd:'GET_DATES', anzahl: 1, date: date});
|
|
return dateInfo && dateInfo.length > 0 ? dateInfo[0].grp : null;
|
|
}
|
|
|
|
async function getGroupParticipants(groupName) {
|
|
const allBeos = await fetchFromDbase({cmd:'GET_BEOS', onlyguides: true, what: 'vorname,name,gruppe'});
|
|
return allBeos.filter(beo => {
|
|
if (!beo.gruppe) return false;
|
|
// Split gruppe by comma and check for exact match
|
|
// This handles cases like "Mo I, Mo II", "A, B", etc.
|
|
const groups = beo.gruppe.split(',').map(g => g.trim());
|
|
return groups.includes(groupName);
|
|
});
|
|
}
|
|
|
|
async function findName(name) {
|
|
const update = { cmd: 'GET_TEILN_NAME', name: name };
|
|
let erg = await putToDbase(update);
|
|
clearElement($('#tnames'));
|
|
|
|
appendHtml($('#tnames'), `
|
|
<thead><tr>
|
|
<th class="col-6">Name</th>
|
|
<th class="col-2">Anzahl</th>
|
|
<th class="col-2">Datum</th>
|
|
<th class="col-2">Finden</th>
|
|
</tr></thead><tbody>`);
|
|
|
|
for (let x of erg) {
|
|
appendHtml($('#tnames'), `
|
|
<tr>
|
|
<td class="tdname col-6">${x.name} ${x.vorname}</td>
|
|
<td class="col-2">${x.anzahl}</td>
|
|
<td class="col-2">${DateTime.fromISO(x.fdatum).toFormat('yyyy-LL-dd')}</td>
|
|
<td class="tdfind col-2"><button type="button" class="findit" value="${x.id}">🔍</button></td>
|
|
</tr>`);
|
|
}
|
|
appendHtml($('#tnames'), `</tbody>`);
|
|
$('#names').style.display = 'block';
|
|
|
|
$all('.findit').forEach(btn => {
|
|
btn.addEventListener('click', async () => {
|
|
const id = btn.value;
|
|
const detail = await getDetailText(id);
|
|
$('#detailtext').innerHTML = detail;
|
|
$('#detaildialog').showModal();
|
|
});
|
|
});
|
|
}
|
|
|
|
$('#absagen').addEventListener('click', () => {
|
|
$('#absagetext').innerHTML = abgesagt ? TEXTE.schonabgesagt : TEXTE.absagetext;
|
|
$('#bittegrund').innerHTML = TEXTE.bittegrund
|
|
$('#absagedialog').showModal();
|
|
|
|
const container = $('#absagegrund');
|
|
clearElement(container);
|
|
absagegrundListe.forEach((g, i) => {
|
|
const line = document.createElement('div');
|
|
line.className = 'absageline';
|
|
const radio = document.createElement('input');
|
|
radio.type = 'radio';
|
|
radio.name = 'grund';
|
|
radio.value = i;
|
|
radio.id = `grund${i}`;
|
|
radio.checked = i === 0; // Default to the first option
|
|
|
|
const label = document.createElement('label');
|
|
label.setAttribute('for', `grund${i}`);
|
|
const span = document.createElement('span');
|
|
span.textContent = g;
|
|
span.style.marginLeft = "20px"
|
|
|
|
line.appendChild(radio);
|
|
line.appendChild(span);
|
|
line.appendChild(document.createElement('br'));
|
|
container.appendChild(line);
|
|
});
|
|
});
|
|
|
|
$('#absagedialog-send').addEventListener('click', async () => {
|
|
const grundIndex = Array.from($all('input[name=grund]')).find(el => el.checked)?.value || 0;
|
|
absagegrund = absagegrundListe[grundIndex];
|
|
|
|
// Versuche das Absagedatum zu speichern, aber breche nicht ab wenn es fehlschlägt
|
|
let datumGespeichert = false;
|
|
try {
|
|
await storeAbsage(liste.ids);
|
|
datumGespeichert = true;
|
|
} catch (error) {
|
|
console.error('Fehler beim Speichern des Absagedatums:', error);
|
|
// Warnung anzeigen, aber weitermachen
|
|
console.warn('Absagedatum konnte nicht gespeichert werden, aber Mail wird trotzdem versendet');
|
|
}
|
|
|
|
// Laufschrift einschalten (falls regular)
|
|
if (query.typ !== 'sonnen') {
|
|
try {
|
|
await fetch('https://laufschrift.rexfue.de/switch/switch_on')
|
|
} catch (error) {
|
|
console.error('Laufschrift konnte nicht eingeschaltet werden:', error);
|
|
}
|
|
}
|
|
|
|
// Mail senden (wichtigster Teil!)
|
|
bodyText = bodytext.replace("{absagegrund}", absagegrund);
|
|
try {
|
|
const mailRet = await fetchFromDbase({
|
|
cmd: 'SENDMYMAIL',
|
|
to: ['rexfue@gmail.com'],
|
|
betreff: betreff,
|
|
body: bodyText,
|
|
bcc: liste.emails
|
|
});
|
|
|
|
if (mailRet.error) {
|
|
$('#errortext').innerHTML = mailRet.errortext
|
|
$('#errordialog-ok').addEventListener('click', () => $('#errordialog').close())
|
|
$('#errordialog').showModal();
|
|
return;
|
|
}
|
|
|
|
console.log("Mailret: ", mailRet, "Gesendet an: ", liste.emails)
|
|
|
|
// Zeige Warnung an, wenn Datum nicht gespeichert werden konnte
|
|
if (!datumGespeichert) {
|
|
$('#errortext').innerHTML = `<strong>Hinweis:</strong><br>Die Absage-Mail wurde erfolgreich versendet.<br><br>Das Absagedatum konnte jedoch nicht in der Datenbank gespeichert werden. Bitte notieren Sie manuell, dass die Absage gesendet wurde.`;
|
|
$('#errordialog-ok').addEventListener('click', () => $('#errordialog').close())
|
|
$('#errordialog').showModal();
|
|
}
|
|
|
|
$('#absagen').innerHTML = TEXTE.absagebutton(abgesagt)
|
|
$('#absagedialog').close();
|
|
|
|
} catch (error) {
|
|
console.error('Fehler beim Mail-Versand:', error);
|
|
$('#errortext').innerHTML = `<strong>Fehler beim Versenden der Absage-Mail:</strong><br>${error.message}`;
|
|
$('#errordialog-ok').addEventListener('click', () => $('#errordialog').close())
|
|
$('#errordialog').showModal();
|
|
$('#absagedialog').close();
|
|
}
|
|
});
|
|
|
|
$('#absagedialog-cancel').addEventListener('click', () => {
|
|
$('#absagedialog').close();
|
|
});
|
|
|
|
async function getDoubles(date) {
|
|
const update = { cmd: 'GET_ANMELDNEW', date: date, special: "alllater" };
|
|
let { data } = await putToDbase(update);
|
|
|
|
data.sort((a, b) => a.name.localeCompare(b.name));
|
|
data.push({name: '', vorname: '', email: ''});
|
|
|
|
let i;
|
|
for(i = 0; i < data.length - 1; i++) {
|
|
data[i].single = false;
|
|
if(data[i].name !== data[i+1].name) {
|
|
data[i].single = true;
|
|
} else {
|
|
while((i < data.length - 1) && (data[i].name === data[i+1].name)) {
|
|
if (data[i].strasse.slice(0,6).toUpperCase() !== data[i+1].strasse.slice(0,6).toUpperCase()
|
|
|| data[i].plz !== data[i+1].plz) {
|
|
data[i].single = true;
|
|
break;
|
|
} else {
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
data[i].single = true;
|
|
return data;
|
|
}
|
|
|
|
async function showDoubles(date) {
|
|
const erg = await getDoubles(date);
|
|
const container = $('#tnames');
|
|
clearElement(container);
|
|
|
|
appendHtml(container, `
|
|
<thead><tr>
|
|
<th class="col-2">Name</th>
|
|
<th class="col-2">Vorname</th>
|
|
<th class="col-3">Email</th>
|
|
<th class="col-1">Anz</th>
|
|
<th class="col-2">Datum</th>
|
|
<th class="col-2">Anmeldung</th>
|
|
</tr></thead><tbody>`);
|
|
|
|
for (let x of erg) {
|
|
if (x.single) continue;
|
|
appendHtml(container, `
|
|
<tr>
|
|
<td class="tdname col-2">${x.name}</td>
|
|
<td class="tdname col-2">${x.vorname}</td>
|
|
<td class="tdname col-3">${x.email}</td>
|
|
<td class="col-1">${x.anzahl}</td>
|
|
<td class="col-2">${DateTime.fromISO(x.fdatum).toFormat('yyyy-LL-dd')}</td>
|
|
<td class="col-2">${DateTime.fromISO(x.angemeldet).toFormat('yyyy-LL-dd')}</td>
|
|
</tr>`);
|
|
}
|
|
|
|
appendHtml(container, `</tbody>`);
|
|
$('#names').style.display = 'block';
|
|
}
|
|
|
|
function switchText() {
|
|
const thead = document.createElement('thead');
|
|
const row = document.createElement('tr');
|
|
let column = query.storno ? "col-2" : "col-3"
|
|
row.className = 'd-flex';
|
|
|
|
row.innerHTML = `
|
|
<th class="col-6">Name</th>
|
|
<th class=${column}>${isSmallScreen ? 'Anz' : 'Personen'}</th>`
|
|
if (query.storno) {
|
|
row.innerHTML += `<th class="col-2">Storno</th>`;
|
|
}
|
|
$('#versn').innerHTML = "Version: " + VERSION + ' vom ' + VDATE
|
|
|
|
|
|
thead.appendChild(row);
|
|
const table = $('#tabAnmeld');
|
|
clearElement(table); // Leert sowohl thead als auch tbody
|
|
table.appendChild(thead);
|
|
table.appendChild(document.createElement('tbody'));
|
|
}
|
|
|
|
// Aus dem rohen Führungsdatum von der Datenbank ein besser
|
|
// leserliches Format erzeugen
|
|
// Params:
|
|
// w -> Wochentag
|
|
// d -> Datum in DBase-Format
|
|
// t -> Uhrzeit
|
|
// Return
|
|
// neu formatierter Datums-String
|
|
const bauDate = (w, d, t) => {
|
|
const ds = String(d ?? '');
|
|
const ts = String(t ?? '');
|
|
const ws = String(w ?? '');
|
|
const dd = ds.replace(/^(\d{4})(\d{2})(\d{2})$/, '$3.$2.$1');
|
|
const hh = ts.substr(0, 2);
|
|
const ww = ws.substr(0, 2);
|
|
return `${ww}, ${dd} ${hh}:00`;
|
|
}
|
|
|
|
|
|
async function buildFuehrungsDates(dates, last) {
|
|
let n = 0;
|
|
let i = 0;
|
|
let summ = 0;
|
|
let select = `<select name='ftermin' id='ftermin'>`;
|
|
let selectedNext = false;
|
|
const today = DateTime.now().startOf('day');
|
|
|
|
for (let d of dates) {
|
|
let isselected = "";
|
|
const fday = DateTime.fromISO(d.datum);
|
|
if (!selectedNext && (isSameorAfter = fday >= today)) {
|
|
selectedNext = true;
|
|
isselected = "selected";
|
|
n = i;
|
|
}
|
|
const count = await fetchFromDbase({cmd: 'GET_COUNTS', fdate: d.datum});
|
|
const isFuehrung = (count && count > 0) ? '' : 'disabled';
|
|
let datstr
|
|
let grpstr
|
|
if (query.typ == 'regular') {
|
|
datstr = bauDate(d.wtag, d.datum, d.uhrzeit)
|
|
grpstr = `(${d.grp})`
|
|
} else {
|
|
datstr = bauDate('So', d.datum, '11')
|
|
grpstr = " "
|
|
}
|
|
if (isSmallScreen) {
|
|
select += `
|
|
<option value="${d.datum}" ${isselected} ${isFuehrung}> ${datstr} ${count || 0}B</option>`;
|
|
} else {
|
|
select += `
|
|
<option value="${d.datum}" ${isselected} ${isFuehrung}> ${datstr} ${grpstr} ${count || 0} Besucher</option>`;
|
|
}
|
|
i++;
|
|
summ += count ? parseInt(count) : 0;
|
|
if (d.datum >= last) break;
|
|
}
|
|
select += `</select>
|
|
<div id="gesamtsumme">Gesamtzahl der Anmeldungen (ab heute): ${summ}</div>`;
|
|
|
|
$('#auswahl').innerHTML = select;
|
|
|
|
$('#ftermin').addEventListener('change', async () => {
|
|
const date = $('#ftermin').value;
|
|
const index = $('#ftermin').selectedIndex;
|
|
setVisibility('#absagen', index === 0 || useDatum);
|
|
await showAktAnmeldungen(date);
|
|
});
|
|
|
|
return n;
|
|
}
|
|
|
|
async function main(n) {
|
|
let index = 0
|
|
const today = DateTime.now().minus({ days: 14 }).toFormat("yyyyLLdd");
|
|
const dates = await fetchFromDbase({cmd:'GET_DATES', anzahl:n, date: today});
|
|
const last = await fetchFromDbase({cmd:'GET_LASTANMELDUNG', date: today});
|
|
|
|
let mq = window.matchMedia('(max-width: 800px)');
|
|
isSmallScreen = mq.matches
|
|
switchText();
|
|
mq.addEventListener('change', async (e) => {
|
|
isSmallScreen = e.matches
|
|
switchText();
|
|
await buildFuehrungsDates(dates, last);
|
|
showAktAnmeldungen(actualdate);
|
|
});
|
|
|
|
index = await buildFuehrungsDates(dates, last);
|
|
|
|
await showAktAnmeldungen(dates[index].datum);
|
|
if (query.name !== 'Null') {
|
|
await findName(query.name);
|
|
}
|
|
if (query.double) {
|
|
await showDoubles(DateTime.now().toFormat("yyyyLLdd"));
|
|
}
|
|
}
|
|
|
|
main(ANZAHL_DATES).catch(console.error);
|
|
}); |