// Home / Dashboard - "Startseite mit großen Aktionen"
function Home({ onNav, accent, density }) {
const [stats, setStats] = React.useState(null);
const [schuljahresBeginn, setSchuljahresBeginn] = React.useState(null);
const [unversandt, setUnversandt] = React.useState([]);
React.useEffect(() => {
window.api.dashboard().then(setStats).catch(console.error);
window.api.einstellungen.get().then(e => {
if (e.schuljahr_naechster_beginn) setSchuljahresBeginn(e.schuljahr_naechster_beginn);
}).catch(console.error);
window.api.buchhaltung.unversandt().then(res => setUnversandt(res.items || [])).catch(console.error);
}, []);
const ActionCard = ({ id, icon, title, subtitle, tone }) => (
);
const Stat = ({ label, value, hint, hintTone }) => (
{label}
{value}
{hint &&
{hint}
}
);
if (!stats) {
return Lade Dashboard...
;
}
const heute = new Date();
heute.setHours(0, 0, 0, 0);
const terminDatum = schuljahresBeginn ? new Date(schuljahresBeginn) : null;
const tageVerbleibend = terminDatum ? Math.ceil((terminDatum - heute) / 86400000) : null;
const terminIstHeute = tageVerbleibend === 0;
const terminAbgelaufen = tageVerbleibend !== null && tageVerbleibend < 0;
const hour = new Date().getHours();
let greeting = "Guten Morgen";
if (hour >= 12 && hour < 18) greeting = "Guten Tag";
else if (hour >= 18) greeting = "Guten Abend";
return (
{new Date().toLocaleDateString('de-DE', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}
{greeting}.
Was möchten Sie als Nächstes erledigen?
{/* Big action cards */}
{/* Stats strip */}
{/* Recent activity & Infos */}
{stats.letzte_vorgaenge.length === 0 ? (
Noch keine Vorgänge vorhanden.
) : stats.letzte_vorgaenge.map((v, i) => {
const typMeta = {
rechnung: { tone: 'blue', icon: 'invoice', label: 'Rechnung' },
gutschrift: { tone: 'green', icon: 'return', label: 'Gutschrift' },
zahlung: { tone: 'slate', icon: 'check', label: 'Zahlung' },
auszahlung: { tone: 'orange', icon: 'arrow-right', label: 'Auszahlung' },
}[v.typ] || { tone: 'slate', icon: 'circle', label: v.typ };
const betrag = v.betrag_cents / 100;
const betragFarbe = betrag >= 0 ? '#047857' : '#0f172a';
const erstellt = new Date(v.erstellt_am.replace(' ', 'T') + 'Z');
const jetzt = new Date();
const diffMin = Math.floor((jetzt - erstellt) / 60000);
let zeitLabel;
if (diffMin < 1) zeitLabel = 'Gerade eben';
else if (diffMin < 60) zeitLabel = `Vor ${diffMin} Min.`;
else if (diffMin < 1440) zeitLabel = `Vor ${Math.floor(diffMin / 60)} Std.`;
else zeitLabel = erstellt.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: '2-digit', hour: '2-digit', minute: '2-digit' });
return (
{typMeta.label}
{v.schueler_name}
{v.typ !== 'zahlung' && v.typ !== 'auszahlung' && (
<>
{v.id}
·
>
)}
{zeitLabel}
{betrag >= 0 ? '+' : ''}{betrag.toFixed(2).replace('.', ',')} €
);
})}
0 ? '#b45309' : '#94a3b8' }}>
Unversandte Rechnungen
{unversandt.length === 0 ? (
Alle Rechnungen wurden bereits versandt.
) : (
<>
{unversandt.length}
{unversandt.length === 1 ? 'Rechnung wartet' : 'Rechnungen warten'} auf den Versand per E-Mail.
{unversandt.slice(0, 3).map(r => (
{r.id}
{r.schueler_name}
{(r.zu_zahlen_cents / 100).toFixed(2).replace('.', ',')} €
))}
{unversandt.length > 3 && (
… und {unversandt.length - 3} weitere
)}
>
)}
{tageVerbleibend === null ? (
Kein Termin konfiguriert.{' '}
In den Einstellungen unter „Nächster Schuljahresbeginn" eintragen.
) : terminIstHeute ? (
Heute ist der erste Tag des neuen Schuljahres!
Jetzt Klassenversetzung durchführen.
) : terminAbgelaufen ? (
Schuljahresbeginn war vor{' '}
{Math.abs(tageVerbleibend)} {Math.abs(tageVerbleibend) === 1 ? 'Tag' : 'Tagen'}.
Versetzung steht noch aus.
) : (
Noch{' '}
{tageVerbleibend} {tageVerbleibend === 1 ? 'Tag' : 'Tage'}
{' '}
bis zum{' '}
{terminDatum.toLocaleDateString('de-DE', { day: '2-digit', month: 'long', year: 'numeric' })}.
)}
);
}
window.Home = Home;