Skip to content

Commit 8bda400

Browse files
committed
chore: migration to zustand, query and custom hooks
1 parent d174729 commit 8bda400

15 files changed

+400
-334
lines changed

packages/cta-ui/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
"class-variance-authority": "^0.7.1",
4747
"clsx": "^2.1.1",
4848
"execa": "^9.5.2",
49-
"jotai": "^2.12.3",
5049
"jotai-tanstack-query": "^0.9.0",
5150
"lucide-react": "^0.476.0",
5251
"next-themes": "^0.4.6",
@@ -58,7 +57,8 @@
5857
"tailwindcss": "^4.0.6",
5958
"tailwindcss-animate": "^1.0.7",
6059
"vinxi": "^0.5.3",
61-
"vite-tsconfig-paths": "^5.1.4"
60+
"vite-tsconfig-paths": "^5.1.4",
61+
"zustand": "^5.0.3"
6262
},
6363
"devDependencies": {
6464
"@testing-library/dom": "^10.4.0",

packages/cta-ui/src/components/cta-sidebar.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { useAtomValue } from 'jotai'
21
import {
32
Sidebar,
43
SidebarContent,
@@ -15,11 +14,11 @@ import ModeSelector from '@/components/sidebar-items/mode-selector'
1514
import TypescriptSwitch from '@/components/sidebar-items/typescript-switch'
1615
import StarterDialog from '@/components/sidebar-items/starter'
1716

18-
import { applicationMode, isInitialized } from '@/store/project'
17+
import { useApplicationMode, useReady } from '@/store/project'
1918

2019
export function AppSidebar() {
21-
const ready = useAtomValue(isInitialized)
22-
const mode = useAtomValue(applicationMode)
20+
const ready = useReady()
21+
const mode = useApplicationMode()
2322

2423
return (
2524
<Sidebar>

packages/cta-ui/src/components/custom-add-on-dialog.tsx

+5-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { useState } from 'react'
2-
import { useAtomValue, useSetAtom } from 'jotai'
32
import { toast } from 'sonner'
43
import { TicketPlusIcon } from 'lucide-react'
54

@@ -13,24 +12,23 @@ import {
1312
DialogTitle,
1413
} from '@/components/ui/dialog'
1514

16-
import { customAddOns, projectOptions, toggleAddOn } from '@/store/project'
15+
import { addCustomAddOn, useAddOns, useRouterMode } from '@/store/project'
1716

1817
export default function CustomAddOnDialog() {
1918
const [url, setUrl] = useState('')
2019
const [open, setOpen] = useState(false)
2120

22-
const mode = useAtomValue(projectOptions).mode
23-
const setCustomAddOns = useSetAtom(customAddOns)
24-
const toggle = useSetAtom(toggleAddOn)
21+
const mode = useRouterMode()
22+
const { toggleAddOn } = useAddOns()
2523

2624
async function onImport() {
2725
const response = await fetch(`/api/load-remote-add-on?url=${url}`)
2826
const data = await response.json()
2927

3028
if (!data.error) {
31-
setCustomAddOns((state) => [...state, data])
29+
addCustomAddOn(data)
3230
if (data.modes.includes(mode)) {
33-
toggle(data.id)
31+
toggleAddOn(data.id)
3432
}
3533
setOpen(false)
3634
} else {

packages/cta-ui/src/components/file-navigator.tsx

+18-28
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { useMemo, useState } from 'react'
2-
import { useAtomValue, useSetAtom } from 'jotai'
32
import { FileText, Folder } from 'lucide-react'
43

54
import FileViewer from './file-viewer'
@@ -11,29 +10,18 @@ import { Label } from '@/components/ui/label'
1110
import { Checkbox } from '@/components/ui/checkbox'
1211

1312
import {
14-
applicationMode,
15-
dryRunAtom,
16-
includeFiles,
17-
isInitialized,
18-
projectFiles,
19-
projectLocalFiles,
13+
useApplicationMode,
14+
useDryRun,
15+
useFilters,
16+
useOriginalOutput,
17+
useProjectLocalFiles,
18+
useReady,
2019
} from '@/store/project'
2120

2221
import { getFileClass, twClasses } from '@/file-classes'
2322

2423
export function Filters() {
25-
const includedFiles = useAtomValue(includeFiles)
26-
const setIncludeFiles = useSetAtom(includeFiles)
27-
function toggleFilter(
28-
filter: 'unchanged' | 'added' | 'modified' | 'deleted' | 'overwritten',
29-
) {
30-
setIncludeFiles((state) => {
31-
if (state.includes(filter)) {
32-
return state.filter((file) => file !== filter)
33-
}
34-
return [...state, filter]
35-
})
36-
}
24+
const { includedFiles, toggleFilter } = useFilters()
3725

3826
return (
3927
<div className="p-2 rounded-md bg-gray-900 file-filters">
@@ -104,19 +92,21 @@ export default function FileNavigator() {
10492
'./package.json',
10593
)
10694

107-
const originalOutput = useAtomValue(projectFiles)
108-
const localTree = useAtomValue(projectLocalFiles)
109-
const { data: output } = useAtomValue(dryRunAtom)
95+
const projectFiles = useOriginalOutput()
96+
const localTree = useProjectLocalFiles()
97+
const dryRunOutput = useDryRun()
98+
99+
const mode = useApplicationMode()
110100

111-
const mode = useAtomValue(applicationMode)
112-
const tree = output.files
113-
const originalTree = mode === 'setup' ? output.files : originalOutput.files
114-
const deletedFiles = output.deletedFiles
101+
const tree = dryRunOutput.files
102+
const originalTree =
103+
mode === 'setup' ? dryRunOutput.files : projectFiles.files
104+
const deletedFiles = dryRunOutput.deletedFiles
115105

116106
const [originalFileContents, setOriginalFileContents] = useState<string>()
117107
const [modifiedFileContents, setModifiedFileContents] = useState<string>()
118108

119-
const includedFiles = useAtomValue(includeFiles)
109+
const { includedFiles } = useFilters()
120110

121111
const fileTree = useMemo(() => {
122112
const treeData: Array<FileTreeItem> = []
@@ -187,7 +177,7 @@ export default function FileNavigator() {
187177
return treeData
188178
}, [tree, originalTree, localTree, includedFiles])
189179

190-
const ready = useAtomValue(isInitialized)
180+
const ready = useReady()
191181

192182
if (!ready) {
193183
return null

packages/cta-ui/src/components/sidebar-items/add-ons.tsx

+7-10
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import { useMemo, useState } from 'react'
2-
import { useAtomValue, useSetAtom } from 'jotai'
32
import { InfoIcon } from 'lucide-react'
43

54
import type { AddOnInfo } from '@/types'
65

76
import { Switch } from '@/components/ui/switch'
87
import { Label } from '@/components/ui/label'
98

10-
import { addOnState, availableAddOns, toggleAddOn } from '@/store/project'
9+
import { useAddOns } from '@/store/project'
1110

1211
import ImportCustomAddOn from '@/components/custom-add-on-dialog'
1312
import AddOnInfoDialog from '@/components/add-on-info-dialog'
@@ -19,15 +18,13 @@ const addOnTypeLabels: Record<string, string> = {
1918
}
2019

2120
export default function SelectedAddOns() {
22-
const addOns = useAtomValue(availableAddOns)
23-
const addOnStatus = useAtomValue(addOnState)
24-
const toggle = useSetAtom(toggleAddOn)
21+
const { availableAddOns, addOnState, toggleAddOn } = useAddOns()
2522

2623
const sortedAddOns = useMemo(() => {
27-
return addOns.sort((a, b) => {
24+
return availableAddOns.sort((a, b) => {
2825
return a.name.localeCompare(b.name)
2926
})
30-
}, [addOns])
27+
}, [availableAddOns])
3128

3229
const [infoAddOn, setInfoAddOn] = useState<AddOnInfo>()
3330

@@ -55,10 +52,10 @@ export default function SelectedAddOns() {
5552
<div className="p-1 flex flex-row items-center">
5653
<Switch
5754
id={addOn.id}
58-
checked={addOnStatus[addOn.id].selected}
59-
disabled={!addOnStatus[addOn.id].enabled}
55+
checked={addOnState[addOn.id].selected}
56+
disabled={!addOnState[addOn.id].enabled}
6057
onCheckedChange={() => {
61-
toggle(addOn.id)
58+
toggleAddOn(addOn.id)
6259
}}
6360
/>
6461
<Label

packages/cta-ui/src/components/sidebar-items/mode-selector.tsx

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1-
import { useAtomValue, useSetAtom } from 'jotai'
21
import { CodeIcon, FileIcon } from 'lucide-react'
32

43
import type { Mode } from '@tanstack/cta-engine'
54

65
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
76

8-
import { applicationMode, modeEditable, projectOptions } from '@/store/project'
7+
import {
8+
setRouterMode,
9+
useApplicationMode,
10+
useModeEditable,
11+
useRouterMode,
12+
} from '@/store/project'
913

1014
export default function ModeSelector() {
11-
const mode = useAtomValue(applicationMode)
12-
const enableMode = useAtomValue(modeEditable)
13-
const routerMode = useAtomValue(projectOptions).mode
14-
const setRouterMode = useSetAtom(projectOptions)
15+
const mode = useApplicationMode()
16+
const enableMode = useModeEditable()
17+
const routerMode = useRouterMode()
1518

1619
if (mode !== 'setup') {
1720
return null
@@ -25,10 +28,7 @@ export default function ModeSelector() {
2528
value={routerMode}
2629
onValueChange={(v: string) => {
2730
if (v) {
28-
setRouterMode((state) => ({
29-
...state,
30-
mode: v as Mode,
31-
}))
31+
setRouterMode(v as Mode)
3232
}
3333
}}
3434
>

packages/cta-ui/src/components/sidebar-items/project-name.tsx

+9-13
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1-
import { useAtomValue, useSetAtom } from 'jotai'
2-
31
import { Input } from '@/components/ui/input'
42
import { SidebarGroupLabel } from '@/components/ui/sidebar'
53

6-
import { applicationMode, projectOptions } from '@/store/project'
4+
import {
5+
setProjectName,
6+
useApplicationMode,
7+
useProjectName,
8+
} from '@/store/project'
79

810
export default function ProjectName() {
9-
const name = useAtomValue(projectOptions)
10-
const mode = useAtomValue(applicationMode)
11-
const setProjectOptions = useSetAtom(projectOptions)
11+
const name = useProjectName()
12+
const mode = useApplicationMode()
1213

1314
if (mode !== 'setup') {
1415
return null
@@ -18,14 +19,9 @@ export default function ProjectName() {
1819
<>
1920
<SidebarGroupLabel>Project Name</SidebarGroupLabel>
2021
<Input
21-
value={name.projectName}
22+
value={name}
2223
placeholder="my-app"
23-
onChange={(e) => {
24-
setProjectOptions((state) => ({
25-
...state,
26-
projectName: e.target.value,
27-
}))
28-
}}
24+
onChange={(e) => setProjectName(e.target.value)}
2925
className="w-full"
3026
/>
3127
</>

packages/cta-ui/src/components/sidebar-items/run-add-ons.tsx

+5-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { useState } from 'react'
2-
import { useAtomValue } from 'jotai'
32

43
import { Button } from '@/components/ui/button'
54
import {
@@ -10,16 +9,15 @@ import {
109
DialogTitle,
1110
} from '@/components/ui/dialog'
1211

13-
import { applicationMode, selectedAddOns } from '@/store/project'
12+
import { useAddOns, useApplicationMode } from '@/store/project'
1413

1514
export default function RunAddOns() {
16-
const currentlySelectedAddOns = useAtomValue(selectedAddOns)
15+
const { chosenAddOns } = useAddOns()
1716
const [isRunning, setIsRunning] = useState(false)
1817
const [output, setOutput] = useState('')
1918
const [finished, setFinished] = useState(false)
2019

21-
const mode = useAtomValue(applicationMode)
22-
const selAddOns = useAtomValue(selectedAddOns)
20+
const mode = useApplicationMode()
2321

2422
if (mode !== 'add') {
2523
return null
@@ -32,7 +30,7 @@ export default function RunAddOns() {
3230
const streamingReq = await fetch('/api/add-to-app', {
3331
method: 'POST',
3432
body: JSON.stringify({
35-
addOns: selAddOns.map((addOn) => addOn.id),
33+
addOns: chosenAddOns,
3634
}),
3735
headers: {
3836
'Content-Type': 'application/json',
@@ -83,7 +81,7 @@ export default function RunAddOns() {
8381
<Button
8482
variant="default"
8583
onClick={onAddToApp}
86-
disabled={currentlySelectedAddOns.length === 0 || isRunning}
84+
disabled={chosenAddOns.length === 0 || isRunning}
8785
className="w-full"
8886
>
8987
Add These Add-Ons To Your App

packages/cta-ui/src/components/sidebar-items/run-create-app.tsx

+10-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { useState } from 'react'
2-
import { useAtomValue } from 'jotai'
32
import { HammerIcon } from 'lucide-react'
43

54
import { Button } from '@/components/ui/button'
@@ -12,21 +11,21 @@ import {
1211
} from '@/components/ui/dialog'
1312

1413
import {
15-
applicationMode,
16-
projectOptions,
17-
projectStarter,
18-
selectedAddOns,
14+
useAddOns,
15+
useApplicationMode,
16+
useProjectOptions,
17+
useProjectStarter,
1918
} from '@/store/project'
2019

2120
export default function RunCreateApp() {
2221
const [isRunning, setIsRunning] = useState(false)
2322
const [output, setOutput] = useState('')
2423
const [finished, setFinished] = useState(false)
2524

26-
const mode = useAtomValue(applicationMode)
27-
const options = useAtomValue(projectOptions)
28-
const selAddOns = useAtomValue(selectedAddOns)
29-
const projStarter = useAtomValue(projectStarter)
25+
const mode = useApplicationMode()
26+
const options = useProjectOptions()
27+
const { chosenAddOns } = useAddOns()
28+
const { projectStarter } = useProjectStarter()
3029

3130
if (mode !== 'setup') {
3231
return null
@@ -41,8 +40,8 @@ export default function RunCreateApp() {
4140
body: JSON.stringify({
4241
options: {
4342
...options,
44-
chosenAddOns: selAddOns.map((addOn) => addOn.id),
45-
starter: projStarter?.url || undefined,
43+
chosenAddOns,
44+
starter: projectStarter?.url || undefined,
4645
},
4746
}),
4847
headers: {

0 commit comments

Comments
 (0)