impl: autosave to local storage

This commit is contained in:
2025-01-26 22:56:20 +00:00
parent fd2c3f118e
commit f6bc7d9196
6 changed files with 113 additions and 38 deletions

View File

@@ -103,14 +103,6 @@ function StageItemActions({ stage }: { stage: string }) {
return (
<div className="flex flex-row space-x-2 invisible group-hover:visible">
<Button
variant="small"
onClick={() => {
deleteStageInEntry(stage, entry.name)
}}
>
Edit
</Button>
<Button
variant="small"
onClick={() => {

View File

@@ -1,17 +1,37 @@
interface Node {
key: string
outs: Record<string, Connection>
}
import {
type Infer,
array,
integer,
min,
object,
record,
string,
} from "superstruct"
interface Entry {
name: string
stages: Node["key"][]
}
const $Connection = object({
nodeKey: string(),
weight: min(integer(), 0),
})
type Connection = Infer<typeof $Connection>
interface Connection {
nodeKey: Node["key"]
weight: number
}
const $Node = object({
key: string(),
outs: record(string(), $Connection),
})
type Node = Infer<typeof $Node>
const $Entry = object({
name: string(),
stages: array(string()),
})
type Entry = Infer<typeof $Entry>
const $Graph = object({
nodes: record(string(), $Node),
starts: array(string()),
entries: record(string(), $Entry),
})
type Graph = Infer<typeof $Graph>
const DEFAULT_NODE = {
applicationSubmittedNode: {
@@ -28,5 +48,5 @@ const DEFAULT_NODE = {
},
} as const
export { DEFAULT_NODE }
export type { Node, Entry, Connection }
export { $Graph, DEFAULT_NODE }
export type { Graph, Node, Entry, Connection }

View File

@@ -1,12 +1,15 @@
import { is } from "superstruct"
import { create } from "zustand/index"
import { immer } from "zustand/middleware/immer"
import { DEFAULT_NODE, type Entry, type Node } from "~/home/graph"
import { $Graph, DEFAULT_NODE, type Entry, type Node } from "~/home/graph"
interface RootStore {
nodes: Record<string, Node>
starts: Node["key"][]
entries: Record<string, Entry>
loadGraphFromLocalStorage: () => boolean
resetGraph: () => void
addEntry: (name: string) => void
hasEntry: (name: string) => boolean
addStageInEntry: (stage: string, entryName: string) => void
@@ -25,6 +28,38 @@ const useRootStore = create<RootStore>()(
starts: [DEFAULT_NODE.applicationSubmittedNode.key],
entries: {},
loadGraphFromLocalStorage: () => {
const graphJson = localStorage.getItem("graph")
if (!graphJson) {
// if there is no saved data, then we simply return
// this is not considered to be an error
return true
}
const graph = JSON.parse(graphJson)
if (!is(graph, $Graph)) {
return false
}
set({
nodes: graph.nodes,
starts: graph.starts,
entries: graph.entries,
})
return true
},
resetGraph: () =>
set({
nodes: {
[DEFAULT_NODE.applicationSubmittedNode.key]:
DEFAULT_NODE.applicationSubmittedNode,
},
starts: [DEFAULT_NODE.applicationSubmittedNode.key],
entries: {},
}),
addEntry: (name) =>
set((state) => {
const currentEntries = state.entries

View File

@@ -16,6 +16,25 @@ export function meta() {
}
export default function Home() {
const loadGraphFromLocalStorage = useRootStore(
(state) => state.loadGraphFromLocalStorage,
)
useEffect(function saveGraphToLocalStorage() {
const unsub = useRootStore.subscribe(({ nodes, starts, entries }) => {
localStorage.setItem("graph", JSON.stringify({ nodes, starts, entries }))
})
return () => {
unsub()
}
}, [])
useEffect(() => {
if (!loadGraphFromLocalStorage()) {
alert("Unable to load locally saved data due to inconsistency.")
}
}, [loadGraphFromLocalStorage])
return (
<div className="flex items-center justify-center p-4 md:px-16 md:pt-20 w-full">
<main className="w-full max-w-xl flex flex-col items-start">
@@ -37,9 +56,11 @@ export default function Home() {
function FlufferChart() {
const nodes = useRootStore((state) => state.nodes)
const starts = useRootStore((state) => state.starts)
const resetGraph = useRootStore((state) => state.resetGraph)
const uiMode = useUiMode()
const data = useMemo(() => {
try {
const rows: [string, string, number][] = []
const queue = new Queue<Node>()
for (const nodeKey of starts) {
@@ -56,7 +77,13 @@ function FlufferChart() {
}
}
return rows
}, [starts, nodes])
} catch {
if (confirm("Invalid data detected. Erase data and reset?")) {
resetGraph()
}
return []
}
}, [starts, nodes, resetGraph])
const hasData = data.length > 0

BIN
bun.lockb

Binary file not shown.

View File

@@ -18,6 +18,7 @@
"react-dom": "^19.0.0",
"react-google-charts": "^5.2.1",
"react-router": "^7.1.3",
"superstruct": "^2.0.2",
"zustand": "^5.0.3"
},
"devDependencies": {