Der im Gespraech vereinbarte Termin wird mit einem Klick am Akquise-Ticket
eingetragen (startDate/startTime/endTime/assignedTo/assignedName) - genau die
Felder, die das WOMS-Planboard liest. Kein zweiter Datensatz, damit Termin und
Ticket nicht auseinanderlaufen koennen.
- server/services/appointments.js: Ticket des Kunden finden, Termin schreiben,
belegte Zeiten lesen; Umrechnung ISO <-> "dd.mm.yyyy" und "hh:mm" <-> "hhmm"
- Routen GET /api/portal-admin/appointments und
POST /api/portal-admin/customers/:id/appointment
- Bereits vergebene Zeiten sind in der Terminauswahl gesperrt ("belegt"), der
eigene Termin bleibt waehlbar; steht am Ticket schon einer, ist er beim
Oeffnen vorausgewaehlt
- Zeiten, die jemand von Hand in WOMS gesetzt hat, tauchen mit auf
Sicherung gegen Datenverlust: bevorzugt wird das Akquise-Ticket. Gibt es keins,
kommt nur ein anderes offenes Ticket OHNE Datum in Frage - bei Webpage- und
Projekttickets bedeutet startDate den Projektstart, den wuerden wir sonst still
ueberschreiben. Die Rueckmeldung nennt immer Nummer und Art des Tickets.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
177 lines
7.0 KiB
HTML
177 lines
7.0 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>Rechnungsanalyse – Webklar</title>
|
||
<link rel="stylesheet" href="/kalkulation.css">
|
||
</head>
|
||
<body>
|
||
<header class="kal-header">
|
||
<div class="kal-header-inner">
|
||
<!-- Der Weg zurueck ins Admin-Panel steckt im Logo. -->
|
||
<a class="kal-brand" href="/admin.html" title="Zurück zum Admin-Panel">webklar</a>
|
||
<span class="kal-spacer"></span>
|
||
<span class="kal-stat">
|
||
<span class="kal-stat-lbl">Netto</span>
|
||
<span class="kal-stat-val big" id="hdr-net">0 €</span>
|
||
</span>
|
||
<span class="kal-stat">
|
||
<span class="kal-stat-lbl">Stunden</span>
|
||
<span class="kal-stat-val" id="hdr-hrs">0,00</span>
|
||
</span>
|
||
<span class="kal-stat">
|
||
<span class="kal-stat-lbl">Monatlich</span>
|
||
<span class="kal-stat-val" id="hdr-monthly">–</span>
|
||
</span>
|
||
</div>
|
||
</header>
|
||
|
||
<main>
|
||
<!-- Preis-Übersicht -->
|
||
<section class="hero">
|
||
<div class="kicker" id="hero-kicker">Ihr Projekt</div>
|
||
<div class="hero-price-row">
|
||
<div class="hero-price" id="hero-net">0 €</div>
|
||
<div class="hero-price-sub" id="hero-sub">netto · 0 € brutto inkl. 19 % MwSt.</div>
|
||
</div>
|
||
<div class="hero-tiles">
|
||
<div class="tile">
|
||
<div class="tile-lbl">Aufwand</div>
|
||
<div class="tile-val" id="hero-hrs">0,00 Std</div>
|
||
<div class="tile-sub" id="hero-rate">Stundensatz 150 €/Std</div>
|
||
</div>
|
||
<div class="tile">
|
||
<div class="tile-lbl">Monatlich</div>
|
||
<div class="tile-val" id="hero-monthly">–</div>
|
||
<div class="tile-sub" id="hero-monthly-sub">nichts Laufendes gewählt</div>
|
||
</div>
|
||
<div class="tile tile-pay">
|
||
<div class="tile-lbl">Zahlung</div>
|
||
<div class="tile-pay-title" id="pay-title">Noch nichts gewählt</div>
|
||
<div class="tile-pay-text" id="pay-text">Kreuze an, was der Kunde braucht.</div>
|
||
</div>
|
||
</div>
|
||
<div class="flag" id="flag"><b>Noch offen.</b> Sobald etwas gewählt ist, steht hier, ob du selbst abschließen kannst.</div>
|
||
<div class="hero-actions">
|
||
<button type="button" class="btn-solid" data-act="open-summary">Zusammenfassung</button>
|
||
<button type="button" class="btn-outline" data-act="reset">Zurücksetzen</button>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Bereiche -->
|
||
<section class="block">
|
||
<h2>Worum geht es?</h2>
|
||
<p class="block-lead">Bereiche antippen — nur die ausgewählten werden unten angezeigt.</p>
|
||
<div class="chips" id="type-chips"></div>
|
||
</section>
|
||
|
||
<!-- Positionen (gerendert) -->
|
||
<div id="sections"></div>
|
||
|
||
<div class="empty-hint" id="empty-hint" hidden>
|
||
<h3>Noch kein Bereich gewählt</h3>
|
||
<p>Tippe oben an, worum es geht — dann erscheinen hier die Positionen.</p>
|
||
</div>
|
||
|
||
<!-- Auswahl -->
|
||
<section class="panel" id="chosen-panel" hidden>
|
||
<div class="group-label" style="margin:0 0 14px" id="chosen-label">Ausgewählt (0)</div>
|
||
<div class="chips" id="chosen-list"></div>
|
||
</section>
|
||
|
||
<!-- Termin -->
|
||
<section class="panel">
|
||
<div class="kicker" style="color:var(--color-neutral-600)">Termin</div>
|
||
<h2>Gespräch festlegen</h2>
|
||
<p class="panel-lead">Nur wenn gewünscht: Mit wem, wann — und worum es geht. Der Termin hängt an der Zusammenfassung.</p>
|
||
|
||
<div class="seg" id="want-seg">
|
||
<button type="button" data-act="want" data-want="0" class="on">Kein Termin nötig</button>
|
||
<button type="button" data-act="want" data-want="1">Termin gewünscht</button>
|
||
</div>
|
||
|
||
<div id="termin-body" hidden>
|
||
<div class="chips" id="person-list"></div>
|
||
|
||
<div class="group-label" id="days-label">Freie Tage</div>
|
||
<div class="day-grid" id="day-list"></div>
|
||
|
||
<div id="slot-wrap" hidden>
|
||
<div class="group-label" id="slots-label">Uhrzeit</div>
|
||
<div class="chips" id="slot-list"></div>
|
||
</div>
|
||
|
||
<div style="margin-top:20px">
|
||
<label class="field-label" for="term-note">Worum geht es im Gespräch?</label>
|
||
<textarea class="kal-input" id="term-note"
|
||
placeholder="z. B. Shop-Anbindung an die Warenwirtschaft klären"></textarea>
|
||
</div>
|
||
|
||
<div class="term-status" id="term-status">
|
||
<b>Noch kein Termin.</b> Wähle einen Tag und eine Uhrzeit — beides landet in der Zusammenfassung.
|
||
</div>
|
||
|
||
<div class="term-save">
|
||
<button type="button" class="btn-accent" id="term-save" data-act="save-termin" disabled>
|
||
Termin ins Ticket eintragen
|
||
</button>
|
||
<span class="term-save-hint" id="term-save-hint">Trägt den Termin am Akquise-Ticket ein — damit steht er im Planboard.</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="margin-top:18px">
|
||
<label class="field-label" for="notes">Weitere Notizen zum Projekt</label>
|
||
<textarea class="kal-input" id="notes" style="min-height:110px"
|
||
placeholder="Alles, was sonst noch wichtig ist: Wünsche, Fristen, Ansprechpartner, Besonderheiten…"></textarea>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Kundenportal -->
|
||
<section class="portal-box">
|
||
<div class="portal-qr">
|
||
<img src="/img/portal-qr.png" alt="QR-Code zum Kundenportal project.webklar.com" width="180" height="180">
|
||
</div>
|
||
<div class="portal-info">
|
||
<div class="kicker" style="color:var(--color-neutral-600)">Kundenportal</div>
|
||
<h2>Code scannen und einloggen</h2>
|
||
<p class="panel-lead">Im Portal sieht der Kunde den Stand des Projekts, alle Dateien und die Rechnungen —
|
||
<a href="https://project.webklar.com/" target="_blank" rel="noreferrer">project.webklar.com</a></p>
|
||
<div class="cred-row">
|
||
<label class="cred">
|
||
<span>E-Mail</span>
|
||
<input id="portal-email" type="email" autocomplete="off" placeholder="kunde@firma.de">
|
||
</label>
|
||
<label class="cred">
|
||
<span>Passwort</span>
|
||
<input id="portal-pass" class="mono" type="text" autocomplete="off" placeholder="wird vergeben">
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<footer>
|
||
webklar GbR · Am Schwimmbad 10 · 67722 Winnweiler · 0170 4969375<br>
|
||
Alle Preise netto zzgl. gesetzlicher Umsatzsteuer. Stundenwerte sind Richtwerte.
|
||
</footer>
|
||
</main>
|
||
|
||
<!-- Zusammenfassung -->
|
||
<div class="backdrop" id="summary-dialog" hidden>
|
||
<div class="dialog" role="dialog" aria-modal="true" aria-labelledby="summary-title">
|
||
<h2 id="summary-title">Zusammenfassung</h2>
|
||
<textarea id="summary-text" readonly></textarea>
|
||
<div class="dialog-actions">
|
||
<button type="button" class="btn-accent" data-act="copy">Kopieren</button>
|
||
<button type="button" class="btn-quiet" data-act="close-summary">Schließen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="/kalkulation.js"></script>
|
||
<script>
|
||
initKalkulation();
|
||
</script>
|
||
</body>
|
||
</html>
|