Rechnungsdialog: Zustand mit Reducer, Appwrite-Lesezugriffe und Hooks für Projektkonto, Vorschau und weitere Tickets
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0f1dbabd98
commit
6a5f943034
4 files changed
+418
No files matched your search
@@ -0,0 +1,93 @@
|
||||
// Zustand des Rechnungsdialogs „Rechnung schreiben“ (rein, ohne React; unter Node testbar).
|
||||
// `anfangszustand` liefert den leeren Zustand, `reduce` verarbeitet die Aktionen des Dialogs. Unbekannte Aktionen,
|
||||
// unbekannte Listen und Felder sowie Aktionen ohne Wirkung geben denselben Zustand (dieselbe Referenz) zurück.
|
||||
import { INVOICE_LIMITS } from './financeView'
|
||||
|
||||
const LISTEN = ['items', 'zusatz']
|
||||
const FELDER = ['description', 'quantity', 'price']
|
||||
const MINDESTENS = { items: 1, zusatz: 0 }
|
||||
|
||||
const leereZeile = (uid) => ({ uid, description: '', quantity: '1', price: '' })
|
||||
|
||||
/** Startzustand. projektId: null = noch zu wählen, '' = Ohne Projekt, Text = dieses Projekt. */
|
||||
export function anfangszustand({ projektId = null } = {}) {
|
||||
return {
|
||||
projektId,
|
||||
art: '',
|
||||
auftragId: '',
|
||||
items: [leereZeile(1)],
|
||||
zusatz: [],
|
||||
abgewaehlt: [],
|
||||
weitereTickets: [],
|
||||
anzahlung: {},
|
||||
zeilentexte: {},
|
||||
zeitraum: '',
|
||||
zeitraumGeaendert: false,
|
||||
days: String(INVOICE_LIMITS.defaultDays),
|
||||
memo: '',
|
||||
}
|
||||
}
|
||||
|
||||
function neueUid(s) {
|
||||
return 1 + Math.max(0, ...s.items.map((z) => z.uid), ...s.zusatz.map((z) => z.uid))
|
||||
}
|
||||
|
||||
export function reduce(s, a) {
|
||||
switch (a?.type) {
|
||||
case 'projekt':
|
||||
return { ...anfangszustand({ projektId: a.projektId }), days: s.days, memo: s.memo }
|
||||
case 'art':
|
||||
return {
|
||||
...s,
|
||||
art: a.art,
|
||||
auftragId: '',
|
||||
zeilentexte: {},
|
||||
zeitraum: s.zeitraumGeaendert ? s.zeitraum : '',
|
||||
}
|
||||
case 'auftrag':
|
||||
return { ...s, auftragId: a.auftragId, anzahlung: {} }
|
||||
case 'position': {
|
||||
if (!LISTEN.includes(a.liste) || !FELDER.includes(a.feld)) return s
|
||||
return { ...s, [a.liste]: s[a.liste].map((z) => (z.uid === a.uid ? { ...z, [a.feld]: a.wert } : z)) }
|
||||
}
|
||||
case 'position+': {
|
||||
if (!LISTEN.includes(a.liste) || s[a.liste].length >= INVOICE_LIMITS.maxItems) return s
|
||||
return { ...s, [a.liste]: [...s[a.liste], leereZeile(neueUid(s))] }
|
||||
}
|
||||
case 'position-': {
|
||||
if (!LISTEN.includes(a.liste) || s[a.liste].length <= MINDESTENS[a.liste]) return s
|
||||
return { ...s, [a.liste]: s[a.liste].filter((z) => z.uid !== a.uid) }
|
||||
}
|
||||
case 'blatt':
|
||||
return { ...s, abgewaehlt: s.abgewaehlt.includes(a.id) ? s.abgewaehlt.filter((id) => id !== a.id) : [...s.abgewaehlt, a.id] }
|
||||
case 'ticket+':
|
||||
return s.weitereTickets.some((t) => t.ticketId === a.ticket.ticketId) ? s : { ...s, weitereTickets: [...s.weitereTickets, a.ticket] }
|
||||
case 'ticket-': {
|
||||
const weg = Array.isArray(a.blattIds) ? a.blattIds : []
|
||||
return {
|
||||
...s,
|
||||
weitereTickets: s.weitereTickets.filter((t) => t.ticketId !== a.ticketId),
|
||||
abgewaehlt: s.abgewaehlt.filter((id) => !weg.includes(id)),
|
||||
}
|
||||
}
|
||||
case 'anzahlung':
|
||||
return { ...s, anzahlung: { ...s.anzahlung, ...a.patch } }
|
||||
case 'zeilentext': {
|
||||
const zeilentexte = { ...s.zeilentexte }
|
||||
if (!String(a.text ?? '').trim() || a.text === a.original) delete zeilentexte[a.schluessel]
|
||||
else zeilentexte[a.schluessel] = a.text
|
||||
return { ...s, zeilentexte }
|
||||
}
|
||||
case 'zeitraum':
|
||||
return { ...s, zeitraum: a.text, zeitraumGeaendert: true }
|
||||
case 'zeitraum-vorschlag':
|
||||
if (s.zeitraumGeaendert || s.zeitraum === a.text) return s
|
||||
return { ...s, zeitraum: a.text }
|
||||
case 'tage':
|
||||
return { ...s, days: a.wert }
|
||||
case 'notiz':
|
||||
return { ...s, memo: a.wert }
|
||||
default:
|
||||
return s
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user