161 lines
4.9 KiB
TypeScript
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;
|