Finanzen nur noch anzeigen, Dashboard und Admin-Konfiguration entfernt (Paket D)
- Finanzen: Liste, Zahlen, PDF und Link zu InvoiceNinja bleiben. Das Formular "Rechnung erstellen" (auf der Seite und als "Rechnung aus Ticket erstellen" im Ticket) ist weg; Rechnungen entstehen direkt in InvoiceNinja. Das Verknüpfen von Kunden mit InvoiceNinja bleibt. - Filter "Bezahlt" repariert: InvoiceNinja liefert status_id als Text. - FinancePage.jsx und InvoicePanel.jsx waren Windows-1252 kodiert und zeigten live "�"; jetzt UTF-8, dazu Umlaute in den Texten. - Dashboard entfernt (doppelte Finanzzahlen, "Offen" zählte falsch); alte Links führen zu den Tickets. - Admin: Reiter "Konfiguration" (nur noch Prioritäten, die seit Paket B niemand liest) mit useAdminConfig entfernt; Admin startet bei Kunden. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
34c4db0228
commit
1a8604f7e6
12 files changed
+40
-617
No files matched your search
+3
-121
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
import {
|
||||
FaFloppyDisk,
|
||||
FaPlus,
|
||||
@@ -11,22 +11,10 @@ import CopyableCredential, {
|
||||
getCustomerPortalPassword,
|
||||
} from '../components/CopyableCredential'
|
||||
import { useAuth } from '../context/AuthContext'
|
||||
import { useAdminConfig } from '../hooks/useAdminConfig'
|
||||
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: [],
|
||||
responseLevels: [],
|
||||
serviceTypes: [],
|
||||
priorities: [],
|
||||
}
|
||||
|
||||
const EMPTY_CUSTOMER = {
|
||||
code: '',
|
||||
name: '',
|
||||
@@ -51,7 +39,6 @@ function Notice({ notice }) {
|
||||
|
||||
export default function AdminPage() {
|
||||
const { user, isAdmin } = useAuth()
|
||||
const { config, loading, error, updateConfig } = useAdminConfig()
|
||||
const {
|
||||
customers,
|
||||
loading: customersLoading,
|
||||
@@ -68,9 +55,7 @@ export default function AdminPage() {
|
||||
deleteEmployee,
|
||||
} = useEmployees()
|
||||
|
||||
const [activeSection, setActiveSection] = useState('config')
|
||||
const [localConfig, setLocalConfig] = useState(EMPTY_CONFIG)
|
||||
const [savingConfig, setSavingConfig] = useState(false)
|
||||
const [activeSection, setActiveSection] = useState('customers')
|
||||
const [customerForm, setCustomerForm] = useState(EMPTY_CUSTOMER)
|
||||
const [editingCustomer, setEditingCustomer] = useState(null)
|
||||
const [customerBusy, setCustomerBusy] = useState(false)
|
||||
@@ -80,12 +65,6 @@ export default function AdminPage() {
|
||||
const [createdCredential, setCreatedCredential] = useState(null)
|
||||
const [notice, setNotice] = useState(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (config && Object.keys(config).length > 0) {
|
||||
setLocalConfig({ ...EMPTY_CONFIG, ...config })
|
||||
}
|
||||
}, [config])
|
||||
|
||||
const showNotice = (type, text) => {
|
||||
setNotice({ type, text })
|
||||
window.setTimeout(() => setNotice(null), 4500)
|
||||
@@ -103,45 +82,6 @@ export default function AdminPage() {
|
||||
)
|
||||
}
|
||||
|
||||
const addConfigItem = (field) => {
|
||||
setLocalConfig((current) => ({
|
||||
...current,
|
||||
[field]: [
|
||||
...(current[field] || []),
|
||||
field === 'priorities'
|
||||
? { value: current.priorities?.length || 0, label: 'Neue Priorität' }
|
||||
: '',
|
||||
],
|
||||
}))
|
||||
}
|
||||
|
||||
const removeConfigItem = (field, index) => {
|
||||
setLocalConfig((current) => ({
|
||||
...current,
|
||||
[field]: (current[field] || []).filter((_, itemIndex) => itemIndex !== index),
|
||||
}))
|
||||
}
|
||||
|
||||
const updateConfigItem = (field, index, value) => {
|
||||
setLocalConfig((current) => {
|
||||
const next = [...(current[field] || [])]
|
||||
next[index] = value
|
||||
return { ...current, [field]: next }
|
||||
})
|
||||
}
|
||||
|
||||
const saveConfiguration = async () => {
|
||||
setSavingConfig(true)
|
||||
const result = await updateConfig(localConfig)
|
||||
setSavingConfig(false)
|
||||
showNotice(
|
||||
result.success ? 'success' : 'error',
|
||||
result.success
|
||||
? 'Konfiguration gespeichert.'
|
||||
: `Speichern fehlgeschlagen: ${result.error || 'Unbekannter Fehler'}`
|
||||
)
|
||||
}
|
||||
|
||||
const resetCustomerForm = () => {
|
||||
setCustomerForm(EMPTY_CUSTOMER)
|
||||
setEditingCustomer(null)
|
||||
@@ -261,7 +201,7 @@ export default function AdminPage() {
|
||||
<div>
|
||||
<span className="admin-eyebrow">Systemverwaltung</span>
|
||||
<h1>Admin</h1>
|
||||
<p>Konfiguration, Kundenkonten und Mitarbeiterzugänge zentral verwalten.</p>
|
||||
<p>Kundenkonten und Mitarbeiterzugänge verwalten.</p>
|
||||
</div>
|
||||
<div className="admin-current-user">
|
||||
<span>Angemeldet als</span>
|
||||
@@ -270,11 +210,9 @@ export default function AdminPage() {
|
||||
</header>
|
||||
|
||||
<Notice notice={notice} />
|
||||
{error && <Notice notice={{ type: 'error', text: error }} />}
|
||||
|
||||
<nav className="admin-tabs" aria-label="Admin-Bereiche">
|
||||
{[
|
||||
['config', 'Konfiguration'],
|
||||
['customers', `Kunden (${customers.length})`],
|
||||
['employees', `Mitarbeiter (${employees.length})`],
|
||||
].map(([id, label]) => (
|
||||
@@ -289,62 +227,6 @@ export default function AdminPage() {
|
||||
))}
|
||||
</nav>
|
||||
|
||||
{activeSection === 'config' && (
|
||||
<section>
|
||||
{loading && !config ? (
|
||||
<div className="admin-loading"><FaSpinner className="spinner" /> Konfiguration wird geladen …</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="admin-config-grid">
|
||||
<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>
|
||||
<button type="button" className="admin-icon-button" onClick={() => addConfigItem('priorities')}>
|
||||
<FaPlus /><span>Hinzufügen</span>
|
||||
</button>
|
||||
</div>
|
||||
<div className="admin-config-list">
|
||||
{(localConfig.priorities || []).map((priority, index) => (
|
||||
<div className="admin-priority-row" key={`priority-${index}`}>
|
||||
<input
|
||||
type="number"
|
||||
className="form-control"
|
||||
value={priority.value}
|
||||
aria-label="Prioritätswert"
|
||||
onChange={(event) => updateConfigItem('priorities', index, {
|
||||
...priority,
|
||||
value: Number(event.target.value),
|
||||
})}
|
||||
/>
|
||||
<input
|
||||
className="form-control"
|
||||
value={priority.label}
|
||||
aria-label="Prioritätsname"
|
||||
onChange={(event) => updateConfigItem('priorities', index, {
|
||||
...priority,
|
||||
label: event.target.value,
|
||||
})}
|
||||
/>
|
||||
<button type="button" className="admin-icon-button danger" onClick={() => removeConfigItem('priorities', index)}>
|
||||
<FaTrash />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div className="admin-savebar">
|
||||
<span>Änderungen gelten für neue und bestehende Ticketformulare.</span>
|
||||
<button type="button" className="admin-primary-button" disabled={savingConfig} onClick={saveConfiguration}>
|
||||
{savingConfig ? <FaSpinner className="spin" /> : <FaFloppyDisk />}
|
||||
Konfiguration speichern
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
)}
|
||||
|
||||
{activeSection === 'customers' && (
|
||||
<section className="admin-split-layout">
|
||||
<form className="admin-form-card" onSubmit={submitCustomer}>
|
||||
|
||||
Reference in new issue
Block a user