generated from knso/webklar-preview-template
Web: Frist in den Kalender + Anruf-Hilfe — die Unterschiede zu ChatGPT ins Web geholt
Der Nutzer konnte in einer Praesentation nicht kontern, und die Ursache war real: Alles, was BehoerdenKlar von ChatGPT unterscheidet (Erinnerungen, Kalender, Antwort-Entwurf), steckte in der App — und die ist noch nicht veroeffentlicht. Das Web-Produkt, das Kunden heute kaufen, war im Kern nur 'Brief erklaeren'. Kalender (.ics, rein im Browser, nichts wird hochgeladen): - Frist und Termin mit Erinnerung 3 Tage und 1 Tag vorher - Zeilen nach BYTES gefaltet (RFC 5545), damit Arabisch/Tigrinya nicht reissen - Termin 23:30 endet korrekt am naechsten Tag - Wanduhrzeit ohne Zeitzone = Ortszeit des Geraets - getestet: Frist, Termin mit Uhrzeit, Mitternacht, langer arabischer Text, Brief ohne Datum (-> keine Datei) Anruf-Hilfe (Worker-Schema um kontakt + anruf erweitert): - Telefonnummer als tel:-Knopf, Aktenzeichen hervorgehoben - Spickzettel: Satz auf Deutsch + Bedeutung in der Nutzersprache - Prompt-Regel: Kontaktdaten nur, wenn woertlich im Brief. Getestet mit Brief ohne Nummer -> telefon: null, nichts erfunden. Nebenbei zwei bestehende Fehler behoben: - Optionale Ergebniskarten wurden nie ausgeblendet: Beim zweiten Brief stand die Checkliste des ERSTEN Briefs noch da. Jetzt Reset zu Beginn. - dir=auto auf den Ergebnistexten: Arabisch/Farsi/Urdu wurden im Web linksbuendig dargestellt (in der App war das schon behoben). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e01813f6ec
commit
fe6899da3d
3 files changed
+249
-10
No files matched your search
+30
-5
@@ -60,13 +60,38 @@ bis 30.09.: Unterlagen einreichen"*.
|
||||
behalten. Ein Übersetzer sagt dir, was dasteht. BehördenKlar meldet sich von
|
||||
selbst, bevor es zu spät ist.
|
||||
|
||||
### 3. Termin in den Kalender — auf einen Tastendruck
|
||||
`AnalyseScreen` → `terminZumKalender`, inklusive Erinnerung einen Tag vorher.
|
||||
### 3. Frist und Termin in den Kalender — auch im Web
|
||||
**App:** `AnalyseScreen` → `terminZumKalender`, auf Tastendruck.
|
||||
|
||||
**Bewusst nicht vollautomatisch:** Ungefragt in den Kalender eines Menschen zu
|
||||
schreiben, wäre übergriffig — und iOS verlangt ohnehin eine Freigabe. Die
|
||||
*Erinnerungen* laufen automatisch, der *Kalendereintrag* auf Wunsch.
|
||||
**Web (seit 27.09.):** Nach der Analyse erscheint direkt unter der Frist
|
||||
„📅 In meinen Kalender eintragen". Der Browser erzeugt eine `.ics`-Datei mit
|
||||
Frist **und** Termin, jeweils mit Erinnerung **3 Tage und 1 Tag vorher**. Die
|
||||
Datei entsteht auf dem Gerät und wird **nirgends hochgeladen** — die Erinnerungen
|
||||
stellt danach der Kalender des Handys selbst. Funktioniert ohne App und ohne Konto.
|
||||
|
||||
Details, die schiefgehen können und abgesichert sind:
|
||||
- Zeilen nach **Bytes** gefaltet (RFC 5545: max. 75), nicht nach Zeichen —
|
||||
Arabisch/Tigrinya brauchen 2–4 Byte pro Zeichen
|
||||
- Termin um 23:30 endet korrekt am **nächsten** Tag
|
||||
- Uhrzeit ohne Zeitzone („Wanduhrzeit") = Ortszeit des Geräts
|
||||
- Titel und Erinnerungstext ohne deutsche Wörter: Symbol + Datum versteht jeder,
|
||||
die `aktion` kommt schon in der Sprache des Nutzers
|
||||
|
||||
**Bewusst nicht vollautomatisch:** Ungefragt in einen fremden Kalender zu
|
||||
schreiben, wäre übergriffig.
|
||||
|
||||
### 3b. Anruf-Hilfe (Web, seit 27.09.)
|
||||
Die KI liest `kontakt` aus dem Brief — Telefon, Ansprechpartner, Aktenzeichen,
|
||||
Sprechzeiten — und schreibt einen **Spickzettel** mit 3–5 Sätzen für das Telefonat.
|
||||
|
||||
- Telefonnummer als **Anruf-Knopf** (`tel:`)
|
||||
- Aktenzeichen **hervorgehoben** — danach wird am Telefon zuerst gefragt
|
||||
- Jeder Satz **auf Deutsch** (so wird er gesagt) und darunter in der Sprache
|
||||
des Nutzers (damit er versteht, was er sagt)
|
||||
|
||||
**Regel im Prompt:** Kontaktdaten nur, wenn sie **wörtlich** im Brief stehen.
|
||||
Getestet mit einem Brief ohne Telefonnummer → `telefon: null`, nichts erfunden.
|
||||
Eine falsche Nummer wäre schlimmer als keine.
|
||||
### 4. Ampel statt Datum
|
||||
Rot ≤ 7 Tage, gelb ≤ 21, sonst grün. Die Startseite sortiert nach
|
||||
**Dringlichkeit**, nicht nach Eingang — man sieht sofort, was brennt.
|
||||
|
||||
+26
-2
@@ -104,6 +104,8 @@ Deine Aufgabe:
|
||||
4. Erstelle eine konkrete Checkliste, was der Nutzer tun muss.
|
||||
5. Sei sachlich und beruhigend, nicht alarmierend.
|
||||
|
||||
6. Telefonnummern, Namen und Aktenzeichen übernimmst du NUR, wenn sie wörtlich im Brief stehen. Eine erfundene Telefonnummer ist schlimmer als keine — dann null.
|
||||
|
||||
Wichtig: Wenn das Bild kein Behördenbrief ist oder unlesbar ist, schreibe das klar in kernaussage und erklaerung_einfach und lasse frist/termin null.`;
|
||||
|
||||
/** Schlanke Demo-Variante des Analyse-Schemas (ohne Antwort-Optionen —
|
||||
@@ -111,7 +113,7 @@ Wichtig: Wenn das Bild kein Behördenbrief ist oder unlesbar ist, schreibe das k
|
||||
const DEMO_SCHEMA = {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
required: ['brieftyp', 'absender', 'kernaussage', 'erklaerung_einfach', 'fachbegriffe', 'frist', 'termin', 'checkliste'],
|
||||
required: ['brieftyp', 'absender', 'kernaussage', 'erklaerung_einfach', 'fachbegriffe', 'frist', 'termin', 'checkliste', 'kontakt', 'anruf'],
|
||||
properties: {
|
||||
brieftyp: { type: 'string', description: 'Kurze Kategorie des Briefs, z. B. "Einladung Jobcenter".' },
|
||||
absender: { type: 'string', description: 'Die Behörde, die den Brief geschickt hat.' },
|
||||
@@ -150,6 +152,28 @@ const DEMO_SCHEMA = {
|
||||
],
|
||||
},
|
||||
checkliste: { type: 'array', description: 'To-do-Liste. Leer wenn nichts zu tun ist.', items: { type: 'string' } },
|
||||
kontakt: {
|
||||
description: 'Kontaktdaten aus dem Brief. Jedes Feld NUR, wenn es wörtlich im Brief steht — sonst null. Niemals erfinden.',
|
||||
type: 'object', additionalProperties: false,
|
||||
required: ['telefon', 'ansprechpartner', 'aktenzeichen', 'sprechzeiten'],
|
||||
properties: {
|
||||
telefon: { description: 'Telefonnummer genau wie im Brief abgedruckt, sonst null.', anyOf: [{ type: 'string' }, { type: 'null' }] },
|
||||
ansprechpartner: { description: 'Name der zuständigen Person, sonst null.', anyOf: [{ type: 'string' }, { type: 'null' }] },
|
||||
aktenzeichen: { description: 'Aktenzeichen, Kunden-, Steuer- oder BG-Nummer — das, wonach am Telefon gefragt wird. Sonst null.', anyOf: [{ type: 'string' }, { type: 'null' }] },
|
||||
sprechzeiten: { description: 'Telefonische Erreichbarkeit laut Brief, sonst null.', anyOf: [{ type: 'string' }, { type: 'null' }] },
|
||||
},
|
||||
},
|
||||
anruf: {
|
||||
description: 'Spickzettel für einen Anruf bei der Behörde: 3 bis 5 kurze, höfliche Sätze, die man am Telefon sagen kann — Begrüßung, Aktenzeichen nennen, Anliegen, Nachfrage. Leeres Array, wenn ein Anruf nicht sinnvoll ist.',
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object', additionalProperties: false, required: ['deutsch', 'bedeutung'],
|
||||
properties: {
|
||||
deutsch: { type: 'string', description: 'Der Satz in einfachem Deutsch — genau so wird er am Telefon gesagt.' },
|
||||
bedeutung: { type: 'string', description: 'Derselbe Satz in der Sprache des Nutzers, damit er versteht, was er sagt.' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -314,7 +338,7 @@ async function demoHandler(request: Request, env: Env): Promise<Response> {
|
||||
max_tokens: MAX_TOKENS_OBERGRENZE,
|
||||
system: sprachCode === 'de'
|
||||
? DEMO_SYSTEM_PROMPT
|
||||
: `${DEMO_SYSTEM_PROMPT}\n\nWICHTIG — ZIELSPRACHE: Schreibe ALLE Textfelder deiner Antwort auf ${sprachName}, nicht auf Deutsch. Ausnahme: das Feld "begriff" bei fachbegriffe bleibt der deutsche Originalbegriff aus dem Brief (der Nutzer muss ihn im Brief wiederfinden) — nur dessen "erklaerung" wird auf ${sprachName} geschrieben. Datumsangaben bleiben im ISO-Format.`,
|
||||
: `${DEMO_SYSTEM_PROMPT}\n\nWICHTIG — ZIELSPRACHE: Schreibe ALLE Textfelder deiner Antwort auf ${sprachName}, nicht auf Deutsch. Ausnahme: das Feld "begriff" bei fachbegriffe bleibt der deutsche Originalbegriff aus dem Brief (der Nutzer muss ihn im Brief wiederfinden) — nur dessen "erklaerung" wird auf ${sprachName} geschrieben. Datumsangaben bleiben im ISO-Format. Ebenfalls NICHT übersetzen: das Feld "deutsch" bei anruf (das wird am Telefon auf Deutsch gesagt) und alle kontakt-Felder (sie stehen so im Brief).`,
|
||||
output_config: { format: { type: 'json_schema', schema: DEMO_SCHEMA } },
|
||||
messages: [
|
||||
{
|
||||
|
||||
+193
-3
@@ -159,6 +159,12 @@
|
||||
|
||||
/* Ergebnis */
|
||||
#ergebnis { display: none; }
|
||||
.anruf-satz { border-inline-start: 3px solid var(--amtsblau); background: #fff; padding: 6px 12px; margin: 10px 0; }
|
||||
.anruf-de { font-weight: 700; margin: 0; }
|
||||
.anruf-bed { color: var(--grau); font-size: 15px; margin: 4px 0 0; }
|
||||
.kontakt-zeile { margin: 6px 0; font-size: 16px; }
|
||||
.az-wert { font-family: 'Courier New', ui-monospace, monospace; font-weight: 700; background: var(--marker); padding: 1px 6px; }
|
||||
#ergAnrufKontakt .cta { display: inline-block; width: auto; margin: 4px 0 8px; }
|
||||
.brief-karte {
|
||||
background: #fff; border: 1px solid rgba(33, 33, 29, 0.28);
|
||||
padding: 22px 24px; position: relative; margin-top: 22px;
|
||||
@@ -374,13 +380,23 @@
|
||||
|
||||
<div class="brief-karte">
|
||||
<span class="karten-label neutral" id="ergBrieftyp">Brief</span>
|
||||
<p style="margin-top: 6px;"><strong id="ergKern"></strong></p>
|
||||
<p style="margin-top: 6px;"><strong id="ergKern" dir="auto"></strong></p>
|
||||
<p class="ampelzeile" id="ergAmpel" style="display: none;"></p>
|
||||
</div>
|
||||
|
||||
<div class="brief-karte" id="ergKalenderKarte" style="display: none; border: 2px solid var(--amtsblau);">
|
||||
<span class="karten-label">Nicht vergessen</span>
|
||||
<div id="ergKalenderText" style="margin-top: 6px;"></div>
|
||||
<button class="cta" id="kalenderKnopf" type="button" style="margin-top: 12px;">📅 In meinen Kalender eintragen</button>
|
||||
<p class="klein">
|
||||
Ihr Handy erinnert Sie <strong>3 Tage</strong> und <strong>1 Tag</strong> vorher.
|
||||
Der Eintrag landet nur in Ihrem eigenen Kalender — wir speichern nichts.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="brief-karte">
|
||||
<span class="karten-label">Einfach erklärt</span>
|
||||
<p id="ergErklaerung" style="white-space: pre-line;"></p>
|
||||
<p id="ergErklaerung" dir="auto" style="white-space: pre-line;"></p>
|
||||
</div>
|
||||
|
||||
<div class="brief-karte" id="ergChecklisteKarte" style="display: none;">
|
||||
@@ -388,6 +404,12 @@
|
||||
<div id="ergCheckliste"></div>
|
||||
</div>
|
||||
|
||||
<div class="brief-karte" id="ergAnrufKarte" style="display: none;">
|
||||
<span class="karten-label neutral">Anrufen</span>
|
||||
<div id="ergAnrufKontakt" style="margin-top: 6px;"></div>
|
||||
<div id="ergAnrufSaetze"></div>
|
||||
</div>
|
||||
|
||||
<div class="brief-karte" id="ergBegriffeKarte" style="display: none;">
|
||||
<span class="karten-label neutral">Schwere Wörter erklärt</span>
|
||||
<div id="ergBegriffe"></div>
|
||||
@@ -688,6 +710,10 @@
|
||||
|
||||
function zeigeErgebnis(analyse, versuch, guthabenRest) {
|
||||
status.style.display = 'none';
|
||||
// Optionale Karten zuruecksetzen. Sonst bleibt beim zweiten Brief z. B.
|
||||
// die Checkliste des ERSTEN Briefs stehen — zwei Briefe vermischt.
|
||||
['ergAmpel', 'ergKalenderKarte', 'ergChecklisteKarte', 'ergAnrufKarte', 'ergBegriffeKarte']
|
||||
.forEach(function (id) { document.getElementById(id).style.display = 'none'; });
|
||||
document.getElementById('ergBrieftyp').textContent = analyse.brieftyp || 'Ihr Brief';
|
||||
document.getElementById('ergKern').textContent = analyse.kernaussage || '';
|
||||
document.getElementById('ergErklaerung').textContent = analyse.erklaerung_einfach || '';
|
||||
@@ -705,7 +731,7 @@
|
||||
if (analyse.checkliste && analyse.checkliste.length) {
|
||||
document.getElementById('ergChecklisteKarte').style.display = 'block';
|
||||
document.getElementById('ergCheckliste').innerHTML = analyse.checkliste
|
||||
.map(function (t) { var d = document.createElement('div'); d.className = 'todo'; d.textContent = t; return d.outerHTML; })
|
||||
.map(function (t) { var d = document.createElement('div'); d.className = 'todo'; d.setAttribute('dir', 'auto'); d.textContent = t; return d.outerHTML; })
|
||||
.join('');
|
||||
}
|
||||
if (analyse.fachbegriffe && analyse.fachbegriffe.length) {
|
||||
@@ -723,6 +749,9 @@
|
||||
.join('');
|
||||
}
|
||||
|
||||
zeigeKalender(analyse);
|
||||
zeigeAnruf(analyse);
|
||||
|
||||
document.getElementById('ergebnis').style.display = 'block';
|
||||
|
||||
// Bezahlter Modus: Guthaben-Stand zeigen, Eingabe für den nächsten Brief öffnen
|
||||
@@ -759,6 +788,167 @@
|
||||
document.getElementById('ergebnis').scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
|
||||
// ---- Kalender (.ics) --------------------------------------------------
|
||||
// Die Datei entsteht im Browser und wird nirgends hochgeladen. Die
|
||||
// Erinnerungen stellt danach der Kalender des Handys selbst.
|
||||
var letzteAnalyse = null;
|
||||
|
||||
function icsText(t) {
|
||||
return String(t || '').replace(/\\/g, '\\\\').replace(/;/g, '\;')
|
||||
.replace(/,/g, '\\,').replace(/\r?\n/g, '\\n');
|
||||
}
|
||||
// RFC 5545: max. 75 BYTE pro Zeile. Nach Bytes falten, nicht nach Zeichen —
|
||||
// arabische oder tigrinische Texte brauchen 2–4 Byte pro Zeichen.
|
||||
function icsFalte(zeile) {
|
||||
var enc = new TextEncoder(), teile = [], akt = '', bytes = 0;
|
||||
for (var zeichen of zeile) {
|
||||
var b = enc.encode(zeichen).length;
|
||||
if (bytes + b > 75) { teile.push(akt); akt = ' ' + zeichen; bytes = 1 + b; }
|
||||
else { akt += zeichen; bytes += b; }
|
||||
}
|
||||
teile.push(akt);
|
||||
return teile.join('\r\n');
|
||||
}
|
||||
function isoGueltig(d) { return typeof d === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(d); }
|
||||
function deDatum(iso) { return iso.split('-').reverse().join('.'); }
|
||||
function zweistellig(x) { return ('0' + x).slice(-2); }
|
||||
// "Wanduhrzeit" ohne Zeitzone: der Kalender nimmt die Ortszeit des Geraets.
|
||||
// Richtig fuer einen Termin in Deutschland, egal wo der Server steht.
|
||||
function wanduhr(dt) {
|
||||
return dt.getUTCFullYear() + zweistellig(dt.getUTCMonth() + 1) + zweistellig(dt.getUTCDate()) +
|
||||
'T' + zweistellig(dt.getUTCHours()) + zweistellig(dt.getUTCMinutes()) + '00';
|
||||
}
|
||||
function nurDatum(dt) { return wanduhr(dt).slice(0, 8); }
|
||||
function ausIso(iso, h, m) { var t = iso.split('-'); return new Date(Date.UTC(+t[0], +t[1] - 1, +t[2], h || 0, m || 0)); }
|
||||
function eindeutig() {
|
||||
return (window.crypto && crypto.randomUUID ? crypto.randomUUID() : Date.now() + '-' + Math.random().toString(16).slice(2)) + '@behoerdenklar.de';
|
||||
}
|
||||
function alarm(ausloeser, text) {
|
||||
return ['BEGIN:VALARM', 'ACTION:DISPLAY', 'TRIGGER:' + ausloeser, 'DESCRIPTION:' + icsText(text), 'END:VALARM'];
|
||||
}
|
||||
|
||||
function baueIcs(a) {
|
||||
var z = ['BEGIN:VCALENDAR', 'VERSION:2.0', 'PRODID:-//Webklar GbR//BehoerdenKlar//DE',
|
||||
'CALSCALE:GREGORIAN', 'METHOD:PUBLISH'];
|
||||
var titel = a.brieftyp || 'Brief', stempel = wanduhr(new Date()) + 'Z', n = 0;
|
||||
|
||||
if (a.frist && isoGueltig(a.frist.datum)) {
|
||||
var fTag = ausIso(a.frist.datum);
|
||||
var fDanach = new Date(fTag.getTime() + 86400000);
|
||||
// Titel und Hinweis ohne deutsche Worte: Datum und Symbol versteht jeder,
|
||||
// "aktion" kommt bereits in der Sprache des Nutzers von der KI.
|
||||
z.push('BEGIN:VEVENT', 'UID:' + eindeutig(), 'DTSTAMP:' + stempel,
|
||||
'DTSTART;VALUE=DATE:' + nurDatum(fTag), 'DTEND;VALUE=DATE:' + nurDatum(fDanach),
|
||||
'SUMMARY:' + icsText('⚠️ ' + titel),
|
||||
'DESCRIPTION:' + icsText((a.frist.aktion || '') + '\n\nbehoerdenklar.de'));
|
||||
// Ganztaegig beginnt um 00:00 — so fallen beide Erinnerungen auf 9 Uhr morgens.
|
||||
z.push.apply(z, alarm('-P2DT15H', '⏰ ' + titel + ' — ' + deDatum(a.frist.datum)));
|
||||
z.push.apply(z, alarm('-PT15H', '⏰ ' + titel + ' — ' + deDatum(a.frist.datum)));
|
||||
z.push('END:VEVENT'); n++;
|
||||
}
|
||||
|
||||
if (a.termin && isoGueltig(a.termin.datum)) {
|
||||
var zeit = a.termin.uhrzeit && String(a.termin.uhrzeit).match(/(\d{1,2})[:.](\d{2})/);
|
||||
var h = zeit ? +zeit[1] : -1, m = zeit ? +zeit[2] : -1;
|
||||
z.push('BEGIN:VEVENT', 'UID:' + eindeutig(), 'DTSTAMP:' + stempel);
|
||||
if (zeit && h <= 23 && m <= 59) {
|
||||
var beginn = ausIso(a.termin.datum, h, m);
|
||||
var ende = new Date(beginn.getTime() + 3600000); // eine Stunde, auch ueber Mitternacht korrekt
|
||||
z.push('DTSTART:' + wanduhr(beginn), 'DTEND:' + wanduhr(ende));
|
||||
z.push.apply(z, alarm('-P3D', '📅 ' + titel + ' — ' + deDatum(a.termin.datum) + ', ' + zweistellig(h) + ':' + zweistellig(m)));
|
||||
z.push.apply(z, alarm('-P1D', '📅 ' + titel + ' — ' + deDatum(a.termin.datum) + ', ' + zweistellig(h) + ':' + zweistellig(m)));
|
||||
} else {
|
||||
var tTag = ausIso(a.termin.datum);
|
||||
z.push('DTSTART;VALUE=DATE:' + nurDatum(tTag), 'DTEND;VALUE=DATE:' + nurDatum(new Date(tTag.getTime() + 86400000)));
|
||||
z.push.apply(z, alarm('-P2DT15H', '📅 ' + titel + ' — ' + deDatum(a.termin.datum)));
|
||||
z.push.apply(z, alarm('-PT15H', '📅 ' + titel + ' — ' + deDatum(a.termin.datum)));
|
||||
}
|
||||
z.push('SUMMARY:' + icsText('📅 ' + titel));
|
||||
if (a.termin.ort) z.push('LOCATION:' + icsText(a.termin.ort));
|
||||
z.push('DESCRIPTION:' + icsText('behoerdenklar.de'), 'END:VEVENT'); n++;
|
||||
}
|
||||
|
||||
z.push('END:VCALENDAR');
|
||||
return n ? z.map(icsFalte).join('\r\n') + '\r\n' : null;
|
||||
}
|
||||
|
||||
function zeigeKalender(a) {
|
||||
var karte = document.getElementById('ergKalenderKarte');
|
||||
var frist = a.frist && isoGueltig(a.frist.datum) ? a.frist : null;
|
||||
var termin = a.termin && isoGueltig(a.termin.datum) ? a.termin : null;
|
||||
if (!frist && !termin) return;
|
||||
letzteAnalyse = a;
|
||||
var box = document.getElementById('ergKalenderText');
|
||||
box.innerHTML = '';
|
||||
function zeile(text) { var p = document.createElement('p'); p.setAttribute('dir', 'auto'); p.textContent = text; box.appendChild(p); }
|
||||
if (frist) zeile('⚠️ Frist: ' + deDatum(frist.datum) + (frist.aktion ? ' — ' + frist.aktion : ''));
|
||||
if (termin) zeile('📅 Termin: ' + deDatum(termin.datum) + (termin.uhrzeit ? ', ' + termin.uhrzeit + ' Uhr' : '') + (termin.ort ? ' — ' + termin.ort : ''));
|
||||
karte.style.display = 'block';
|
||||
}
|
||||
|
||||
document.getElementById('kalenderKnopf').addEventListener('click', function () {
|
||||
if (!letzteAnalyse) return;
|
||||
var ics = baueIcs(letzteAnalyse);
|
||||
if (!ics) return;
|
||||
var url = URL.createObjectURL(new Blob([ics], { type: 'text/calendar;charset=utf-8' }));
|
||||
var link = document.createElement('a');
|
||||
link.href = url; link.download = 'BehoerdenKlar-Termin.ics';
|
||||
document.body.appendChild(link); link.click(); link.remove();
|
||||
setTimeout(function () { URL.revokeObjectURL(url); }, 10000);
|
||||
});
|
||||
|
||||
// ---- Anruf-Hilfe ------------------------------------------------------
|
||||
function zeigeAnruf(a) {
|
||||
var karte = document.getElementById('ergAnrufKarte');
|
||||
var k = a.kontakt || {}, saetze = Array.isArray(a.anruf) ? a.anruf : [];
|
||||
if (!k.telefon && !saetze.length) return;
|
||||
|
||||
var box = document.getElementById('ergAnrufKontakt');
|
||||
box.innerHTML = '';
|
||||
if (k.telefon) {
|
||||
var knopf = document.createElement('a');
|
||||
knopf.className = 'cta';
|
||||
knopf.href = 'tel:' + String(k.telefon).replace(/[^\d+]/g, '');
|
||||
knopf.textContent = '📞 ' + k.telefon;
|
||||
box.appendChild(knopf);
|
||||
}
|
||||
function angabe(label, wert, hervorheben) {
|
||||
if (!wert) return;
|
||||
var p = document.createElement('p'); p.className = 'kontakt-zeile';
|
||||
var b = document.createElement('strong'); b.textContent = label + ': ';
|
||||
var w = document.createElement('span'); w.textContent = wert;
|
||||
if (hervorheben) w.className = 'az-wert';
|
||||
p.appendChild(b); p.appendChild(w); box.appendChild(p);
|
||||
}
|
||||
angabe('Ansprechpartner', k.ansprechpartner);
|
||||
angabe('Erreichbar', k.sprechzeiten);
|
||||
angabe('Danach werden Sie gefragt', k.aktenzeichen, true);
|
||||
|
||||
var liste = document.getElementById('ergAnrufSaetze');
|
||||
liste.innerHTML = '';
|
||||
if (saetze.length) {
|
||||
var kopf = document.createElement('p');
|
||||
kopf.style.marginTop = '12px';
|
||||
kopf.innerHTML = '<strong>Das können Sie am Telefon sagen:</strong>';
|
||||
liste.appendChild(kopf);
|
||||
var mitBedeutung = zielsprache() !== 'de';
|
||||
saetze.forEach(function (s) {
|
||||
var d = document.createElement('div'); d.className = 'anruf-satz';
|
||||
var de = document.createElement('p'); de.className = 'anruf-de';
|
||||
de.textContent = '„' + s.deutsch + '“';
|
||||
d.appendChild(de);
|
||||
if (mitBedeutung && s.bedeutung && s.bedeutung !== s.deutsch) {
|
||||
var be = document.createElement('p'); be.className = 'anruf-bed';
|
||||
be.setAttribute('dir', 'auto');
|
||||
be.textContent = s.bedeutung;
|
||||
d.appendChild(be);
|
||||
}
|
||||
liste.appendChild(d);
|
||||
});
|
||||
}
|
||||
karte.style.display = 'block';
|
||||
}
|
||||
|
||||
// ---- Guthaben kaufen ------------------------------------------------
|
||||
function rendrePakete() {
|
||||
paketListe.innerHTML = '';
|
||||
|
||||
Reference in new issue
Block a user