'use client'; import { useEffect, useState, useCallback, useRef } from 'react'; import { DateTime } from 'luxon'; interface DataItem { day: string; status: boolean; einheit: number; } interface Schema { curdate: string; months: string[]; years: string[]; data: DataItem[]; einheit: number; } export interface SysParams { testing: boolean; doinit: boolean; einheit: number; version: string; date: string; } interface Props { sysParams: SysParams; } function CellContent({ day, einheit }: { day: DateTime; einheit: number }) { return (
{day.toFormat('d')}
{day.setLocale('de').toFormat('ccc')}
{einheit !== 0 ? einheit : ''}
); } function buildMonthsLabel(s: Schema): string { let months = s.months.join(' - '); months += ' '; months += s.years.join('/'); return months; } export default function SpritzClient({ sysParams }: Props) { const [schema, setSchema] = useState(null); const [curEinheit, setCurEinheit] = useState(sysParams.einheit); const [monthsLabel, setMonthsLabel] = useState(''); const [einheitInput, setEinheitInput] = useState(sysParams.einheit); const schemaRef = useRef(null); const curEinheitRef = useRef(sysParams.einheit); schemaRef.current = schema; curEinheitRef.current = curEinheit; const apiUrl = sysParams.testing ? '/api/data?test=true' : '/api/data'; async function fetchData(): Promise<{ data: Schema | null; err: string | null }> { const res = await fetch(apiUrl); return res.json(); } async function storeData(data: Schema): Promise { const response = await fetch(apiUrl, { method: 'POST', body: JSON.stringify(data), headers: { 'Content-Type': 'application/json' }, }); return response.json(); } async function initSchema(startdate: string): Promise { const setArray: DataItem[] = []; const monthArray: string[] = []; const yearsArray: string[] = []; const ld0 = DateTime.fromISO(startdate); let k = 0; for (let i = 0; i < 35; i++) { const elem: DataItem = { status: false, einheit: 0, day: '' }; if (i === 17) { elem.day = ''; } else { const ld = ld0.plus({ day: k }); elem.day = ld.toFormat('y-LL-dd'); const month = ld.setLocale('de').toFormat('LLLL'); const year = ld.toFormat('y'); if (!monthArray.includes(month)) monthArray.push(month); if (!yearsArray.includes(year)) yearsArray.push(year); k++; } setArray.push(elem); } const newSchema: Schema = { curdate: startdate, months: monthArray, years: yearsArray, data: setArray, einheit: curEinheitRef.current, }; await storeData(newSchema); return newSchema; } function applySchema(s: Schema) { // Update einheit input to last non-zero einheit found in data let lastEinheit = s.einheit; for (const item of s.data) { if (item.einheit !== 0) lastEinheit = item.einheit; } if (lastEinheit !== 0 && curEinheitRef.current === 0) { setCurEinheit(lastEinheit); curEinheitRef.current = lastEinheit; setEinheitInput(lastEinheit); } setMonthsLabel(buildMonthsLabel(s)); setSchema(s); } useEffect(() => { async function init() { let s: Schema | null = null; if (sysParams.doinit) { s = await initSchema('2023-05-01'); } if (!s) { const ret = await fetchData(); s = ret.data; } if (!s) { // DB leer → neues Schema mit aktuellem Datum anlegen const today = DateTime.now().toFormat('y-LL-dd'); s = await initSchema(today); } if (s) { applySchema(s); } } init(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const handleClick = useCallback(async (e: React.MouseEvent) => { const button = (e.target as HTMLElement).closest('button') as HTMLButtonElement | null; if (!button || button.disabled) return; const currentSchema = schemaRef.current; if (!currentSchema) return; const idNum = parseInt(button.id.slice(2)) - 1; // 0-basierter Index const lastDay = currentSchema.data[34].day; const newSchema: Schema = { ...currentSchema, data: currentSchema.data.map((item, idx) => { if (idx === idNum) { return { ...item, status: !item.status, einheit: curEinheitRef.current }; } return item; }), }; await storeData(newSchema); applySchema(newSchema); // Letzten Button (bt35) geklickt → nächste Periode anlegen if (button.id === 'bt35') { const nextDate = DateTime.fromISO(lastDay).plus({ day: 1 }).toFormat('y-LL-dd'); const nextSchema = await initSchema(nextDate); applySchema(nextSchema); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const handleEinheitChange = useCallback((e: React.ChangeEvent) => { const val = parseInt(e.target.value) || 0; setCurEinheit(val); curEinheitRef.current = val; setEinheitInput(val); if (schemaRef.current) { schemaRef.current = { ...schemaRef.current, einheit: val }; } }, []); if (!schema) { return
Lade…
; } return (

Spritz-Tabelle

{monthsLabel}

{schema.data.map((item, idx) => { const btId = `bt${idx + 1}`; const day = item.day ? DateTime.fromISO(item.day) : null; const isDisabled = item.day === ''; const ariaLabel = isDisabled ? 'o' : item.status ? 'x' : ''; const displayEinheit = item.einheit !== 0 ? item.einheit : (item.status ? schema.einheit : 0); return ( ); })}
); }