Dokumente und Paperless aus WOMS entfernt (Paket C)

Paperless enthält 0 Dokumente, der Upload wurde nie benutzt. Weg sind:
die Seite Dokumente (alte Links führen zu den Tickets), der Menüpunkt,
der Reiter Dokumente in Ticketkarte und Kundenseite, der Upload-Dialog
(legte Dateien zusätzlich ungelesen im Appwrite-Bucket ab), das Feld
"Paperless-Korrespondent" und das Abzeichen "Docs", auf dem Dashboard
die Kachel Dokumente und "Dokument hochladen", im API-Client die
Paperless-Aufrufe. Gespeicherte Daten bleiben unverändert; die
Paperless-Routen im Backend ticket-integrations sind nicht angefasst.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-09-27 15:52:46 +00:00
1 parent 062d992b63
commit 34c4db0228
11 files changed
+6 -437

No files matched your search

-7
View File
@@ -5,7 +5,6 @@ import { databases, DATABASE_ID, COLLECTIONS, Query } from '../lib/appwrite'
import { PageHeader, Card, Button, Badge, Tabs, EmptyState, Field } from '../components/ui'
import { StatusPill, UrgentPill } from '../components/ui/StatusPill'
import InvoicePanel from '../components/InvoicePanel'
import DocumentsPanel from '../components/DocumentsPanel'
import AssignedProjectCard from '../components/AssignedProjectCard'
import CopyableCredential, { getCustomerPortalPassword } from '../components/CopyableCredential'
import { updateCustomerWithPortalAccess } from '../lib/customerAdminApi'
@@ -18,7 +17,6 @@ const TABS = [
{ id: 'overview', label: 'Uebersicht' },
{ id: 'tickets', label: 'Tickets' },
{ id: 'invoices', label: 'Rechnungen' },
{ id: 'documents', label: 'Dokumente' },
{ id: 'projects', label: 'Projekte' },
]
@@ -92,7 +90,6 @@ export default function CustomerDetailPage() {
<InvoicePanel ticket={syntheticTicket} />
)
)}
{tab === 'documents' && <DocumentsPanel ticket={syntheticTicket} initialScope="customer" />}
{tab === 'projects' && <CustomerProjects customerId={customer.$id} />}
</div>
)
@@ -114,7 +111,6 @@ function OverviewTab({ customer, stage, onSaved }) {
location: customer.location || '',
notes: customer.notes || '',
importantCustomer: !!customer.importantCustomer,
paperlessCorrespondent: customer.paperlessCorrespondent || '',
})
const [busy, setBusy] = useState(false)
const [msg, setMsg] = useState(null)
@@ -186,9 +182,6 @@ function OverviewTab({ customer, stage, onSaved }) {
<span className="muted">InvoiceNinja</span>
{customer.invoiceNinjaClientId ? <Badge tone="ok" dot>verknuepft</Badge> : <Badge tone="muted">nicht verknuepft</Badge>}
</div>
<Field label="Paperless-Korrespondent" hint="Leer = Kundenname wird verwendet">
<input className="form-control" value={form.paperlessCorrespondent} onChange={(e) => setForm({ ...form, paperlessCorrespondent: e.target.value })} placeholder={customer.name} />
</Field>
</Card>
<PortalAccessCard customer={customer} onSaved={onSaved} />
+1 -2
View File
@@ -1,6 +1,6 @@
import { useState, useMemo } from 'react'
import { Link } from 'react-router-dom'
import { FaSpinner, FaPlus, FaStar, FaFileInvoiceDollar, FaFileLines } from 'react-icons/fa6'
import { FaSpinner, FaPlus, FaStar, FaFileInvoiceDollar } from 'react-icons/fa6'
import { useCustomers } from '../hooks/useCustomers'
import { useAuth } from '../context/AuthContext'
import { createLead, customerStage, isArchived } from '../lib/leads'
@@ -92,7 +92,6 @@ export default function CustomersPage() {
{st === 'customer' && <Badge tone="ok" dot>Fester Kunde</Badge>}
{st === 'lost' && <Badge tone="muted">Abgesagt</Badge>}
{c.invoiceNinjaClientId && <Badge tone="info"><FaFileInvoiceDollar /> Ninja</Badge>}
{c.paperlessCorrespondent && <Badge tone="info"><FaFileLines /> Docs</Badge>}
</div>
</Link>
)
+3 -10
View File
@@ -1,8 +1,8 @@
import { useState, useEffect } from 'react'
import { Link } from 'react-router-dom'
import { databases, DATABASE_ID, COLLECTIONS, Query } from '../lib/appwrite'
import { FaTicket, FaClipboardList, FaClock, FaCircleCheck, FaPlus, FaFileInvoiceDollar, FaUpload } from 'react-icons/fa6'
import { integrationsApi, formatMoney } from '../lib/integrationsApi'
import { FaTicket, FaClipboardList, FaClock, FaCircleCheck, FaPlus, FaFileInvoiceDollar } from 'react-icons/fa6'
import { formatMoney } from '../lib/integrationsApi'
import { useFinanceSummary } from '../hooks/useFinanceSummary'
import { PageHeader, Card, Button, Kpi, EmptyState } from '../components/ui'
import { StatusPill, UrgentPill } from '../components/ui/StatusPill'
@@ -11,7 +11,6 @@ export default function DashboardPage() {
const { summary, loading: finLoading } = useFinanceSummary()
const [stats, setStats] = useState({ total: 0, open: 0, awaiting: 0, closed: 0 })
const [recent, setRecent] = useState([])
const [docCount, setDocCount] = useState(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
@@ -33,10 +32,6 @@ export default function DashboardPage() {
} finally {
if (active) setLoading(false)
}
try {
const d = await integrationsApi.listDocuments({ page_size: 1 })
if (active) setDocCount(d.count)
} catch { /* ignore */ }
})()
return () => { active = false }
}, [])
@@ -45,7 +40,7 @@ export default function DashboardPage() {
<div className="page">
<PageHeader
title="Dashboard"
subtitle="Ueberblick ueber Tickets, Finanzen und Dokumente"
subtitle="Überblick über Tickets und Finanzen"
actions={
<>
<Button variant="ghost" as={Link} to="/finance"><FaFileInvoiceDollar /> Finanzen</Button>
@@ -65,7 +60,6 @@ export default function DashboardPage() {
<Kpi label="Offener Betrag" value={finLoading ? '...' : formatMoney(summary?.outstanding)} sub={`${summary?.openCount ?? 0} offene Rechnungen`} accent="var(--warn)" />
<Kpi label="Ueberfaellig" value={finLoading ? '...' : formatMoney(summary?.overdue)} sub={`${summary?.overdueCount ?? 0} ueberfaellig`} accent="var(--danger)" />
<Kpi label="Bezahlt (Monat)" value={finLoading ? '...' : formatMoney(summary?.paidThisMonth)} accent="var(--ok)" />
<Kpi label="Dokumente" value={docCount ?? '...'} accent="var(--info)" />
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))', gap: 16 }}>
@@ -95,7 +89,6 @@ export default function DashboardPage() {
<div className="flex flex-col gap-2">
<Button as={Link} to="/tickets"><FaPlus /> Neues Ticket erstellen</Button>
<Button as={Link} to="/finance"><FaFileInvoiceDollar /> Rechnung erstellen</Button>
<Button as={Link} to="/documents"><FaUpload /> Dokument hochladen</Button>
<Button as={Link} to="/customers" variant="ghost">Kunden verwalten</Button>
</div>
</Card>
-111
View File
@@ -1,111 +0,0 @@
import { useState, useEffect, useCallback } from 'react'
import { FaFileLines, FaEye, FaDownload, FaSpinner, FaMagnifyingGlass, FaRotate, FaUpload } from 'react-icons/fa6'
import { integrationsApi } from '../lib/integrationsApi'
import { useCustomers } from '../hooks/useCustomers'
import { PageHeader, Card, Button, EmptyState } from '../components/ui'
import FileUploadModal from '../components/FileUploadModal'
export default function DocumentsPage() {
const { customers } = useCustomers()
const [docs, setDocs] = useState([])
const [count, setCount] = useState(0)
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
const [query, setQuery] = useState('')
const [correspondent, setCorrespondent] = useState('')
const [busyId, setBusyId] = useState(null)
const [showUpload, setShowUpload] = useState(false)
const load = useCallback(async () => {
setLoading(true)
setError(null)
try {
const params = { page_size: 50 }
if (query) params.query = query
if (correspondent) params.correspondent = correspondent
const data = await integrationsApi.listDocuments(params)
setDocs(data.documents || [])
setCount(data.count || 0)
} catch (err) {
setError(err.message)
setDocs([])
} finally {
setLoading(false)
}
}, [query, correspondent])
useEffect(() => { load() }, [load])
const open = async (id, kind) => {
setBusyId(id + kind)
try { await integrationsApi.openDocument(id, kind) }
catch (err) { alert('Fehler: ' + err.message) }
finally { setBusyId(null) }
}
return (
<div className="page">
<PageHeader
title="Dokumente"
subtitle="Alle Belege und Kundendokumente aus Paperless-ngx"
actions={
<>
<Button variant="ghost" onClick={load}><FaRotate /> Aktualisieren</Button>
<Button variant="primary" onClick={() => setShowUpload(true)}><FaUpload /> Hochladen</Button>
</>
}
/>
<Card pad={false}>
<div className="ui-card-head" style={{ gap: 12, flexWrap: 'wrap' }}>
<select className="form-control" style={{ maxWidth: 240 }} value={correspondent} onChange={(e) => setCorrespondent(e.target.value)}>
<option value="">Alle Kunden</option>
{customers.map((c) => <option key={c.$id} value={c.name}>{c.name}</option>)}
</select>
<div className="flex gap-2 grow" style={{ minWidth: 220 }}>
<input className="form-control" placeholder="Volltextsuche..." value={query} onChange={(e) => setQuery(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && load()} />
<Button variant="primary" onClick={load}><FaMagnifyingGlass /></Button>
</div>
</div>
<div className="ui-card-body" style={{ padding: 0 }}>
{loading ? (
<div className="empty"><FaSpinner className="spinner" /></div>
) : error ? (
<div className="empty text-red">{error}</div>
) : docs.length === 0 ? (
<EmptyState icon={<FaFileLines />} title="Keine Dokumente" hint="Keine Dokumente gefunden." />
) : (
<>
<div className="faint" style={{ padding: '8px 16px', fontSize: 12 }}>{count} Dokument(e)</div>
<div className="list">
{docs.map((d) => (
<div key={d.id} className="list-row">
<FaFileLines style={{ color: 'var(--accent)' }} />
<div className="grow">
<div className="text-ellipsis" style={{ fontWeight: 600 }}>{d.title}</div>
<div className="muted">{d.created ? new Date(d.created).toLocaleDateString('de-DE') : ''}</div>
</div>
<Button size="sm" variant="ghost" disabled={busyId === d.id + 'preview'} onClick={() => open(d.id, 'preview')} title="Vorschau">
{busyId === d.id + 'preview' ? <FaSpinner className="spinner" /> : <FaEye />}
</Button>
<Button size="sm" variant="ghost" disabled={busyId === d.id + 'download'} onClick={() => open(d.id, 'download')} title="Download">
{busyId === d.id + 'download' ? <FaSpinner className="spinner" /> : <FaDownload />}
</Button>
</div>
))}
</div>
</>
)}
</div>
</Card>
<FileUploadModal
isOpen={showUpload}
onClose={() => setShowUpload(false)}
workorderId=""
ticket={correspondent ? { customerName: correspondent, woid: '' } : { customerName: '', woid: '' }}
onUploadComplete={() => setTimeout(load, 1500)}
/>
</div>
)
}