Files
tickte-system/src/components/finance/LiquidityHead.jsx
T

92 lines
3.3 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useId, useState } from 'react'
import { Stat } from '../ui'
import { euroWhole } from '../../lib/financeFormat'
import { breakdownRows, lowPointLines } from '../../lib/financeView'
import { isNegText } from '../../lib/financeUi'
const isNum = (v) => typeof v === 'number' && Number.isFinite(v)
/** Block 1: Kontostand, „Verfügbar nach Rücklagen“ (mit Aufschlüsselung) und „Tiefster Stand in 30 Tagen“. */
export default function LiquidityHead({ kopf }) {
const k = kopf && typeof kopf === 'object' ? kopf : {}
const [open, setOpen] = useState(false)
const listId = useId()
const hasBalance = isNum(k.kontostandCents)
const shortfall = isNum(k.verfuegbarCents) && k.verfuegbarCents < 0
const lows = lowPointLines(k)
const balanceText = euroWhole(k.kontostandCents)
return (
<div className="stats fin-head">
<Stat
label="Kontostand"
value={balanceText}
tone={isNegText(balanceText) ? 'neg' : ''}
sub={hasBalance ? 'gebucht' : 'Noch kein Kontostand abgerufen'}
/>
<Stat
label="Verfügbar nach Rücklagen"
value={euroWhole(k.verfuegbarCents)}
tone={shortfall ? 'neg' : ''}
sub={
<>
{shortfall && <div className="fin-flag">Rücklagen nicht gedeckt</div>}
{isNum(k.verfuegbarCents) && (
<>
<button
type="button"
className="link"
aria-expanded={open}
aria-controls={listId}
onClick={() => setOpen((v) => !v)}
>
Aufschlüsselung <span aria-hidden="true">{open ? '▴' : '▾'}</span>
</button>
{open && (
<ul id={listId} className="fin-breakdown">
{breakdownRows(k.aufschluesselung, k.verfuegbarCents).map((row) => (
<li key={row.key} className={row.total ? 'fin-breakdown-total' : undefined}>
<span>{row.label}</span>
<span className="fin-amount">{row.text}</span>
</li>
))}
</ul>
)}
</>
)}
{!isNum(k.verfuegbarCents) && <div>Ohne Kontostand nicht berechenbar</div>}
</>
}
/>
<Stat
label="Tiefster Stand in 30 Tagen"
value={
lows.some(Boolean) ? (
<div className="fin-lows">
{lows.map(
(low) =>
low && (
<div key={low.key} className="fin-low">
<span className="fin-low-label">{low.label}:</span>
<span className={`fin-amount${low.negative ? ' fin-neg' : ''}`}>{low.amountText}</span>
<span className="fin-low-when">
am {low.dateText}
{low.negative && <span className="fin-flag"> · im Minus</span>}
</span>
</div>
)
)}
</div>
) : (
'–'
)
}
sub={lows.some(Boolean) ? '„erwartet“ zählt offene Rechnungen und Abos dazu' : 'Ohne Kontostand nicht berechenbar'}
/>
</div>
)
}