Files
Webklar-Kundenbereich/public/dashboard.html
T
ClaudeandClaude Opus 5.5 48b13f4488 Abo: Uebernahmegutschrift fuer beim Altanbieter bezahlte Leistungen
- Kunde (Dashboard, nur mit laufendem Abo): Antrag mit Leistung, Anbieter,
  Domain, Rechnungsbetrag/-zeitraum, Grund, Rechnungsnachweis (PDF/JPG/PNG)
  und E-Mail fuer die Antwort; Eingangsbestaetigung per Mail, Status und
  Antwort auf Rueckfragen im Portal
- Admin-Mail mit Link admin.html?gutschrift=<id> (Login leitet zurueck)
- Admin-Tab "Gutschriften": Kunde und Vertrag, Nachweise, Verlauf,
  automatische Pruefungen (Duplikate per Datei-Hash/Domain, Abo, Zeitraum,
  DNS-Abgleich Nameserver/MX mit Anbieter), Pflicht-Checkliste, Rechner
  (tagesgenau anteilig), Genehmigen/Rueckfrage/Ablehnen mit Kundenmail
- Genehmigte Gutschrift als Stripe-Kundenguthaben (idempotent, Nachbuchen
  bei Stripe-Stoerung)
- Nachweise in upload-data/ (gitignored), Loeschung abgelehnter nach 6 Monaten
- Stundenkontingent auch fuer Monate, die ein Guthaben komplett deckt
  (invoice.total statt amount_paid)
- Datenschutzerklaerung Abschnitt 7 ergaenzt, PDF neu erzeugt
- Collection takeoverCredits in setup-abo-collections.mjs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 19:34:33 +00:00

276 lines
16 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Kundenbereich – Webklar</title>
<link rel="stylesheet" href="/login.css">
<style>
.viewas-banner {
display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
padding: 10px 16px; margin-bottom: 16px; border-radius: 12px;
background: rgba(251,191,36,0.12); border: 1px solid rgba(251,191,36,0.45);
}
/* Tabs (Muster aus Admin, violett) */
.portal-tabs { display: flex; gap: 8px; margin: 14px 0 22px; flex-wrap: wrap; }
.portal-tab {
background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.14);
color: inherit; padding: 9px 18px; border-radius: 999px; cursor: pointer; font: inherit;
display: inline-flex; align-items: center; gap: 8px;
}
.portal-tab.active { background: rgba(157,61,254,0.22); border-color: rgba(157,61,254,0.65); }
.portal-section { display: none; }
.portal-section.active { display: block; }
.portal-section-title { margin: 0 0 14px; font-size: 20px; }
.badge-pill {
display: inline-flex; min-width: 20px; height: 20px; padding: 0 6px;
align-items: center; justify-content: center; border-radius: 999px;
background: #9d3dfe; color: #fff; font-size: 12px; font-weight: 700;
}
.simple-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.simple-table th, .simple-table td { text-align: left; padding: 8px 10px; border-bottom: 1px solid rgba(255,255,255,0.08); }
.simple-table th { font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; opacity: 0.65; }
.simple-table tr:last-child td { border-bottom: none; }
.btn-link { color: #7dd3fc; text-decoration: none; }
.btn-link:hover { text-decoration: underline; }
.pill { display: inline-block; padding: 2px 10px; border-radius: 999px; font-size: 12px; border: 1px solid transparent; white-space: nowrap; }
.pill-ok { background: rgba(74,222,128,0.15); border-color: rgba(74,222,128,0.4); }
.pill-warn { background: rgba(157,61,254,0.18); border-color: rgba(157,61,254,0.5); }
.pill-red { background: rgba(248,113,113,0.15); border-color: rgba(248,113,113,0.45); }
.pill-muted { background: rgba(255,255,255,0.07); border-color: rgba(255,255,255,0.16); opacity: 0.85; }
.pill-git { background: rgba(251,146,60,0.16); border-color: rgba(251,146,60,0.5); }
.activity-item { padding: 10px 0; border-bottom: 1px solid rgba(255,255,255,0.08); }
.activity-item:last-child { border-bottom: none; }
.activity-status {
display: inline-block; margin-left: 6px; padding: 1px 9px; border-radius: 999px; font-size: 12px;
background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.16);
}
.activity-ws { font-size: 13px; opacity: 0.85; margin: 4px 0 4px 14px; white-space: pre-wrap; }
/* Projekt-Details */
.project-details-toggle { margin-top: 10px; }
.project-details { margin-top: 14px; border-top: 1px solid rgba(255,255,255,0.1); padding-top: 12px; }
.project-details h3 { margin: 12px 0 8px; font-size: 15px; }
.commit-item { padding: 6px 0; border-bottom: 1px solid rgba(255,255,255,0.06); font-size: 14px; }
.commit-item:last-child { border-bottom: none; }
.commit-meta { font-size: 12px; opacity: 0.6; }
.commit-body { font-size: 13px; opacity: 0.8; white-space: pre-wrap; margin: 2px 0 0 8px; }
/* Projekt-Vorschaufenster */
.preview-frame {
position: relative; display: block; width: 100%; aspect-ratio: 16 / 10;
overflow: hidden; border-radius: 12px; border: 1px solid rgba(255,255,255,0.14);
background: rgba(255,255,255,0.04); margin-bottom: 1rem; text-decoration: none;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.preview-frame:hover { border-color: rgba(157,61,254,0.7); box-shadow: 0 0 0 1px rgba(157,61,254,0.35); }
.preview-frame__view {
position: absolute; top: 0; left: 0; width: 1280px; height: 800px; border: 0;
transform-origin: 0 0; pointer-events: none; background: #fff;
}
.preview-frame__hint {
position: absolute; left: 0; right: 0; bottom: 0; padding: 10px 14px;
font-size: 13px; font-weight: 600; color: #fff;
background: linear-gradient(to top, rgba(0,0,0,0.6), rgba(0,0,0,0));
opacity: 0; transition: opacity 0.2s ease; pointer-events: none;
}
.preview-frame:hover .preview-frame__hint { opacity: 1; }
.preview-frame--empty {
display: flex; align-items: center; justify-content: center;
color: rgba(255,255,255,0.5); font-size: 14px; aspect-ratio: 16 / 10;
}
.preview-open { margin: 0.75rem 0 0; }
/* Chat */
.chat-box { display: flex; flex-direction: column; height: 480px; }
.chat-messages { flex: 1; overflow-y: auto; padding: 6px 4px; display: flex; flex-direction: column; gap: 10px; }
.chat-bubble { max-width: 78%; padding: 10px 14px; border-radius: 16px; font-size: 14px; white-space: pre-wrap; word-break: break-word; }
.chat-bubble .chat-meta { display: block; font-size: 11px; opacity: 0.6; margin-top: 4px; }
.chat-bubble.mine { align-self: flex-end; background: rgba(157,61,254,0.3); border: 1px solid rgba(157,61,254,0.5); border-bottom-right-radius: 4px; }
.chat-bubble.theirs { align-self: flex-start; background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.14); border-bottom-left-radius: 4px; }
.chat-input-row { display: flex; gap: 10px; margin-top: 12px; align-items: flex-end; }
.chat-input-row textarea {
flex: 1; resize: none; min-height: 46px; max-height: 130px; padding: 12px 14px;
border-radius: 14px; border: 1px solid rgba(255,255,255,0.18);
background: rgba(255,255,255,0.06); color: inherit; font: inherit;
}
.chat-send {
background: #9d3dfe; border: none; color: #fff; font: inherit; font-weight: 600;
padding: 12px 20px; border-radius: 14px; cursor: pointer;
}
.chat-send:disabled { opacity: 0.5; cursor: default; }
.chat-hint { font-size: 12px; opacity: 0.55; margin-top: 6px; }
/* Wartungs-Abo (Startseite) */
.abo-card {
position: relative; margin: 6px 0 26px; padding: 26px 28px; border-radius: 22px;
background: linear-gradient(135deg, rgba(157,61,254,0.20), rgba(255,255,255,0.06) 60%);
border: 1px solid rgba(157,61,254,0.45);
}
.abo-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 18px; flex-wrap: wrap; }
.abo-kicker { font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; opacity: 0.7; margin: 0 0 4px; }
.abo-title { font-size: 24px; margin: 0; }
.abo-price { text-align: right; }
.abo-price strong { font-size: 32px; font-variant-numeric: tabular-nums; }
.abo-price span { display: block; font-size: 13px; opacity: 0.7; }
.abo-features { list-style: none; padding: 0; margin: 18px 0 20px; display: grid; gap: 10px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 640px) { .abo-features { grid-template-columns: 1fr; } .abo-price { text-align: left; } }
.abo-features li { padding-left: 26px; position: relative; font-size: 15px; }
.abo-features li::before { content: '✓'; position: absolute; left: 0; color: #c9a2ff; font-weight: 700; }
.abo-features small { display: block; opacity: 0.65; font-size: 13px; }
.abo-actions { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.abo-btn {
background: #9d3dfe; border: none; color: #fff; font: inherit; font-weight: 700;
padding: 13px 24px; border-radius: 999px; cursor: pointer;
}
.abo-btn:hover { background: #8a25f0; }
.abo-btn:disabled { opacity: 0.5; cursor: default; }
.abo-note { font-size: 13px; opacity: 0.7; }
.abo-order { margin-top: 20px; padding-top: 18px; border-top: 1px solid rgba(255,255,255,0.14); }
.abo-order h3 { margin: 0 0 10px; font-size: 17px; }
.abo-summary { width: 100%; border-collapse: collapse; font-size: 14px; margin-bottom: 14px; }
.abo-summary td { padding: 6px 0; border-bottom: 1px solid rgba(255,255,255,0.08); vertical-align: top; }
.abo-summary td:last-child { text-align: right; }
.abo-check { display: flex; gap: 10px; align-items: flex-start; margin: 10px 0; font-size: 14px; line-height: 1.45; cursor: pointer; }
.abo-check input { margin-top: 3px; width: 18px; height: 18px; accent-color: #9d3dfe; flex: none; }
.abo-card a { color: #d4b4ff; }
.abo-balance { display: flex; gap: 28px; flex-wrap: wrap; margin: 18px 0; }
.abo-stat strong { display: block; font-size: 34px; line-height: 1.1; font-variant-numeric: tabular-nums; }
.abo-stat span { font-size: 13px; opacity: 0.7; }
.abo-alert { padding: 10px 14px; border-radius: 12px; margin: 12px 0; font-size: 14px; background: rgba(251,191,36,0.12); border: 1px solid rgba(251,191,36,0.45); }
.abo-history { margin-top: 14px; }
.abo-history summary { cursor: pointer; font-size: 14px; opacity: 0.85; }
.abo-history table { margin-top: 10px; }
.portal-legal { margin-top: 40px; font-size: 13px; display: flex; gap: 18px; flex-wrap: wrap; align-items: center; }
.portal-legal a { color: rgba(255,255,255,0.7); }
.portal-legal a.legal-strong { color: #fff; font-weight: 700; text-decoration: underline; }
.abo-start-choice { border: none; padding: 0; margin: 14px 0 8px; }
.abo-start-choice legend { font-weight: 700; margin-bottom: 4px; }
.abo-summary-box { margin: 16px 0 10px; padding: 14px 16px; border-radius: 14px; background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.14); }
.abo-summary-box .abo-summary { margin-bottom: 6px; }
.abo-summary td:first-child { width: 26%; opacity: 0.75; }
.abo-summary td:last-child { text-align: left; }
.abo-withdraw { color: #fff; font-weight: 700; padding: 9px 16px; border-radius: 999px; border: 1px solid rgba(255,255,255,0.6); text-decoration: none; }
.abo-withdraw:hover { background: #fff; color: #1c1c1c; }
/* Übernahmegutschrift */
.pill-amber { background: rgba(251,191,36,0.15); border-color: rgba(251,191,36,0.45); }
.takeover-card {
margin: -8px 0 26px; padding: 22px 28px; border-radius: 22px;
background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.14);
}
.takeover-card .abo-title { font-size: 20px; }
.takeover-card a { color: #d4b4ff; }
.tk-intro { margin: 8px 0 14px; max-width: 70ch; }
.tk-notice { padding: 10px 14px; border-radius: 12px; margin: 12px 0; font-size: 14px; background: rgba(74,222,128,0.12); border: 1px solid rgba(74,222,128,0.4); }
.tk-form { margin-top: 18px; padding-top: 18px; border-top: 1px solid rgba(255,255,255,0.14); display: grid; gap: 14px; }
.tk-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 14px; }
.tk-field { border: none; padding: 0; margin: 0; min-width: 0; }
.tk-field > label, .tk-field > legend { display: block; font-size: 13px; font-weight: 600; margin-bottom: 6px; }
.tk-field small { font-weight: 400; opacity: 0.65; }
.tk-field input:not([type=checkbox]), .tk-field select, .tk-field textarea, .tk-reply textarea, .tk-reply input[type=file] {
width: 100%; box-sizing: border-box; padding: 10px 12px; border-radius: 12px;
border: 1px solid rgba(255,255,255,0.2); background: rgba(255,255,255,0.06); color: inherit; font: inherit;
}
.tk-field select option { color: #111; }
.tk-field textarea, .tk-reply textarea { min-height: 90px; resize: vertical; }
.tk-services { display: flex; flex-wrap: wrap; gap: 8px 20px; }
.tk-services label { display: inline-flex; gap: 8px; align-items: center; font-size: 14px; cursor: pointer; }
.tk-services input { width: 17px; height: 17px; accent-color: #9d3dfe; }
.tk-conditions summary { cursor: pointer; font-size: 14px; }
.tk-conditions ol { margin: 8px 0 0; padding-left: 20px; font-size: 13.5px; opacity: 0.85; }
.tk-conditions li { margin: 4px 0; }
.tk-list { margin-top: 16px; display: grid; gap: 10px; }
.tk-item { padding: 12px 16px; border-radius: 14px; background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.1); font-size: 14px; }
.tk-item p { margin: 8px 0 0; }
.tk-item-head { display: flex; justify-content: space-between; gap: 10px; flex-wrap: wrap; align-items: center; margin-bottom: 4px; }
.tk-question { margin: 10px 0 0; padding: 10px 12px; border-radius: 12px; white-space: pre-wrap; background: rgba(251,191,36,0.12); border: 1px solid rgba(251,191,36,0.45); }
.tk-reply { margin-top: 10px; display: grid; gap: 8px; }
</style>
</head>
<body class="portal-page dashboard-page">
<div class="dot-background" aria-hidden="true">
<div class="dot-pattern"></div>
<div class="dot-fade"></div>
</div>
<header class="portal-header">
<a class="portal-logo" href="/dashboard.html">Webklar</a>
<div class="portal-header-actions">
<span class="portal-meta" id="customer-meta"></span>
<button type="button" class="portal-btn-outline" id="logout-btn">Abmelden</button>
</div>
</header>
<main class="portal-main dashboard-content-reveal">
<div id="viewas-banner" class="viewas-banner hidden"></div>
<h1 class="portal-title">Kundenbereich</h1>
<p id="load-error" class="error hidden"></p>
<!-- Wartungs-Abo: Angebot bzw. Stundenkonto -->
<section id="abo-card" class="abo-card hidden" aria-live="polite"></section>
<!-- Übernahmegutschrift: beim bisherigen Anbieter schon bezahlte Zeit anrechnen -->
<section id="takeover-card" class="takeover-card hidden" aria-live="polite"></section>
<div class="portal-tabs">
<button class="portal-tab active" data-tab="projects">Projekte</button>
<button class="portal-tab" data-tab="activity">Was wurde gemacht</button>
<button class="portal-tab" data-tab="invoices">Rechnungen</button>
<button class="portal-tab" data-tab="chat">Chat <span id="chat-badge" class="badge-pill hidden"></span></button>
</div>
<!-- Projekte -->
<section class="portal-section active" id="tab-projects">
<p id="loading" class="muted portal-status">Projekte werden geladen…</p>
<ul id="projects" class="projects hidden"></ul>
<p id="empty" class="portal-card hidden portal-status">Noch keine Website-Projekte zugewiesen.</p>
</section>
<!-- Was wurde gemacht -->
<section class="portal-section" id="tab-activity">
<h2 class="portal-section-title">Was wurde gemacht</h2>
<div class="portal-card" id="activity-body"><p class="muted">Lade…</p></div>
</section>
<!-- Rechnungen -->
<section class="portal-section" id="tab-invoices">
<h2 class="portal-section-title">Meine Rechnungen</h2>
<div class="portal-card" id="invoices-body"><p class="muted">Lade…</p></div>
</section>
<!-- Chat -->
<section class="portal-section" id="tab-chat">
<h2 class="portal-section-title">Chat mit dem Webklar-Team</h2>
<div class="portal-card chat-box">
<div class="chat-messages" id="chat-messages"></div>
<div class="chat-input-row">
<textarea id="chat-input" maxlength="2000" placeholder="Nachricht an das Webklar-Team…"></textarea>
<button type="button" class="chat-send" id="chat-send">Senden</button>
</div>
<p class="chat-hint">Enter = Senden, Shift+Enter = Zeilenumbruch. Wir antworten in der Regel innerhalb eines Werktags.</p>
</div>
</section>
<footer class="portal-legal">
<a class="legal-strong" href="/kuendigen.html">Verträge hier kündigen</a>
<a class="legal-strong" href="/widerrufen.html">Vertrag widerrufen</a>
<a href="/abo-bedingungen.html" target="_blank" rel="noopener">Vertragsbedingungen</a>
<a href="/datenschutz.html" target="_blank" rel="noopener">Datenschutz</a>
<a href="https://webklar.com/impressum" target="_blank" rel="noopener">Impressum</a>
</footer>
</main>
<script src="/app.js"></script>
<script>
initDashboardPage();
</script>
</body>
</html>