Ticketkarte und Admin an das neue Formular angepasst

- Ticketkarte: Response-Level-Auswahl entfernt; Service Type als
  "Fernwartung" / "Vor Ort" (altes "Off Site" zählt als Fernwartung)
- Admin > Konfiguration: Ticketarten, Systeme, Reaktions- und Leistungsarten
  ausgeblendet, da das Formular sie fest vorgibt; editierbar bleiben die
  Prioritäten, die Listen bleiben beim Speichern im Config-Dokument erhalten
- Kaputtes Trennzeichen "�" wieder "·" (Ticketkarte, Kundenliste, Kundendetails)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-09-26 19:39:13 +00:00
1 parent d86f9c07cd
commit d6becf0151
6 files changed
+25 -70

No files matched your search

+3 -55
View File
@@ -16,6 +16,9 @@ import { useCustomers } from '../hooks/useCustomers'
import { useEmployees } from '../hooks/useEmployees'
import './AdminPage.css'
// Ticketarten, Systeme, Reaktions- und Leistungsarten gibt das Ticketformular
// fest vor; sie sind hier nicht mehr editierbar, bleiben beim Speichern aber
// im Config-Dokument erhalten. Editierbar sind nur noch die Prioritaeten.
const EMPTY_CONFIG = {
ticketTypes: [],
systems: [],
@@ -41,56 +44,11 @@ const EMPTY_EMPLOYEE = {
isAdmin: false,
}
const CONFIG_GROUPS = [
{ field: 'ticketTypes', title: 'Ticketarten', placeholder: 'Neue Ticketart' },
{ field: 'systems', title: 'Betroffene Systeme', placeholder: 'Neues System' },
{ field: 'responseLevels', title: 'Reaktionsstufen', placeholder: 'Neue Stufe' },
{ field: 'serviceTypes', title: 'Leistungsarten', placeholder: 'Neue Leistungsart' },
]
function Notice({ notice }) {
if (!notice?.text) return null
return <div className={`admin-notice ${notice.type}`}>{notice.text}</div>
}
function ConfigList({ title, field, values, placeholder, onAdd, onRemove, onChange }) {
return (
<section className="admin-config-card">
<div className="admin-section-head">
<div>
<h3>{title}</h3>
<span>{values.length} Einträge</span>
</div>
<button type="button" className="admin-icon-button" onClick={() => onAdd(field)}>
<FaPlus />
<span>Hinzufügen</span>
</button>
</div>
<div className="admin-config-list">
{values.length === 0 && <p className="admin-empty-line">Noch keine Einträge.</p>}
{values.map((value, index) => (
<div className="admin-config-row" key={`${field}-${index}`}>
<input
className="form-control"
value={value}
placeholder={placeholder}
onChange={(event) => onChange(field, index, event.target.value)}
/>
<button
type="button"
className="admin-icon-button danger"
onClick={() => onRemove(field, index)}
aria-label={`${value || 'Eintrag'} entfernen`}
>
<FaTrash />
</button>
</div>
))}
</div>
</section>
)
}
export default function AdminPage() {
const { user, isAdmin } = useAuth()
const { config, loading, error, updateConfig } = useAdminConfig()
@@ -338,16 +296,6 @@ export default function AdminPage() {
) : (
<>
<div className="admin-config-grid">
{CONFIG_GROUPS.map((group) => (
<ConfigList
key={group.field}
{...group}
values={localConfig[group.field] || []}
onAdd={addConfigItem}
onRemove={removeConfigItem}
onChange={updateConfigItem}
/>
))}
<section className="admin-config-card priorities">
<div className="admin-section-head">
<div><h3>Prioritäten</h3><span>{localConfig.priorities?.length || 0} Einträge</span></div>
+2 -2
View File
@@ -68,7 +68,7 @@ export default function CustomerDetailPage() {
{archived && <Badge tone="muted"><FaBoxArchive /> Archiviert</Badge>}
</span>
}
subtitle={[customer.location, customer.email, customer.phone].filter(Boolean).join(' � ')}
subtitle={[customer.location, customer.email, customer.phone].filter(Boolean).join(' · ')}
actions={
<>
<Button variant="ghost" onClick={toggleArchived}>
@@ -305,7 +305,7 @@ function CustomerTickets({ customerId }) {
<span className="mono" style={{ color: 'var(--accent)', minWidth: 60 }}>{t.woid || t.$id.slice(-5)}</span>
<div className="grow">
<div style={{ fontWeight: 600 }}>{t.topic || t.title || '-'}</div>
<div className="muted">{t.type} � {t.systemType || 'n/a'}</div>
<div className="muted">{t.type} · {t.systemType || 'n/a'}</div>
</div>
<PriorityPill priority={t.priority} />
<StatusPill status={t.status} />
+2 -2
View File
@@ -82,9 +82,9 @@ export default function CustomersPage() {
<div style={{ fontWeight: 700, display: 'flex', alignItems: 'center', gap: 8 }}>
{c.importantCustomer && <FaStar style={{ color: 'var(--warn)' }} title="Wichtiger Kunde" />}
{c.name}
{c.code && <span className="faint" style={{ fontWeight: 400 }}>� {c.code}</span>}
{c.code && <span className="faint" style={{ fontWeight: 400 }}>· {c.code}</span>}
</div>
<div className="muted">{[c.location, c.email].filter(Boolean).join(' � ') || '-'}</div>
<div className="muted">{[c.location, c.email].filter(Boolean).join(' · ') || '-'}</div>
</div>
<div className="flex gap-2 wrap" style={{ justifyContent: 'flex-end' }}>
{isArchived(c) && <Badge tone="muted">Archiviert</Badge>}