From 4ee346c69d62bd98f65af0231280e2e002300713 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Sun, 18 Jan 2026 01:24:36 +0000 Subject: [PATCH 1/4] Add data source editor hook --- .../molecules/DataSourceEditorDialog.tsx | 42 ++++---------- src/hooks/data/use-data-source-editor.ts | 58 +++++++++++++++++++ 2 files changed, 68 insertions(+), 32 deletions(-) create mode 100644 src/hooks/data/use-data-source-editor.ts diff --git a/src/components/molecules/DataSourceEditorDialog.tsx b/src/components/molecules/DataSourceEditorDialog.tsx index 5b13f15..38098d4 100644 --- a/src/components/molecules/DataSourceEditorDialog.tsx +++ b/src/components/molecules/DataSourceEditorDialog.tsx @@ -1,4 +1,3 @@ -import { useEffect, useState } from 'react' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' import { DataSource } from '@/types/json-ui' @@ -7,6 +6,7 @@ import { DataSourceIdField } from '@/components/molecules/data-source-editor/Dat import { KvSourceFields } from '@/components/molecules/data-source-editor/KvSourceFields' import { StaticSourceFields } from '@/components/molecules/data-source-editor/StaticSourceFields' import { ComputedSourceFields } from '@/components/molecules/data-source-editor/ComputedSourceFields' +import { useDataSourceEditor } from '@/hooks/data/use-data-source-editor' import dataSourceEditorCopy from '@/data/data-source-editor-dialog.json' interface DataSourceEditorDialogProps { @@ -24,11 +24,15 @@ export function DataSourceEditorDialog({ onOpenChange, onSave, }: DataSourceEditorDialogProps) { - const [editingSource, setEditingSource] = useState(dataSource) - - useEffect(() => { - setEditingSource(dataSource) - }, [dataSource]) + const { + editingSource, + updateField, + addDependency, + removeDependency, + availableDeps, + selectedDeps, + unselectedDeps, + } = useDataSourceEditor(dataSource, allDataSources) const handleSave = () => { if (!editingSource) return @@ -36,34 +40,8 @@ export function DataSourceEditorDialog({ onOpenChange(false) } - const updateField = (field: K, value: DataSource[K]) => { - if (!editingSource) return - setEditingSource({ ...editingSource, [field]: value }) - } - - const addDependency = (depId: string) => { - if (!editingSource || editingSource.type !== 'computed') return - const deps = editingSource.dependencies || [] - if (!deps.includes(depId)) { - updateField('dependencies', [...deps, depId]) - } - } - - const removeDependency = (depId: string) => { - if (!editingSource || editingSource.type !== 'computed') return - const deps = editingSource.dependencies || [] - updateField('dependencies', deps.filter(d => d !== depId)) - } - if (!editingSource) return null - const availableDeps = allDataSources.filter( - ds => ds.id !== editingSource.id && ds.type !== 'computed', - ) - - const selectedDeps = editingSource.dependencies || [] - const unselectedDeps = availableDeps.filter(ds => !selectedDeps.includes(ds.id)) - return ( diff --git a/src/hooks/data/use-data-source-editor.ts b/src/hooks/data/use-data-source-editor.ts new file mode 100644 index 0000000..8c5c9af --- /dev/null +++ b/src/hooks/data/use-data-source-editor.ts @@ -0,0 +1,58 @@ +import { useCallback, useEffect, useMemo, useState } from 'react' +import { DataSource } from '@/types/json-ui' + +export function useDataSourceEditor( + dataSource: DataSource | null, + allDataSources: DataSource[], +) { + const [editingSource, setEditingSource] = useState(dataSource) + + useEffect(() => { + setEditingSource(dataSource) + }, [dataSource]) + + const updateField = useCallback((field: K, value: DataSource[K]) => { + setEditingSource(prev => (prev ? { ...prev, [field]: value } : prev)) + }, []) + + const addDependency = useCallback((depId: string) => { + setEditingSource(prev => { + if (!prev || prev.type !== 'computed') return prev + const deps = prev.dependencies || [] + if (deps.includes(depId)) return prev + return { ...prev, dependencies: [...deps, depId] } + }) + }, []) + + const removeDependency = useCallback((depId: string) => { + setEditingSource(prev => { + if (!prev || prev.type !== 'computed') return prev + const deps = prev.dependencies || [] + return { ...prev, dependencies: deps.filter(dep => dep !== depId) } + }) + }, []) + + const availableDeps = useMemo(() => { + if (!editingSource) return [] + return allDataSources.filter( + ds => ds.id !== editingSource.id && ds.type !== 'computed', + ) + }, [allDataSources, editingSource]) + + const selectedDeps = useMemo(() => editingSource?.dependencies || [], [editingSource]) + + const unselectedDeps = useMemo( + () => availableDeps.filter(ds => !selectedDeps.includes(ds.id)), + [availableDeps, selectedDeps], + ) + + return { + editingSource, + updateField, + addDependency, + removeDependency, + availableDeps, + selectedDeps, + unselectedDeps, + } +} From 67007058420490e2c4c8eb4eb86b0e724432a872 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Sun, 18 Jan 2026 01:25:04 +0000 Subject: [PATCH 2/4] Add component binding dialog hook --- .../molecules/ComponentBindingDialog.tsx | 20 ++++------ src/hooks/use-component-binding-dialog.ts | 40 +++++++++++++++++++ 2 files changed, 47 insertions(+), 13 deletions(-) create mode 100644 src/hooks/use-component-binding-dialog.ts diff --git a/src/components/molecules/ComponentBindingDialog.tsx b/src/components/molecules/ComponentBindingDialog.tsx index 7b8fb9e..344660b 100644 --- a/src/components/molecules/ComponentBindingDialog.tsx +++ b/src/components/molecules/ComponentBindingDialog.tsx @@ -1,10 +1,10 @@ -import { useState } from 'react' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { BindingEditor } from '@/components/molecules/BindingEditor' import { DataSource, UIComponent } from '@/types/json-ui' import { Link } from '@phosphor-icons/react' +import { useComponentBindingDialog } from '@/hooks/use-component-binding-dialog' interface ComponentBindingDialogProps { open: boolean @@ -21,18 +21,12 @@ export function ComponentBindingDialog({ onOpenChange, onSave, }: ComponentBindingDialogProps) { - const [editingComponent, setEditingComponent] = useState(component) - - const handleSave = () => { - if (!editingComponent) return - onSave(editingComponent) - onOpenChange(false) - } - - const updateBindings = (bindings: Record) => { - if (!editingComponent) return - setEditingComponent({ ...editingComponent, bindings }) - } + const { editingComponent, handleSave, updateBindings } = useComponentBindingDialog({ + component, + open, + onOpenChange, + onSave, + }) if (!editingComponent) return null diff --git a/src/hooks/use-component-binding-dialog.ts b/src/hooks/use-component-binding-dialog.ts new file mode 100644 index 0000000..148c651 --- /dev/null +++ b/src/hooks/use-component-binding-dialog.ts @@ -0,0 +1,40 @@ +import { useCallback, useEffect, useState } from 'react' +import { UIComponent } from '@/types/json-ui' + +interface UseComponentBindingDialogOptions { + component: UIComponent | null + open: boolean + onOpenChange: (open: boolean) => void + onSave: (component: UIComponent) => void +} + +export function useComponentBindingDialog({ + component, + open, + onOpenChange, + onSave, +}: UseComponentBindingDialogOptions) { + const [editingComponent, setEditingComponent] = useState(component) + + useEffect(() => { + if (open) { + setEditingComponent(component) + } + }, [component, open]) + + const updateBindings = useCallback((bindings: Record) => { + setEditingComponent(prev => (prev ? { ...prev, bindings } : prev)) + }, []) + + const handleSave = useCallback(() => { + if (!editingComponent) return + onSave(editingComponent) + onOpenChange(false) + }, [editingComponent, onOpenChange, onSave]) + + return { + editingComponent, + handleSave, + updateBindings, + } +} From 9cd0ed818aecc135aa0244c6add605eec36e47d4 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Sun, 18 Jan 2026 01:25:37 +0000 Subject: [PATCH 3/4] Add shared Next.js config props type --- .../project-settings/NextJsApplicationCard.tsx | 9 ++------- src/components/project-settings/NextJsConfigTab.tsx | 9 ++------- src/components/project-settings/NextJsFeaturesCard.tsx | 9 ++------- src/components/project-settings/types.ts | 6 ++++++ 4 files changed, 12 insertions(+), 21 deletions(-) create mode 100644 src/components/project-settings/types.ts diff --git a/src/components/project-settings/NextJsApplicationCard.tsx b/src/components/project-settings/NextJsApplicationCard.tsx index ac97ba6..a9ab41d 100644 --- a/src/components/project-settings/NextJsApplicationCard.tsx +++ b/src/components/project-settings/NextJsApplicationCard.tsx @@ -1,18 +1,13 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' -import { NextJsConfig } from '@/types/project' import projectSettingsCopy from '@/data/project-settings.json' - -interface NextJsApplicationCardProps { - nextjsConfig: NextJsConfig - onNextjsConfigChange: (config: NextJsConfig | ((current: NextJsConfig) => NextJsConfig)) => void -} +import { NextJsConfigSectionProps } from '@/components/project-settings/types' export function NextJsApplicationCard({ nextjsConfig, onNextjsConfigChange, -}: NextJsApplicationCardProps) { +}: NextJsConfigSectionProps) { const { application } = projectSettingsCopy.nextjs return ( diff --git a/src/components/project-settings/NextJsConfigTab.tsx b/src/components/project-settings/NextJsConfigTab.tsx index fab7665..43501bc 100644 --- a/src/components/project-settings/NextJsConfigTab.tsx +++ b/src/components/project-settings/NextJsConfigTab.tsx @@ -1,16 +1,11 @@ -import { NextJsConfig } from '@/types/project' import { NextJsApplicationCard } from '@/components/project-settings/NextJsApplicationCard' import { NextJsFeaturesCard } from '@/components/project-settings/NextJsFeaturesCard' - -interface NextJsConfigTabProps { - nextjsConfig: NextJsConfig - onNextjsConfigChange: (config: NextJsConfig | ((current: NextJsConfig) => NextJsConfig)) => void -} +import { NextJsConfigSectionProps } from '@/components/project-settings/types' export function NextJsConfigTab({ nextjsConfig, onNextjsConfigChange, -}: NextJsConfigTabProps) { +}: NextJsConfigSectionProps) { return (
NextJsConfig)) => void -} +import { NextJsConfigSectionProps } from '@/components/project-settings/types' export function NextJsFeaturesCard({ nextjsConfig, onNextjsConfigChange, -}: NextJsFeaturesCardProps) { +}: NextJsConfigSectionProps) { const { features } = projectSettingsCopy.nextjs return ( diff --git a/src/components/project-settings/types.ts b/src/components/project-settings/types.ts new file mode 100644 index 0000000..eeead81 --- /dev/null +++ b/src/components/project-settings/types.ts @@ -0,0 +1,6 @@ +import { NextJsConfig } from '@/types/project' + +export type NextJsConfigSectionProps = { + nextjsConfig: NextJsConfig + onNextjsConfigChange: (config: NextJsConfig | ((current: NextJsConfig) => NextJsConfig)) => void +} From 925adc9712dd34c47a738c4b79baa4de9ea0aac8 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Sun, 18 Jan 2026 01:26:08 +0000 Subject: [PATCH 4/4] Add shared global search types --- .../global-search/RecentSearches.tsx | 20 +------------------ .../global-search/SearchResults.tsx | 11 +--------- src/components/global-search/types.ts | 18 +++++++++++++++++ .../global-search/useGlobalSearchData.tsx | 20 +------------------ 4 files changed, 21 insertions(+), 48 deletions(-) create mode 100644 src/components/global-search/types.ts diff --git a/src/components/global-search/RecentSearches.tsx b/src/components/global-search/RecentSearches.tsx index 99b2728..161b134 100644 --- a/src/components/global-search/RecentSearches.tsx +++ b/src/components/global-search/RecentSearches.tsx @@ -2,25 +2,7 @@ import { ClockCounterClockwise, X } from '@phosphor-icons/react' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { CommandGroup, CommandItem, CommandSeparator } from '@/components/ui/command' - -interface SearchHistoryItem { - id: string - query: string - timestamp: number - resultId?: string - resultTitle?: string - resultCategory?: string -} - -interface SearchResult { - id: string - title: string - subtitle?: string - category: string - icon: React.ReactNode - action: () => void - tags?: string[] -} +import type { SearchHistoryItem, SearchResult } from './types' interface RecentSearchesProps { recentSearches: Array<{ historyItem: SearchHistoryItem; result?: SearchResult }> diff --git a/src/components/global-search/SearchResults.tsx b/src/components/global-search/SearchResults.tsx index 87a8dc1..d5093ba 100644 --- a/src/components/global-search/SearchResults.tsx +++ b/src/components/global-search/SearchResults.tsx @@ -1,15 +1,6 @@ import { Badge } from '@/components/ui/badge' import { CommandGroup, CommandItem, CommandSeparator } from '@/components/ui/command' - -interface SearchResult { - id: string - title: string - subtitle?: string - category: string - icon: React.ReactNode - action: () => void - tags?: string[] -} +import type { SearchResult } from './types' interface SearchResultsProps { groupedResults: Record diff --git a/src/components/global-search/types.ts b/src/components/global-search/types.ts new file mode 100644 index 0000000..8fa320f --- /dev/null +++ b/src/components/global-search/types.ts @@ -0,0 +1,18 @@ +export interface SearchResult { + id: string + title: string + subtitle?: string + category: string + icon: React.ReactNode + action: () => void + tags?: string[] +} + +export interface SearchHistoryItem { + id: string + query: string + timestamp: number + resultId?: string + resultTitle?: string + resultCategory?: string +} diff --git a/src/components/global-search/useGlobalSearchData.tsx b/src/components/global-search/useGlobalSearchData.tsx index cb217b3..26113b4 100644 --- a/src/components/global-search/useGlobalSearchData.tsx +++ b/src/components/global-search/useGlobalSearchData.tsx @@ -33,25 +33,7 @@ import { Workflow, } from '@/types/project' import navigationData from '@/data/global-search.json' - -export interface SearchResult { - id: string - title: string - subtitle?: string - category: string - icon: React.ReactNode - action: () => void - tags?: string[] -} - -export interface SearchHistoryItem { - id: string - query: string - timestamp: number - resultId?: string - resultTitle?: string - resultCategory?: string -} +import type { SearchHistoryItem, SearchResult } from './types' const navigationIconMap = { BookOpen,