Compare commits

..

9 Commits

Author SHA1 Message Date
rxf
fbce630b52 V 2.0.0. sofue
Vor dem Senden der Email kann noch ein Text eingefügt werden
2026-03-17 11:36:10 +01:00
rxf
33f3795e5b V 1.0.6 beoanswer
Buttons abgesagt/verschobe werden nun auch aktiviert beim Klick
2026-03-17 10:51:51 +01:00
rxf
561e3a06c6 doc: NAS ohne VPN 2026-03-17 10:16:08 +01:00
rxf
a8f29717b4 checkfuehrung: mehrere Einträge pro Tag behandeln 2026-03-01 16:25:05 +01:00
rxf
ede0cce8ba Beoanswer: Text vei 'Verschoben' 2026-02-21 09:00:07 +01:00
rxf
98580d45d9 Bei zugesagter Sonderführung wird der Kalender-Eintrag updated, der bei 'offen' angelegt wurde 2026-02-06 14:41:59 +01:00
rxf
142ad57f98 datenschutz.html: Typo 2026-02-04 10:48:46 +01:00
rxf
63c760f8df gitignore: Verzeichnis sternwarte/javascript auch ignorieren 2026-02-04 10:37:51 +01:00
rxf
7d0624addf DB4js_all.php: SoFü Einträge ab gestern 0h00 anzeigen 2026-02-03 13:19:17 +01:00
16 changed files with 371 additions and 48 deletions

2
.gitignore vendored
View File

@@ -10,5 +10,5 @@ video
download download
*.log *.log
webseiten webseiten
sternwarte/javascript
sternwarte/beoanswer/.env.production sternwarte/beoanswer/.env.production

9
.vscode/launch.json vendored
View File

@@ -37,6 +37,15 @@
"pathMappings": { "pathMappings": {
"/var/www/html": "${workspaceFolder}/sternwarte" "/var/www/html": "${workspaceFolder}/sternwarte"
} }
},
{
"name": "Debug checkfuehrung.js",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/sternwarte/checkfuehrung/checkfuehrung.js",
"args": ["-d", "2"],
"cwd": "${workspaceFolder}/sternwarte/checkfuehrung",
"console": "integratedTerminal"
} }
] ]
} }

View File

@@ -0,0 +1,96 @@
# NAS ohne VPN
## Übersicht
Auf das NAS-Laufwerk in der Sternwarte kann auf zwei Arten zugegriffen werden:
1. Über VPN (Wireguard), wie in der ersten Anleitung beschrieben (<https://www.sternwarte-welzheim.de/intern/download/Wireguard/Wireguard.pdf>
**Vorteil**:
Der Zugriff ist einfach, das NAS kann direkt in das lokale Filesystem eingebunden werden.
**Nachteil**:
Es muss der Wirguard-Client auf dem lokalen Gerät installiert werden.
2. Direkt über den WEB-Browser.
**Vorteil**:
Einfacher Zugriff direkt im Browser.
**Nachteil**:
Es wird ein zusätzliches Login für die QNAP-Cloud benötigt.
## Vorgehen
* Aufruf im WEB-Browser mit der URL
**https://qlink.to/stwnaswelzheim**
Es erscheint folgendes Fenster:
![alt text](Screenshot 2025-06-09 181601.png)
Auch wenn hier "Problemlösung" steht ist das nichts Anderes als die Aufforderung, sich an der QNAP-Cloud anzumelden.
Also nun auf **anmelden** klicken, dann kommt:
![alt text](Screenshot 2026-03-09 100121.png)
und hier dann ausfüllen:
bei E-Mail Adresse oder Telefonnummer:
**sternwarte.welzheim@gmx.de**
und bei Kennwort:
**Ekg!Te4!8?W5**
Dieses Kennwort ist absichtlich etwas kompliziert, um den Zugang zu userem NAS gut abzusichern. Bitte aufschreiben oder in dem Passwortsafe des Browsers abspeichern.
Das Anklicken von "Ich möchte angemeldet bleiben" bringt nicht viel, da die Anmeldung nur für 2 Tage gemerkt wird und man sich dann eh neu anmelden muss.
Dann Klick auf **anmelden**. Es kann nun sein, dass die Cloud eine 2-Faktor-Authentifizierung anfordert. Sie macht das nicht jedesmal, aber beim ersten mal ziemlich sicher und dann irgendwann wieder. Sie sendet dann einen Code an die die Anmlede-EMail (*sternwarte.welzheim@gmx.de*). Ich habe diese E-Mail-Adresse gewählt, weil dann jeder von Euch direkt über die GMX-Webseite sich anmelden und die Email abrufen kann (siehe Anhang).
Dann kommt nochmal eine Info-Seite, die durch Klick auf **OK** geschlossen werden kann.
![alt text](Screenshot 2025-06-09 182441.png)
Nun verbindet sich die QNAP-Cloud mit der NAS namens **Goldgrube**, diese will nun (auch) eine Anmeldung:
![alt text](Screenshot 2025-06-09 182504.png)
Hier ist der Benutzername:
**Plejaden**
klick auf "Weiter", das Passwort heißt:
**Subaru7ge\***
(Eselsbrücke: Das japanische Wort für Plejaden = Subaru und 7ge* soll "Siebengestirn" bedeuten).
Nun auf Anmeldung klicken und schon landet man auf dem NAS.
Um nun Dateien runter zu laden, auf das Icon "File Station 5" klicken. Damit kommt man zu den Verzeichnissen und Dateien auf dem NAS.
![alt text](Screenshot 2025-06-09 182650.png)
![alt text](Screenshot 2025-06-09 182837.png)
Nun auswählen, welche Datei aus welchem Verzeichnis man runterladen will, diese dann links an dem Kästchen ankreuzen, oben in der Menüleiste der Filestation auf den Schraubenschlüssel klicken und dann "Herunterladen" auswählen. Nun wird die Datei von dem NAS auf den lokalen Rechner kopiert und zwar in das Verzeichnis, das beim Browser eingestellt ist. In der Rgel ist das das Verzeichnis **Downloads**.
![alt text](Screenshot 2025-06-09 182909.png)
Das Ganze klingt jetzt sehr kompliziert, aber wenn man es ein paarmal gemacht hat und wenn die Zugangsdaten abgespeichert wurden, geht das ganz flott. Wie gesagt, der große Vorteil ist, dass man nix extra installieren muss.
Im Moment ist der Zugriff über diesen Zugang **nur lesend !** Auch das dient zur Absicherung unseres NAS. Wer von Euch schreibenden Zugang auf das NAS benötigt, sollte bitte den VPN (Wirguard)-Zugang verwenden. Falls das nicht möglich ist, bitte bei mir melden, ich mache dann einen extra Zuganng zum Schreiben.
Sollte ich was vergessen haben oder etwas unklar sein, einfach eine Mail an mich schreiben (<rxf@fuerst-stuttgart.de>)
## Anmeldung bei GMX
Diese Anmeldung ist nur notwendig, wenn - wie oben erwähnt - die QNAP-Cloud eine 2-Faktor-Authentifizierung anfordert. Dann wie folgt vorgehen:
Die Webseite von GMX im Browser aufrufen: **gmx.de**. Ganz unten am Bildschirm steht dann: **GMX Zum Postfach**. Hier dann drauf klicken. Nun anmleden mit:
E-Mail -> **sternwarte.welzheim@gmx.de**, dann *weiter* und
Passwort -> **c3zx4C6Yy6CU** , dann *Login*.
![alt text](gmx_anmeldung.png)
Nun erscheint die Seite mit den EMails. Hier dann die Neueste von QNAP nehmen (steht normalerweise ganz oben), den gesendeten Code ausschneiden und dann in die Maske bei der QNAP-Anmeldung einfügen.
Bitte **unbedingt** bei GMX wieder abmelden.
Rechts oben auf den kleine Kreis mit **SW** klicken, dann auf **Logout**.
![alt text](gmx_logout.png)
### Versionen
Datum | Version | Bemerkung
-----|:-----:|----
2026-03-09 | 1.1 | Zugang zu Cloud geändert, GMX dazu
2025-06-12 | 1.0 | erste veröffentlichte Version

Binary file not shown.

View File

@@ -612,7 +612,7 @@ class RepoSoFue
// Bei status=4 (stattgefunden) macht 'neu' keinen Sinn, ignoriere termin // Bei status=4 (stattgefunden) macht 'neu' keinen Sinn, ignoriere termin
// termin kann 'all', 'neu' sein - nur 'neu' wird behandelt // termin kann 'all', 'neu' sein - nur 'neu' wird behandelt
if ($termin === 'neu' && (int)$status !== 4) { if ($termin === 'neu' && (int)$status !== 4) {
$countSql .= " AND wtermin >= NOW()"; $countSql .= " AND wtermin >= CURDATE() - INTERVAL 1 DAY ";
} }
// Lastdate-Filter auch beim Count hinzufügen // Lastdate-Filter auch beim Count hinzufügen
@@ -652,7 +652,7 @@ class RepoSoFue
// Bei status=4 (stattgefunden) macht 'neu' keinen Sinn, ignoriere termin // Bei status=4 (stattgefunden) macht 'neu' keinen Sinn, ignoriere termin
// termin kann 'all', 'neu' sein - nur 'neu' wird behandelt // termin kann 'all', 'neu' sein - nur 'neu' wird behandelt
if ($termin === 'neu' && (int)$status !== 4) { if ($termin === 'neu' && (int)$status !== 4) {
$sql .= " AND wtermin >= NOW()"; $sql .= " AND wtermin >= CURDATE() - INTERVAL 1 DAY ";
} }
// Lastdate-Filter hinzufügen // Lastdate-Filter hinzufügen
@@ -898,6 +898,7 @@ class RepoKalender
{ {
const TBL = 'kalender'; const TBL = 'kalender';
public static function getEntries(string $start, string $end): array public static function getEntries(string $start, string $end): array
{ {
$s = date('Ymd', strtotime($start)); $s = date('Ymd', strtotime($start));
@@ -905,6 +906,14 @@ class RepoKalender
return DB::all("SELECT * FROM " . self::TBL . " WHERE start >= ? AND start <= ?", [$s, $e]); return DB::all("SELECT * FROM " . self::TBL . " WHERE start >= ? AND start <= ?", [$s, $e]);
} }
public static function getOneEntry(string $start): array
{
$end = new DateTime($start);
$end->modify('+1 day');
$end = $end->format('Y-m-d');
return RepoKalender::getEntries($start, $end);
}
public static function insert(array $data): bool public static function insert(array $data): bool
{ {
$sql = "INSERT INTO " . self::TBL . " (start, end, title, description) VALUES (?, ?, ?, ?)"; $sql = "INSERT INTO " . self::TBL . " (start, end, title, description) VALUES (?, ?, ?, ?)";
@@ -917,6 +926,68 @@ class RepoKalender
DB::exec("DELETE FROM " . self::TBL . " WHERE id=?", [$id]); DB::exec("DELETE FROM " . self::TBL . " WHERE id=?", [$id]);
return true; return true;
} }
public static function updateBeos(int $id, string $mitarbeiter): bool
{
// First, get the existing calendar entry
$existingEntry = DB::one("SELECT * FROM " . self::TBL . " WHERE id=?", [$id]);
if (!$existingEntry) {
error_log("RepoKalender::updateBeos - Calendar entry with ID {$id} not found.");
return false;
}
// Extract the original Sonderführung name from the existing title
// Expected format: "WK, SF [Sonderführung Name], [Old Mitarbeiter]"
$oldTitle = $existingEntry['title'];
$sofueName = '';
if (preg_match('/^WK, SF (.*), .*$/', $oldTitle, $matches)) {
$sofueName = trim($matches[1]);
}
$newTitle = '';
if (!empty($sofueName)) {
$newTitle = "WK, SF {$sofueName}, {$mitarbeiter}";
} else {
// Fallback: If we can't extract the original Sonderführung name,
// we'll try to keep the original structure if possible, or
// simply create a title indicating the BEO update.
// For now, let's just make it clear it's a BEO update.
$newTitle = "Kalender BEO: {$mitarbeiter}"; // More general fallback
error_log("RepoKalender::updateBeos - Could not parse original SF name from title '{$oldTitle}'. Using generic title fallback.");
}
$sql = "UPDATE " . self::TBL . " SET title=? WHERE id=?";
DB::exec($sql, [$newTitle, $id]);
error_log("Kalender-Eintrag ID {$id} BEOs aktualisiert zu: {$mitarbeiter} (Titel: '{$newTitle}')");
return true;
}
public static function findEntryBySofueIdAndTermin(int $sofueId, string $wtermin): ?int
{
// Fetch Sonderführung details to get its name
$sofue = RepoSoFue::getById($sofueId);
if (!$sofue) {
error_log("RepoKalender::findEntryBySofueIdAndTermin - Sofue ID {$sofueId} not found.");
return null;
}
$sofueName = trim($sofue['name']);
$searchTitlePart = "WK, SF " . $sofueName; // We'll look for this part in the title
// Convert wtermin to YYYY-MM-DD H:i format for comparison with kalender.start
$terminDate = new DateTime($wtermin);
$startDateStr = $terminDate->format('Y-m-d H:i');
// Find calendar entry that matches the start date and contains the sofue name in its title
// Use LIKE for title matching because the full title includes the BEO's name which might change.
$sql = "SELECT id FROM " . self::TBL . " WHERE start = ? AND title LIKE ?";
$params = [$startDateStr, "%{$searchTitlePart}%"];
$result = DB::one($sql, $params);
return $result ? (int)$result['id'] : null;
}
} }
// ---- Email Service (einfach) ---- // ---- Email Service (einfach) ----
@@ -1013,8 +1084,10 @@ class Commands
'SENDMAIL2BEO' => 'Mail an Mitarbeiter', 'SENDMAIL2BEO' => 'Mail an Mitarbeiter',
'SENDMAIL2LISTE' => 'Mail an Verteiler', 'SENDMAIL2LISTE' => 'Mail an Verteiler',
'PUT2KALENDER' => 'Kalender-Eintrag', 'PUT2KALENDER' => 'Kalender-Eintrag',
'UPDATE_KALENDER_BEO' => 'Kalender-Eintrag BEOS aktualisieren',
'GET_FDATES' => 'Führungstermine für Kalenderansicht', 'GET_FDATES' => 'Führungstermine für Kalenderansicht',
'GET_CALENTRIES' => 'Kalendereinträge abrufen', 'GET_CALENTRIES' => 'Kalendereinträge abrufen',
'GET_ONEENTRY' => 'einen Kalendereintrag abrufen',
'PUT_CALENTRY' => 'Kalendereintrag erstellen', 'PUT_CALENTRY' => 'Kalendereintrag erstellen',
'DEL_CALENTRY' => 'Kalendereintrag löschen', 'DEL_CALENTRY' => 'Kalendereintrag löschen',
'GET_YEARS' => 'Liste verfügbare Jahre (Statistik)', 'GET_YEARS' => 'Liste verfügbare Jahre (Statistik)',
@@ -1234,7 +1307,6 @@ try {
IBAN DE18 6007 0070 0122 0383 00 IBAN DE18 6007 0070 0122 0383 00
BIC: DEUTDESSXXX BIC: DEUTDESSXXX
Mit sternfreundlichen Grüßen Mit sternfreundlichen Grüßen
Reinhard X. Fürst Reinhard X. Fürst
Sternwarte Welzheim Sternwarte Welzheim
@@ -1243,6 +1315,8 @@ try {
respond(['success' => $ok]); respond(['success' => $ok]);
case 'SENDMAIL2BEO': case 'SENDMAIL2BEO':
$mailNote = trim((string)($input['mail_note'] ?? ''));
$mailNoteBlock = $mailNote !== '' ? "\n " . $mailNote . "\n" : "\n";
$mail = RepoBeos::email($input['ma']); $mail = RepoBeos::email($input['ma']);
$vor = RepoBeos::vorname($input['ma']); $vor = RepoBeos::vorname($input['ma']);
$dt = date('d.m.Y H:i', strtotime($input['termin'])); $dt = date('d.m.Y H:i', strtotime($input['termin']));
@@ -1261,6 +1335,8 @@ Hallo " . $vor .",
Die Kontaktdaten sind auf der Sonderführungsseite ( https://sternwarte-welzheim.de/intern/sofue/sofue.php ) zu finden. Die Kontaktdaten sind auf der Sonderführungsseite ( https://sternwarte-welzheim.de/intern/sofue/sofue.php ) zu finden.
{$mailNoteBlock}
Viele Grüße Viele Grüße
Reinhard Reinhard
@@ -1272,16 +1348,13 @@ Hallo " . $vor .",
case 'SENDMAIL2LISTE': case 'SENDMAIL2LISTE':
$info = RepoSoFue::getById((int)$input['id']); $info = RepoSoFue::getById((int)$input['id']);
if (!$info) respondError('Führung nicht gefunden', 404); if (!$info) respondError('Führung nicht gefunden', 404);
$mailNote = trim((string)($input['mail_note'] ?? ''));
$to = $input['to'] ?? LISTE_EMAIL; $to = $input['to'] ?? LISTE_EMAIL;
$subject = 'Neue Anfrage Sonderführung am ' . date('d.m.Y', strtotime($info['wtermin'])); $subject = 'Neue Anfrage Sonderführung am ' . date('d.m.Y', strtotime($info['wtermin']));
$body = " $body = "
Liebe BEOs, Liebe BEOs,
wer kann folgende Sonderführung übernehmen? wer kann folgende Sonderführung übernehmen?
Viele Grüße
Reinhard
---------------------------------------------------------------------------------------------------"; ---------------------------------------------------------------------------------------------------";
$body = $body . " $body = $body . "
@@ -1294,6 +1367,10 @@ Weitere Fragen oder Mitteilungen:
" . $info['mitteilung'] . " " . $info['mitteilung'] . "
Spendenbescheinigung: \t" . $info['spende'] . " Spendenbescheinigung: \t" . $info['spende'] . "
---------------------------------------------------------------------------------------------------"; ---------------------------------------------------------------------------------------------------";
if ($mailNote !== '') {
$body .= "\n\n" . $mailNote . "\n";
}
$body .= "\n\nViele Grüße\nReinhard\n";
$ok = Mailer::sendPlain($to, $subject, $body); $ok = Mailer::sendPlain($to, $subject, $body);
respond(['success' => $ok]); respond(['success' => $ok]);
@@ -1328,6 +1405,29 @@ Spendenbescheinigung: \t" . $info['spende'] . "
]); ]);
error_log('Kalender-Eintrag erstellt: ' . $input['id'] . ' ' . $input['termin'] . ' ' . $input['mitarbeiter']); error_log('Kalender-Eintrag erstellt: ' . $input['id'] . ' ' . $input['termin'] . ' ' . $input['mitarbeiter']);
respond(['success' => true]); respond(['success' => true]);
case 'UPDATE_KALENDER_BEO':
if (!isset($input['id'], $input['mitarbeiter'])) respondError('Missing fields for calendar update');
$sofueId = (int)$input['id'];
$mitarbeiter = $input['mitarbeiter'];
// Fetch Sonderführung details to get wtermin
$sofue = RepoSoFue::getById($sofueId);
if (!$sofue) respondError('Sonderführung not found for calendar update', 404);
$wtermin = $sofue['wtermin'];
// Find the calendar entry ID based on sofueId and wtermin
$kalenderId = RepoKalender::findEntryBySofueIdAndTermin($sofueId, $wtermin);
if (!$kalenderId) {
error_log("UPDATE_KALENDER_BEO: Could not find calendar entry for Sofue ID {$sofueId} and wtermin {$wtermin}.");
respondError('Corresponding calendar entry not found.', 404);
}
RepoKalender::updateBeos($kalenderId, $mitarbeiter);
respond(['success' => true]);
case 'GET_FDATES': case 'GET_FDATES':
// Returns führungen for calendar display // Returns führungen for calendar display
if (!isset($input['start'], $input['end'])) respondError('start and end required'); if (!isset($input['start'], $input['end'])) respondError('start and end required');
@@ -1352,6 +1452,9 @@ Spendenbescheinigung: \t" . $info['spende'] . "
case 'GET_CALENTRIES': case 'GET_CALENTRIES':
if (!isset($input['start'], $input['end'])) respondError('start and end required'); if (!isset($input['start'], $input['end'])) respondError('start and end required');
respond(RepoKalender::getEntries($input['start'], $input['end'])); respond(RepoKalender::getEntries($input['start'], $input['end']));
case 'GET_ONEENTRY':
if (!isset($input['date'])) respondError('date required');
respond(RepoKalender::getOneEntry($input['date']));
case 'PUT_CALENTRY': case 'PUT_CALENTRY':
RepoKalender::insert($input['data']); RepoKalender::insert($input['data']);
respond(['success' => true]); respond(['success' => true]);

View File

@@ -17,6 +17,20 @@ server {
try_files $uri $uri/ /index.php; try_files $uri $uri/ /index.php;
} }
# Serve beoanswer frontend build output from dist
location = /beoanswer/ {
try_files /beoanswer/dist/index.html =404;
}
location /beoanswer/assets/ {
alias /var/www/html/beoanswer/dist/assets/;
try_files $uri =404;
}
location = /beoanswer/anleitung.html {
try_files /beoanswer/dist/anleitung.html =404;
}
# Specify the details of favicon.ico # Specify the details of favicon.ico
location = /favicon.ico { access_log off; log_not_found off; } location = /favicon.ico { access_log off; log_not_found off; }

View File

@@ -10,7 +10,7 @@ $allowedOrigins = [
'https://ihre-produktions-domain.de' // Ersetzen Sie durch Ihre echte Domain 'https://ihre-produktions-domain.de' // Ersetzen Sie durch Ihre echte Domain
]; ];
$origin = $_SERVER['HTTP_ORIGIN'] ?? ''; $origin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '';
if (in_array($origin, $allowedOrigins)) { if (in_array($origin, $allowedOrigins)) {
header("Access-Control-Allow-Origin: $origin"); header("Access-Control-Allow-Origin: $origin");
} else { } else {
@@ -41,8 +41,15 @@ $backendUrl = 'https://sternwarte-welzheim.de/intern/sofue/php/sofueDB.php';
// Credentials sicher laden - verschiedene Optionen: // Credentials sicher laden - verschiedene Optionen:
// Option 1: Aus Environment Variables (empfohlen) // Option 1: Aus Environment Variables (empfohlen)
$username = getenv('SOFUE_USERNAME') ?: $_ENV['SOFUE_USERNAME'] ?? null; $username = getenv('SOFUE_USERNAME');
$password = getenv('SOFUE_PASSWORD') ?: $_ENV['SOFUE_PASSWORD'] ?? null; if ($username === false || $username === '') {
$username = isset($_ENV['SOFUE_USERNAME']) ? $_ENV['SOFUE_USERNAME'] : null;
}
$password = getenv('SOFUE_PASSWORD');
if ($password === false || $password === '') {
$password = isset($_ENV['SOFUE_PASSWORD']) ? $_ENV['SOFUE_PASSWORD'] : null;
}
// Option 2: Aus separater Config-Datei (Fallback) // Option 2: Aus separater Config-Datei (Fallback)
if (!$username || !$password) { if (!$username || !$password) {
@@ -50,7 +57,8 @@ if (!$username || !$password) {
if (file_exists($configFile)) { if (file_exists($configFile)) {
include $configFile; include $configFile;
// cors-config.php sollte enthalten: // cors-config.php sollte enthalten:
// <?php $username = 'beogruppe'; $password = 'ArktUhr'; ?> // $username = 'beogruppe';
// $password = 'ArktUhr';
} }
} }

View File

@@ -1,7 +1,7 @@
{ {
"name": "beoanswer_react", "name": "beoanswer_react",
"private": true, "private": true,
"version": "1.0.4", "version": "1.0.6",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@@ -29,7 +29,7 @@ section {
border-bottom : 1px solid rgb(187, 185, 185); border-bottom : 1px solid rgb(187, 185, 185);
text-align: left; text-align: left;
margin: 0 auto 20px auto; margin: 0 auto 20px auto;
padding: 0 0em 1em 2em; padding: 0 0em 0.5em 2em;
font-weight: bold; font-weight: bold;
} }

View File

@@ -12,9 +12,9 @@ export default function FandStattVer({left, right, title, onNext, radioName = "f
const handleRadioChange = (e) => { const handleRadioChange = (e) => {
const value = e.target.value const value = e.target.value
updateFormData(fieldName, value) updateFormData(fieldName, value)
setAuswahl(value)
setbackButton(true) setbackButton(true)
if(radioName !== 'abgesagt') { if(radioName !== 'abgesagt') {
setAuswahl(value)
onNext(value) onNext(value)
} else { } else {
onNext() onNext()

View File

@@ -53,6 +53,9 @@ export default function Verschoben({onNext, isCompleted}) {
<button className="okbutton" onClick={handleOK}>OK</button> <button className="okbutton" onClick={handleOK}>OK</button>
</div> </div>
<p style={{ fontWeight: 'normal', fontSize: '0.9em', marginTop: '1.5em' }}>
Falls noch kein Termin bekannt ist, bitte <strong>31.12.2099 20:00</strong> eingeben.
</p>
</section> </section>
<Modal <Modal

View File

@@ -15,6 +15,9 @@ Dieses Programm kann auch die Überwachung machen, dass Einträge in der DB gel
Versions: Versions:
V 1.0.2 2026-03-01 rxf
- Mehrere Führungen am gleichen Tag werden nun alle verarbeitet (je eine E-Mail pro Eintrag).
V 1.0.1 2025-11-17 rxf V 1.0.1 2025-11-17 rxf
- Übergabe der Tage bis zu 'gestern' als Commandline Parameter: '-d x'. Ohne -d wird 1 angesetzt. - Übergabe der Tage bis zu 'gestern' als Commandline Parameter: '-d x'. Ohne -d wird 1 angesetzt.
@@ -61,30 +64,44 @@ const transporter = DEVELOP ? nodemailer.createTransport({
}); });
// Ajax-Call, um die Daten von der DBase zu holen // Daten aus der DB holen und für jeden Eintrag eine E-Mail senden
async function fetchDatafromDB(conn,termin) { async function fetchDatafromDB(conn, termin) {
let result;
let id;
let query = "select mitarbeiter,id from SoFue2 where DATE(wtermin) = '" + termin + "' and status = 2";
try { try {
result = await conn.query(query); const query = "select mitarbeiter,id from SoFue2 where DATE(wtermin) = '" + termin + "' and status = 2";
if (result[0].length == 0) { const [rows] = await conn.query(query);
if (rows.length === 0) {
console.log("Keine Führung gewesen"); console.log("Keine Führung gewesen");
return; return;
} }
let ma = result[0][0].mitarbeiter;
id = result[0][0].id; console.log(`${rows.length} Führung(en) gefunden für ${termin}`);
query = "select vorname,email_1 from beos where name = '" + ma + "'";
result = await conn.query(query); for (const row of rows) {
const ma = row.mitarbeiter;
const id = row.id;
const beoQuery = "select vorname,email_1 from beos where name = '" + ma + "'";
const [beoRows] = await conn.query(beoQuery);
if (beoRows.length === 0) {
console.log(`Kein BEO-Eintrag gefunden für Mitarbeiter: ${ma} (id=${id})`);
continue;
} }
catch(err) {
send2BEO({
name: beoRows[0].vorname,
email: (beoRows[0].email_1).toLowerCase(),
date: termin,
id: id
});
}
}
catch (err) {
console.log(err); console.log(err);
} }
finally { finally {
conn.end(); conn.end();
} }
let erg = result[0];
send2BEO({name: erg[0].vorname, email: erg[0].email_1, date: termin, id: id});
} }
@@ -116,9 +133,13 @@ function send2BEO(info) {
async function main() { async function main() {
// console.log(DB_host, DB_port, DB_user, DB_pass, DB_dbase); // console.log(DB_host, DB_port, DB_user, DB_pass, DB_dbase);
console.log('Start: ' + moment().format('YYYY-MM-DD HH:mm')) console.log('Start: ' + moment().format('YYYY-MM-DD HH:mm'))
const connection = await mysql.createConnection({ const connection = await mysql.createConnection(DEVELOP ? {
// host: DB_host, host: DB_host,
// port: DB_port, port: DB_port,
user: DB_user,
password: DB_pass,
database: DB_dbase,
} : {
user: DB_user, user: DB_user,
password: DB_pass, password: DB_pass,
database: DB_dbase, database: DB_dbase,

View File

@@ -25,7 +25,7 @@ function checkFuehrung(was) {
} }
//Übersicht über alle Stati //Übersicht über alle Stati
//Jeweils nur 5 Einträge, kein Pager //Jeweils nur 5 Einträge, kein Pager/
function uebersicht() { function uebersicht() {
for (var i=0; i< stati.length; i++) { for (var i=0; i< stati.length; i++) {
buildGrid(''+(i+1),15,false); buildGrid(''+(i+1),15,false);
@@ -565,13 +565,31 @@ function saveSettings() {
// 'Content-Type': 'application/json' // 'Content-Type': 'application/json'
// } // }
// }) // })
if (status == 1 || status == 2) {
doAjaxCall_arr(ajaxURL, cmd, function() {
openMailNoteDialog(status, id, marb, termin);
});
} else {
doAjaxCall_arr(ajaxURL,cmd,showajaxerg); doAjaxCall_arr(ajaxURL,cmd,showajaxerg);
}
}
if(status == 1) { // offen -> mail an Liste function openMailNoteDialog(status, id, marb, termin) {
sendmail2liste(id, termin); $('#mailnote_text').val('');
$('#div_mailnote').data({
status: status,
id: id,
marb: marb,
termin: termin
}).dialog('open');
}
function sendStatusMails(status, id, marb, termin, mailNote) {
if (status == 1) { // offen -> mail an Liste
sendmail2liste(id, termin, mailNote);
} }
if (status == 2) { if (status == 2) {
sendmail2beo(id, marb, termin) sendmail2beo(id, marb, termin, mailNote);
sendmailzusage(id, marb, termin); sendmailzusage(id, marb, termin);
} }
} }
@@ -612,9 +630,12 @@ function showdbase(val) {
} }
// Status wurde auf 'offen' gesetzt -> nun eine Mail an die Liste mit der Anfrage senden // Status wurde auf 'offen' gesetzt -> nun eine Mail an die Liste mit der Anfrage senden
function sendmail2liste(id, termin) { function sendmail2liste(id, termin, mailNote) {
const liste = 'sternwarte@planetariumsgesellschaft.de'; const liste = 'sternwarte@planetariumsgesellschaft.de';
let cmd = {cmd: 'SENDMAIL2LISTE', id: id, to: liste} let cmd = {cmd: 'SENDMAIL2LISTE', id: id, to: liste}
if (mailNote) {
cmd.mail_note = mailNote;
}
console.log("Sende mail to Liste"); console.log("Sende mail to Liste");
doAjaxCall_arr(ajaxURL,cmd,showajaxerg); doAjaxCall_arr(ajaxURL,cmd,showajaxerg);
console.log("Mail gesendet"); console.log("Mail gesendet");
@@ -624,13 +645,16 @@ function sendmail2liste(id, termin) {
} }
// Status wurde auf 'zugesgat' gesetzt -> nun eine Mail an den BEO senden // Status wurde auf 'zugesgat' gesetzt -> nun eine Mail an den BEO senden
function sendmail2beo(id, beo, termin) { function sendmail2beo(id, beo, termin, mailNote) {
let cmd = {cmd: 'SENDMAIL2BEO', ma: beo, termin: termin} let cmd = {cmd: 'SENDMAIL2BEO', ma: beo, termin: termin}
if (mailNote) {
cmd.mail_note = mailNote;
}
console.log("Sende mail to " + beo); console.log("Sende mail to " + beo);
doAjaxCall_arr(ajaxURL,cmd,showajaxerg); doAjaxCall_arr(ajaxURL,cmd,showajaxerg);
console.log("Mail gesendet"); console.log("Mail gesendet");
// und in den Kalender eintragen // und in den Kalender eintragen
cmd = {cmd: 'PUT2KALENDER', id: id, termin: termin, mitarbeiter: beo} cmd = {cmd: 'UPDATE_KALENDER_BEO', id: id, mitarbeiter: beo}
doAjaxCall_arr(ajaxURL,cmd,showajaxerg); doAjaxCall_arr(ajaxURL,cmd,showajaxerg);
} }
@@ -786,8 +810,44 @@ $(document).ready(function() {
], ],
}); });
// 3. Dialog für optionale Mail-Bemerkung
$('#div_mailnote').dialog({
autoOpen: false,
width: 600,
modal: true,
title: 'Zusätzliche Bemerkung für E-Mail',
buttons: [
{
text: 'Ohne Bemerkung senden',
class: 'btnOK ui-button-left',
click: function() {
var data = $('#div_mailnote').data();
$(this).dialog('close');
sendStatusMails(data.status, data.id, data.marb, data.termin, '');
}
},
{
text: 'Mit Bemerkung senden',
class: 'btnOK ui-button-left',
click: function() {
var data = $('#div_mailnote').data();
var note = $('#mailnote_text').val().trim();
$(this).dialog('close');
sendStatusMails(data.status, data.id, data.marb, data.termin, note);
}
},
{
text: 'Abbrechen',
click: function() {
$(this).dialog('close');
location.reload(true);
}
}
]
});
// 3. Dialog für die Anleitung
// 4. Dialog für die Anleitung
// Dieser hat KEINEN Button (wird über das Schließkreuz beendet) und // Dieser hat KEINEN Button (wird über das Schließkreuz beendet) und
// eine etwas kleinere Schrift // eine etwas kleinere Schrift
$("#anleitung").dialog({ $("#anleitung").dialog({
@@ -812,7 +872,7 @@ $(document).ready(function() {
}); });
// 4. Dialog für die Wartungs-Meldung // 5. Dialog für die Wartungs-Meldung
// Wird nur aufgerufen, wenn das Flag WARTUNG gesetzt ist // Wird nur aufgerufen, wenn das Flag WARTUNG gesetzt ist
// Kann NICHT beendet werden // Kann NICHT beendet werden
$('#maint').dialog({ $('#maint').dialog({

View File

@@ -1,11 +1,16 @@
// VersiosNummern und -Geschichte // VersiosNummern und -Geschichte
var VERSION="1.95"; var VERSION="2.0.0";
var VDATE="2026-01-19"; var VDATE="2026-03-17";
/* History /* History
Rev. Datum Entwickler Rev. Datum Entwickler
2.0.0 2026-03-17
- Semantische Versionierung eiungeführt
- Vor dem Senden der Mail bei 'offen' und 'zugesagt' erscheint ein Popup,
in welches man einen Text eingeben kann, der in der Mail mit gesendet wird.
1.95 2026-01-19 1.95 2026-01-19
- Eintrag in den Kalender auch bei 'offen' - Eintrag in den Kalender auch bei 'offen'

View File

@@ -66,6 +66,10 @@ if ((isset($_GET['fuehrung'])) && ($_GET['fuehrung'] == "TFC7364gf:l@vtr")) {
<div id="div_loesch"> <div id="div_loesch">
<p>Diesen Eintrag wirklich aus der Datenbank löschen ?</p> <p>Diesen Eintrag wirklich aus der Datenbank löschen ?</p>
</div> </div>
<div id="div_mailnote" style="display:none;">
<p>Optional: Zusätzliche Bemerkung für die ausgehende E-Mail.</p>
<textarea id="mailnote_text" rows="6" style="width:98%;"></textarea>
</div>
<div id="anleitung"></div> <div id="anleitung"></div>
<div id="maint"></div> <div id="maint"></div>