From 72c2bef2ed8717b357482ecc3df3b0e25a7a0ffc Mon Sep 17 00:00:00 2001 From: Francesco Sansalvadore Date: Mon, 23 Mar 2026 15:54:02 +0100 Subject: [PATCH] fix types --- .../TableGridEditor/TableGridEditor.tsx | 76 +++++---- .../ui/AIAssistantPanel/ModelSelector.tsx | 8 +- apps/studio/components/v2/Chooser.tsx | 2 - apps/studio/components/v2/DataListGrid.tsx | 151 ++++++++---------- apps/studio/components/v2/HomeView.tsx | 20 ++- .../v2/Navigation/MobileNavigationBar.tsx | 4 +- .../v2/Navigation/MobileSheetContext.tsx | 2 +- .../v2/Navigation/NavigationIconLink.tsx | 103 ++++++------ .../ProjectBranchSelectorTrigger.tsx | 26 ++- .../v2/Navigation/StudioMobileSheetNav.tsx | 4 +- apps/studio/components/v2/RightPanel.tsx | 11 +- apps/studio/components/v2/TabBar.tsx | 10 +- .../components/v2/V2OrgProjectSelector.tsx | 2 +- .../components/v2/V2ProjectBranchSelector.tsx | 17 +- 14 files changed, 230 insertions(+), 206 deletions(-) diff --git a/apps/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx b/apps/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx index d28fb66ade8..672e207e70e 100644 --- a/apps/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx +++ b/apps/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx @@ -7,12 +7,10 @@ import { useParams } from 'common' import { SupabaseGrid } from 'components/grid/SupabaseGrid' import { useSyncTableEditorStateFromLocalStorageWithUrl } from 'components/grid/SupabaseGrid.utils' import { - Entity, isForeignTable, isMaterializedView, isTableLike, isView, - TableLike, } from 'data/table-editor/table-editor-types' import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions' import { useDashboardHistory } from 'hooks/misc/useDashboardHistory' @@ -23,6 +21,8 @@ import { TableEditorTableStateContextProvider } from 'state/table-editor-table' import { createTabId, useTabsStateSnapshot } from 'state/tabs' import { Button } from 'ui' import { Admonition, GenericSkeletonLoader } from 'ui-patterns' + +import type { Entity, TableLike } from 'data/table-editor/table-editor-types' import DeleteConfirmationDialogs from './DeleteConfirmationDialogs' import { SidePanelEditor } from './SidePanelEditor/SidePanelEditor' import { TableDefinition } from './TableDefinition' @@ -67,14 +67,23 @@ export const TableGridEditor = ({ (table: { id: number }) => { if (variant === 'v2') { const base = projectRef ? `/v2/project/${projectRef}` : '' - router.push( - `${base}/data/tables/${table.id}/data${ - !!selectedSchema ? `?schema=${selectedSchema}` : '' - }` - ) + if (router) { + router.push( + `${base}/data/tables/${table.id}/data${ + !!selectedSchema ? `?schema=${selectedSchema}` : '' + }` + ) + } else if (typeof window !== 'undefined') { + window.location.assign( + `${base}/data/tables/${table.id}/data${ + !!selectedSchema ? `?schema=${selectedSchema}` : '' + }` + ) + } return } + if (!router) return router.push( `/project/${projectRef}/editor/${table.id}${ !!selectedSchema ? `?schema=${selectedSchema}` : '' @@ -84,6 +93,19 @@ export const TableGridEditor = ({ [projectRef, router, selectedSchema, variant] ) + const closeLegacyTab = useCallback( + (id: string) => { + if (!router) return + tabs.handleTabClose({ + id, + router, + editor: 'table', + onClearDashboardHistory: () => setLastVisitedTable(undefined), + }) + }, + [router, setLastVisitedTable, tabs] + ) + const onTableDeleted = useCallback(async () => { if (!selectedTable) return @@ -96,17 +118,16 @@ export const TableGridEditor = ({ setLastVisitedTable(undefined) const base = projectRef ? `/v2/project/${projectRef}` : '' - router.push(`${base}/data/tables`) + if (router) { + router.push(`${base}/data/tables`) + } else if (typeof window !== 'undefined') { + window.location.assign(`${base}/data/tables`) + } return } - tabs.handleTabClose({ - id: tabId, - router, - editor: 'table', - onClearDashboardHistory: () => setLastVisitedTable(undefined), - }) - }, [projectRef, router, selectedTable, tabs, setLastVisitedTable, variant]) + closeLegacyTab(tabId) + }, [closeLegacyTab, projectRef, router, selectedTable, tabs, setLastVisitedTable, variant]) const { isSchemaLocked } = useIsProtectedSchema({ schema: selectedTable?.schema ?? '' }) @@ -141,7 +162,7 @@ export const TableGridEditor = ({ return ( // When any click happens in a table tab, the tab becomes permanent -
tabs.makeActiveTabPermanent()}> +
tabs.makeActiveTabPermanent()}> {!selectedTable ? (
@@ -159,7 +180,11 @@ export const TableGridEditor = ({ tabs.removeTab(tabId) setLastVisitedTable(undefined) const base = projectRef ? `/v2/project/${projectRef}` : '' - router.push(`${base}/data/tables`) + if (router) { + router.push(`${base}/data/tables`) + } else if (typeof window !== 'undefined') { + window.location.assign(`${base}/data/tables`) + } }} > Close tab @@ -188,18 +213,7 @@ export const TableGridEditor = ({ ) ) : !!tabId ? ( - ) : openTabs.length > 0 ? ( @@ -261,9 +275,7 @@ export const TableGridEditor = ({
diff --git a/apps/studio/components/ui/AIAssistantPanel/ModelSelector.tsx b/apps/studio/components/ui/AIAssistantPanel/ModelSelector.tsx index 016b57c0c68..5eeb2fac6db 100644 --- a/apps/studio/components/ui/AIAssistantPanel/ModelSelector.tsx +++ b/apps/studio/components/ui/AIAssistantPanel/ModelSelector.tsx @@ -26,8 +26,7 @@ interface ModelSelectorProps { export const ModelSelector = ({ selectedModel, onSelectModel }: ModelSelectorProps) => { const router = useRouter() const { data: organization } = useSelectedOrganizationQuery() - const { hasAccess: hasAccessToAdvanceModel, isLoading: isLoadingEntitlements } = - useCheckEntitlements('assistant.advance_model') + const { hasAccess: hasAccessToAdvanceModel } = useCheckEntitlements('assistant.advance_model') const [open, setOpen] = useState(false) @@ -38,7 +37,8 @@ export const ModelSelector = ({ selectedModel, onSelectModel }: ModelSelectorPro const handleSelectModel = (model: 'gpt-5' | 'gpt-5-mini') => { if (model === 'gpt-5' && !hasAccessToAdvanceModel) { setOpen(false) - void router.push(upgradeHref) + if (router) void router.push(upgradeHref) + else if (typeof window !== 'undefined') window.location.assign(upgradeHref) return } @@ -83,7 +83,7 @@ export const ModelSelector = ({ selectedModel, onSelectModel }: ModelSelectorPro
- + Upgrade
diff --git a/apps/studio/components/v2/Chooser.tsx b/apps/studio/components/v2/Chooser.tsx index e60386ba44f..cba871b3ddc 100644 --- a/apps/studio/components/v2/Chooser.tsx +++ b/apps/studio/components/v2/Chooser.tsx @@ -17,7 +17,6 @@ import { } from 'lucide-react' import { useRouter } from 'next/navigation' import { useState } from 'react' -import { cn } from 'ui' import { Input } from 'ui/src/components/shadcn/ui/input' import { TypeBadge } from './TypeBadge' @@ -72,7 +71,6 @@ const COUNT_KEY_MAP: Record> = types: 'types', roles: 'roles', extensions: 'extensions', - indexes: 'indexes', publications: 'publications', users: 'users', buckets: 'buckets', diff --git a/apps/studio/components/v2/DataListGrid.tsx b/apps/studio/components/v2/DataListGrid.tsx index 31dceee42ea..48d5a9b4737 100644 --- a/apps/studio/components/v2/DataListGrid.tsx +++ b/apps/studio/components/v2/DataListGrid.tsx @@ -2,9 +2,7 @@ import { ArrowDown, ArrowUp } from 'lucide-react' import { useCallback, useMemo, useState, type ReactNode } from 'react' -import DataGrid, { Row, type Column } from 'react-data-grid' import { cn } from 'ui' -import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader' export type DataListColumnDef = { key: string @@ -93,50 +91,6 @@ export function DataListGrid({ [columnDefs, sortKey] ) - const columns: Column[] = useMemo( - () => - columnDefs.map((col) => ({ - key: col.key, - name: col.name, - resizable: true, - minWidth: col.minWidth ?? 120, - sortable: false, - headerCellClass: 'sb-grid-column-header sb-grid-column-header--cursor', - renderHeaderCell: () => { - const active = sortKey === col.key - const canSort = Boolean(col.sortValue) - return ( - - ) - }, - renderCell: (props) => ( -
- {col.renderCell(props.row)} -
- ), - })), - [columnDefs, sortKey, sortDir, onHeaderClick] - ) - return (
{(toolbar != null || toolbarTrailing != null) && ( @@ -151,43 +105,74 @@ export function DataListGrid({
{tabs}
) : null}
- rowKeyGetter(row)} - rowClass={() => - cn( - '[&>.rdg-cell]:flex [&>.rdg-cell]:items-center', - '[&>.rdg-cell]:border-box [&>.rdg-cell]:outline-none [&>.rdg-cell]:shadow-none', - (onRowClick || onRowDoubleClick) && 'cursor-pointer' - ) - } - renderers={{ - renderRow(_, props) { - return ( - onRowClick?.(props.row)} - onDoubleClick={() => onRowDoubleClick?.(props.row)} - /> - ) - }, - noRowsFallback: isLoading ? ( -
- -
- ) : ( -
- {emptyMessage ?? 'No rows'} -
- ), - }} - /> + {isLoading ? ( +
Loading...
+ ) : sortedRows.length === 0 ? ( +
+ {emptyMessage ?? 'No rows'} +
+ ) : ( +
+ + + + {columnDefs.map((col) => { + const active = sortKey === col.key + const canSort = Boolean(col.sortValue) + return ( + + ) + })} + + + + {sortedRows.map((row) => { + const key = rowKeyGetter(row) + return ( + onRowClick?.(row)} + onDoubleClick={() => onRowDoubleClick?.(row)} + > + {columnDefs.map((col) => ( + + ))} + + ) + })} + +
+ +
+
{col.renderCell(row)}
+
+
+ )}
) diff --git a/apps/studio/components/v2/HomeView.tsx b/apps/studio/components/v2/HomeView.tsx index d08cf642d90..c10a5fe5cd1 100644 --- a/apps/studio/components/v2/HomeView.tsx +++ b/apps/studio/components/v2/HomeView.tsx @@ -24,6 +24,7 @@ import { Badge, Button, cn, copyToClipboard } from 'ui' import { useV2Params } from '@/app/v2/V2ParamsContext' import { useV2DashboardStore } from '@/stores/v2-dashboard' +import type { InfraMonitoringAttribute } from 'data/analytics/infra-monitoring-query' function maskConnectionString(conn: string | null | undefined) { if (!conn) return '' @@ -69,7 +70,7 @@ export function HomeView() { const startDate = now.subtract(1, 'day').toISOString() const endDate = now.toISOString() - const attributes = useMemo( + const attributes = useMemo( () => [ 'avg_cpu_usage', 'ram_usage', @@ -83,7 +84,7 @@ export function HomeView() { [] ) - const { data: infraData, isLoading: infraLoading } = useInfraMonitoringAttributesQuery( + const { data: infraData } = useInfraMonitoringAttributesQuery( { projectRef, attributes, @@ -103,7 +104,7 @@ export function HomeView() { const connections = parseConnectionsData(infraData, maxConnectionsData) const { recentItems } = useV2DashboardStore((s) => ({ recentItems: s.recentItems })) - const addDetailTab = useV2DashboardStore((s) => s.addDetailTab) + const openDataTab = useV2DashboardStore((s) => s.openDataTab) const { openSidebar } = useSidebarManagerSnapshot() const { setSelectedItem } = useAdvisorStateSnapshot() @@ -114,9 +115,16 @@ export function HomeView() { openSidebar(SIDEBAR_KEYS.ADVISOR_PANEL) } - const handleOpenRecent = (item: { id: string; label: string; path: string }) => { + const handleOpenRecent = (item: (typeof recentItems)[number]) => { if (!projectRef) return - addDetailTab({ id: item.id, label: item.label, path: item.path }) + openDataTab({ + id: item.id, + label: item.label, + type: 'detail', + category: item.category, + domain: item.domain, + path: item.path, + }) router.push(item.path) } @@ -257,7 +265,7 @@ export function HomeView() { className="text-left border border-border rounded-md p-3 hover:bg-sidebar-accent/50" >
{item.label}
-
{item.type}
+
{item.category}
))}
diff --git a/apps/studio/components/v2/Navigation/MobileNavigationBar.tsx b/apps/studio/components/v2/Navigation/MobileNavigationBar.tsx index e9c4b5fbe4c..53fcd5a5c09 100644 --- a/apps/studio/components/v2/Navigation/MobileNavigationBar.tsx +++ b/apps/studio/components/v2/Navigation/MobileNavigationBar.tsx @@ -4,7 +4,7 @@ import { ConnectButton } from 'components/interfaces/ConnectButton/ConnectButton import { LocalDropdown } from 'components/interfaces/LocalDropdown' import { SidebarContent } from 'components/interfaces/Sidebar' import { UserDropdown } from 'components/interfaces/UserDropdown' -import FloatingMobileToolbar from 'components/layouts/Navigation/FloatingMobileToolbar/FloatingMobileToolbar' +import { FloatingMobileToolbar } from 'components/layouts/Navigation/FloatingMobileToolbar/FloatingMobileToolbar' import { useOrganizationsQuery } from 'data/organizations/organizations-query' import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization' import { IS_PLATFORM } from 'lib/constants' @@ -14,7 +14,7 @@ import { useState } from 'react' import { Button, cn } from 'ui' import { CommandMenuTrigger, MobileSheetNav } from 'ui-patterns' -import { HomeIcon } from '../LayoutHeader/HomeIcon' +import { HomeIcon } from 'components/layouts/Navigation/LayoutHeader/HomeIcon' import { useMobileSheet } from './MobileSheetContext' import { OrgSelector } from './OrgSelector' import { ProjectBranchSelector } from './ProjectBranchSelector' diff --git a/apps/studio/components/v2/Navigation/MobileSheetContext.tsx b/apps/studio/components/v2/Navigation/MobileSheetContext.tsx index 52392aa2d97..1b243674832 100644 --- a/apps/studio/components/v2/Navigation/MobileSheetContext.tsx +++ b/apps/studio/components/v2/Navigation/MobileSheetContext.tsx @@ -1,7 +1,7 @@ import type { PropsWithChildren, ReactNode } from 'react' import { createContext, useCallback, useContext, useRef, useState } from 'react' -import type { TYPEOF_SIDEBAR_KEYS } from '../../ProjectLayout/LayoutSidebar/LayoutSidebarProvider' +import type { TYPEOF_SIDEBAR_KEYS } from 'components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider' /** * Sheet content: null = closed; sidebar id = one of SIDEBAR_KEYS; ReactNode = custom content (menu, search, etc.). diff --git a/apps/studio/components/v2/Navigation/NavigationIconLink.tsx b/apps/studio/components/v2/Navigation/NavigationIconLink.tsx index 4316d1eb983..828cc544c6e 100644 --- a/apps/studio/components/v2/Navigation/NavigationIconLink.tsx +++ b/apps/studio/components/v2/Navigation/NavigationIconLink.tsx @@ -1,17 +1,12 @@ import { noop } from 'lodash' import Link from 'next/link' -import { - AnchorHTMLAttributes, - cloneElement, - ComponentPropsWithoutRef, - forwardRef, - isValidElement, -} from 'react' +import { cloneElement, forwardRef, isValidElement } from 'react' import { cn, Tooltip, TooltipContent, TooltipTrigger } from 'ui' import type { Route } from 'components/ui/ui.types' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' import { LOCAL_STORAGE_KEYS } from 'common' +import type { AnchorHTMLAttributes } from 'react' interface NavigationIconButtonProps extends AnchorHTMLAttributes { route: Route @@ -46,48 +41,64 @@ const NavigationIconLink = forwardRef>( - function LinkComponent(props, ref) { - if (route.linkElement && isValidElement(route.linkElement)) { - return cloneElement(route.linkElement, { ...props, ref }) + const sharedProps = { + role: 'button' as const, + 'aria-current': isActive, + ref, + href: route.link || '#', + ...props, + onClick: (e: React.MouseEvent) => { + if (!route.link) { + e.preventDefault() } - - return - } - ) + onClick(e) + }, + className: cn(classes, props.className), + } const linkContent = ( - { - if (!route.link) { - e.preventDefault() // Prevent navigation if there's no link - } - onClick(e) - }} - className={cn(classes, props.className)} - > - - {route.icon} - - - {route.label} - - + <> + {route.linkElement && isValidElement(route.linkElement) ? ( + cloneElement(route.linkElement, sharedProps, [ + + {route.icon} + , + + {route.label} + , + ]) + ) : ( + + + {route.icon} + + + {route.label} + + + )} + ) if (!allowNavPanelToExpand) { diff --git a/apps/studio/components/v2/Navigation/ProjectBranchSelectorTrigger.tsx b/apps/studio/components/v2/Navigation/ProjectBranchSelectorTrigger.tsx index a6c369da79c..0317d9d8546 100644 --- a/apps/studio/components/v2/Navigation/ProjectBranchSelectorTrigger.tsx +++ b/apps/studio/components/v2/Navigation/ProjectBranchSelectorTrigger.tsx @@ -1,10 +1,17 @@ +import type { OrganizationsData } from 'data/organizations/organizations-query' import { ChevronsUpDown, GitBranch } from 'lucide-react' import * as React from 'react' -import { cn, SidebarMenuButton as SidebarMenuButtonComponent } from 'ui' +import { + cn, + SidebarMenuButton as SidebarMenuButtonComponent, + Tooltip, + TooltipContent, + TooltipTrigger, +} from 'ui' export interface ProjectBranchSelectorTriggerProps { displayProjectName: string - selectedOrgInitial: string + selectedOrg: OrganizationsData isBranch: boolean isProductionBranch: boolean branchDisplayName: string @@ -22,7 +29,7 @@ export const ProjectBranchSelectorTrigger = React.forwardRef< ( { displayProjectName, - selectedOrgInitial, + selectedOrg, isBranch, isProductionBranch: _isProductionBranch, branchDisplayName, @@ -31,6 +38,8 @@ export const ProjectBranchSelectorTrigger = React.forwardRef< }, ref ) => { + const selectedOrgInitial = selectedOrg?.name?.trim().charAt(0).toUpperCase() ?? 'O' + return ( -
- {selectedOrgInitial} -
+ + +
+ {selectedOrgInitial} +
+
+ {selectedOrg.name} +
{displayProjectName}
) } - if (isSidebarId(content) && activeSidebar?.id === content) { + if (isSidebarId(content) && activeSidebar && activeSidebar.id === content) { return activeSidebar.component?.() ?? null } if (!isSidebarId(content)) return content diff --git a/apps/studio/components/v2/RightPanel.tsx b/apps/studio/components/v2/RightPanel.tsx index d4464db1c76..a74ca700287 100644 --- a/apps/studio/components/v2/RightPanel.tsx +++ b/apps/studio/components/v2/RightPanel.tsx @@ -1,8 +1,7 @@ 'use client' -import { usePathname } from 'next/navigation' import { X } from 'lucide-react' -import { Button, cn } from 'ui' +import { Button } from 'ui' import { useV2DashboardStore } from '@/stores/v2-dashboard' @@ -24,13 +23,13 @@ export function RightPanel() {
{title}
diff --git a/apps/studio/components/v2/TabBar.tsx b/apps/studio/components/v2/TabBar.tsx index 054b41fdde0..b7813092b87 100644 --- a/apps/studio/components/v2/TabBar.tsx +++ b/apps/studio/components/v2/TabBar.tsx @@ -6,7 +6,7 @@ import { usePathname } from 'next/navigation' import { cn } from 'ui' import { useV2Params } from '@/app/v2/V2ParamsContext' -import { useV2DashboardStore } from '@/stores/v2-dashboard' +import { useV2DashboardStore, type DataTab } from '@/stores/v2-dashboard' const CATEGORY_LABELS: Record = { tables: 'Tables', @@ -27,7 +27,8 @@ const CATEGORY_LABELS: Record = { export function TabBar() { const pathname = usePathname() const { projectRef } = useV2Params() - const { detailTabs, removeDetailTab } = useV2DashboardStore() + const { dataTabs, closeDataTab } = useV2DashboardStore() + const detailTabs = dataTabs.filter((t) => t.type === 'detail') const base = projectRef ? `/v2/project/${projectRef}` : '' const pathParts = @@ -35,7 +36,6 @@ export function TabBar() { ?.replace(/^\/v2\/[^/]+\/[^/]+/, '') .split('/') .filter(Boolean) ?? [] - const activity = pathParts[0] const category = pathParts[1] const categoryLabel = category ? (CATEGORY_LABELS[category] ?? category) : 'Data' const categoryListHref = category ? `${base}/data/${category}` : `${base}/data/tables` @@ -64,7 +64,7 @@ export function TabBar() { > - {detailTabs.map((tab) => { + {detailTabs.map((tab: DataTab) => { const isActive = activeTabPath === tab.path return (
{ e.preventDefault() - removeDetailTab(tab.id) + closeDataTab(tab.id) }} className="opacity-0 group-hover:opacity-100 p-0.5 hover:bg-background rounded text-foreground-light hover:text-foreground" aria-label="Close tab" diff --git a/apps/studio/components/v2/V2OrgProjectSelector.tsx b/apps/studio/components/v2/V2OrgProjectSelector.tsx index ba57eb31217..683b9a13697 100644 --- a/apps/studio/components/v2/V2OrgProjectSelector.tsx +++ b/apps/studio/components/v2/V2OrgProjectSelector.tsx @@ -51,7 +51,7 @@ export function V2OrgProjectSelector() { return ( -