From a3466c105beeb1f152f7f7280df9a1ba6daa79c6 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Tue, 22 Jul 2025 15:28:43 -0700 Subject: [PATCH 1/3] use getEnv on the client-side when we need to inject vars for docker runtime, fix folder container & removed folder/subfolder creation modals --- .../components/deploy-modal/deploy-modal.tsx | 8 +- .../components/google-drive-picker.tsx | 4 +- .../file-selector/file-selector-input.tsx | 6 +- .../components/create-menu/create-menu.tsx | 110 ++++++++--------- .../folder-context-menu.tsx | 111 ++++++++---------- .../folder-tree/components/folder-item.tsx | 6 +- .../folder-tree/components/workflow-item.tsx | 4 +- .../workspace-selector/workspace-selector.tsx | 31 ++--- .../w/components/sidebar/sidebar.tsx | 20 +++- apps/sim/contexts/socket-context.tsx | 4 +- .../executor/handlers/agent/agent-handler.ts | 4 +- apps/sim/instrumentation-client.ts | 4 +- apps/sim/lib/auth-client.ts | 10 +- apps/sim/lib/env.ts | 18 +-- apps/sim/lib/urls/utils.ts | 6 +- apps/sim/lib/workflows/utils.ts | 4 +- apps/sim/tools/http/request.ts | 4 +- 17 files changed, 171 insertions(+), 183 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/deploy-modal/deploy-modal.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/deploy-modal/deploy-modal.tsx index 30238ca624..2cf5ac229c 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/deploy-modal/deploy-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/deploy-modal/deploy-modal.tsx @@ -17,7 +17,7 @@ import { Button } from '@/components/ui/button' import { Card, CardContent } from '@/components/ui/card' import { CopyButton } from '@/components/ui/copy-button' import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog' -import { env } from '@/lib/env' +import { getEnv } from '@/lib/env' import { createLogger } from '@/lib/logs/console-logger' import { cn } from '@/lib/utils' import { ChatDeploy } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/deploy-modal/components/chat-deploy/chat-deploy' @@ -225,7 +225,7 @@ export function DeployModal({ } const data = await response.json() - const endpoint = `${env.NEXT_PUBLIC_APP_URL}/api/workflows/${workflowId}/execute` + const endpoint = `${getEnv('NEXT_PUBLIC_APP_URL')}/api/workflows/${workflowId}/execute` const inputFormatExample = getInputFormatExample() setDeploymentInfo({ @@ -288,7 +288,7 @@ export function DeployModal({ } // Update the local deployment info - const endpoint = `${env.NEXT_PUBLIC_APP_URL}/api/workflows/${workflowId}/execute` + const endpoint = `${getEnv('NEXT_PUBLIC_APP_URL')}/api/workflows/${workflowId}/execute` const inputFormatExample = getInputFormatExample() const newDeploymentInfo = { @@ -597,7 +597,7 @@ export function DeployModal({ { diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/file-selector/file-selector-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/file-selector/file-selector-input.tsx index aae757d5e7..aa3a6ec837 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/file-selector/file-selector-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/file-selector/file-selector-input.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip' -import { env } from '@/lib/env' +import { getEnv } from '@/lib/env' import type { SubBlockConfig } from '@/blocks/types' import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow' import { useWorkflowRegistry } from '@/stores/workflows/registry/store' @@ -169,8 +169,8 @@ export function FileSelectorInput({ } // For Google Drive - const clientId = env.NEXT_PUBLIC_GOOGLE_CLIENT_ID || '' - const apiKey = env.NEXT_PUBLIC_GOOGLE_API_KEY || '' + const clientId = getEnv('NEXT_PUBLIC_GOOGLE_CLIENT_ID') || '' + const apiKey = getEnv('NEXT_PUBLIC_GOOGLE_API_KEY') || '' // Render Google Calendar selector if (isGoogleCalendar) { diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/create-menu/create-menu.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/create-menu/create-menu.tsx index 7e887cf5a6..e7e709d4da 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/create-menu/create-menu.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/create-menu/create-menu.tsx @@ -5,9 +5,6 @@ import { logger } from '@sentry/nextjs' import { File, Folder, Plus, Upload } from 'lucide-react' import { useParams, useRouter } from 'next/navigation' import { Button } from '@/components/ui/button' -import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog' -import { Input } from '@/components/ui/input' -import { Label } from '@/components/ui/label' import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' import { cn } from '@/lib/utils' import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/w/components/providers/workspace-permissions-provider' @@ -25,8 +22,6 @@ export function CreateMenu({ isCollapsed, isCreatingWorkflow = false, }: CreateMenuProps) { - const [showFolderDialog, setShowFolderDialog] = useState(false) - const [folderName, setFolderName] = useState('') const [isCreating, setIsCreating] = useState(false) const [isOpen, setIsOpen] = useState(false) const [pressTimer, setPressTimer] = useState(null) @@ -61,10 +56,47 @@ export function CreateMenu({ } }, [onCreateWorkflow, isCreatingWorkflow, router, workspaceId]) - const handleCreateFolder = useCallback(() => { + const handleCreateFolder = useCallback(async () => { setIsOpen(false) - setShowFolderDialog(true) - }, []) + + if (isCreating) { + logger.info('Folder creation already in progress, ignoring request') + return + } + + if (!workspaceId) { + logger.error('No workspaceId available for folder creation') + return + } + + try { + setIsCreating(true) + + // Get existing folders to find the next folder number + const { folders } = useFolderStore.getState() + const existingFolders = Object.values(folders).filter((f) => f.workspaceId === workspaceId) + + // Find the next available folder number (always use highest + 1) + const folderNumbers = existingFolders + .map((f) => f.name.match(/^Folder (\d+)$/)) + .filter((match) => match !== null) + .map((match) => Number.parseInt(match![1], 10)) + + const nextNumber = folderNumbers.length > 0 ? Math.max(...folderNumbers) + 1 : 1 + const folderName = `Folder ${nextNumber}` + + await createFolder({ + name: folderName, + workspaceId: workspaceId, + }) + + logger.info(`Created folder: ${folderName}`) + } catch (error) { + logger.error('Failed to create folder:', { error }) + } finally { + setIsCreating(false) + } + }, [createFolder, workspaceId, isCreating]) const handleImportWorkflow = useCallback(() => { setIsOpen(false) @@ -146,30 +178,6 @@ export function CreateMenu({ } }, [pressTimer]) - const handleFolderSubmit = async (e: React.FormEvent) => { - e.preventDefault() - if (!folderName.trim() || !workspaceId) return - - setIsCreating(true) - try { - await createFolder({ - name: folderName.trim(), - workspaceId: workspaceId, - }) - setFolderName('') - setShowFolderDialog(false) - } catch (error) { - logger.error('Failed to create folder:', { error }) - } finally { - setIsCreating(false) - } - } - - const handleCancel = () => { - setFolderName('') - setShowFolderDialog(false) - } - return ( <> @@ -221,11 +229,15 @@ export function CreateMenu({ {userPermissions.canEdit && ( @@ -246,36 +258,6 @@ export function CreateMenu({ disabled={!userPermissions.canEdit} onClose={() => setIsOpen(false)} /> - - {/* Folder creation dialog */} - - - - Create New Folder - -
-
- - setFolderName(e.target.value)} - placeholder='Enter folder name...' - autoFocus - required - /> -
-
- - -
-
-
-
) } diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/folder-context-menu/folder-context-menu.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/folder-context-menu/folder-context-menu.tsx index bc6432283b..afababa63c 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/folder-context-menu/folder-context-menu.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/folder-context-menu/folder-context-menu.tsx @@ -4,15 +4,12 @@ import { useState } from 'react' import { File, Folder, MoreHorizontal, Pencil, Trash2 } from 'lucide-react' import { useParams } from 'next/navigation' import { Button } from '@/components/ui/button' -import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' -import { Input } from '@/components/ui/input' -import { Label } from '@/components/ui/label' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import { createLogger } from '@/lib/logs/console-logger' import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/w/components/providers/workspace-permissions-provider' @@ -37,8 +34,6 @@ export function FolderContextMenu({ onStartEdit, level, }: FolderContextMenuProps) { - const [showSubfolderDialog, setShowSubfolderDialog] = useState(false) - const [subfolderName, setSubfolderName] = useState('') const [isCreating, setIsCreating] = useState(false) const params = useParams() const workspaceId = params.workspaceId as string @@ -46,14 +41,58 @@ export function FolderContextMenu({ // Get user permissions for the workspace const userPermissions = useUserPermissionsContext() - const { createFolder, deleteFolder } = useFolderStore() + const { createFolder, deleteFolder, setExpanded } = useFolderStore() const handleCreateWorkflow = () => { + // Ensure folder is expanded so user can see the new workflow + setExpanded(folderId, true) onCreateWorkflow(folderId) } - const handleCreateSubfolder = () => { - setShowSubfolderDialog(true) + const handleCreateSubfolder = async () => { + if (isCreating) { + logger.info('Subfolder creation already in progress, ignoring request') + return + } + + if (!workspaceId) { + logger.error('No workspaceId available for subfolder creation') + return + } + + try { + setIsCreating(true) + + // Ensure parent folder is expanded so user can see the new subfolder + setExpanded(folderId, true) + + // Get existing folders to find the next subfolder number + const { folders } = useFolderStore.getState() + const existingSubfolders = Object.values(folders).filter( + (f) => f.workspaceId === workspaceId && f.parentId === folderId + ) + + // Find the next available subfolder number (always use highest + 1) + const subfolderNumbers = existingSubfolders + .map((f) => f.name.match(/^Subfolder (\d+)$/)) + .filter((match) => match !== null) + .map((match) => Number.parseInt(match![1], 10)) + + const nextNumber = subfolderNumbers.length > 0 ? Math.max(...subfolderNumbers) + 1 : 1 + const subfolderName = `Subfolder ${nextNumber}` + + await createFolder({ + name: subfolderName, + workspaceId: workspaceId, + parentId: folderId, + }) + + logger.info(`Created subfolder: ${subfolderName}`) + } catch (error) { + logger.error('Failed to create subfolder:', { error }) + } finally { + setIsCreating(false) + } } const handleRename = () => { @@ -76,31 +115,6 @@ export function FolderContextMenu({ } } - const handleSubfolderSubmit = async (e: React.FormEvent) => { - e.preventDefault() - if (!subfolderName.trim() || !workspaceId) return - - setIsCreating(true) - try { - await createFolder({ - name: subfolderName.trim(), - workspaceId: workspaceId, - parentId: folderId, - }) - setSubfolderName('') - setShowSubfolderDialog(false) - } catch (error) { - logger.error('Failed to create subfolder:', { error }) - } finally { - setIsCreating(false) - } - } - - const handleCancel = () => { - setSubfolderName('') - setShowSubfolderDialog(false) - } - return ( <> @@ -175,37 +189,6 @@ export function FolderContextMenu({ )} - - {/* Subfolder creation dialog */} - - e.stopPropagation()}> - - Create New Subfolder - -
-
- - setSubfolderName(e.target.value)} - placeholder='Enter folder name...' - maxLength={50} - autoFocus - required - /> -
-
- - -
-
-
-
) } diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/folder-tree/components/folder-item.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/folder-tree/components/folder-item.tsx index 0a6fe0ce61..f426148d13 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/folder-tree/components/folder-item.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/folder-tree/components/folder-item.tsx @@ -304,7 +304,7 @@ export function FolderItem({ onChange={(e) => setEditValue(e.target.value)} onKeyDown={handleKeyDown} onBlur={handleInputBlur} - className='flex-1 border-0 bg-transparent p-0 text-muted-foreground text-sm outline-none focus:outline-none focus:ring-0 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0' + className='min-w-0 flex-1 border-0 bg-transparent p-0 text-muted-foreground text-sm outline-none focus:outline-none focus:ring-0 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0' maxLength={50} disabled={isRenaming} onClick={(e) => e.stopPropagation()} // Prevent folder toggle when clicking input @@ -314,7 +314,9 @@ export function FolderItem({ spellCheck='false' /> ) : ( - {folder.name} + + {folder.name} + )} {!isEditing && ( diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/folder-tree/components/workflow-item.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/folder-tree/components/workflow-item.tsx index e17a7eaffa..5945d69ddb 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/folder-tree/components/workflow-item.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/folder-tree/components/workflow-item.tsx @@ -225,7 +225,7 @@ export function WorkflowItem({ onChange={(e) => setEditValue(e.target.value)} onKeyDown={handleKeyDown} onBlur={handleInputBlur} - className={`flex-1 border-0 bg-transparent p-0 font-medium text-sm outline-none focus:outline-none focus:ring-0 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 ${ + className={`min-w-0 flex-1 border-0 bg-transparent p-0 font-medium text-sm outline-none focus:outline-none focus:ring-0 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 ${ active && !isDragOver ? 'text-foreground' : 'text-muted-foreground' }`} maxLength={100} @@ -237,7 +237,7 @@ export function WorkflowItem({ spellCheck='false' /> ) : ( - + {workflow.name} {isMarketplace && ' (Preview)'} diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-selector/workspace-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-selector/workspace-selector.tsx index 3a5548f423..49b32a6cb6 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-selector/workspace-selector.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-selector/workspace-selector.tsx @@ -16,6 +16,7 @@ import { import { Button } from '@/components/ui/button' import { ScrollArea } from '@/components/ui/scroll-area' import { Skeleton } from '@/components/ui/skeleton' +import { isDev } from '@/lib/environment' import { createLogger } from '@/lib/logs/console-logger' import { cn } from '@/lib/utils' import { useUserPermissionsContext } from '../../../providers/workspace-permissions-provider' @@ -251,20 +252,22 @@ export function WorkspaceSelector({ {/* Bottom Actions */}
- {/* Send Invite */} - + {/* Send Invite - Hide in development */} + {!isDev && ( + + )} {/* Create Workspace */}