mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
improvement(stores/ui): consolidated init/create of workflows and fixed hydration error
This commit is contained in:
+4
-16
@@ -46,7 +46,7 @@ function WorkflowContent() {
|
||||
|
||||
// Store access
|
||||
const { addNotification } = useNotificationStore()
|
||||
const { workflows, setActiveWorkflow, addWorkflow } = useWorkflowRegistry()
|
||||
const { workflows, setActiveWorkflow, createWorkflow } = useWorkflowRegistry()
|
||||
const { blocks, edges, loops, addBlock, updateBlockPosition, addEdge, removeEdge } =
|
||||
useWorkflowStore()
|
||||
|
||||
@@ -65,25 +65,13 @@ function WorkflowContent() {
|
||||
useEffect(() => {
|
||||
if (!isInitialized) return
|
||||
|
||||
const createInitialWorkflow = () => {
|
||||
const id = crypto.randomUUID()
|
||||
const newWorkflow = {
|
||||
id,
|
||||
name: 'Workflow 1',
|
||||
lastModified: new Date(),
|
||||
description: 'New workflow',
|
||||
color: '#3972F6',
|
||||
}
|
||||
addWorkflow(newWorkflow)
|
||||
return id
|
||||
}
|
||||
|
||||
const validateAndNavigate = () => {
|
||||
const workflowIds = Object.keys(workflows)
|
||||
const currentId = params.id as string
|
||||
|
||||
if (workflowIds.length === 0) {
|
||||
const newId = createInitialWorkflow()
|
||||
// Create initial workflow using the centralized function
|
||||
const newId = createWorkflow({ isInitial: true })
|
||||
router.replace(`/w/${newId}`)
|
||||
return
|
||||
}
|
||||
@@ -97,7 +85,7 @@ function WorkflowContent() {
|
||||
}
|
||||
|
||||
validateAndNavigate()
|
||||
}, [params.id, workflows, setActiveWorkflow, addWorkflow, router, isInitialized])
|
||||
}, [params.id, workflows, setActiveWorkflow, createWorkflow, router, isInitialized])
|
||||
|
||||
// Transform blocks and loops into ReactFlow nodes
|
||||
const nodes = useMemo(() => {
|
||||
|
||||
@@ -12,31 +12,12 @@ import { NavItem } from './components/nav-item/nav-item'
|
||||
import { SettingsModal } from './components/settings-modal/settings-modal'
|
||||
|
||||
export function Sidebar() {
|
||||
const WORKFLOW_COLORS = ['#3972F6', '#F639DD', '#F6B539', '#8139F6', '#F64439']
|
||||
const { workflows, addWorkflow } = useWorkflowRegistry()
|
||||
const { workflows, createWorkflow } = useWorkflowRegistry()
|
||||
const router = useRouter()
|
||||
const [showSettings, setShowSettings] = useState(false)
|
||||
|
||||
const handleCreateWorkflow = () => {
|
||||
const id = crypto.randomUUID()
|
||||
const workflowArray = Object.values(workflows)
|
||||
const lastWorkflow = workflowArray[workflowArray.length - 1]
|
||||
|
||||
// Find the index of the last used color, defaulting to first color if undefined
|
||||
const lastColorIndex = lastWorkflow?.color ? WORKFLOW_COLORS.indexOf(lastWorkflow.color) : -1
|
||||
|
||||
// Get next color index, wrapping around to 0 if we reach the end
|
||||
const nextColorIndex = (lastColorIndex + 1) % WORKFLOW_COLORS.length
|
||||
|
||||
const newWorkflow = {
|
||||
id,
|
||||
name: `Workflow ${workflowArray.length + 1}`,
|
||||
lastModified: new Date(),
|
||||
description: 'New workflow',
|
||||
color: WORKFLOW_COLORS[nextColorIndex],
|
||||
}
|
||||
|
||||
addWorkflow(newWorkflow)
|
||||
const id = createWorkflow()
|
||||
router.push(`/w/${id}`)
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,6 @@ export function Toolbar() {
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
name={`search-input-${Math.random()}`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,8 +2,7 @@ import * as React from 'react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<'input'>>(
|
||||
({ className, type, autoComplete = 'off', name, ...props }, ref) => {
|
||||
const randomName = name || `input-${Math.random()}`
|
||||
({ className, type, autoComplete = 'off', ...props }, ref) => {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
@@ -16,7 +15,6 @@ const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<'input'>>(
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
name={randomName}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ import { addDeletedWorkflow } from '../../sync-manager'
|
||||
import { useWorkflowStore } from '../store'
|
||||
import { useSubBlockStore } from '../subblock/store'
|
||||
import { WorkflowMetadata, WorkflowRegistry } from './types'
|
||||
import { generateUniqueName } from './utils'
|
||||
import { generateUniqueName, getNextWorkflowColor } from './utils'
|
||||
|
||||
export const useWorkflowRegistry = create<WorkflowRegistry>()(
|
||||
devtools(
|
||||
@@ -81,18 +81,23 @@ export const useWorkflowRegistry = create<WorkflowRegistry>()(
|
||||
set({ activeWorkflowId: id, error: null })
|
||||
},
|
||||
|
||||
// Create new workflow with default starter block
|
||||
addWorkflow: (metadata: WorkflowMetadata) => {
|
||||
const uniqueName = generateUniqueName(get().workflows)
|
||||
const updatedMetadata = { ...metadata, name: uniqueName }
|
||||
/**
|
||||
* Creates a new workflow with appropriate metadata and initial blocks
|
||||
* @param options - Optional configuration for workflow creation
|
||||
* @returns The ID of the newly created workflow
|
||||
*/
|
||||
createWorkflow: (options = {}) => {
|
||||
const { workflows } = get()
|
||||
const id = crypto.randomUUID()
|
||||
|
||||
set((state) => ({
|
||||
workflows: {
|
||||
...state.workflows,
|
||||
[metadata.id]: updatedMetadata,
|
||||
},
|
||||
error: null,
|
||||
}))
|
||||
// Generate workflow metadata with appropriate name and color
|
||||
const newWorkflow: WorkflowMetadata = {
|
||||
id,
|
||||
name: generateUniqueName(workflows),
|
||||
lastModified: new Date(),
|
||||
description: 'New workflow',
|
||||
color: getNextWorkflowColor(workflows),
|
||||
}
|
||||
|
||||
// Create starter block for new workflow
|
||||
const starterId = crypto.randomUUID()
|
||||
@@ -210,18 +215,29 @@ export const useWorkflowRegistry = create<WorkflowRegistry>()(
|
||||
lastSaved: Date.now(),
|
||||
}
|
||||
|
||||
// Add workflow to registry
|
||||
set((state) => ({
|
||||
workflows: {
|
||||
...state.workflows,
|
||||
[id]: newWorkflow,
|
||||
},
|
||||
error: null,
|
||||
}))
|
||||
|
||||
// Save workflow list to localStorage
|
||||
const workflows = get().workflows
|
||||
localStorage.setItem('workflow-registry', JSON.stringify(workflows))
|
||||
const updatedWorkflows = get().workflows
|
||||
localStorage.setItem('workflow-registry', JSON.stringify(updatedWorkflows))
|
||||
|
||||
// Save initial workflow state to localStorage
|
||||
localStorage.setItem(`workflow-${metadata.id}`, JSON.stringify(initialState))
|
||||
localStorage.setItem(`workflow-${id}`, JSON.stringify(initialState))
|
||||
|
||||
// If this is the first workflow, set it as active and update workflow store
|
||||
if (Object.keys(workflows).length === 1) {
|
||||
set({ activeWorkflowId: metadata.id })
|
||||
// If this is the first workflow or it's an initial workflow, set it as active
|
||||
if (options.isInitial || Object.keys(updatedWorkflows).length === 1) {
|
||||
set({ activeWorkflowId: id })
|
||||
useWorkflowStore.setState(initialState)
|
||||
}
|
||||
|
||||
return id
|
||||
},
|
||||
|
||||
// Delete workflow and clean up associated storage
|
||||
|
||||
@@ -15,9 +15,9 @@ export interface WorkflowRegistryState {
|
||||
|
||||
export interface WorkflowRegistryActions {
|
||||
setActiveWorkflow: (id: string) => Promise<void>
|
||||
addWorkflow: (metadata: WorkflowMetadata) => void
|
||||
removeWorkflow: (id: string) => void
|
||||
updateWorkflow: (id: string, metadata: Partial<WorkflowMetadata>) => void
|
||||
createWorkflow: (options?: { isInitial?: boolean }) => string
|
||||
}
|
||||
|
||||
export type WorkflowRegistry = WorkflowRegistryState & WorkflowRegistryActions
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { WorkflowMetadata } from './types'
|
||||
|
||||
// Available workflow colors
|
||||
export const WORKFLOW_COLORS = ['#3972F6', '#F639DD', '#F6B539', '#8139F6', '#F64439']
|
||||
|
||||
export function generateUniqueName(existingWorkflows: Record<string, WorkflowMetadata>): string {
|
||||
// Extract numbers from existing workflow names using regex
|
||||
const numbers = Object.values(existingWorkflows)
|
||||
@@ -17,3 +20,26 @@ export function generateUniqueName(existingWorkflows: Record<string, WorkflowMet
|
||||
const nextNumber = Math.max(...numbers) + 1
|
||||
return `Workflow ${nextNumber}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the next color to use for a new workflow based on the last used color
|
||||
* @param existingWorkflows - Current workflows in the registry
|
||||
* @returns The next color from the predefined color palette
|
||||
*/
|
||||
export function getNextWorkflowColor(existingWorkflows: Record<string, WorkflowMetadata>): string {
|
||||
const workflowArray = Object.values(existingWorkflows)
|
||||
|
||||
if (workflowArray.length === 0) {
|
||||
return WORKFLOW_COLORS[0]
|
||||
}
|
||||
|
||||
const lastWorkflow = workflowArray[workflowArray.length - 1]
|
||||
|
||||
// Find the index of the last used color, defaulting to first color if undefined
|
||||
const lastColorIndex = lastWorkflow?.color ? WORKFLOW_COLORS.indexOf(lastWorkflow.color) : -1
|
||||
|
||||
// Get next color index, wrapping around to 0 if we reach the end
|
||||
const nextColorIndex = (lastColorIndex + 1) % WORKFLOW_COLORS.length
|
||||
|
||||
return WORKFLOW_COLORS[nextColorIndex]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user