Files
tickte-system/src/components/auftraege/AuftraegeUndRechnungen.jsx
T

172 lines
6.1 KiB
React

import { useRef, useState } from 'react'
import { Button, Card, EmptyState } from '../ui'
import AddressWarning from '../finance/AddressWarning'
import InvoiceTable from '../finance/InvoiceTable'
import ProjektkontoListe from './ProjektkontoListe'
import RechnungDialog from './RechnungDialog'
import AuftragListe from './AuftragListe'
import AuftragDialog from './AuftragDialog'
import { useFinanceResource } from '../../hooks/useFinanceResource'
import { useScrollIntoView } from '../../hooks/useScrollIntoView'
import { getProjektkonten, listAuftraege } from '../../lib/financeApi'
import { customerInvoicesGate } from '../../lib/financeView'
import { noticeProps } from '../../lib/financeUi'
import {
KONTEN_FEHLER,
ladeAuftraegeAnsicht,
ansichtsZustand,
kundeMitKundenart,
erstelltMeldung,
auftragMeldung,
} from '../../lib/auftraegeAnsicht'
/**
* Reiter „Aufträge und Rechnungen“ am Kunden (ersetzt CustomerInvoices). Kann für den Kunden keine Rechnung geschrieben
* werden (noch Lead, abgesagt, keine E-Mail), steht der Grund da, mit „Daten erfassen“; es werden keine Abrufe gestartet.
* Sonst von oben nach unten: Adresswarnung, Rückmeldung, Hinweis zum Neuladen, Projekte (Projektkonten), Aufträge (nur
* mit eingeschaltetem Schalter AUFTRAEGE_ENABLED) und die Rechnungsliste. Die Rechnungsliste und die Adresswarnung hängen
* nie vom Laden der Projekte oder Aufträge ab: fällt eines davon aus, bleibt „Rechnung schreiben“ bedienbar.
* Die Dialoge stehen als Geschwister hinter den Abschnitten (nie in InvoiceTable oder .fin-body).
*/
export default function AuftraegeUndRechnungen({ customer, onShowOverview }) {
const gate = customerInvoicesGate(customer)
if (!gate.ok) {
return (
<EmptyState
title={gate.title}
hint={gate.hint}
action={onShowOverview ? <Button variant="primary" onClick={onShowOverview}>Daten erfassen</Button> : null}
/>
)
}
return <AuftraegeInhalt customer={customer} onShowOverview={onShowOverview} />
}
function AuftraegeInhalt({ customer, onShowOverview }) {
const res = useFinanceResource(() => ladeAuftraegeAnsicht(customer.$id, { getProjektkonten, listAuftraege }), KONTEN_FEHLER)
const ansicht = ansichtsZustand(res)
const reload = res.reload
const [meldung, setMeldung] = useState(null) // { tone, text } oder null
const [rechnungsDialog, setRechnungsDialog] = useState(null) // { projektId } oder null
const [auftragsDialog, setAuftragsDialog] = useState(null) // { projektId, auftrag } oder null
const [listeToken, setListeToken] = useState(0)
const meldungRef = useRef(null)
useScrollIntoView(meldungRef, meldung)
// Die Dialoge bekommen die frische Kundenart des Portals (die Kundenseite hält ggf. noch den alten Datensatz)
const kunde = kundeMitKundenart(customer, ansicht.kunde)
function openRechnung(projektId) {
setMeldung(null)
setRechnungsDialog({ projektId })
}
function openAuftrag(projektId, auftrag = null) {
setMeldung(null)
setAuftragsDialog({ projektId, auftrag })
}
function handleCreated(rechnung, info) {
setRechnungsDialog(null)
setMeldung(erstelltMeldung(rechnung, info))
setListeToken((n) => n + 1)
reload()
}
function handleSaved(auftrag) {
setAuftragsDialog(null)
setMeldung(auftragMeldung(auftrag))
reload()
}
return (
<>
<AddressWarning customer={customer} onShowOverview={onShowOverview} variant="page" />
{meldung && (
<p ref={meldungRef} {...noticeProps(meldung.tone, 'fin-notice')}>
{meldung.text}
</p>
)}
{ansicht.projekteStatus === 'bereit' && ansicht.nachladeFehler && (
<p {...noticeProps('warn', 'fin-notice')}>
Die Übersicht konnte nicht neu geladen werden: {ansicht.nachladeFehler}{' '}
<button type="button" className="link" onClick={reload}>
Erneut versuchen
</button>
</p>
)}
{ansicht.projekteStatus === 'laden' && (
<Card title="Projekte">
<div className="loading" role="status">Projekte werden geladen …</div>
</Card>
)}
{ansicht.projekteStatus === 'fehler' && (
<Card title="Projekte">
<div className="fin-load-error">
<p className="msg warn" role="alert">{ansicht.fehler}</p>
<Button onClick={reload}>Erneut versuchen</Button>
</div>
</Card>
)}
{ansicht.projekteStatus === 'bereit' && (
<ProjektkontoListe
konten={ansicht.konten}
ohneProjekt={ansicht.ohneProjekt}
auftraegeAktiv={ansicht.auftraegeAktiv}
stripeOk={ansicht.stripeOk}
onRechnung={openRechnung}
onAuftrag={(projektId) => openAuftrag(projektId)}
/>
)}
{ansicht.auftraegeStatus === 'bereit' && (
<AuftragListe
auftraege={ansicht.auftraege}
konten={ansicht.konten}
onChanged={reload}
onBearbeiten={(auftrag) => openAuftrag(auftrag.projektId || null, auftrag)}
onShowOverview={onShowOverview}
/>
)}
{ansicht.auftraegeStatus === 'fehler' && (
<Card title="Aufträge">
<div className="fin-load-error">
<p className="msg warn" role="alert">{ansicht.auftraegeFehler}</p>
<Button onClick={reload}>Erneut versuchen</Button>
</div>
</Card>
)}
<InvoiceTable customer={customer} onShowOverview={onShowOverview} refreshToken={listeToken} onChanged={reload} />
{rechnungsDialog && (
<RechnungDialog
open
onClose={() => setRechnungsDialog(null)}
onCreated={handleCreated}
kunde={kunde}
projektId={rechnungsDialog.projektId}
auftraegeAktiv={ansicht.auftraegeAktiv}
onShowOverview={onShowOverview}
/>
)}
{auftragsDialog && (
<AuftragDialog
open
onClose={() => setAuftragsDialog(null)}
onSaved={handleSaved}
kunde={kunde}
projektId={auftragsDialog.projektId}
auftrag={auftragsDialog.auftrag}
konten={ansicht.konten}
onShowOverview={onShowOverview}
/>
)}
</>
)
}