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:
1 parent
062d992b63
commit
34c4db0228
11 files changed
+6
-437
No files matched your search
@@ -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,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>
|
||||
)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user