Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/studio/components/grid/SupabaseGrid.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { keepPreviousData, useQueryClient } from '@tanstack/react-query'
import { useFlag, useParams } from 'common'
import { useParams } from 'common'
import { isMsSqlForeignTable } from 'data/table-editor/table-editor-types'
import { useTableRowsQuery } from 'data/table-rows/table-rows-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import type { PostgresTable } from '@supabase/postgres-meta'
import { ArrowRight } from 'lucide-react'
import type { PropsWithChildren } from 'react'
import type { RenderCellProps } from 'react-data-grid'

import { convertByteaToHex } from 'components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.utils'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { useTableEditorQuery } from 'data/table-editor/table-editor-query'
import { isTableLike } from 'data/table-editor/table-editor-types'
import { useTableQuery } from 'data/tables/table-retrieve-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { ArrowRight } from 'lucide-react'
import type { PropsWithChildren } from 'react'
import type { RenderCellProps } from 'react-data-grid'
import { Popover_Shadcn_, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_ } from 'ui'
import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'

import type { SupaRow } from '../../types'
import { NullValue } from '../common/NullValue'
import { ReferenceRecordPeek } from './ReferenceRecordPeek'
Expand Down Expand Up @@ -83,7 +83,14 @@ export const ForeignKeyFormatter = (props: Props) => {
tooltip={{ content: { side: 'bottom', text: 'View referencing record' } }}
/>
</PopoverTrigger_Shadcn_>
<PopoverContent_Shadcn_ align="end" className="p-0 w-96">
<PopoverContent_Shadcn_
align="end"
className="p-0 w-96"
onDoubleClick={(e) => {
e.preventDefault()
e.stopPropagation()
}}
>
<ReferenceRecordPeek
table={targetTable}
column={relationship.target_column_name}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import { PostgresTable } from '@supabase/postgres-meta'
import { Key } from 'lucide-react'
import DataGrid, { Column } from 'react-data-grid'

import { keepPreviousData } from '@tanstack/react-query'
import { useParams } from 'common'
import { COLUMN_MIN_WIDTH } from 'components/grid/constants'
import {
ESTIMATED_CHARACTER_PIXEL_WIDTH,
getColumnDefaultWidth,
} from 'components/grid/utils/gridColumns'
import { convertByteaToHex } from 'components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.utils'
import { isArrayColumn, isBinaryColumn, isJsonColumn } from 'components/grid/utils/types'
import { EditorTablePageLink } from 'data/prefetchers/project.$ref.editor.$id'
import { useTableRowsQuery } from 'data/table-rows/table-rows-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { Button, cn, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
import { Key } from 'lucide-react'
import { useMemo } from 'react'
import DataGrid, { Column } from 'react-data-grid'
import { Button, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'

import { BinaryFormatter } from './BinaryFormatter'
import { DefaultFormatter } from './DefaultFormatter'
import { JsonFormatter } from './JsonFormatter'

interface ReferenceRecordPeekProps {
table: PostgresTable
column: string
Expand Down Expand Up @@ -44,59 +48,52 @@ export const ReferenceRecordPeek = ({ table, column, value }: ReferenceRecordPee
{ placeholderData: keepPreviousData }
)

const primaryKeys = table.primary_keys.map((x) => x.name)
const primaryKeys = useMemo(() => table.primary_keys.map((x) => x.name), [table.primary_keys])

const columns = (table?.columns ?? []).map((column) => {
const columnDefaultWidth = getColumnDefaultWidth({
dataType: column.data_type,
format: column.format,
} as any)
const columnWidthBasedOnName =
(column.name.length + column.format.length) * ESTIMATED_CHARACTER_PIXEL_WIDTH
const columnWidth =
columnDefaultWidth < columnWidthBasedOnName ? columnWidthBasedOnName : columnDefaultWidth
const isPrimaryKey = primaryKeys.includes(column.name)
const columns = useMemo(() => {
return (table?.columns ?? []).map((column) => {
const columnDefaultWidth = getColumnDefaultWidth({
dataType: column.data_type,
format: column.format,
} as any)
const columnWidthBasedOnName =
(column.name.length + column.format.length) * ESTIMATED_CHARACTER_PIXEL_WIDTH
const columnWidth =
columnDefaultWidth < columnWidthBasedOnName ? columnWidthBasedOnName : columnDefaultWidth
const isPrimaryKey = primaryKeys.includes(column.name)

const res: Column<any> = {
key: column.name,
name: column.name,
resizable: false,
draggable: false,
sortable: false,
width: columnWidth,
minWidth: COLUMN_MIN_WIDTH,
headerCellClass: 'outline-none !shadow-none',
renderHeaderCell: () => (
<div className="flex h-full items-center justify-center gap-x-2">
{isPrimaryKey && (
<Tooltip>
<TooltipTrigger>
<Key size={14} strokeWidth={2} className="text-brand rotate-45" />
</TooltipTrigger>
<TooltipContent side="bottom">Primary key</TooltipContent>
</Tooltip>
)}
<span className="text-xs truncate">{column.name}</span>
<span className="text-xs text-foreground-light font-normal">{column.format}</span>
</div>
),
renderCell: ({ column: col, row }) => {
const value = row[col.name as any]
const formattedValue = column.format === 'bytea' ? convertByteaToHex(value) : value
return (
<div
className={cn(
'flex items-center h-full w-full whitespace-pre',
formattedValue === null && 'text-foreground-lighter'
const res: Column<any> = {
key: column.name,
name: column.name,
resizable: false,
draggable: false,
sortable: false,
width: columnWidth,
minWidth: COLUMN_MIN_WIDTH,
headerCellClass: 'outline-none !shadow-none',
renderHeaderCell: () => (
<div className="flex h-full items-center justify-center gap-x-2">
{isPrimaryKey && (
<Tooltip>
<TooltipTrigger>
<Key size={14} strokeWidth={2} className="text-brand rotate-45" />
</TooltipTrigger>
<TooltipContent side="bottom">Primary key</TooltipContent>
</Tooltip>
)}
>
{formattedValue === null ? 'NULL' : formattedValue}
<span className="text-xs truncate">{column.name}</span>
<span className="text-xs text-foreground-light font-normal">{column.format}</span>
</div>
)
},
}
return res
})
),
renderCell: isBinaryColumn(column.data_type)
? BinaryFormatter
: isJsonColumn(column.data_type) && !isArrayColumn(column.data_type)
? JsonFormatter
: DefaultFormatter,
}
return res
})
}, [table?.columns, primaryKeys])

return (
<>
Expand Down
6 changes: 4 additions & 2 deletions apps/studio/components/grid/components/grid/Grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -237,15 +237,17 @@ export const Grid = memo(
{(rows ?? []).length === 0 && (
<div
className={cn(
'absolute inset-0 flex flex-col items-center justify-center p-2 z-[1]',
'absolute w-full inset-0 flex flex-col items-center justify-center p-2 z-[1]',
isTableEmpty && isDraggedOver && 'border-2 border-dashed',
isValidFileDraggedOver ? 'border-brand-600' : 'border-destructive-600'
)}
onDragOver={onDragOver}
onDragLeave={onDragOver}
onDrop={onFileDrop}
>
{isLoading && !isDisabled && <GenericSkeletonLoader />}
{isLoading && !isDisabled && (
<GenericSkeletonLoader className="w-full top-9 absolute p-2" />
)}

{isError && <GridError error={error} />}

Expand Down
17 changes: 2 additions & 15 deletions apps/studio/components/interfaces/App/AppBannerWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,19 @@
import { useFlag } from 'common'
import { ClockSkewBanner } from 'components/layouts/AppLayout/ClockSkewBanner'
import { IncidentBanner } from 'components/layouts/AppLayout/IncidentBanner'
import { NoticeBanner } from 'components/layouts/AppLayout/NoticeBanner'
import { StatusPageBanner } from 'components/layouts/AppLayout/StatusPageBanner'
import { PropsWithChildren } from 'react'

import { OrganizationResourceBanner } from '../Organization/HeaderBanner'
import { MaintenanceBanner } from '@/components/layouts/AppLayout/MaintenanceBanner'
import { useIncidentStatusQuery } from '@/data/platform/incident-status-query'

export const AppBannerWrapper = ({ children }: PropsWithChildren<{}>) => {
const { data: allStatusPageEvents } = useIncidentStatusQuery()
const { maintenanceEvents = [], incidents = [] } = allStatusPageEvents ?? {}

// Only show incident banner for incidents with real impact (not "none")
const hasBannerWorthyIncidents = incidents.some((incident) => incident.impact !== 'none')
const ongoingIncident =
useFlag('ongoingIncident') ||
process.env.NEXT_PUBLIC_ONGOING_INCIDENT === 'true' ||
hasBannerWorthyIncidents
const ongoingMaintenance = maintenanceEvents.length > 0

const showNoticeBanner = useFlag('showNoticeBanner')
const clockSkewBanner = useFlag('clockSkewBanner')

return (
<div className="flex flex-col">
<div className="flex-shrink-0">
{ongoingIncident ? <IncidentBanner /> : ongoingMaintenance ? <MaintenanceBanner /> : null}
<StatusPageBanner />
{showNoticeBanner && <NoticeBanner />}
<OrganizationResourceBanner />
{clockSkewBanner && <ClockSkewBanner />}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,54 @@
import { ChevronDown, Code, Terminal } from 'lucide-react'
import { parseAsString, useQueryState } from 'nuqs'
import { useRouter } from 'next/router'

import { useParams } from 'common'
import { SIDEBAR_KEYS } from 'components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider'
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { ChevronDown, Code, Terminal } from 'lucide-react'
import { useRouter } from 'next/router'
import { parseAsString, useQueryState } from 'nuqs'
import { useAiAssistantStateSnapshot } from 'state/ai-assistant-state'
import { useSidebarManagerSnapshot } from 'state/sidebar-manager-state'
import {
AiIconAnimation,
Button,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from 'ui'
import { SIDEBAR_KEYS } from 'components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider'
import { useSidebarManagerSnapshot } from 'state/sidebar-manager-state'

import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
import { useIsProjectActive } from '@/hooks/misc/useSelectedProject'

export const DeployEdgeFunctionButton = () => {
const router = useRouter()
const { ref } = useParams()
const { data: org } = useSelectedOrganizationQuery()

const snap = useAiAssistantStateSnapshot()
const { openSidebar } = useSidebarManagerSnapshot()

const { mutate: sendEvent } = useSendEventMutation()
const [, setCreateMethod] = useQueryState('create', parseAsString)

const isProjectActive = useIsProjectActive()

return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button type="primary" iconRight={<ChevronDown className="w-4 h-4" strokeWidth={1.5} />}>
<DropdownMenuTrigger asChild disabled={!isProjectActive}>
<ButtonTooltip
type="primary"
disabled={!isProjectActive}
iconRight={<ChevronDown className="w-4 h-4" strokeWidth={1.5} />}
tooltip={{
content: {
side: 'bottom',
text: !isProjectActive
? 'Unable to deploy function as project is inactive'
: undefined,
},
}}
>
Deploy a new function
</Button>
</ButtonTooltip>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-80">
<DropdownMenuItem
Expand Down
23 changes: 13 additions & 10 deletions apps/studio/components/interfaces/Settings/Addons/Addons.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
import dayjs from 'dayjs'
import { AlertCircle, ChevronRight, ExternalLink, Info } from 'lucide-react'
import { useTheme } from 'next-themes'
import Image from 'next/image'
import Link from 'next/link'
import { useMemo } from 'react'

import { SupportCategories } from '@supabase/shared-types/out/constants'
import { useFlag, useParams } from 'common'
import {
Expand All @@ -14,7 +7,6 @@ import {
import { NoticeBar } from 'components/interfaces/DiskManagement/ui/NoticeBar'
import ProjectUpdateDisabledTooltip from 'components/interfaces/Organization/BillingSettings/ProjectUpdateDisabledTooltip'
import { SupportLink } from 'components/interfaces/Support/SupportLink'
import { useIsProjectActive } from 'components/layouts/ProjectLayout/ProjectContext'
import {
ScaffoldContainer,
ScaffoldDivider,
Expand All @@ -31,16 +23,27 @@ import { useProjectDetailQuery } from 'data/projects/project-detail-query'
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
import type { ProjectAddonVariantMeta } from 'data/subscriptions/types'
import dayjs from 'dayjs'
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { useIsOrioleDbInAws, useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import {
useIsOrioleDbInAws,
useIsProjectActive,
useSelectedProjectQuery,
} from 'hooks/misc/useSelectedProject'
import { getCloudProviderArchitecture } from 'lib/cloudprovider-utils'
import { BASE_PATH, DOCS_URL, INSTANCE_MICRO_SPECS, INSTANCE_NANO_SPECS } from 'lib/constants'
import { getDatabaseMajorVersion, getSemanticVersion } from 'lib/helpers'
import { AlertCircle, ChevronRight, ExternalLink, Info } from 'lucide-react'
import { useTheme } from 'next-themes'
import Image from 'next/image'
import Link from 'next/link'
import { useMemo } from 'react'
import { useAddonsPagePanel } from 'state/addons-page'
import { Alert, AlertDescription_Shadcn_, AlertTitle_Shadcn_, Alert_Shadcn_, Button } from 'ui'
import { Alert, Alert_Shadcn_, AlertDescription_Shadcn_, AlertTitle_Shadcn_, Button } from 'ui'
import { ComputeBadge } from 'ui-patterns/ComputeBadge'
import { GenericSkeletonLoader, ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'

import CustomDomainSidePanel from './CustomDomainSidePanel'
import IPv4SidePanel from './IPv4SidePanel'
import PITRSidePanel from './PITRSidePanel'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useEffect, useState } from 'react'
import { toast } from 'sonner'

import { useParams } from 'common'
import { useIsProjectActive } from 'components/layouts/ProjectLayout/ProjectContext'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { PasswordStrengthBar } from 'components/ui/PasswordStrengthBar'
import { useDatabasePasswordResetMutation } from 'data/database/database-password-reset-mutation'
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { useIsProjectActive, useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { DEFAULT_MINIMUM_PASSWORD_STRENGTH } from 'lib/constants'
import { passwordStrength, PasswordStrengthScore } from 'lib/password-strength'
import { generateStrongPassword } from 'lib/project'
import { useEffect, useState } from 'react'
import { toast } from 'sonner'
import { Button, Card, CardContent, Input, Modal } from 'ui'
import { FormLayout } from 'ui-patterns/form/Layout/FormLayout'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { Pause } from 'lucide-react'
import { useRouter } from 'next/router'
import { useState } from 'react'
import { toast } from 'sonner'

import { useIsProjectActive } from 'components/layouts/ProjectLayout/ProjectContext'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { useSetProjectStatus } from 'data/projects/project-detail-query'
import { useProjectPauseMutation } from 'data/projects/project-pause-mutation'
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { useIsAwsK8sCloudProvider, useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import {
useIsAwsK8sCloudProvider,
useIsProjectActive,
useSelectedProjectQuery,
} from 'hooks/misc/useSelectedProject'
import { PROJECT_STATUS } from 'lib/constants'
import { Pause } from 'lucide-react'
import { useRouter } from 'next/router'
import { useState } from 'react'
import { toast } from 'sonner'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'

const PauseProjectButton = () => {
Expand Down
Loading
Loading