mirror of
https://github.com/supabase/supabase.git
synced 2026-10-10 20:05:06 +03:00
fix types
This commit is contained in:
14 files changed
+230
-206
No files matched your search
@@ -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
|
||||
<div className="h-full" onClick={() => tabs.makeActiveTabPermanent()}>
|
||||
<div className="h-full" onPointerDownCapture={() => tabs.makeActiveTabPermanent()}>
|
||||
{!selectedTable ? (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<div className="w-[400px]">
|
||||
@@ -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 = ({
|
||||
</Button>
|
||||
)
|
||||
) : !!tabId ? (
|
||||
<Button
|
||||
type="default"
|
||||
className="mt-2"
|
||||
onClick={() => {
|
||||
tabs.handleTabClose({
|
||||
id: tabId,
|
||||
router,
|
||||
editor: 'table',
|
||||
onClearDashboardHistory: () => setLastVisitedTable(undefined),
|
||||
})
|
||||
}}
|
||||
>
|
||||
<Button type="default" className="mt-2" onClick={() => closeLegacyTab(tabId)}>
|
||||
Close tab
|
||||
</Button>
|
||||
) : openTabs.length > 0 ? (
|
||||
@@ -261,9 +275,7 @@ export const TableGridEditor = ({
|
||||
|
||||
<SidePanelEditor
|
||||
editable={editable}
|
||||
selectedTable={
|
||||
isTableSelected || isForeignTableSelected ? (selectedTable as TableLike) : undefined
|
||||
}
|
||||
selectedTable={isTableSelected || isForeignTableSelected ? (selectedTable as TableLike) : undefined}
|
||||
onTableCreated={onTableCreated}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -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
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div>
|
||||
<Badge role="button" variant="warning">
|
||||
<Badge variant="warning">
|
||||
Upgrade
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
@@ -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<string, keyof ReturnType<typeof useV2DataCounts>> =
|
||||
types: 'types',
|
||||
roles: 'roles',
|
||||
extensions: 'extensions',
|
||||
indexes: 'indexes',
|
||||
publications: 'publications',
|
||||
users: 'users',
|
||||
buckets: 'buckets',
|
||||
|
||||
@@ -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<TRow> = {
|
||||
key: string
|
||||
@@ -93,50 +91,6 @@ export function DataListGrid<TRow>({
|
||||
[columnDefs, sortKey]
|
||||
)
|
||||
|
||||
const columns: Column<TRow>[] = 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 (
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
'sb-grid-column-header flex h-full w-full items-center px-2 text-left',
|
||||
canSort && 'hover:bg-surface-200/80 dark:hover:bg-surface-200/20'
|
||||
)}
|
||||
disabled={!canSort}
|
||||
onClick={() => canSort && onHeaderClick(col.key)}
|
||||
>
|
||||
<div className="sb-grid-column-header__inner min-w-0 flex-1">
|
||||
<span className="sb-grid-column-header__inner__name">{col.name}</span>
|
||||
</div>
|
||||
{canSort && active ? (
|
||||
sortDir === 'asc' ? (
|
||||
<ArrowUp className="h-3.5 w-3.5 shrink-0 text-foreground-light" />
|
||||
) : (
|
||||
<ArrowDown className="h-3.5 w-3.5 shrink-0 text-foreground-light" />
|
||||
)
|
||||
) : null}
|
||||
</button>
|
||||
)
|
||||
},
|
||||
renderCell: (props) => (
|
||||
<div className="flex h-full w-full items-center px-2 text-xs">
|
||||
{col.renderCell(props.row)}
|
||||
</div>
|
||||
),
|
||||
})),
|
||||
[columnDefs, sortKey, sortDir, onHeaderClick]
|
||||
)
|
||||
|
||||
return (
|
||||
<div className={cn('sb-grid flex h-full min-h-0 flex-col', className)}>
|
||||
{(toolbar != null || toolbarTrailing != null) && (
|
||||
@@ -151,43 +105,74 @@ export function DataListGrid<TRow>({
|
||||
<div className="flex h-9 shrink-0 items-center border-b border-border px-2">{tabs}</div>
|
||||
) : null}
|
||||
<div className="relative min-h-0 flex-1">
|
||||
<DataGrid
|
||||
className="h-full flex-1"
|
||||
style={{ height: '100%' }}
|
||||
rowHeight={36}
|
||||
headerRowHeight={34}
|
||||
columns={columns}
|
||||
rows={sortedRows}
|
||||
rowKeyGetter={(row) => 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 (
|
||||
<Row
|
||||
key={rowKeyGetter(props.row)}
|
||||
{...props}
|
||||
onClick={() => onRowClick?.(props.row)}
|
||||
onDoubleClick={() => onRowDoubleClick?.(props.row)}
|
||||
/>
|
||||
)
|
||||
},
|
||||
noRowsFallback: isLoading ? (
|
||||
<div className="absolute top-10 w-full px-4">
|
||||
<GenericSkeletonLoader />
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex h-full items-center justify-center p-8 text-center text-sm text-foreground-lighter">
|
||||
{emptyMessage ?? 'No rows'}
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
{isLoading ? (
|
||||
<div className="absolute top-10 w-full px-4">Loading...</div>
|
||||
) : sortedRows.length === 0 ? (
|
||||
<div className="flex h-full items-center justify-center p-8 text-center text-sm text-foreground-lighter">
|
||||
{emptyMessage ?? 'No rows'}
|
||||
</div>
|
||||
) : (
|
||||
<div className="h-full overflow-auto">
|
||||
<table className="w-full border-collapse text-xs">
|
||||
<thead className="sticky top-0 z-10 bg-background">
|
||||
<tr className="border-b border-border">
|
||||
{columnDefs.map((col) => {
|
||||
const active = sortKey === col.key
|
||||
const canSort = Boolean(col.sortValue)
|
||||
return (
|
||||
<th
|
||||
key={col.key}
|
||||
className="h-[34px] px-2 text-left font-medium text-foreground-light"
|
||||
style={{ minWidth: col.minWidth ?? 120 }}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
'flex h-full w-full items-center gap-1 text-left',
|
||||
canSort && 'hover:text-foreground'
|
||||
)}
|
||||
disabled={!canSort}
|
||||
onClick={() => canSort && onHeaderClick(col.key)}
|
||||
>
|
||||
<span className="min-w-0 flex-1 truncate">{col.name}</span>
|
||||
{canSort && active ? (
|
||||
sortDir === 'asc' ? (
|
||||
<ArrowUp className="h-3.5 w-3.5 shrink-0" />
|
||||
) : (
|
||||
<ArrowDown className="h-3.5 w-3.5 shrink-0" />
|
||||
)
|
||||
) : null}
|
||||
</button>
|
||||
</th>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{sortedRows.map((row) => {
|
||||
const key = rowKeyGetter(row)
|
||||
return (
|
||||
<tr
|
||||
key={key}
|
||||
className={cn(
|
||||
'h-9 border-b border-border',
|
||||
(onRowClick || onRowDoubleClick) && 'cursor-pointer hover:bg-surface-200/30'
|
||||
)}
|
||||
onClick={() => onRowClick?.(row)}
|
||||
onDoubleClick={() => onRowDoubleClick?.(row)}
|
||||
>
|
||||
{columnDefs.map((col) => (
|
||||
<td key={col.key} className="px-2 align-middle">
|
||||
<div className="min-w-0 truncate">{col.renderCell(row)}</div>
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -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<InfraMonitoringAttribute[]>(
|
||||
() => [
|
||||
'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"
|
||||
>
|
||||
<div className="text-sm truncate">{item.label}</div>
|
||||
<div className="text-xs text-foreground-lighter truncate">{item.type}</div>
|
||||
<div className="text-xs text-foreground-lighter truncate">{item.category}</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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.).
|
||||
|
||||
@@ -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<HTMLAnchorElement> {
|
||||
route: Route
|
||||
@@ -46,48 +41,64 @@ const NavigationIconLink = forwardRef<HTMLAnchorElement, NavigationIconButtonPro
|
||||
`${isActive && '!bg-selection shadow-sm'}`,
|
||||
]
|
||||
|
||||
const LinkComponent = forwardRef<HTMLAnchorElement, ComponentPropsWithoutRef<typeof Link>>(
|
||||
function LinkComponent(props, ref) {
|
||||
if (route.linkElement && isValidElement(route.linkElement)) {
|
||||
return cloneElement<any>(route.linkElement, { ...props, ref })
|
||||
const sharedProps = {
|
||||
role: 'button' as const,
|
||||
'aria-current': isActive,
|
||||
ref,
|
||||
href: route.link || '#',
|
||||
...props,
|
||||
onClick: (e: React.MouseEvent<HTMLAnchorElement>) => {
|
||||
if (!route.link) {
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
return <Link ref={ref} {...props} />
|
||||
}
|
||||
)
|
||||
onClick(e)
|
||||
},
|
||||
className: cn(classes, props.className),
|
||||
}
|
||||
|
||||
const linkContent = (
|
||||
<LinkComponent
|
||||
role="button"
|
||||
aria-current={isActive}
|
||||
ref={ref}
|
||||
href={route.link || '#'} // Provide a fallback href
|
||||
{...props}
|
||||
onClick={(e) => {
|
||||
if (!route.link) {
|
||||
e.preventDefault() // Prevent navigation if there's no link
|
||||
}
|
||||
onClick(e)
|
||||
}}
|
||||
className={cn(classes, props.className)}
|
||||
>
|
||||
<span id="icon-link" className={cn(...iconClasses)} {...props}>
|
||||
{route.icon}
|
||||
</span>
|
||||
<span
|
||||
className={cn(
|
||||
'min-w-[128px] text-sm text-foreground-light',
|
||||
'group-hover/item:text-foreground',
|
||||
'group-aria-current/item:text-foreground',
|
||||
'absolute left-10 md:left-7 md:group-data-[state=expanded]:left-12',
|
||||
'opacity-100 md:opacity-0 md:group-data-[state=expanded]:opacity-100',
|
||||
`${isActive && 'text-foreground hover:text-foreground'}`,
|
||||
'transition-all'
|
||||
)}
|
||||
>
|
||||
{route.label}
|
||||
</span>
|
||||
</LinkComponent>
|
||||
<>
|
||||
{route.linkElement && isValidElement(route.linkElement) ? (
|
||||
cloneElement(route.linkElement, sharedProps, [
|
||||
<span key="icon" id="icon-link" className={cn(...iconClasses)} {...props}>
|
||||
{route.icon}
|
||||
</span>,
|
||||
<span
|
||||
key="label"
|
||||
className={cn(
|
||||
'min-w-[128px] text-sm text-foreground-light',
|
||||
'group-hover/item:text-foreground',
|
||||
'group-aria-current/item:text-foreground',
|
||||
'absolute left-10 md:left-7 md:group-data-[state=expanded]:left-12',
|
||||
'opacity-100 md:opacity-0 md:group-data-[state=expanded]:opacity-100',
|
||||
`${isActive && 'text-foreground hover:text-foreground'}`,
|
||||
'transition-all'
|
||||
)}
|
||||
>
|
||||
{route.label}
|
||||
</span>,
|
||||
])
|
||||
) : (
|
||||
<Link {...sharedProps}>
|
||||
<span id="icon-link" className={cn(...iconClasses)} {...props}>
|
||||
{route.icon}
|
||||
</span>
|
||||
<span
|
||||
className={cn(
|
||||
'min-w-[128px] text-sm text-foreground-light',
|
||||
'group-hover/item:text-foreground',
|
||||
'group-aria-current/item:text-foreground',
|
||||
'absolute left-10 md:left-7 md:group-data-[state=expanded]:left-12',
|
||||
'opacity-100 md:opacity-0 md:group-data-[state=expanded]:opacity-100',
|
||||
`${isActive && 'text-foreground hover:text-foreground'}`,
|
||||
'transition-all'
|
||||
)}
|
||||
>
|
||||
{route.label}
|
||||
</span>
|
||||
</Link>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
|
||||
if (!allowNavPanelToExpand) {
|
||||
|
||||
@@ -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 (
|
||||
<SidebarMenuButtonComponent
|
||||
ref={ref}
|
||||
@@ -38,9 +47,14 @@ export const ProjectBranchSelectorTrigger = React.forwardRef<
|
||||
className="group px-1 py-0.5 gap-1.5 w-full max-w-[250px] flex h-auto text-left data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground touch-manipulation"
|
||||
{...buttonProps}
|
||||
>
|
||||
<div className="relative flex h-7 aspect-square shrink-0 items-center bg-background-muted group-hover:border-stronger justify-center rounded border border-strong text-xs">
|
||||
{selectedOrgInitial}
|
||||
</div>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="relative flex h-7 aspect-square shrink-0 items-center bg-background-muted group-hover:border-stronger justify-center rounded border border-strong text-xs">
|
||||
{selectedOrgInitial}
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{selectedOrg.name}</TooltipContent>
|
||||
</Tooltip>
|
||||
<div className="text-left flex-grow min-w-0">
|
||||
<div className="w-full truncate text-foreground leading-tight">{displayProjectName}</div>
|
||||
<div
|
||||
|
||||
@@ -6,7 +6,7 @@ import { CommandWrapper, MobileSheetNav } from 'ui-patterns'
|
||||
import {
|
||||
SIDEBAR_KEYS,
|
||||
type TYPEOF_SIDEBAR_KEYS,
|
||||
} from '../../ProjectLayout/LayoutSidebar/LayoutSidebarProvider'
|
||||
} from 'components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider'
|
||||
import type { MobileSheetContentType } from './MobileSheetContext'
|
||||
import { useMobileSheet } from './MobileSheetContext'
|
||||
|
||||
@@ -29,7 +29,7 @@ function getSheetChildren(
|
||||
</CommandWrapper>
|
||||
)
|
||||
}
|
||||
if (isSidebarId(content) && activeSidebar?.id === content) {
|
||||
if (isSidebarId(content) && activeSidebar && activeSidebar.id === content) {
|
||||
return activeSidebar.component?.() ?? null
|
||||
}
|
||||
if (!isSidebarId(content)) return content
|
||||
|
||||
@@ -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() {
|
||||
<div className="flex items-center justify-between px-3 py-2 border-b border-border shrink-0">
|
||||
<span className="text-sm font-medium text-foreground">{title}</span>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
type="text"
|
||||
size="tiny"
|
||||
className="h-7 w-7"
|
||||
onClick={closeRightPanel}
|
||||
aria-label="Close panel"
|
||||
icon={<X className="h-4 w-4" />}
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
<span className="sr-only">Close panel</span>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex-1 min-h-0 overflow-auto">
|
||||
|
||||
@@ -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<string, string> = {
|
||||
tables: 'Tables',
|
||||
@@ -27,7 +27,8 @@ const CATEGORY_LABELS: Record<string, string> = {
|
||||
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() {
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
</button>
|
||||
{detailTabs.map((tab) => {
|
||||
{detailTabs.map((tab: DataTab) => {
|
||||
const isActive = activeTabPath === tab.path
|
||||
return (
|
||||
<div
|
||||
@@ -83,7 +83,7 @@ export function TabBar() {
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
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"
|
||||
|
||||
@@ -51,7 +51,7 @@ export function V2OrgProjectSelector() {
|
||||
return (
|
||||
<Popover_Shadcn_ open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger_Shadcn_ asChild>
|
||||
<Button variant="ghost" size="tiny" className="gap-1 text-foreground font-medium h-7 px-2">
|
||||
<Button type="text" size="tiny" className="gap-1 text-foreground font-medium h-7 px-2">
|
||||
<span className="text-foreground-lighter text-xs truncate max-w-[120px]">
|
||||
{selectedOrg?.name ?? orgSlug ?? 'Org'}
|
||||
</span>
|
||||
|
||||
@@ -1,25 +1,23 @@
|
||||
'use client'
|
||||
|
||||
import { useBranchesQuery } from 'data/branches/branches-query'
|
||||
import { useOrganizationsQuery } from 'data/organizations/organizations-query'
|
||||
import { useProjectDetailQuery } from 'data/projects/project-detail-query'
|
||||
import { IS_PLATFORM } from 'lib/constants'
|
||||
import { useMemo, useState } from 'react'
|
||||
|
||||
import {
|
||||
Popover_Shadcn_,
|
||||
PopoverContent_Shadcn_,
|
||||
PopoverTrigger_Shadcn_,
|
||||
Popover_Shadcn_,
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
} from 'ui'
|
||||
|
||||
import { IS_PLATFORM } from 'lib/constants'
|
||||
import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
|
||||
|
||||
import { useV2Params } from '@/app/v2/V2ParamsContext'
|
||||
import { ProjectBranchSelectorPopover } from '@/components/v2/Navigation/ProjectBranchSelectorPopover'
|
||||
import { ProjectBranchSelectorTrigger } from '@/components/v2/Navigation/ProjectBranchSelectorTrigger'
|
||||
import { useBranchesQuery } from 'data/branches/branches-query'
|
||||
import { useOrganizationsQuery } from 'data/organizations/organizations-query'
|
||||
import { useProjectDetailQuery } from 'data/projects/project-detail-query'
|
||||
import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
|
||||
|
||||
export function V2ProjectBranchSelector() {
|
||||
const { orgSlug, projectRef } = useV2Params()
|
||||
@@ -53,7 +51,6 @@ export function V2ProjectBranchSelector() {
|
||||
const selectedBranch = branches?.find((b) => b.project_ref === projectRef)
|
||||
const branchDisplayName = selectedBranch?.name ?? 'main'
|
||||
const isProductionBranch = selectedBranch?.is_default ?? true
|
||||
const selectedOrgInitial = selectedOrg?.name?.trim().charAt(0).toUpperCase() ?? 'O'
|
||||
|
||||
const isTriggerLoading = Boolean(projectRef) && (isProjectPending || !displayProject)
|
||||
|
||||
@@ -81,7 +78,7 @@ export function V2ProjectBranchSelector() {
|
||||
) : (
|
||||
<ProjectBranchSelectorTrigger
|
||||
displayProjectName={displayProject?.name ?? projectRef ?? 'Project'}
|
||||
selectedOrgInitial={selectedOrgInitial}
|
||||
selectedOrg={selectedOrg}
|
||||
isBranch={!isProductionBranch}
|
||||
isProductionBranch={isProductionBranch}
|
||||
branchDisplayName={branchDisplayName}
|
||||
|
||||
Reference in new issue
Block a user