Finish prototype

This commit is contained in:
Dominic Zimmer 2025-05-03 13:16:13 +02:00
parent 4fc6f94b0e
commit 3fb4ec8b7b
6 changed files with 172 additions and 78 deletions

View File

@ -24,7 +24,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>Shouldnthave</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

View File

@ -12,16 +12,6 @@
"date": "Sat May 03 2025 12:08:00 GMT+0200 (Central European Summer Time)",
"item": "nothing"
},
{
"variant": "eats",
"date": "Sat May 03 2025 12:09:03 GMT+0200 (Central European Summer Time)",
"item": "nuttin"
},
{
"variant": "eats",
"date": "Sat May 03 2025 12:09:56 GMT+0200 (Central European Summer Time)",
"item": "foo"
},
{
"variant": "eats",
"date": "Sat May 03 2025 12:12:57 GMT+0200 (Central European Summer Time)",
@ -40,6 +30,15 @@
"variant": "eats",
"date": "Sat May 03 2025 12:16:13 GMT+0200 (Central European Summer Time)",
"item": "Mushrooms"
},
{
"variant": "eats",
"date": "Sat May 03 2025 12:29:55 GMT+0200 (Central European Summer Time)",
"item": "something"
},
{
"variant": "allergy",
"date": "Sat May 03 2025 13:03:52 GMT+0200 (Central European Summer Time)"
}
]
}

View File

@ -9,7 +9,8 @@ const key = "c3dpZ2dpdHlzd29vdHkK";
const reStoreWrite = new RegExp(`/write/${key}/?`)
const reStoreRead = new RegExp(`/read/${key}/?`)
const hostname = '127.0.0.1';
let hostname = '127.0.0.1';
hostname = "0.0.0.0";
const port = 4444;
const server = createServer((req, res) => {

View File

@ -1,38 +1,63 @@
body {
width: 100%;
height: 100vh
}
#root {
width: 100%;
height: 100%;
}
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
background-color: #A0C878;
}
.toprow {
display: flex;
flex-direction: row;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
gap: 15px;
width: 100%;
}
.toprow > div {
width: 45%;
}
h1,h2,h3 {
user-select: none;
}
.App-link {
color: #61dafb;
.eats form div {
display: flex;
flex-direction: column;
gap: 10px;
}
.entry-table .reacts {
background-color: #DDEB9D;
}
.entry-table .eats:nth-child(2n+1) {
background-color: #FFFDF6;
}
.entry-table .eats:nth-child(2n) {
background-color: #FAF6E9;
}
.entry-table .delete {
background-color: #F16767;
}
.entry-table table td {
text-align: center;
}
.toprow .reacts {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
.entry-table {
width: 80%;
}
.entry-table table {
border-spacing: 12px 2px;
}

View File

@ -1,7 +1,6 @@
import React, { JSX, useEffect, useState } from 'react';
import logo from './logo.svg';
import './App.css';
import { addEntry, ensureToday, getAPI, getToday } from './api';
import { addEntry, APIEndPoint, ensureToday, getAPI, getToday, removeEntry } from './api';
import { ItemType, Store, StoreEntry } from './types';
import dayjs from 'dayjs';
@ -11,7 +10,8 @@ const defaultStore: Store = {
entries: {},
};
const key="c3dpZ2dpdHlzd29vdHkK"
//const key="c3dpZ2dpdHlzd29vdHkK"
const key = localStorage.getItem("apikey") ?? "";
export const App : React.FC = () => {
const [API, setAPI] = useState(() => getAPI(key));
@ -24,42 +24,58 @@ export const App : React.FC = () => {
f();
}, [API]);
const [showAPIField, setShowAPIField] = useState(false);
const [input, setInput] = useState<string>("");
return (
<div className="App">
<div className="eats">
<h1>I ate ...</h1>
<form>
<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>
</form>
</div>
<div className="reacts">
<h1>I have allergy</h1>
<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} );
}}
>Now!</button>
<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 ?
<RenderTable store={store} />
<div className={"entry-table"}>
<RenderTable API={API} store={store} setStore={setStore} />
</div>
:null}
<hr/>
<details name='foo'>
<summary>Raw Store</summary>
<pre>
@ -72,21 +88,66 @@ export const App : React.FC = () => {
const DATEFORMAT = "YYYY MM DD HH:mm";
export const RenderTable : React.FC<{store: Store}> = ({store}) => {
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 key={index} className="eats"><td>{datestring}</td><td><small>ate</small></td><td> {item.item}</td></tr>;
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 key={index} className="eats"><td>{datestring}</td><td colSpan={2}><small>Reaction!</small></td></tr>;
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 className="entry-table">
return <table>
<thead>
<tr><th>Time</th><th colSpan={2}>Event</th></tr>
</thead>

View File

@ -1,11 +1,11 @@
import { ItemType, Store, StoreEntry } from "./types"
const defaultEndpoint = "http://localhost:4444/"
const defaultEndpoint = "http://slateport:4444"
type APIEndPoint = {
export type APIEndPoint = {
write: (json: string) => Promise<void>,
read: () => Promise<Store>,
read: () => Promise<Store>,
};
const makeEndpoint = (key: string, method: string, endPointOverride?: string): string => {
@ -65,6 +65,14 @@ type APIEndPoint = {
return store;
}
export const removeEntry = (store: Store, entry: ItemType): Store => {
const [storeIndex, dateString] = getToday(ensureToday(store))!;
const today = store.entries[storeIndex];
today.items = today.items.filter((item) => JSON.stringify(item) !== JSON.stringify(entry));
store.entries[storeIndex] = today;
return store;
}
export const getToday = (store: Store) => {
if (!store) return;
const today : Date = new Date();