📄 Kunden-Dokument

Bild oder Dokument hochladen

Nach dem Upload erscheint die Datei hier links in einer großen, professionellen Vollseiten-Ansicht.

Unterstützte Formate: PDF · DOCX · XLSX · XLS · PPTX · JPG · PNG · GIF · WebP · TXT · CSV · XML Alte Formate (.doc / .ppt) bitte vorher als .docx / .pptx speichern. Maximale Dateigröße: 15 MB
Datei hierher ziehen oder auswählen

IT-RECHTBOX Analyse

Impressum, Datenschutzerklärung, AGB, Widerruf und KI-Transparenz prüfen.

Willkommen bei IT-RECHTBOX.
Lade dein Dokument (Impressum, AGB, Datenschutzerklärung o. Ä.) hoch oder beschreibe dein Anliegen.
Aktives Modul: IT-RECHTBOXStrg+Enter

KI-Analyse

76Stärke-Score
MittelRisiko
3 WochenFrist / Hinweis
BereitVorlage
Bei einer echten Kündigung zuerst Zugangstag, Schriftform, Betriebsrat-Anhörung und Klagefrist prüfen.

Fertiges Dokument

Wenn der Vorgang abgeschlossen ist, bekommt der Kunde eine klare Ergebnisbox mit den wichtigsten Aktionen.

'; const blob = new Blob(['\ufeff', html], { type: 'application/msword' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = 'rechtbox-analyse.doc'; document.body.appendChild(a); a.click(); a.remove(); setTimeout(function () { URL.revokeObjectURL(a.href); }, 1000); } function rbExportEmail(btn) { openDocEmailModal(rbGetMsgText(btn), 'Analyse'); } // ✅ FIX: E-Mail-Anfrage-Modal — Web3Forms ile GERÇEK gönderim (aynı access_key index.html'deki çalışan // Kontaktformular ile aynı; mailto: sadece backend gerçekten ulaşılamazsa son çare olarak kullanılır). const RB_W3F_ACCESS_KEY = '184806df-3cb7-4c6f-91c7-6fc18e5b5a81'; let _docEmailPendingText = ''; let _docEmailPendingLabel = 'IT-RECHTBOX-Analyse'; function openDocEmailModal(text, label) { _docEmailPendingText = text || ''; _docEmailPendingLabel = label || 'IT-RECHTBOX-Analyse'; const m = document.getElementById('docEmailModal'); const input = document.getElementById('docEmailInput'); const status = document.getElementById('docEmailStatus'); const btn = document.getElementById('docEmailSendBtn'); if (status) { status.textContent = ''; status.style.color = 'var(--muted)'; } if (input) input.value = ''; if (btn) { btn.disabled = false; btn.textContent = 'Senden →'; } if (m) m.classList.add('show'); } function closeDocEmailModal() { const m = document.getElementById('docEmailModal'); if (m) m.classList.remove('show'); } async function sendDocEmailConfirm() { const input = document.getElementById('docEmailInput'); const status = document.getElementById('docEmailStatus'); const btn = document.getElementById('docEmailSendBtn'); const email = input ? input.value.trim() : ''; if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { status.style.color = '#c9a15a'; status.textContent = '⚠️ Bitte eine gültige E-Mail-Adresse eingeben.'; return; } btn.disabled = true; btn.textContent = '⏳ Wird gesendet...'; status.style.color = 'var(--muted)'; status.textContent = ''; try { const res = await fetch('https://api.web3forms.com/submit', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({ access_key: RB_W3F_ACCESS_KEY, subject: 'IT-RECHTBOX Dokument-Anfrage — ' + _docEmailPendingLabel, from_name: 'IT-RECHTBOX Dokument-Versand', email: email, message: 'Ein Nutzer möchte folgendes Dokument per E-Mail erhalten.\n\nZiel-E-Mail-Adresse: ' + email + '\n\n— Dokumentinhalt —\n\n' + _docEmailPendingText.slice(0, 4000) }) }); const data = await res.json().catch(() => ({})); if (res.ok && data.success !== false) { status.style.color = '#5fa887'; status.textContent = '✅ Angefordert! Sie erhalten Ihr Dokument in Kürze per E-Mail an ' + email + '.'; setTimeout(closeDocEmailModal, 2200); btn.disabled = false; btn.textContent = 'Senden →'; return; } throw new Error('send-failed'); } catch (e) { // Backend/Web3Forms nicht erreichbar — als letzte Möglichkeit mailto: anbieten (transparent, nicht automatisch) status.style.color = '#e6c88a'; status.textContent = '⚠️ Versand momentan nicht möglich. Alternativ als PDF/DOCX herunterladen, oder '; const fallbackLink = document.createElement('a'); fallbackLink.href = 'mailto:?subject=' + encodeURIComponent('Meine ' + _docEmailPendingLabel) + '&body=' + encodeURIComponent(_docEmailPendingText.slice(0, 1800)); fallbackLink.textContent = 'eigenes E-Mail-Programm öffnen.'; fallbackLink.style.color = '#c9a15a'; fallbackLink.style.fontWeight = '800'; status.appendChild(fallbackLink); } btn.disabled = false; btn.textContent = 'Senden →'; } // ➕ Fertiges Dokument box functions function getLastBotText() { const msgs = document.querySelectorAll('.msg.bot'); for (let i = msgs.length - 1; i >= 0; i--) { const d = msgs[i].querySelector('div'); if (d && d.innerText && d.innerText.trim().length > 20) return d.innerText; } // fallback: any bot message text const allBot = document.querySelectorAll('.msg.bot'); if (allBot.length > 0) return allBot[allBot.length - 1].innerText || ''; return ''; } function fertigDownloadPdf() { generateRealPdf(getLastBotText(), 'Fertiges-Dokument'); } function fertigDownloadDocx() { const text = getLastBotText(); const safe = text.replace(/&/g,'&').replace(//g,'>'); const html = '' + 'IT-RECHTBOX Analyse' + '
' + safe + '
'; const blob = new Blob(['', html], { type: 'application/msword' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = 'rechtbox-analyse.doc'; document.body.appendChild(a); a.click(); a.remove(); setTimeout(function(){ URL.revokeObjectURL(a.href); }, 1000); } function fertigEmail() { openDocEmailModal(getLastBotText(), 'Fertiges Dokument'); } function fertigNewChat() { if (confirm('Neue Analyse starten? Die aktuelle Konversation wird gelöscht.')) { location.reload(); } } function showFertigBox() { const box = document.getElementById('fertigBox'); if (box) { box.style.display = 'block'; box.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } } // ➕ ersenbox-multisite-chat skill: Uzman yönlendirme (Steuerberater/Rechtsanwalt) — gerçek mekanizma let expertSelectedType = 'Steuerberater'; function openExpertModal() { const m = document.getElementById('expertModal'); if (m) m.classList.add('show'); } function closeExpertModal() { const m = document.getElementById('expertModal'); if (m) m.classList.remove('show'); const status = document.getElementById('expertStatus'); if (status) { status.textContent = ''; status.style.color = 'var(--muted)'; } } document.addEventListener('DOMContentLoaded', function () { const typeBtns = document.querySelectorAll('.expert-type-btn'); typeBtns.forEach(function (b) { b.addEventListener('click', function () { typeBtns.forEach(function (x) { x.classList.remove('active'); x.style.borderColor = ''; x.style.color = ''; }); b.classList.add('active'); expertSelectedType = b.dataset.type; }); }); }); // NOT: api.ersenbox.com'da gerçek bir /api/contact uç noktası DOĞRULANAMADI (bu repo'da yok). // Bu fonksiyon önce backend'i dener; yoksa (404/network hatası) mailto: ile zarif şekilde geri düşer. // Backend eklenirse bu fonksiyon otomatik olarak gerçek gönderimi kullanmaya başlar, kod değişikliği gerekmez. // ✅ FIX: Backend /api/contact hiç bağlı değildi (her zaman mailto:'ya düşüyordu — ekran görüntüsünde // görülen "Backend noch nicht verbunden" hatası buradan). Artık Web3Forms ile GERÇEKTEN gönderiliyor // (index.html'deki çalışan Kontaktformular ile aynı access_key / aynı yöntem). async function sendExpertRequest() { const email = document.getElementById('expertEmail').value.trim(); const message = document.getElementById('expertMessage').value.trim(); const status = document.getElementById('expertStatus'); const btn = document.getElementById('expertSendBtn'); if (!email || !message) { status.style.color = '#c9a15a'; status.textContent = '⚠️ Bitte E-Mail und Nachricht ausfüllen.'; return; } if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { status.style.color = '#c9a15a'; status.textContent = '⚠️ Bitte eine gültige E-Mail-Adresse eingeben.'; return; } btn.disabled = true; btn.textContent = '⏳ Wird gesendet...'; status.style.color = 'var(--muted)'; status.textContent = ''; try { const res = await fetch('https://api.web3forms.com/submit', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({ access_key: RB_W3F_ACCESS_KEY, subject: 'IT-RECHTBOX Experten-Anfrage — ' + expertSelectedType, from_name: 'IT-RECHTBOX Experten-Kontakt', email: email, message: 'Bereich: ' + expertSelectedType + '\n' + 'Von: ' + email + '\n\n' + message }) }); const data = await res.json().catch(() => ({})); if (!res.ok || data.success === false) throw new Error('send-failed'); status.style.color = '#5fa887'; status.textContent = '✅ Anfrage gesendet! Wir melden uns bald.'; setTimeout(closeExpertModal, 1800); } catch (e) { status.style.color = '#e6c88a'; status.innerHTML = '⚠️ Versand momentan nicht möglich. Bitte direkt an ' + 'info@ersenbox.com senden.'; } btn.disabled = false; btn.textContent = 'Anfrage senden →'; } function removeFile() { selectedFile = null; const uploadArea = document.getElementById('uploadArea'); uploadArea.classList.remove('has-file'); const preview = uploadArea.querySelector('.file-preview'); if (preview) preview.remove(); const fileStatus = document.getElementById('fileStatus'); fileStatus.style.color = 'var(--muted)'; fileStatus.textContent = 'Datei hierher ziehen oder auswählen'; const input = document.getElementById('fileInput'); if (input) input.value = ''; // ➕ ersenbox-multisite-chat skill: viewer paneli temizliği const viewerBody = document.getElementById('viewerBody'); const fileBar = document.getElementById('viewerFileBar'); const zoomControls = document.getElementById('viewerZoomControls'); if (viewerObjectUrl) { URL.revokeObjectURL(viewerObjectUrl); viewerObjectUrl = null; } if (viewerBody) { const wrap = viewerBody.querySelector('.viewer-frame-wrap'); if (wrap) wrap.remove(); } uploadArea.style.display = ''; if (fileBar) fileBar.style.display = 'none'; if (zoomControls) zoomControls.style.display = 'none'; } // ✅ FIX 3 v2: Dosyayı base64'e çevir — tüm formatlar async function extractFileContent(file) { if (file.size > 15 * 1024 * 1024) { throw new Error('Datei zu groß. Maximum 15 MB erlaubt.'); } const ext = file.name.split('.').pop().toLowerCase(); // PDF → base64 olarak gönder (Anthropic API direkt okur, scan dahil) if (ext === 'pdf' || file.type === 'application/pdf') { return await new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = e => { const base64 = e.target.result.split(',')[1]; resolve({ type: 'pdf', base64: base64, name: file.name }); }; reader.onerror = () => reject(new Error('PDF konnte nicht gelesen werden.')); reader.readAsDataURL(file); }); } // Görsel → base64 (JPEG, PNG, WebP, HEIC — Anthropic vision) if (file.type.match(/^image\//) || ['jpg','jpeg','png','gif','webp','heic','heif'].includes(ext)) { return await new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = e => { const base64 = e.target.result.split(',')[1]; resolve({ type: 'image', base64: base64, mediaType: file.type, name: file.name }); }; reader.onerror = () => reject(new Error('Bild konnte nicht gelesen werden.')); reader.readAsDataURL(file); }); } // ✅ FIX: DOCX → mammoth.js ile GERÇEK metin çıkarımı (artık sahte placeholder yok) if (ext === 'docx') { if (typeof mammoth === 'undefined') { return { type: 'error', message: 'DOCX-Bibliothek konnte nicht geladen werden. Bitte Seite neu laden.' }; } try { const buf = await file.arrayBuffer(); const result = await mammoth.extractRawText({ arrayBuffer: buf }); return { type: 'text', content: '[DOCX-Datei: ' + file.name + ']\n\n' + result.value.slice(0, 12000) }; } catch (e) { return { type: 'error', message: 'DOCX-Datei "' + file.name + '" konnte nicht gelesen werden (beschädigt oder ungültiges Format).' }; } } // ✅ FIX: XLSX/XLS → SheetJS ile GERÇEK içerik çıkarımı (önceden tamamen sessizce düşüyordu) if (ext === 'xlsx' || ext === 'xls') { if (typeof XLSX === 'undefined') { return { type: 'error', message: 'Tabellen-Bibliothek konnte nicht geladen werden. Bitte Seite neu laden.' }; } try { const buf = await file.arrayBuffer(); const workbook = XLSX.read(new Uint8Array(buf), { type: 'array' }); let out = '[Tabellen-Datei: ' + file.name + ']\n\n'; workbook.SheetNames.forEach(name => { out += '— Blatt: ' + name + ' —\n'; out += XLSX.utils.sheet_to_csv(workbook.Sheets[name]) + '\n\n'; }); return { type: 'text', content: out.slice(0, 12000) }; } catch (e) { return { type: 'error', message: 'Tabellen-Datei "' + file.name + '" konnte nicht gelesen werden (beschädigt oder ungültiges Format).' }; } } // ✅ FIX: PPTX → JSZip ile slayt metinlerinin GERÇEK çıkarımı (önceden tamamen sessizce düşüyordu) if (ext === 'pptx') { if (typeof JSZip === 'undefined') { return { type: 'error', message: 'PPTX-Bibliothek konnte nicht geladen werden. Bitte Seite neu laden.' }; } try { const buf = await file.arrayBuffer(); const zip = await JSZip.loadAsync(buf); const slideFiles = Object.keys(zip.files) .filter(n => /^ppt\/slides\/slide\d+\.xml$/.test(n)) .sort((a, b) => parseInt(a.match(/slide(\d+)\.xml/)[1], 10) - parseInt(b.match(/slide(\d+)\.xml/)[1], 10)); if (!slideFiles.length) throw new Error('no-slides'); const parser = new DOMParser(); let out = '[Präsentation: ' + file.name + ']\n\n'; for (let i = 0; i < slideFiles.length; i++) { const xml = await zip.files[slideFiles[i]].async('string'); const doc = parser.parseFromString(xml, 'application/xml'); const nodes = doc.getElementsByTagName('a:t'); const lines = []; for (let j = 0; j < nodes.length; j++) { if (nodes[j].textContent && nodes[j].textContent.trim()) lines.push(nodes[j].textContent.trim()); } out += 'Folie ' + (i + 1) + ':\n' + (lines.join('\n') || '(kein Text)') + '\n\n'; } return { type: 'text', content: out.slice(0, 12000) }; } catch (e) { return { type: 'error', message: 'PPTX-Datei "' + file.name + '" konnte nicht gelesen werden (beschädigt oder ungültiges Format).' }; } } // Düz metin / CSV / XML (.txt, .csv, .xml) if (ext === 'txt' || ext === 'csv' || ext === 'xml' || file.type.includes('text')) { return await new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = e => resolve({ type: 'text', content: e.target.result.slice(0, 12000) }); reader.onerror = () => reject(new Error('Datei konnte nicht gelesen werden.')); reader.readAsText(file); }); } // ✅ FIX: Eski binär .doc / .ppt — dürüst hata mesajı (sessizce yutulmaz, sahte de mesaj vermez) if (ext === 'doc' || ext === 'ppt') { return { type: 'error', message: 'Das ältere Format ".' + ext + '" kann nicht automatisch gelesen werden. Bitte als .' + (ext === 'doc' ? 'docx' : 'pptx') + ' oder PDF speichern und erneut hochladen, oder den Text manuell einfügen.' }; } // Hiçbir tanınan formata uymuyor — yine de sessiz kalmadan açıkça bildir return { type: 'error', message: 'Dateiformat "' + file.name + '" wird nicht unterstützt.' }; } // ✅ FIX 1: Kredi gösterimi sadece kozmetik — gerçek kontrol backend'de function getLocalCreditsDisplay() { const token = localStorage.getItem('rb_token'); if (token) return 999; return Math.max(0, 20 - parseInt(localStorage.getItem('rb_credits_used') || '0')); } function updateCreditDisplay() { const badge = document.getElementById('creditBadge'); if (!badge) return; const token = localStorage.getItem('rb_token'); if (token) { try { const u = JSON.parse(localStorage.getItem('rb_user') || '{}'); badge.textContent = '✅ ' + (u.name || u.email || 'Eingeloggt'); badge.style.background = 'rgba(95,168,135,.14)'; badge.style.color = '#7ec49f'; } catch(e) { badge.textContent = '✅ Eingeloggt'; } } else { const r = getLocalCreditsDisplay(); badge.textContent = r > 0 ? `🎁 ${r} kostenlose Analysen` : '🔒 Registrieren für mehr'; badge.style.background = r > 5 ? 'rgba(95,168,135,.14)' : r > 0 ? 'rgba(201,161,90,.14)' : 'rgba(185,97,79,.14)'; badge.style.color = r > 5 ? '#7ec49f' : r > 0 ? '#e6c88a' : '#d98a7d'; } } // ════ HAUPTFUNKTION: Analyse starten ════ async function startAnalysis() { const textarea = document.getElementById('userText'); const btn = document.getElementById('analyzeBtn'); const messages = document.getElementById('messages'); const text = textarea ? textarea.value.trim() : ''; const file = selectedFile || null; if (!text && !file) { alert('Bitte beschreibe dein Problem oder lade ein Dokument hoch.'); return; } // Turnstile token — optional, backend validiert server-side // Wenn Token noch nicht da: kurz warten, dann trotzdem senden if (!window._turnstileToken) { safeTurnstileExecute(true); // Max 3sn bekle — sonra ohne Token weitermachen let waited = 0; while (!window._turnstileToken && waited < 3000) { await new Promise(r => setTimeout(r, 200)); waited += 200; } // Kein Token? Trotzdem senden — Backend entscheidet } // Kullanıcı mesajını göster if (messages) { const um = document.createElement('div'); um.className = 'msg user'; um.textContent = text || (file ? `📎 ${file.name}` : ''); messages.appendChild(um); messages.scrollTop = messages.scrollHeight; } btn.disabled = true; btn.textContent = '⏳ KI analysiert...'; // Yükleniyor mesajı let loadingMsg = null; if (messages) { loadingMsg = document.createElement('div'); loadingMsg.className = 'msg bot'; loadingMsg.innerHTML = '⏳ Dokument wird verarbeitet…'; messages.appendChild(loadingMsg); messages.scrollTop = messages.scrollHeight; } try { // ✅ FIX 3 v2: Dosyayı oku — PDF base64 olarak gönderilir (scan dahil) let fileContent = null; if (file) { try { fileContent = await extractFileContent(file); } catch(e) { fileContent = { type: 'error', message: e.message }; } } // ✅ FIX-A: Aktif modülü belirle — sadece adını gönder, prompt'u değil const activeMod = document.querySelector('.module-btn.active'); const currentMod = activeMod ? activeMod.dataset.module : (new URLSearchParams(location.search).get('module') || 'itmedienrecht'); const VALID_MODULES = ['itmedienrecht']; const safeModule = VALID_MODULES.includes(currentMod) ? currentMod : 'itmedienrecht'; const authToken = localStorage.getItem('rb_token') || null; // Request body — dosya tipine göre pdf/image/text eklenir const requestBody = { module: safeModule, message: text || 'Analysiere das hochgeladene Dokument.', turnstile: window._turnstileToken || '', token: authToken, }; if (fileContent) { if (fileContent.type === 'pdf') { requestBody.pdf = { base64: fileContent.base64, name: fileContent.name }; } else if (fileContent.type === 'image') { requestBody.image = { base64: fileContent.base64, mediaType: fileContent.mediaType, name: fileContent.name }; } else if (fileContent.type === 'text') { requestBody.message += '\n\n[Dokumentinhalt]:\n' + fileContent.content; } else if (fileContent.type === 'error') { requestBody.message += '\n\n[Datei-Fehler: ' + fileContent.message + ']'; } } const response = await fetch(RB_AI_API + '/api/ai', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(requestBody) }); // Yükleniyor mesajını kaldır if (loadingMsg) loadingMsg.remove(); // Backend 429 = Quota, 403 = Turnstile/Auth if (response.status === 429) { // Quota doldu → Register modal göster const modal = document.getElementById('chatAuthModal'); if (modal) { modal.style.display = 'flex'; switchChatTab('register'); } if (messages) { const em = document.createElement('div'); em.className = 'msg bot'; em.innerHTML = `
⚠️ Analysenlimit erreicht. Kostenlos registrieren →
`; messages.appendChild(em); messages.scrollTop = messages.scrollHeight; } btn.disabled = false; btn.textContent = 'Analyse starten →'; return; } if (response.status === 403) { // Turnstile / Auth hatası → modal açma, sadece hata mesajı if (messages) { const em = document.createElement('div'); em.className = 'msg bot'; em.innerHTML = `⚠️ Sicherheitsprüfung fehlgeschlagen. Bitte Seite neu laden (F5).`; messages.appendChild(em); messages.scrollTop = messages.scrollHeight; } btn.disabled = false; btn.textContent = 'Analyse starten →'; return; } if (!response.ok) { const errData = await response.json().catch(() => ({})); throw new Error(errData.error || `HTTP ${response.status}`); } const data = await response.json(); const aiText = data.text; if (!aiText) throw new Error('Keine Antwort von der KI erhalten.'); // Başarılı analiz sonrası verlauf'a kaydet ve temizle saveVerlauf(text || (file ? file.name : ''), safeModule); // Günlük kullanım sayacını güncelle (UI) const today2 = new Date().toISOString().slice(0, 10); const authTok2 = localStorage.getItem('rb_token'); const usedKey2 = authTok2 ? `rb_used_auth_${today2}` : `rb_used_guest_${today2}`; localStorage.setItem(usedKey2, String(parseInt(localStorage.getItem(usedKey2) || '0') + 1)); updateDashboard(); removeFile(); // ✅ FIX 1: Yerel sayacı sadece UI için güncelle (güvenlik için kullanma) if (!authToken) { const used = parseInt(localStorage.getItem('rb_credits_used') || '0'); localStorage.setItem('rb_credits_used', used + 1); } updateCreditDisplay(); // Yanıtı göster if (messages) { const bm = document.createElement('div'); bm.className = 'msg bot'; const tt = translations[currentLang] || translations.de; const remaining = getLocalCreditsDisplay(); const loginHint = (!authToken && remaining <= 3 && remaining > 0) ? `
⚠️ ${(tt.creditsLeftPrefix||'Noch')} ${remaining} ${(tt.creditsLeftSuffix||'kostenlose Analyse(n).')} ${tt.registerFreeLink||'Kostenlos registrieren →'}
` : ''; bm.innerHTML = `
${aiText.replace(/&/g,'&').replace(//g,'>')}
${tt.noLegalAdviceFooter||'Keine verbindliche Rechtsberatung · IT-RECHTBOX KI'}
${loginHint}`; messages.appendChild(bm); messages.scrollTop = messages.scrollHeight; // ➕ Show Fertiges Dokument box after analysis complete showFertigBox(); } if (textarea) { textarea.value = ''; textarea.focus(); } // ✅ FIX 4: Turnstile'ı sıfırla (her gönderimde yeni token üret) window._turnstileToken = ''; safeTurnstileExecute(true); } catch(e) { if (loadingMsg) loadingMsg.remove(); if (messages) { const em = document.createElement('div'); em.className = 'msg bot'; em.innerHTML = `⚠️ Fehler: ${e.message}`; messages.appendChild(em); } window._turnstileToken = ''; safeTurnstileExecute(true); } btn.disabled = false; btn.textContent = 'Analyse starten →'; } // ════ VERLAUF — Son 10 analizi localStorage'da sakla ════ const VERLAUF_KEY = 'rb_verlauf'; const VERLAUF_MAX = 10; function saveVerlauf(question, module) { const text = (question || '').slice(0, 80) || 'Analyse'; const list = loadVerlauf(); list.unshift({ title: text, module: module || 'itmedienrecht', date: new Date().toLocaleDateString('de-DE', { day:'2-digit', month:'2-digit', hour:'2-digit', minute:'2-digit' }) }); if (list.length > VERLAUF_MAX) list.splice(VERLAUF_MAX); try { localStorage.setItem(VERLAUF_KEY, JSON.stringify(list)); } catch(e) {} renderVerlauf(); } function loadVerlauf() { try { return JSON.parse(localStorage.getItem(VERLAUF_KEY) || '[]'); } catch { return []; } } function renderVerlauf() { const list = loadVerlauf(); const el = document.getElementById('verlaufList'); if (!el) return; if (!list.length) { const t4 = translations[currentLang] || translations.de; el.innerHTML = '
'+(t4.verlaufEmpty||'Noch keine Analysen')+'
'; return; } const icons = { itmedienrecht:'' }; el.innerHTML = list.map((item, i) => `
${icons[item.module] || '📋'} ${item.title}
${item.date}
`).join(''); } function loadVerlaufItem(i) { const list = loadVerlauf(); const item = list[i]; if (!item) return; // Modülü ayarla setModule(item.module); // Soruyu textarea'ya koy const ta = document.getElementById('userText'); if (ta) ta.value = item.title; } function clearVerlauf() { if (confirm('Verlauf löschen?')) { localStorage.removeItem(VERLAUF_KEY); renderVerlauf(); } } // ════ DASHBOARD — Plan + Kota göstergesi ════ function updateDashboard() { const token = localStorage.getItem('rb_token'); const userRaw = localStorage.getItem('rb_user'); const planBadge = document.getElementById('dashPlanBadge'); const quotaNumbers = document.getElementById('quotaNumbers'); const quotaBarFill = document.getElementById('quotaBarFill'); let plan = 'free', limit = 5, used = 0; if (token && userRaw) { try { const u = JSON.parse(userRaw); plan = u.plan || 'free'; } catch(e) {} } // Limit tablosu const limits = { free: 5, pro: 40, business: 120 }; limit = limits[plan] || 5; // Günlük kullanımı localStorage'dan oku (UI için) const today = new Date().toISOString().slice(0, 10); const usedKey = token ? `rb_used_auth_${today}` : `rb_used_guest_${today}`; used = parseInt(localStorage.getItem(usedKey) || '0'); // Plan badge if (planBadge) { const t3 = translations[currentLang] || translations.de; const planLabels = { free: t3.dashPlanFree||'🆓 Free', pro: t3.dashPlanPro||'⭐ Pro', business: t3.dashPlanBiz||'💼 Business' }; const planClasses = { free:'plan-free', pro:'plan-pro', business:'plan-business' }; planBadge.textContent = planLabels[plan] || '🆓 Free Plan'; planBadge.className = 'plan-badge ' + (planClasses[plan] || 'plan-free'); } // Kota bar const pct = Math.min(100, Math.round((used / limit) * 100)); if (quotaNumbers) quotaNumbers.textContent = `${used} / ${limit}`; if (quotaBarFill) { quotaBarFill.style.width = pct + '%'; quotaBarFill.className = 'quota-bar-fill' + (pct >= 80 ? ' danger' : pct >= 60 ? ' warn' : ''); } } window.addEventListener('load', () => { updateCreditDisplay(); renderVerlauf(); updateDashboard(); }); // Ctrl+Enter ile analiz başlat (function() { var ta = document.getElementById('userText'); if (ta) { ta.addEventListener('keydown', function(e) { if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { e.preventDefault(); startAnalysis(); } }); } })(); // Aktif modül takibi let currentModule = new URLSearchParams(location.search).get('module') || 'itmedienrecht'; document.querySelectorAll('.module-btn').forEach(btn => { btn.addEventListener('click', () => { currentModule = btn.dataset.module || 'itmedienrecht'; }); }); // ════ Turnstile Invisible Widget ════ window._turnstileToken = ''; window._turnstileReady = false; window._turnstileWidgetId = null; window._turnstileExecuting = false; // ✅ DÜZELT 1: çift execute() çağrısını önleyen güvenli sarmalayıcı // ("already executing" konsol hatasının kaynağı buydu) function safeTurnstileExecute(forceReset) { if (!window.turnstile || window._turnstileWidgetId === null) return; if (window._turnstileExecuting) return; window._turnstileExecuting = true; try { if (forceReset) window.turnstile.reset(window._turnstileWidgetId); window.turnstile.execute(window._turnstileWidgetId); } catch (e) { window._turnstileExecuting = false; } } function onTurnstileLoad() { window._turnstileReady = false; // ✅ DÜZELT 1: size:'invisible' GEÇERSİZ bir parametreydi (Cloudflare sadece // "compact","flexible","normal" kabul ediyor) — kaldırıldı. Görünmezlik, // sitekey'in Cloudflare panelinde "Invisible" tipi olarak tanımlanmasından gelir. window._turnstileWidgetId = window.turnstile.render('#turnstileContainer', { sitekey: '0x4AAAAAADMGGYsKD-t9HSfh', callback: function(token) { window._turnstileToken = token; window._turnstileReady = true; window._turnstileExecuting = false; }, 'error-callback': function() { window._turnstileToken = ''; window._turnstileReady = false; window._turnstileExecuting = false; }, 'expired-callback': function() { window._turnstileToken = ''; window._turnstileReady = false; window._turnstileExecuting = false; // Token süresi dolunca otomatik reset + execute safeTurnstileExecute(true); } }); // Widget render edilince execute et setTimeout(() => { safeTurnstileExecute(false); }, 500); } // ════ ➕ PWA: Service Worker kaydı + Install Banner (ersenbox-multisite-chat skill — madde 7 standardı) ════ // ✅ FIX: Service Worker artık GERÇEKTEN kayıt ediliyor (yorum eskiden yanlıştı — kod yoktu). // Bu SW hiçbir şeyi önbelleğe almaz / offline desteklemez — sadece "Installieren" butonunun // gerçekten çalışması için tarayıcının kurulabilirlik şartını karşılar (bkz. sw.js). if ('serviceWorker' in navigator) { window.addEventListener('load', function () { navigator.serviceWorker.register('/sw.js').catch(function () { /* sessiz geç — install banner yine de denenir */ }); }); } (function () { var DISMISS_KEY = 'rb_pwa_dismissed'; var banner = document.getElementById('pwaInstallBanner'); var btn = document.getElementById('pwaInstallBtn'); var dismissBtn = document.getElementById('pwaInstallDismiss'); var textEl = document.getElementById('pwaInstallText'); var deferredPrompt = null; function isStandalone() { return window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true; } function isIOS() { return /iPad|iPhone|iPod/i.test(navigator.userAgent); } if (isStandalone() || localStorage.getItem(DISMISS_KEY) === '1') { // Zaten kurulu veya kullanıcı kapatmış — banner hiç gösterilmez } else { window.addEventListener('beforeinstallprompt', function (e) { e.preventDefault(); deferredPrompt = e; }); setTimeout(function () { if (isStandalone() || localStorage.getItem(DISMISS_KEY) === '1') return; if (!banner) return; if (isIOS()) { const tPwa = (typeof translations !== 'undefined' && typeof currentLang !== 'undefined') ? (translations[currentLang] || translations.de) : {}; textEl.textContent = tPwa.pwaInstallTextIOS || '📲 IT-RECHTBOX als App installieren — Teilen → "Zum Home-Bildschirm"'; btn.style.display = 'none'; } banner.classList.add('show'); }, 1500); } if (dismissBtn) { dismissBtn.onclick = function () { localStorage.setItem(DISMISS_KEY, '1'); banner.classList.remove('show'); }; } if (btn) { btn.onclick = function () { if (deferredPrompt) { deferredPrompt.prompt(); deferredPrompt.userChoice.then(function () { banner.classList.remove('show'); deferredPrompt = null; }); } else { banner.classList.remove('show'); } }; } })();