patch({ customerId: c.$id, ticketId: '', newTicket: null })}>
-
-
{c.name}{c.code && · {c.code} }
-
{[c.location, c.email].filter(Boolean).join(' · ') || '-'}
-
- {state.customerId === c.$id &&
}
-
+ {state.step === 'kind' && (
+
+ {kinds.map((k) => (
+
patch({ kind: k.key, newTicket: null, ticketId: '' })}
+ >
+ {k.label}
+
))}
- {filteredCustomers.length === 0 &&
Kein Kunde gefunden.
}
- >
- )}
+ )}
- {state.step === 'ticket' && (
- <>
- {ticketsLoading ?
: ticketsError ? (
-
-
Die Tickets des Kunden konnten nicht geladen werden.
-
setTicketsReload((n) => n + 1)}>Erneut versuchen
-
- ) : (
-
- {openTickets.map((t) => (
-
patch({ ticketId: t.$id, newTicket: null })}>
-
-
#{t.woid} {t.topic}
-
{[t.type, t.status].filter(Boolean).join(' · ')}
-
- {state.ticketId === t.$id &&
}
-
- ))}
- {openTickets.length === 0 &&
Dieser Kunde hat keine offenen Tickets.
}
-
- )}
- {!ticketsError && !ticketsLoading && (
-
Neues Ticket anlegen
- )}
- {state.newTicket && (
-
- )}
- >
- )}
+ {state.step === 'customer?' && (
+
+ { patch({ forCustomer: true }); go('customer') }}>Ja, für einen Kunden
+ { patch({ forCustomer: false, customerId: '', ticketId: '', newTicket: null }); go('summary') }}>Nein, internes Projekt
+
+ )}
- {state.step === 'summary' && (
- <>
-
- Art: {kindObj?.label}
- {state.forCustomer ? <>Kunde: {customer?.name} > : 'Internes Projekt (ohne Kunde)'}
- {state.forCustomer && (
-
- {state.ticketId
- ? <>Ticket: {openTickets.find((t) => t.$id === state.ticketId)?.topic || 'vorhandenes Ticket'} >
- : <>Neues Ticket: {state.newTicket?.topic} >}
-
- )}
-
- {isWebsiteWithCustomer && hasPreviewAlready && (
-
- {['deployed', 'ready'].includes(String(project.status || '').toLowerCase())
- ? <>Preview läuft bereits unter https://{project.subdomain}.project.webklar.com und bleibt unverändert.>
- : <>Preview ist unter https://{project.subdomain}.project.webklar.com eingerichtet (Status: {project.status || 'unbekannt'}). Neu bauen geht im Projekt über „Preview".>}
-
- )}
- {isWebsiteWithCustomer && !hasPreviewAlready && (
-
-
- setPreviewOn(e.target.checked)} />
- Preview einschalten
-
- {previewOn && (
-
'}.project.webklar.com`}>
- setSubdomain(e.target.value)} />
-
+ {state.step === 'customer' && (
+ <>
+
+ setSearch(e.target.value)} />
+ {isAdmin && (
+ setShowNewCustomer((s) => !s)}>Neuer Kunde
)}
+ {showNewCustomer && isAdmin && (
+
setShowNewCustomer(false)}
+ onCreate={async (data) => {
+ const r = await createCustomer(data)
+ if (r.success) {
+ setShowNewCustomer(false)
+ patch({ customerId: r.data.$id, ticketId: '', newTicket: null })
+ }
+ return r
+ }}
+ />
+ )}
+
+ {filteredCustomers.slice(0, 100).map((c) => {
+ const picked = state.customerId === c.$id
+ return (
+
patch({ customerId: c.$id, ticketId: '', newTicket: null })}>
+
+ {c.name}{c.code && · {c.code} }
+ {[c.location, c.email].filter(Boolean).join(' · ') || '-'}
+
+ {picked && Ausgewählt }
+
+ )
+ })}
+ {filteredCustomers.length === 0 &&
Kein Kunde gefunden.
}
+
+ >
+ )}
+
+ {state.step === 'ticket' && (
+ <>
+ {ticketsLoading ? Wird geladen …
: ticketsError ? (
+ <>
+ Die Tickets des Kunden konnten nicht geladen werden.
+
+ setTicketsReload((n) => n + 1)}>Erneut versuchen
+
+ >
+ ) : (
+
+ {openTickets.map((t) => {
+ const picked = state.ticketId === t.$id
+ return (
+
patch({ ticketId: t.$id, newTicket: null })}>
+
+ #{t.woid} {t.topic}
+ {[t.type, t.status].filter(Boolean).join(' · ')}
+
+ {picked && Ausgewählt }
+
+ )
+ })}
+ {openTickets.length === 0 &&
Dieser Kunde hat keine offenen Tickets.
}
+
+ )}
+ {!ticketsError && !ticketsLoading && (
+
+ Neues Ticket anlegen
+
+ )}
+ {state.newTicket && (
+ <>
+
+
+
+
+
+
+ Ticket-Art: {kindObj?.ticketType === 'Migration' ? 'Migration' : 'Webpage'} (ergibt sich aus der Art)
+ >
+ )}
+ >
+ )}
+
+ {state.step === 'summary' && (
+ <>
+
+ Art: {kindObj?.label}
+ {state.forCustomer ? <>Kunde: {customer?.name} > : 'Internes Projekt (ohne Kunde)'}
+ {state.forCustomer && (
+
+ {state.ticketId
+ ? <>Ticket: {openTickets.find((t) => t.$id === state.ticketId)?.topic || 'vorhandenes Ticket'} >
+ : <>Neues Ticket: {state.newTicket?.topic} >}
+
+ )}
+
+ {isWebsiteWithCustomer && hasPreviewAlready && (
+
+ {['deployed', 'ready'].includes(String(project.status || '').toLowerCase())
+ ? <>Preview läuft bereits unter https://{project.subdomain}.project.webklar.com und bleibt unverändert.>
+ : <>Preview ist unter https://{project.subdomain}.project.webklar.com eingerichtet (Status: {project.status || 'unbekannt'}). Neu bauen geht im Projekt über „Preview".>}
+
+ )}
+ {isWebsiteWithCustomer && !hasPreviewAlready && (
+ <>
+
+ setPreviewOn(e.target.checked)} />
+ Preview einschalten
+
+ {previewOn && (
+ '}.project.webklar.com`}>
+ setSubdomain(e.target.value)} />
+
+ )}
+ >
+ )}
+
+
+ {busy ? 'Speichert …' : 'Abschließen'}
+
+
+ >
+ )}
+
+ {error && {error}
}
+
+
+ {state.step !== 'kind' && (
+
go(prevStep(state))}>Zurück
)}
-
- {busy ? : <> Abschließen>}
-
- >
- )}
-
- {error &&
{error}
}
-
-
- {state.step !== 'kind' && (
- go(prevStep(state))}> Zurück
- )}
- {(state.step === 'kind' || state.step === 'customer' || state.step === 'ticket') && (
- go(nextStep(state))}>Weiter
- )}
+ {(state.step === 'kind' || state.step === 'customer' || state.step === 'ticket') && (
+ go(nextStep(state))}>Weiter
+ )}
+
@@ -401,5 +421,5 @@ function TicketLink({ ticketId }) {
databases.getDocument(DATABASE_ID, COLLECTIONS.WORKORDERS, ticketId)
.then((t) => setWoid(t.woid)).catch(() => setWoid(null))
}, [ticketId])
- return woid ?
Zum Ticket #{woid} : null
+ return woid ?
Zum Ticket #{woid} : null
}
diff --git a/src/styles/global.css b/src/styles/global.css
index 7853c44..04d6790 100644
--- a/src/styles/global.css
+++ b/src/styles/global.css
@@ -1183,6 +1183,11 @@ button.list-row:hover, button.list-row[aria-pressed="true"] { background: var(--
button.list-row .cell-sub { display: block; }
button.list-row:focus-visible { outline-offset: -2px; } /* in .pick-list (Scroll-Container) wuerde der Ring sonst abgeschnitten */
.summary { list-style: none; display: flex; flex-direction: column; gap: 6px; }
+/* Inhalt eines Schritts als Spalte mit Abstand (der Reset setzt jeden Aussenabstand auf 0); Segment und Haken bleiben so breit wie ihr Inhalt */
+.assign-step { display: flex; flex-direction: column; gap: 12px; }
+.assign-step > .seg, .assign-step > .check { align-self: flex-start; }
+.page .assign-step .field { margin-bottom: 0; } /* KOMPAT gibt .page .field 12 px unten; hier steht der Abstand der Spalte (P10 loescht beide) */
+button.list-row:hover .faint, button.list-row[aria-pressed="true"] .faint { color: var(--text-2); } /* .faint hat auf --bg-hover nur 2,9:1 (Muster: .table tr.row:hover .faint) */
/* ===== ENDE Projekte (P5) ===== */
/* ===== KOMPAT (bis P10) ===== */