Kunden: Reiter "Abgesagt", Absage eintragen und Warnung vor Doppelten

- Kundenliste mit Reiter "Abgesagt" (vorher nur unter "Alle"), rotes Abzeichen,
  Suche auch nach Telefon
- Kundenseite: "Absage eintragen" fuer Leads (Grund freiwillig); Vermerk
  "Abgesagt am TT.MM.JJJJ (Name): Grund" oben in die Notizen, offene
  Akquise-Tickets auf "Abgesagt"; roter Hinweis "nicht erneut anrufen" und
  "Wieder als Lead fuehren"
- Neuer Lead (jetzt mit Telefon) und Neuer Kunde gleichen live mit allen
  Kunden ab (auch archivierte/abgesagte): Name ohne Rechtsform, E-Mail,
  Firmen-Domain (ohne Freemailer), Telefon; Abgesagte rot und zuerst,
  Knopf heisst dann "Trotzdem anlegen"
- Uebersicht wird nach Speichern neu aufgebaut (key), damit kein alter
  Notizstand den Vermerk ueberschreibt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-06 16:01:21 +00:00
1 parent 5d8130e8ef
commit d2883018fc
10 files changed
+378 -23

No files matched your search

+27 -8
View File
@@ -5,6 +5,8 @@ import { useAuth } from '../context/AuthContext'
import { createLead } from '../lib/leads'
import { CUSTOMER_TABS, customerBadges, customerTitle, filterCustomers, stageCounts } from '../lib/customerList'
import { isRowActivation } from '../lib/rowClick'
import { findDuplicates } from '../lib/leadDuplicates'
import DuplicateWarning from '../components/customer/DuplicateWarning'
import { PageHeader, Tabs, Button, Badge, EmptyState, Field, Dialog } from '../components/ui'
export default function CustomersPage() {
@@ -13,7 +15,7 @@ export default function CustomersPage() {
const navigate = useNavigate()
const [search, setSearch] = useState('')
// 105 von 110 Eintraegen sind Leads der Lead-Automatik, deshalb startet die Liste bei den festen Kunden
const [stage, setStage] = useState('customer') // customer | lead | archived | all
const [stage, setStage] = useState('customer') // customer | lead | lost | archived | all
const [showCreate, setShowCreate] = useState(null) // null | 'lead' | 'customer'
const filtered = useMemo(() => filterCustomers(customers, { stage, search }), [customers, stage, search])
@@ -91,7 +93,7 @@ export default function CustomersPage() {
<input
className="input search"
type="search"
placeholder="Kunde suchen …"
placeholder="Name, E-Mail oder Telefon …"
aria-label="Kunde suchen"
value={search}
onChange={(e) => setSearch(e.target.value)}
@@ -102,6 +104,7 @@ export default function CustomersPage() {
{showCreate === 'lead' && (
<CreateLeadForm
customers={customers}
onCancel={() => setShowCreate(null)}
onDone={async (data) => {
const r = await createLead(data, user)
@@ -114,6 +117,7 @@ export default function CustomersPage() {
{showCreate === 'customer' && isAdmin && (
<CreateCustomerForm
customers={customers}
onCancel={() => setShowCreate(null)}
onCreate={async (data) => {
const r = await createCustomer(data)
@@ -130,18 +134,21 @@ export default function CustomersPage() {
)
}
function CreateLeadForm({ onCancel, onDone }) {
function CreateLeadForm({ customers, onCancel, onDone }) {
const [company, setCompany] = useState('')
const [email, setEmail] = useState('')
const [phone, setPhone] = useState('')
const [busy, setBusy] = useState(false)
const [error, setError] = useState(null)
// Abgleich mit allen Kunden (auch archivierten und abgesagten), damit niemand doppelt angerufen wird
const matches = useMemo(() => findDuplicates(customers, { name: company, email, phone }), [customers, company, email, phone])
const submit = async (e) => {
e.preventDefault()
if (!company.trim()) { setError('Unternehmen erforderlich'); return }
setBusy(true); setError(null)
try {
await onDone({ company: company.trim(), email: email.trim() })
await onDone({ company: company.trim(), email: email.trim(), phone: phone.trim() })
} catch (err) {
setError(err.message || 'Fehler')
setBusy(false)
@@ -151,6 +158,7 @@ function CreateLeadForm({ onCancel, onDone }) {
return (
<Dialog
as="form"
wide
title="Neuer Lead (potenzieller Kunde)"
busy={busy}
onClose={onCancel}
@@ -159,31 +167,41 @@ function CreateLeadForm({ onCancel, onDone }) {
<>
<span className="grow" />
<Button type="button" disabled={busy} onClick={onCancel}>Abbrechen</Button>
<Button variant="primary" type="submit" disabled={busy}>{busy ? 'Speichert …' : 'Lead anlegen + Akquise-Ticket'}</Button>
<Button variant="primary" type="submit" disabled={busy}>
{busy ? 'Speichert …' : matches.length ? 'Trotzdem anlegen' : 'Lead anlegen + Akquise-Ticket'}
</Button>
</>
}
>
{error && <div className="msg err" role="alert">{error}</div>}
<p className="muted small">
Fuer einen Lead reichen Unternehmen und E-Mail. Es wird automatisch ein Akquise-Ticket erstellt.
Fuer einen Lead reichen Unternehmen und E-Mail oder Telefon. Es wird automatisch ein Akquise-Ticket erstellt.
</p>
<div className="field-row">
<DuplicateWarning matches={matches} />
<div className="field-row-3">
<Field label="Unternehmen" htmlFor="lead-company">
<input id="lead-company" className="input" value={company} onChange={(e) => setCompany(e.target.value)} required />
</Field>
<Field label="E-Mail" htmlFor="lead-email">
<input id="lead-email" className="input" type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
</Field>
<Field label="Telefon" htmlFor="lead-phone">
<input id="lead-phone" className="input" type="tel" value={phone} onChange={(e) => setPhone(e.target.value)} />
</Field>
</div>
</Dialog>
)
}
// Fester Kunde mit Zugang zum Kundenportal (nur Admins; legt den Login im Portal an)
export function CreateCustomerForm({ onCancel, onCreate }) {
export function CreateCustomerForm({ customers, onCancel, onCreate }) {
const [form, setForm] = useState({ name: '', email: '', code: '', phone: '', location: '', password: '' })
const [busy, setBusy] = useState(false)
const [error, setError] = useState(null)
const matches = useMemo(
() => findDuplicates(customers, { name: form.name, email: form.email, phone: form.phone }),
[customers, form.name, form.email, form.phone]
)
const set = (field) => (e) => setForm((f) => ({ ...f, [field]: e.target.value }))
const submit = async (e) => {
@@ -214,6 +232,7 @@ export function CreateCustomerForm({ onCancel, onCreate }) {
}
>
{error && <div className="msg err" role="alert">{error}</div>}
<DuplicateWarning matches={matches} />
<div className="field-row-3">
<Field label="Unternehmen / Name" htmlFor="customer-name">
<input id="customer-name" className="input" name="name" value={form.name} onChange={set('name')} required />