feat: CRM-Stufen (Lead/Kunde) + InvoiceNinja-Sync (eine Pflege)

- Zwei Kundenstufen: Lead (potenziell) und fester Kunde; Lead braucht nur
  Unternehmen + E-Mail und erzeugt automatisch ein Akquise-Ticket
- Akquise-Worksheet-Ergebnis (In Kontakt/Zugesagt/Abgesagt) sichtbar in WSIDs;
  "Zugesagt" macht den Lead automatisch zum festen Kunden, "Abgesagt" -> verloren
- Kunde wird nur in Appwrite gepflegt und automatisch als InvoiceNinja-Client
  angelegt/aktualisiert (beim Upgrade bzw. bei Datenpflege)
- Rechnungen nur fuer feste Kunden mit vollstaendigen Daten (Strasse/PLZ/Stadt)
- Kundenliste mit Stufen-Filter, neue Rechnungs-/Adressfelder

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Webklar DeployandCursor committed 2026-06-17 11:18:28 +00:00
1 parent 7d764ce6bc
commit a1cb90372f
8 files changed
+296 -80

No files matched your search

+12 -3
View File
@@ -3,6 +3,7 @@ import { FaFilePdf, FaPlus, FaLink, FaSpinner, FaRotate } from 'react-icons/fa6'
import { databases, DATABASE_ID, COLLECTIONS } from '../lib/appwrite'
import { integrationsApi, formatMoney, INVOICE_STATUS } from '../lib/integrationsApi'
import { useInvoices } from '../hooks/useInvoices'
import { customerStage, isInvoiceReady } from '../lib/leads'
const card = {
background: 'rgba(45, 55, 72, 0.5)',
@@ -74,7 +75,15 @@ export default function InvoicePanel({ ticket }) {
{customerError && <div className="text-red" style={{ marginBottom: 8 }}>{customerError}</div>}
{!clientId ? (
{customer && customerStage(customer) === 'lead' ? (
<p style={{ color: '#a0aec0', margin: 0 }}>
Dieser Kunde ist noch ein Lead. Erst zum festen Kunden machen (im Kunden-Bereich, vollstaendige Daten noetig) - dann sind Rechnungen moeglich.
</p>
) : !clientId && !isInvoiceReady(customer) ? (
<p style={{ color: '#a0aec0', margin: 0 }}>
Rechnungsdaten unvollstaendig. Bitte im Kunden-Bereich Unternehmen, E-Mail, Strasse, PLZ und Stadt ergaenzen.
</p>
) : !clientId ? (
<LinkClient customer={customer} onLinked={loadCustomer} />
) : (
<InvoiceList
@@ -177,7 +186,7 @@ function LinkClient({ customer, onLinked }) {
<div key={c.id} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '8px', borderBottom: '1px solid rgba(255,255,255,0.06)' }}>
<div>
<strong style={{ color: 'var(--dark-text)' }}>{c.name}</strong>
{c.number ? <span style={{ color: '#a0aec0' }}> · {c.number}</span> : null}
{c.number ? <span style={{ color: '#a0aec0' }}> � {c.number}</span> : null}
{c.email ? <div style={{ color: '#a0aec0', fontSize: '12px' }}>{c.email}</div> : null}
</div>
<button className="btn btn-green" style={{ margin: 0 }} disabled={saving} onClick={() => saveMapping(c.id)}>
@@ -248,7 +257,7 @@ function InvoiceList({ ticket, clientId, invoices, loading, error, createInvoice
</thead>
<tbody>
{invoices.map((inv) => {
const st = INVOICE_STATUS[inv.status_id] || { label: '—', color: '#a0aec0' }
const st = INVOICE_STATUS[inv.status_id] || { label: '�', color: '#a0aec0' }
const overdue = Number(inv.balance) > 0 && inv.due_date && new Date(inv.due_date) < new Date()
return (
<tr key={inv.id}>