shulneff/src/App.tsx
2025-05-12 00:02:02 +02:00

174 lines
5.0 KiB
TypeScript

import React, { JSX, useEffect, useState } from 'react';
import './App.css';
import { addEntry, APIEndPoint, getAPI, removeEntry } from './api';
import { ItemType, Store, WithoutDate } from './types';
import dayjs from 'dayjs';
const defaultStore: Store = {
version: 1,
entries: {},
};
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(st);
};
f();
}, [API]);
const [showAPIField, setShowAPIField] = useState(false);
const formSubmit = async () => {
const entry : WithoutDate<ItemType> = {variant: "eats", item: input};
const newStore = addEntry(store, entry);
console.log("newstore is", newStore);
API.write(JSON.stringify(store)).then(async () => {
setTimeout(async () => {
const fromserver = await API.read();
setStore({...fromserver} );
setInput("");
}, 150);
})
};
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);}
} onKeyDown={async (e) =>{
if (e.key === "Enter") {
e.preventDefault();
await formSubmit();
}
}} value={input} />
<button
onClick={formSubmit}
disabled={input.trim().length < 2}
>Submit</button>
</div>
</form>
</div>
<div className="reacts">
<button
onClick={ async () => {
const newst = addEntry(store, {variant: "allergy"} );
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 = "MMM 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);
API.write(JSON.stringify(store)).then(async () => {
setTimeout(async () => {
const fromserver = await API.read();
setStore({...fromserver} );
}, 150);
});
}
}}
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;