shulneff/src/App.tsx
2025-05-03 13:16:13 +02:00

161 lines
4.9 KiB
TypeScript

import React, { JSX, useEffect, useState } from 'react';
import './App.css';
import { addEntry, APIEndPoint, ensureToday, getAPI, getToday, removeEntry } from './api';
import { ItemType, Store, StoreEntry } from './types';
import dayjs from 'dayjs';
const defaultStore: Store = {
index: [],
version: 0,
entries: {},
};
//const key="c3dpZ2dpdHlzd29vdHkK"
const key = localStorage.getItem("apikey") ?? "";
export const App : React.FC = () => {
const [API, setAPI] = useState(() => getAPI(key));
const [store, setStore] = useState<Store>(defaultStore);
useEffect(() => {
const f = async () => {
const st = await API.read();
setStore(ensureToday(st));
};
f();
}, [API]);
const [showAPIField, setShowAPIField] = useState(false);
const [input, setInput] = useState<string>("");
return (
<div className="App">
<h1>SHULNEFF</h1>
<div className="toprow">
<div className="eats">
<h2 onContextMenu={(e) => {e.preventDefault(); setShowAPIField(!showAPIField);}}>I ate ...</h2>
{showAPIField?
<input placeholder='API Token' onChange={(e) => {
localStorage.setItem("apikey", e.target.value);
}}/>
:null}
<form>
<div>
<input type="text" placeholder='Stinking Fish' onChange={(e) => setInput(e.target.value)} value={input} />
<button
onClick={ async () => {
const newst = addEntry(store, {variant: "eats", date: new Date().toString(), item: input} );
console.log("newstore is", newst);
await API.write(JSON.stringify(store))
const fromserver = await API.read();
setStore({...fromserver} );
setInput("");
}}
disabled={input.trim().length < 2}
>Submit</button>
</div>
</form>
</div>
<div className="reacts">
<button
onClick={ async () => {
const newst = addEntry(store, {variant: "allergy", date: new Date().toString()} );
await API.write(JSON.stringify(store))
const fromserver = await API.read();
setStore({...fromserver} );
}}
><h3>Allergy Alert!</h3></button>
</div>
</div>
<hr/>
{store ?
<div className={"entry-table"}>
<RenderTable API={API} store={store} setStore={setStore} />
</div>
:null}
<hr/>
<details name='foo'>
<summary>Raw Store</summary>
<pre>
{JSON.stringify(store, null, 4)}
</pre>
</details>
</div>
);
};
const DATEFORMAT = "YYYY MM DD HH:mm";
export const RenderTable : React.FC<{API: APIEndPoint, store: Store, setStore: (store: Store) => void}> = ({store, API, setStore}) => {
const [selectedItem, setSelected] = useState<ItemType|null>(null);
const listEntries = Object
.values(store.entries)
.flatMap<ItemType>((entry) => entry.items)
.sort((a,b) => b.date.localeCompare(a.date));
const itemToRow = (item: ItemType, index: number): JSX.Element => {
const datestring = dayjs(new Date(item.date)).format(DATEFORMAT)
const isSelected = (selectedItem !== null) && JSON.stringify(item) === JSON.stringify(selectedItem);
if (item.variant === "eats") {
return <tr
onContextMenu={(e) => {
e.preventDefault();
if (isSelected) setSelected(null);
else setSelected(item);
}}
onClick={async (e) => {
if (isSelected) {
const newst = removeEntry(store, item );
console.log("newstore is", newst);
await API.write(JSON.stringify(store))
const fromserver = await API.read();
setStore({...fromserver} );
}
}}
key={index} className="eats"><td>{datestring}</td><td><small>ate</small></td>
{isSelected ?
<td className="delete">X</td>
: <td> {item.item}</td>
}
</tr>;
} else if (item.variant === "allergy") {
return <tr
onContextMenu={(e) => {
e.preventDefault();
if (isSelected) setSelected(null);
else setSelected(item);
}}
onClick={async (e) => {
if (isSelected) {
const newst = removeEntry(store, item );
console.log("newstore is", newst);
await API.write(JSON.stringify(store))
const fromserver = await API.read();
setStore({...fromserver} );
}
}}
key={index} className="reacts"><td>{datestring}</td>
{isSelected ?
<td colSpan={2} className="delete">X</td>
: <td colSpan={2}><small>Reaction!</small></td>
}
</tr>;
}
return <></>;
}
return <table>
<thead>
<tr><th>Time</th><th colSpan={2}>Event</th></tr>
</thead>
<tbody>
{listEntries.map(itemToRow)}
</tbody>
</table>
};
export default App;