- |
-
- |
{Array.from({ length: COLUMN_COUNT }).map((_, colIndex) => (
-}
-
-export default async function TablesPage({ params }: TablesPageProps) {
- const { workspaceId } = await params
- const session = await getSession()
-
- if (!session?.user?.id) {
- redirect('/')
- }
-
- const hasPermission = await verifyWorkspaceMembership(session.user.id, workspaceId)
- if (!hasPermission) {
- redirect('/')
- }
-
- const permissionConfig = await getUserPermissionConfig(session.user.id)
- if (permissionConfig?.hideTablesTab) {
- redirect(`/workspace/${workspaceId}`)
- }
-
- return
-}
+export default Tables
diff --git a/apps/sim/app/workspace/[workspaceId]/tables/tables.tsx b/apps/sim/app/workspace/[workspaceId]/tables/tables.tsx
index 95ed0801d9f..a26aa3a18ed 100644
--- a/apps/sim/app/workspace/[workspaceId]/tables/tables.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/tables/tables.tsx
@@ -1,6 +1,6 @@
'use client'
-import { useCallback, useMemo, useRef, useState } from 'react'
+import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { createLogger } from '@sim/logger'
import { useParams, useRouter } from 'next/navigation'
import type { ComboboxOption } from '@/components/emcn'
@@ -38,6 +38,7 @@ import {
} from '@/hooks/queries/tables'
import { useWorkspaceMembersQuery } from '@/hooks/queries/workspace'
import { useDebounce } from '@/hooks/use-debounce'
+import { usePermissionConfig } from '@/hooks/use-permission-config'
const logger = createLogger('Tables')
@@ -54,6 +55,14 @@ export function Tables() {
const params = useParams()
const router = useRouter()
const workspaceId = params.workspaceId as string
+
+ const { config: permissionConfig } = usePermissionConfig()
+ useEffect(() => {
+ if (permissionConfig.hideTablesTab) {
+ router.replace(`/workspace/${workspaceId}`)
+ }
+ }, [permissionConfig.hideTablesTab, router, workspaceId])
+
const userPermissions = useUserPermissionsContext()
const { data: tables = [], isLoading, error } = useTablesList(workspaceId)
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/knowledge-tag-filters/knowledge-tag-filters.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/knowledge-tag-filters/knowledge-tag-filters.tsx
index ad3c5478845..5963ec9a829 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/knowledge-tag-filters/knowledge-tag-filters.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/knowledge-tag-filters/knowledge-tag-filters.tsx
@@ -196,7 +196,7 @@ export function KnowledgeTagFilters({
if (isReadOnly) return
const updatedFilters = filters.map((f) => (f.id === id ? { ...f, [field]: value } : f))
- const jsonValue = updatedFilters.length > 0 ? JSON.stringify(updatedFilters) : null
+ const jsonValue = updatedFilters.length > 0 ? JSON.stringify(updatedFilters) : ''
emitTagSelection(jsonValue)
}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/workflow-item/workflow-item.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/workflow-item/workflow-item.tsx
index 37f276ff686..07185435df3 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/workflow-item/workflow-item.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/workflow-item/workflow-item.tsx
@@ -388,12 +388,13 @@ export function WorkflowItem({
data-item-id={workflow.id}
className={clsx(
'group mx-0.5 flex h-[30px] items-center gap-2 rounded-lg px-2 text-sm',
- (active || isContextMenuOpen) && 'bg-[var(--surface-active)]',
+ (active || isContextMenuOpen || (isSelected && selectedWorkflows.size > 1)) &&
+ 'bg-[var(--surface-active)]',
!active &&
!isContextMenuOpen &&
+ !(isSelected && selectedWorkflows.size > 1) &&
!isAnyDragActive &&
'hover-hover:bg-[var(--surface-hover)]',
- isSelected && selectedWorkflows.size > 1 && !active && 'bg-[var(--surface-active)]',
(isDragging || (isAnyDragActive && isSelected)) && 'opacity-50'
)}
draggable={!isEditing && !dragDisabled}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.tsx
index fe33dc9ab00..93defdd2d05 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.tsx
@@ -120,6 +120,7 @@ export function WorkspaceHeader({
const [contextMenuPosition, setContextMenuPosition] = useState({ x: 0, y: 0 })
const [isContextMenuOpen, setIsContextMenuOpen] = useState(false)
+ const [menuOpenWorkspaceId, setMenuOpenWorkspaceId] = useState(null)
const contextMenuRef = useRef(null)
const capturedWorkspaceRef = useRef(null)
const isRenamingRef = useRef(false)
@@ -185,6 +186,7 @@ export function WorkspaceHeader({
contextMenuClosedRef.current = false
capturedWorkspaceRef.current = workspace
+ setMenuOpenWorkspaceId(workspace.id)
setContextMenuPosition({ x, y })
setIsContextMenuOpen(true)
}
@@ -210,6 +212,7 @@ export function WorkspaceHeader({
contextMenuClosedRef.current = true
setIsContextMenuOpen(false)
+ setMenuOpenWorkspaceId(null)
const isOpeningAnother = isContextMenuOpeningRef.current
isContextMenuOpeningRef.current = false
if (!isRenamingRef.current && !isOpeningAnother) {
@@ -494,8 +497,11 @@ export function WorkspaceHeader({
className={cn(
'group flex cursor-pointer select-none items-center gap-2 rounded-[5px] px-2 py-[5px] font-medium text-[var(--text-body)] text-caption outline-none transition-colors',
workspace.id !== workspaceId &&
+ menuOpenWorkspaceId !== workspace.id &&
'hover-hover:bg-[var(--surface-hover)]',
- workspace.id === workspaceId && 'bg-[var(--surface-active)]'
+ (workspace.id === workspaceId ||
+ menuOpenWorkspaceId === workspace.id) &&
+ 'bg-[var(--surface-active)]'
)}
onClick={() => onWorkspaceSwitch(workspace)}
onContextMenu={(e) => handleContextMenu(e, workspace)}
@@ -513,7 +519,10 @@ export function WorkspaceHeader({
const rect = e.currentTarget.getBoundingClientRect()
openContextMenuAt(workspace, rect.right, rect.top)
}}
- className='flex h-[18px] w-[18px] flex-shrink-0 items-center justify-center rounded-sm opacity-0 transition-opacity group-hover:opacity-100'
+ className={cn(
+ 'flex h-[18px] w-[18px] flex-shrink-0 items-center justify-center rounded-sm opacity-0 transition-opacity group-hover:opacity-100',
+ menuOpenWorkspaceId === workspace.id && 'opacity-100'
+ )}
>
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx
index 7c89b20e191..e27de3a895c 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx
@@ -203,7 +203,7 @@ const SidebarTaskItem = memo(function SidebarTaskItem({
onMoreClick(e, task.id)
}}
className={cn(
- 'flex h-[18px] w-[18px] items-center justify-center rounded-sm opacity-0 group-hover:opacity-100',
+ 'flex h-[18px] w-[18px] items-center justify-center rounded-sm opacity-0 transition-opacity group-hover:opacity-100',
isMenuOpen && 'opacity-100'
)}
>
@@ -1329,11 +1329,8 @@ export const Sidebar = memo(function Sidebar() {
!hasOverflowTop && 'border-transparent'
)}
>
-
-
+
+
All tasks
{!isCollapsed && (
@@ -1454,10 +1451,10 @@ export const Sidebar = memo(function Sidebar() {
-
+
Workflows
{!isCollapsed && (
diff --git a/apps/sim/hooks/kb/use-tag-selection.ts b/apps/sim/hooks/kb/use-tag-selection.ts
index 37ef53f9b97..490a75f8015 100644
--- a/apps/sim/hooks/kb/use-tag-selection.ts
+++ b/apps/sim/hooks/kb/use-tag-selection.ts
@@ -9,7 +9,7 @@ export function useTagSelection(blockId: string, subblockId: string) {
const { collaborativeSetTagSelection } = useCollaborativeWorkflow()
const emitTagSelectionValue = useCallback(
- (value: any) => {
+ (value: string) => {
collaborativeSetTagSelection(blockId, subblockId, value)
},
[blockId, subblockId, collaborativeSetTagSelection]
diff --git a/apps/sim/hooks/queries/workspace-files.ts b/apps/sim/hooks/queries/workspace-files.ts
index 074ed3b8c8d..befea6e7f65 100644
--- a/apps/sim/hooks/queries/workspace-files.ts
+++ b/apps/sim/hooks/queries/workspace-files.ts
@@ -33,6 +33,23 @@ export interface StorageInfo {
plan?: string
}
+/**
+ * Hook to fetch a single workspace file record by ID.
+ * Shares the `list(workspaceId, 'active')` query key with {@link useWorkspaceFiles} so no extra
+ * network request is made when the list is already cached (warm path).
+ * On a cold path (e.g. direct navigation to a file URL), this fetches the full active file list
+ * for the workspace and selects the matching record via `select`.
+ */
+export function useWorkspaceFileRecord(workspaceId: string, fileId: string) {
+ return useQuery({
+ queryKey: workspaceFilesKeys.list(workspaceId, 'active'),
+ queryFn: ({ signal }) => fetchWorkspaceFiles(workspaceId, 'active', signal),
+ enabled: !!workspaceId && !!fileId,
+ staleTime: 30 * 1000,
+ select: (files) => files.find((f) => f.id === fileId) ?? null,
+ })
+}
+
/**
* Fetch workspace files from API
*/
diff --git a/apps/sim/hooks/use-collaborative-workflow.ts b/apps/sim/hooks/use-collaborative-workflow.ts
index b8b85b3cbd1..4093bed8b20 100644
--- a/apps/sim/hooks/use-collaborative-workflow.ts
+++ b/apps/sim/hooks/use-collaborative-workflow.ts
@@ -1281,7 +1281,7 @@ export function useCollaborativeWorkflow() {
// Immediate tag selection (uses queue but processes immediately, no debouncing)
const collaborativeSetTagSelection = useCallback(
- (blockId: string, subblockId: string, value: any) => {
+ (blockId: string, subblockId: string, value: string) => {
if (isApplyingRemoteChange.current) return
if (isBaselineDiffView) {
|