Migrate to new date format
This commit is contained in:
parent
1293f058b3
commit
1c0b107188
1
.env.production
Normal file
1
.env.production
Normal file
@ -0,0 +1 @@
|
||||
REACT_APP_API_URL=https://shulneffapi.leafbla.de
|
26
data/file.json
Normal file
26
data/file.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"entries": {
|
||||
"20250511": {
|
||||
"items": [
|
||||
{
|
||||
"variant": "allergy",
|
||||
"date": "2025-05-11 23:57:10"
|
||||
},
|
||||
{
|
||||
"variant": "eats",
|
||||
"item": "Nix gesse",
|
||||
"date": "2025-05-11 23:57:13"
|
||||
},
|
||||
{
|
||||
"variant": "eats",
|
||||
"item": "Manchmal was gesse",
|
||||
"date": "2025-05-11 23:57:18"
|
||||
},
|
||||
{
|
||||
"variant": "allergy",
|
||||
"date": "2025-05-11 23:57:26"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -33,6 +33,9 @@ h1,h2,h3 {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
.entry-table td {
|
||||
min-width: 40px;
|
||||
}
|
||||
.entry-table .reacts {
|
||||
background-color: #DDEB9D;
|
||||
}
|
||||
|
32
src/App.tsx
32
src/App.tsx
@ -1,12 +1,11 @@
|
||||
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 { addEntry, APIEndPoint, getAPI, removeEntry } from './api';
|
||||
import { ItemType, Store, WithoutDate } from './types';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const defaultStore: Store = {
|
||||
index: [],
|
||||
version: 0,
|
||||
version: 1,
|
||||
entries: {},
|
||||
};
|
||||
|
||||
@ -18,7 +17,7 @@ export const App : React.FC = () => {
|
||||
useEffect(() => {
|
||||
const f = async () => {
|
||||
const st = await API.read();
|
||||
setStore(ensureToday(st));
|
||||
setStore(st);
|
||||
};
|
||||
f();
|
||||
}, [API]);
|
||||
@ -27,15 +26,16 @@ export const App : React.FC = () => {
|
||||
|
||||
|
||||
const formSubmit = async () => {
|
||||
const newst = addEntry(store, {variant: "eats", date: new Date().toString(), item: input} );
|
||||
console.log("newstore is", newst);
|
||||
API.write(JSON.stringify(store)).then(async () => {
|
||||
setTimeout(async () => {
|
||||
const fromserver = await API.read();
|
||||
setStore({...fromserver} );
|
||||
setInput("");
|
||||
}, 150);
|
||||
})
|
||||
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>("");
|
||||
|
||||
@ -71,7 +71,7 @@ export const App : React.FC = () => {
|
||||
<div className="reacts">
|
||||
<button
|
||||
onClick={ async () => {
|
||||
const newst = addEntry(store, {variant: "allergy", date: new Date().toString()} );
|
||||
const newst = addEntry(store, {variant: "allergy"} );
|
||||
await API.write(JSON.stringify(store))
|
||||
const fromserver = await API.read();
|
||||
setStore({...fromserver} );
|
||||
@ -96,7 +96,7 @@ export const App : React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const DATEFORMAT = "YYYY MM DD HH:mm";
|
||||
const DATEFORMAT = "MMM DD, HH:mm";
|
||||
|
||||
export const RenderTable : React.FC<{API: APIEndPoint, store: Store, setStore: (store: Store) => void}> = ({store, API, setStore}) => {
|
||||
|
||||
|
62
src/api.ts
62
src/api.ts
@ -1,7 +1,7 @@
|
||||
import { ItemType, Store, StoreEntry } from "./types"
|
||||
import { currentTimestamp, getDateID, ItemType, Store, StoreEntry, WithoutDate } from "./types"
|
||||
|
||||
|
||||
const defaultEndpoint = "https://shulneffapi.leafbla.de"
|
||||
const endpoint = process.env.REACT_APP_API_URL ?? "API endpoint missing!";
|
||||
|
||||
export type APIEndPoint = {
|
||||
write: (json: string) => Promise<void>,
|
||||
@ -9,7 +9,7 @@ export type APIEndPoint = {
|
||||
};
|
||||
|
||||
const makeEndpoint = (key: string, method: string, endPointOverride?: string): string => {
|
||||
return `${endPointOverride ?? defaultEndpoint}\/${method}\/${key}\/`;
|
||||
return `${endPointOverride ?? endpoint}\/${method}\/${key}\/`;
|
||||
}
|
||||
|
||||
|
||||
@ -33,53 +33,23 @@ export type APIEndPoint = {
|
||||
}
|
||||
});
|
||||
|
||||
export const ensureToday = (store: Store): Store => {
|
||||
const today : Date = new Date();
|
||||
const storeEntry = store.index.find(([key, dateString]) => {
|
||||
const date = new Date(dateString);
|
||||
const [y, m, d] = [today.getFullYear(), today.getMonth(), today.getDay()];
|
||||
return (date.getFullYear() === y && date.getMonth() === m && date.getDay() == d);
|
||||
});
|
||||
|
||||
if (storeEntry !== undefined) return store;
|
||||
|
||||
const indexKey = today.getDay() + today.getMonth() * 100 + today.getFullYear() * 10000;
|
||||
const entries = store.entries;
|
||||
const newEntry : StoreEntry = {
|
||||
items: [],
|
||||
}
|
||||
entries[indexKey] = newEntry;
|
||||
|
||||
return {
|
||||
entries: entries,
|
||||
index: [[indexKey, today.toString()], ...store.index ],
|
||||
version: store.version,
|
||||
};
|
||||
}
|
||||
|
||||
export const addEntry = (store: Store, entry: ItemType): Store => {
|
||||
const [storeIndex, dateString] = getToday(ensureToday(store))!;
|
||||
const today = store.entries[storeIndex];
|
||||
today.items = [...today.items, entry];
|
||||
store.entries[storeIndex] = today;
|
||||
export const addEntry = (store: Store, entry: WithoutDate<ItemType> ): Store => {
|
||||
const dID = getDateID()
|
||||
const storeEntry = store.entries[dID] ?? { items: [] };
|
||||
storeEntry.items = [...storeEntry.items, {...entry, date: currentTimestamp() }];
|
||||
store.entries[dID] = storeEntry;
|
||||
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;
|
||||
console.log("removing", entry, "from", store)
|
||||
Object.keys(store.entries).forEach((k) => {
|
||||
const storeEntry = store.entries[parseInt(k)];
|
||||
storeEntry.items = [...storeEntry.items.filter((item) =>
|
||||
!(item.date === entry.date && item.variant === entry.variant && ("item" in item && "item" in entry ? item.item === entry.item : true))
|
||||
)];
|
||||
store.entries[parseInt(k)] = storeEntry;
|
||||
});
|
||||
return store;
|
||||
}
|
||||
|
||||
export const getToday = (store: Store) => {
|
||||
if (!store) return;
|
||||
const today : Date = new Date();
|
||||
const storeEntry = store.index.find(([key, dateString]) => {
|
||||
const date = new Date(dateString);
|
||||
const [y, m, d] = [today.getFullYear(), today.getMonth(), today.getDay()];
|
||||
return (date.getFullYear() === y && date.getMonth() === m && date.getDay() == d);
|
||||
});
|
||||
return storeEntry;
|
||||
};
|
||||
|
31
src/types.ts
31
src/types.ts
@ -1,17 +1,28 @@
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export type DateString = string;
|
||||
export type VersionedStore = {
|
||||
1: {
|
||||
index: Array<[number, DateString]>,
|
||||
entries: {[ key: number ]: StoreEntry }
|
||||
version: 0,
|
||||
},
|
||||
export type DateID = number;
|
||||
export type Timestamp = string; /* Strings formatted YYYY-MM-DD HH:mm:ss */
|
||||
|
||||
export type Store = {
|
||||
entries: {[ key: DateID ]: StoreEntry }
|
||||
version: 1,
|
||||
};
|
||||
|
||||
export const getDateID = (): DateID => {
|
||||
const d = dayjs(new Date());
|
||||
return d.year() * 10000 + (d.month()+1) * 100 + d.date();
|
||||
}
|
||||
export const dateFromDateId = (dateID: DateID): Date => {
|
||||
return new Date(Math.floor(dateID / 10000), Math.floor(dateID % 10000 / 100)-1, dateID % 100);
|
||||
}
|
||||
export const currentTimestamp = () => {
|
||||
return dayjs(new Date()).format("YYYY-MM-DD HH:mm:ss");
|
||||
}
|
||||
|
||||
export type Store = VersionedStore[1];
|
||||
|
||||
export type WithoutDate<T> = T extends any ? Omit<T, "date"> : never;
|
||||
export type ItemType = ({
|
||||
date: DateString,
|
||||
variant: "eats" | "allergy",
|
||||
date: Timestamp,
|
||||
} & ({
|
||||
variant: "eats",
|
||||
item: string
|
||||
|
Loading…
Reference in New Issue
Block a user