Files
sternwarte/sternwarte/javascript/anmeldung.js

472 lines
18 KiB
JavaScript

// anmeldung.js rxf 2020-10.12
// Dynamik für die Anmelde-Seite
//
$(document).ready(() => {
const ajaxURL="../DB4js.php";
const maxPersonen = 10;
const dummyTln = {
name:"Zuname{z}",
vorname:"",
email:"rxf{z}@gmx.de",
telefon:"0711123456789",
plz:"12345",
stadt:"Ort{z}",
strasse:"Straße {z}",
fid: "",
anzahl:1,
teilgenommen: 0,
remarks:"Bemerkung Nr {z}",
abgesagt: 0,
angemeldet: moment().format("YYYY-MM-DD")
};
let aktualTln = {
name:"",
vorname:"",
email:"",
telefon:"",
plz:"",
stadt:"",
strasse:"",
fid: "",
anzahl:1,
teilgenommen: 0,
remarks:"",
abgesagt: 0,
angemeldet: ""
};
const mandatory = {
name:true,
vorname:false,
email:true,
telefon:false,
plz:true,
stadt:true,
strasse:true,
fid: false,
anzahl:false,
teilgenommen: false,
remarks:false,
abgesagt: false,
angemeldet: true
};
let allParticipants = [];
let errtext = "";
// Von der Datenbank Werte abholen
// Param:
// body: Object mit cmd und param
// Return:
// angeforderte Daten als JSON
const fetchFromDbase = async (body) => {
const response = await fetch(ajaxURL,{
method: 'POST',
headers: {'Content-Type': 'application/js'},
body: JSON.stringify(body)
});
return await response.json();
}
// In die Datenbank Werte eintragen
// Param:
// body: Object mit cmd und param
// Return:
// angeforderte Daten als JSON
const putToDbase = async (body) => {
const response = await fetch(ajaxURL,{
method: 'POST',
headers: {'Content-Type': 'application/js'},
body: JSON.stringify(body)
});
return await response.json();
}
// Beim Senden der Anmeldeseite die Nummer der Führung mit schicken
$('form').submit((event) => {
let nr = $('#ftermin').find(':selected');
console.log('nr: ', nr[0].value);
$('#fid').val(nr[0].value);
});
// zum Testen die Anmelde-Seite automatisch ausfüllen
$('#autofillbutton').click(() => {
let rand = Math.floor(Math.random() * 100);
for (let f in dummyTln) {
if (f=='fid') {
continue;
}
let x = dummyTln[f].toString();
aktualTln[f] = x.replace("{z}",rand);
$('#'+f).val(x.replace("{z}",rand));
}
});
// Aus dem rohen Führungsdatum von der Datenbank ein besser
// leserliches Format erzeugen
// Params:
// w -> Wocjentag
// d -> Datum in DBase-Format
// t -> Uhrzeit
// Return
// neu formatierter Datums-String
const bauDate = (w,d,t) => {
let dd = d.replace(/^(\d{4})(\d{2})(\d{2})$/, '$3.$2.$1',);
return w.substr(0,2) + ', ' + dd + ' ' + t;
}
// Aus der Datenbank die nächsten 'n' Führungsdaten holen und
// in den <option>-Tags anzeigen.
// Zusätzlich für jeden Tag zählen, wieviel schon angemeldet sind.
// Sind es >= 10, dass diese <option> als "disbaled" markieren.
// Die Anzahl der noch freien Plätze mit anzeigen
async function buildfuehrungdates(n) {
const dates = await fetchFromDbase({cmd:'GET_DATES',anzahl:n, fid:0});
str = "";
for (let d of dates) {
const count = await fetchFromDbase({cmd: 'GET_COUNTS', fid: d.fid});
str += `<option ${count >= maxPersonen ? "disabled" : ""} ` +
`value=${d.fid}> ${bauDate(d.weekday, d.date, d.time)} &nbsp;&nbsp;Frei: ${maxPersonen - count}</>`;
}
$('#fid').append(str);
}
// Mit der fid das Führungsdatum aus der DB holen und in leserliche
// Form umsetzen
// Params:
// fid -> ID des Eintrages in der DB
// Return
// String mit dem Führungsdatum
const getfdateformatted = async (fid) => {
const nxd = await fetchFromDbase({cmd: 'GET_DATES', anzahl: 1, fid: fid});
let newdate = bauDate(nxd[0].weekday, nxd[0].date, nxd[0].time);
return newdate.replace(/(\d+) Uhr/, "um $1 Uhr");
}
// Infomail über die Anmeldung bzw. Stornierung an rexfue@gmail.com senden
// Parameters:
// storno -> true: Stornierung, fals: Anmeldung
const sendInfoMail = async(storno) => {
const fdatum = await getfdateformatted(aktualTln.fid);
const subj = storno ? "Stornierung einer Stern-Führung" : "Neue Anmeldung zu einer Stern-Führung";
const ret = await putToDbase({
cmd: 'SEND_INFO_MAIL',
to: 'rexfue@gmail.com',
subject: subj,
body: '<html>' +
'<body><table>' +
'<tr><td>Name, Vorname</td><td style="text-align: right;">' + aktualTln.name + ', ' + aktualTln.vorname + '</td></tr>' +
'<tr><td>Strasse</td><td style="text-align: right;">' + aktualTln.strasse + '</td></tr>' +
'<tr><td>Ort</td><td style="text-align: right;">' + aktualTln.plz + ', ' + aktualTln.stadt + '</td></tr>' +
'<tr><td>Telefon</td><td style="text-align: right;">' + aktualTln.telefon + '</td></tr>' +
'<tr><td>E-mail</td><td style="text-align: right;">' + aktualTln.email + '</td></tr>' +
'<tr><td>Termin</td><td style="text-align: right;">' + fdatum + '</td></tr>' +
'<tr><td>Personen</td><td style="text-align: right;">' + aktualTln.anzahl + '</td></tr>' +
'<tr><td>Bemerkungen</td><td class="fr">' + aktualTln.remarks + '</td></tr>' +
'</table></body></html>'
});
}
// Bestätigungsmail an den Anmeldeneden senden
// Params:
// storno -> true: Stornierung, fals: Anmeldung
const sendConfirmation = async (storno) => {
const fdatum = await getfdateformatted(aktualTln.fid);
let body = "Sehr geehrte Dame, sehr geehrter Herr, \r\n\r\nhiermit bestätigen wir Ihre "
body += storno ? "Stornierung der " : "";
body += "Anmeldung zu der Führung auf der Sternwarte Welzheim am\r\n\r\n" +
`${fdatum} für ${aktualTln.name} ${aktualTln.vorname} mit ${aktualTln.anzahl} ${aktualTln.anzahl==1?"Person.":"Personen."}` +
"\r\n\r\n";
if (!storno) {
body +=
"Bitte bringen Sie diese Bestätigung als Ausdruck oder digital zur Führung mit. \r\n" +
"Ohne diese Bestätigung erfolgt ausnahmslos k e i n Einlass.\r\n\r\n" +
"Falls Sie den Führungstermin absagen wollen, verwenden Sie bitte folgenden Link \r\n";
body += develop == "true" ?
"http://localhost:8082/anmeldung.php?storno=" :
"https://sternwarte-welzheim.de/anmeldung.php?storno=";
body += md5(aktualTln.email + aktualTln.fid) + "\n\r";
body += "Die Führung findet NUR bei sternklarem Himmel statt. Falls der Himmel bedeckt ist \r\n" +
"und die Führung ausfällt, bitten wir Sie um eine neue Anmeldung.\r\n\r\n" +
"Die Hygienevorschriften sind zu beachten: Die Teilnehmer/-innen müssen Gesichtsmasken \r\n" +
"tragen und den vorgeschriebenen Abstand halten. " +
"Nicht teilnehmen dürfen Personen, \r\ndie in den letzten vierzehn Tagen Kontakt " +
"mit einem Coronavirus-Infizierten \r\nhatten oder Infektionssymptome zeigen.\r\n\r\n";
}
body += "Mit freundlichen Grüßen\r\n\r\n" +
"Beobachterteam der Sternwarte Welzheim\r\n\r\n" +
"www.sternwarte-welzheim.de"
const ret = await putToDbase({
cmd: 'SEND_MAIL',
to: aktualTln.email,
subject: storno ?
"Stornierung Ihrer Anmeldung zu einer Führung auf der Sternwarte Welzheim" :
"Anmeldung zu einer Führung auf der Sternwarte Welzheim",
body: body,
});
console.log("hier wird die Email gesendet");
sendInfoMail(storno);
}
// MD5-Hash ausrechnen
const md5 = (value) => {
return CryptoJS.MD5(value).toString();
}
// Den neuen Teilnehmer in die DB eintragen
const insertEntry = async () => {
let ret = await putToDbase({cmd: 'INSERT_TLN', data: aktualTln})
sendConfirmation(false);
}
// Den Teilnehmer mit der ID id aus der DB löschen
const deleteEntry = async (id) => {
let ret = await putToDbase({cmd: 'DELETE_TLN', id:id});
return ret;
}
// Den Teilnehmer mit der ID id mit den neuen Daten versorgen
const updateEntry = async (id) => {
let ret = await putToDbase({cmd: 'UPDATE_TLN', data: aktualTln, id: id })
sendConfirmation(false);
}
// Dialogbox für 'doppelt eingetragen'
$("#dialogdoppelt").dialog({
dialogClass: 'no-close',
autoOpen: false,
closeOnEscape: false,
width: 700,
modal: true,
position: 'center',
title: 'Doppelter Eintrag',
open: () => {
showdoppelText();
},
buttons: [
{
text: 'Auswahl übernehmen',
click: async function() {
let gewaehlt = [];
$.each($("input[name='dopp']:checked"), function() {
gewaehlt.push($(this).val());
});
let tln = $("#dialogdoppelt").data('fids').otln;
if (gewaehlt.length === 1) {
if (gewaehlt[0] == "new") {
deleteEntry($("#dialogdoppelt").data('fids').otln.id);
insertEntry();
tln = $("#dialogdoppelt").data('fids').ntln;
}
} else {
insertEntry();
tln = $("#dialogdoppelt").data('fids').ntln;
}
$(this).dialog('close');
let meld = await afterDialogtext(tln);
$('#mainContent').html(meld);
$('#mainContent').css('height','430px');
}
}
]
}).prev(".ui-dialog-titlebar").css("background","red");
// Der Eintrag wird jetzt doppelt: dieses melden in einer
// kleinen Dialog-Box
function showDoppelt(oldone, newone) {
$('#dialogdoppelt').data('fids', {otln: oldone, ntln: newone}).dialog('open');
}
// Text für doppelten Eintrag anzeigen
async function showdoppelText() {
const ofdate = await fetchFromDbase({
cmd: 'GET_ONE_DATE',
fid: $("#dialogdoppelt").data('fids').otln.fid
});
const nfdate = await fetchFromDbase({
cmd: 'GET_ONE_DATE',
fid: $("#dialogdoppelt").data('fids').ntln.fid
});
$('#dialogdoppelttext').html('Sie haben sich doppelt eingetragen.<br />' +
'Wenn das so gewollt ist, dann kreuzen Sie bitte unten beide Führungen an,<br />' +
'andernfalls nur diejenige, an der Sie wirklich teilnehmen wollen.<br /> <br />' +
'<input type="checkbox" name="dopp" value="new" checked style="margin-right:10px;">' +
bauDate(nfdate.wtag, nfdate.datum, nfdate.uhrzeit) + '<br />' +
'<input type="checkbox" value="old" name="dopp" style="margin-right:10px;">' +
bauDate(ofdate.wtag, ofdate.datum, ofdate.uhrzeit) + '<br />');
}
const errnum = {'no':1, 'error':2, 'double':3};
// Alle Einträge auf Plausibilität prüfen
// Return:
// Object mit:
// error: 'true', 'false'
// action: 'store', 'exit', 'none'
async function checkPlausibilität() {
let error = false;
// Zuerst prüfen, ob auch alle notwendigen Felder ausgefüllt sind
for (let feld in aktualTln) {
aktualTln[feld] = $('#' + feld).val(); // Feld des actualTln auf dem Wert setzen
if (!mandatory[feld]) {
continue;
}
if (aktualTln[feld] == "") { // Falls das Feld leer ist, den Rahmen
$('#' + feld).css('border-color', 'red'); // rot machen und das
error = true; // errorfralg setzen
}
}
if (error) { // wenn Error, dieses melden
errtext = "Bitte korrigieren Sie die fehlenden Einträge in den rot umrandeten Feldern";
return errnum.error; // und raus
}
// Als Nächstes die PLZ prüfen: Das müssen 5 Ziffern sein
const plz = $('#plz').val();
let re = RegExp(/^[0-9]{5}$/);
if(!re.test(plz)) {
$('#plz').css('border-color', 'red');
errtext = "Bitte geben Sie bei der Postleitzahl mindestens fünf Ziffern und keine Buchstaben ein";
return errnum.error; // und raus
}
// Die Email prüfen
re = RegExp(/^[a-z0-9]+([-_\.]?[a-z0-9])+@[a-z0-9]+([-_\.]?[a-z0-9])+\.[a-z]{2,4}/);
let mail = $('#email').val().toLowerCase();
if(!re.test(mail)) {
$('#email').css('border-color', 'red');
errtext = "Bitte geben Sie eine gültige E-Mail-Adresse ein";
return errnum.error; // und raus
}
// Nun noch doppelte Einträge checken
// dazu prüfen, ob die email_Adresse in den Teilnehmern schon enthalten ist
const aktMail = aktualTln.email;
let allParticipants = await fetchAlleTln();
const find = allParticipants.findIndex((f) => f.email === aktMail);
if (find == -1) {
insertEntry();
return errnum.no;
}
let oldTln = allParticipants[find];
// Email enthalten. Ist der Führungstag identisch? Wenn ja, dann einfach die neuen
// Werte eintragen (also überschreiben)
if (aktualTln.fid === oldTln.fid) {
updateEntry(oldTln.id);
return errnum.no;
} else {
// Hier nun 'Doppelt' melden
showDoppelt(oldTln, aktualTln);
return errnum.double;
}
return errnum.no;
}
// Text für den Dialog der Anmeldung
const afterDialogtext = async(tln) => {
let fdate = await getfdateformatted(tln.fid);
return '<p>Vielen Dank für Ihre Anmeldung. <br /><br />' +
'Wir freuen uns über Ihren Besuch auf der Sternwarte Welzheim am<br /> ' +
'<div id="antwort_fdatum" style="text-align:center;">' +
`${fdate} mit ${tln.anzahl} ${tln.anzahl == 1 ? "Person" : "Personen"}` +
'</div> <br />' +
'Wir haben Ihnen die Anmelde-Bestätigung per Email zugesandt.</p>' +
'<p><a class="button" href="/index.php">Zurück</a></p>';
}
// Formular ist ausgefüllt - der Anmelde-Knopf wurde gerdückt.
// Alle Eingaben in den aktuelTln eintragen und dann
// eine Plausibilitäts-Prüfung durchführen und wenn Alles
// OK ist, die Einträge in die Datenbank schreiben
$('input[name="submit"]').click(async () => {
$('#errordiv').css('visibility','hidden'); // erst mal die evtl. vorhandenen Fehlermeldung
for ( let f in aktualTln) { // löschen und alle Rahmen
$('#'+f).css('border-color','black'); // wieder schwarz machen
}
let nr = $('#fid').find(':selected');
aktualTln.fid = nr[0].value; // selektiertes Führungsdatum merken
const ret = await checkPlausibilität();
if (ret === errnum.error) {
$('#errordiv').html(errtext);
$('#errordiv').css('visibility', 'visible');
} else if (ret === errnum.no) {
let meld = await afterDialogtext(aktualTln);
$('#mainContent').html(meld);
$('#mainContent').css('height','430px');
}
});
// Storno
// Der Link für die Stornierung wurde aufgerufen:
// Zuerst die email-Adresse suchen (mit Hilfe des übergebenen md5-hash),
// dann die Stornierung anzeigen und dann noch eine Bestätigung senden
const doStorno = async(md5hash) => {
let emails = await fetchFromDbase({cmd:'GET_ALL_EMAILS'});
for (let em of emails) {
if(md5((em.email+em.fid)) == md5hash) {
// komplette Info dieses teilnehmers holen
let teilnehmer = await fetchFromDbase({cmd: 'GET_TEILN', id: em.id, isid: true});
aktualTln = Object.assign({}, teilnehmer[0]);
// Löschen des Eintrages
let ret = putToDbase({cmd:'DELETE_TLN', id: em.id});
// Meldung anzeigen
let fdate = await getfdateformatted(em.fid);
let meld = '<p><h1 style="text-align:center">Stornierung</h1><br />' +
'Ihre Anmeldung für die Führung auf der Sternwarte Welzheim am<br /><br />' +
'<div id="antwort_fdatum" style="text-align:center;">' +
`${fdate} mit ${aktualTln.anzahl} ${aktualTln.anzahl == 1 ? "Person" : "Personen"}` +
'</div> <br />' +
'wurde storniert.<br /><br /> Wir haben Ihnen die Stornierungs-Bestätigung per Email zugesandt.</p>' +
'<p><a class="button" href="/index.php">Zurück</a></p>';
$('#mainContent').html(meld);
$('#mainContent').css('height','430px');
sendConfirmation(true);
return;
}
}
}
// Alle eingetragenen Teilnehmer ab 'morgen' in das globale Array einlesen
const fetchAlleTln = async () => {
const nxtDate = await fetchFromDbase({cmd: 'GET_DATES', anzahl: 1, fid:0});
return await fetchFromDbase({cmd: 'GET_ALLTEILN', fid: nxtDate[0].fid});
}
async function main() {
// Falls Storno angefordert, dann anzeigen
if(md5Hash != "") {
await doStorno(md5Hash);
return;
}
// Alle eingetragenen Teilnehmer ab 'morgen' in das globale Array einlesen
// die nächste 30 Führungsdaten anzeigen
buildfuehrungdates(30);
}
main().catch(console.err);
});