From fd2c3f118ee7d78523312548394a5d23e1f4c909 Mon Sep 17 00:00:00 2001 From: Kenneth Date: Sun, 26 Jan 2025 18:06:46 +0000 Subject: [PATCH] fix: application list item store not separated --- app/home/application-list.tsx | 150 +++++++++++++++++++++------------- app/home/store.ts | 18 +++- app/routes/home.tsx | 10 +-- 3 files changed, 112 insertions(+), 66 deletions(-) diff --git a/app/home/application-list.tsx b/app/home/application-list.tsx index cf4f835..eb44b83 100644 --- a/app/home/application-list.tsx +++ b/app/home/application-list.tsx @@ -6,20 +6,20 @@ import { useEffect, useRef, } from "react" -import { create } from "zustand" +import { createStore, useStore } from "zustand" import { useShallow } from "zustand/react/shallow" import { Button } from "~/components/button" import { DEFAULT_NODE, type Entry } from "~/home/graph" -import { useStore } from "./store" +import { useRootStore } from "./store" function ApplicationList() { - const entries = useStore(useShallow((state) => state.entries)) + const entries = useRootStore(useShallow((state) => state.entries)) return Object.values(entries).map((entry) => ( )) } -interface ListItemStore { +interface ListItemState { isAddingStage: boolean newStageValue: string @@ -28,67 +28,101 @@ interface ListItemStore { addStage: (entryName: string) => void } -const useListItemStore = create()((set, get) => ({ - isAddingStage: false, - newStageValue: "", +function createListItemStore() { + return createStore()((set, get) => ({ + isAddingStage: false, + newStageValue: "", - setNewStageValue: (newStageValue: string) => set({ newStageValue }), - setIsAddingStage: (isAddingStage: boolean) => set({ isAddingStage }), - addStage: (entryName) => { - const store = useStore.getState() - let stage = get().newStageValue - if (stage) { - if (stage === DEFAULT_NODE.acceptedNode.key.toLowerCase()) { - stage = DEFAULT_NODE.acceptedNode.key - } else if (stage === DEFAULT_NODE.rejectedNode.key.toLowerCase()) { - stage = DEFAULT_NODE.rejectedNode.key + setNewStageValue: (newStageValue: string) => set({ newStageValue }), + setIsAddingStage: (isAddingStage: boolean) => set({ isAddingStage }), + addStage: (entryName) => { + const store = useRootStore.getState() + let stage = get().newStageValue + if (stage) { + if (stage === DEFAULT_NODE.acceptedNode.key.toLowerCase()) { + stage = DEFAULT_NODE.acceptedNode.key + } else if (stage === DEFAULT_NODE.rejectedNode.key.toLowerCase()) { + stage = DEFAULT_NODE.rejectedNode.key + } + store.addStageInEntry(stage, entryName) + set({ isAddingStage: false, newStageValue: "" }) } - store.addStageInEntry(stage, entryName) - set({ isAddingStage: false, newStageValue: "" }) - } - }, -})) + }, + })) +} +type ListItemStore = ReturnType + +const ListItemStoreContext = createContext( + null as unknown as ListItemStore, +) + +function useListItemStore(selector: (state: ListItemState) => T): T { + const store = useContext(ListItemStoreContext) + return useStore(store, selector) +} const EntryContext = createContext(null as unknown as Entry) -const ApplicationListItem = memo(({ entry }: { entry: Entry }) => ( - -
- {entry.name} -
    - {entry.stages.map((step) => ( - - ))} - -
- -
-
-)) - -const StageItem = memo(({ step }: { step: string }) => { - const entry = useContext(EntryContext) - const deleteStageInEntry = useStore((state) => state.deleteStageInEntry) +const ApplicationListItem = memo(({ entry }: { entry: Entry }) => { + const store = useRef(null) + if (!store.current) { + store.current = createListItemStore() + } return ( -
  • -
    - {step} - {step !== DEFAULT_NODE.applicationSubmittedNode.key ? ( - - ) : null} -
    -
  • + + +
    + {entry.name} +
      + {entry.stages.map((step) => ( + + ))} + +
    + +
    +
    +
    ) }) +const StageItem = memo(({ stage }: { stage: string }) => ( +
  • +
    + {stage} + {stage !== DEFAULT_NODE.applicationSubmittedNode.key ? ( + + ) : null} +
    +
  • +)) + +function StageItemActions({ stage }: { stage: string }) { + const entry = useContext(EntryContext) + const deleteStageInEntry = useRootStore((state) => state.deleteStageInEntry) + + return ( +
    + + +
    + ) +} + function NewStageInput() { const isAddingStage = useListItemStore((state) => state.isAddingStage) const inputRef = useRef(null) @@ -142,8 +176,8 @@ function ApplicationActions() { const DefaultActions = memo(() => { const entry = useContext(EntryContext) const setIsAddingStage = useListItemStore((state) => state.setIsAddingStage) - const addStageToEntry = useStore((state) => state.addStageInEntry) - const deleteEntry = useStore((state) => state.deleteEntry) + const addStageToEntry = useRootStore((state) => state.addStageInEntry) + const deleteEntry = useRootStore((state) => state.deleteEntry) const isApplicationFinalized = entry.stages.at(-1) === DEFAULT_NODE.acceptedNode.key || diff --git a/app/home/store.ts b/app/home/store.ts index a5e23ad..3346adf 100644 --- a/app/home/store.ts +++ b/app/home/store.ts @@ -2,7 +2,7 @@ import { create } from "zustand/index" import { immer } from "zustand/middleware/immer" import { DEFAULT_NODE, type Entry, type Node } from "~/home/graph" -interface Store { +interface RootStore { nodes: Record starts: Node["key"][] entries: Record @@ -10,11 +10,12 @@ interface Store { addEntry: (name: string) => void hasEntry: (name: string) => boolean addStageInEntry: (stage: string, entryName: string) => void + editStageInEntry: (i: number, stage: string, entryName: string) => void deleteStageInEntry: (stage: string, entryName: string) => void deleteEntry: (entryName: string) => void } -const useStore = create()( +const useRootStore = create()( immer((set, get) => ({ isAddingEntry: false, nodes: { @@ -72,6 +73,17 @@ const useStore = create()( } }), + editStageInEntry: (i, stage, entryName) => + set((state) => { + const entry = state.entries[entryName] + if (entry && stage) { + if (i >= entry.stages.length) { + return + } + entry.stages[i] = stage + } + }), + deleteStageInEntry: (stage, entryName) => set((state) => { if (stage === DEFAULT_NODE.applicationSubmittedNode.key) { @@ -119,4 +131,4 @@ const useStore = create()( })), ) -export { useStore } +export { useRootStore } diff --git a/app/routes/home.tsx b/app/routes/home.tsx index debc662..fe31386 100644 --- a/app/routes/home.tsx +++ b/app/routes/home.tsx @@ -4,7 +4,7 @@ import Chart from "react-google-charts" import { Button } from "~/components/button" import { ApplicationList } from "~/home/application-list" import type { Node } from "~/home/graph" -import { useStore } from "~/home/store" +import { useRootStore } from "~/home/store" import { Queue } from "~/queue" import { useUiMode } from "~/use-ui-mode" @@ -35,8 +35,8 @@ export default function Home() { } function FlufferChart() { - const nodes = useStore((state) => state.nodes) - const starts = useStore((state) => state.starts) + const nodes = useRootStore((state) => state.nodes) + const starts = useRootStore((state) => state.starts) const uiMode = useUiMode() const data = useMemo(() => { @@ -86,8 +86,8 @@ function FlufferChart() { function AddApplicationForm() { const [isAddingEntry, setIsAddingEntry] = useState(false) - const addEntry = useStore((state) => state.addEntry) - const hasEntry = useStore((state) => state.hasEntry) + const addEntry = useRootStore((state) => state.addEntry) + const hasEntry = useRootStore((state) => state.hasEntry) const inputRef = useRef(null) useEffect(() => {