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/docs/content/guides/platform/billing-on-supabase.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Monthly costs for paid plans include a fixed subscription fee based on your chos

- [Your monthly invoice](/docs/guides/platform/your-monthly-invoice) - For a detailed breakdown of what a monthly invoice includes
- [Manage your usage](/docs/guides/platform/manage-your-usage) - For details on how the different usage items are billed, and how to optimize usage and reduce costs
- [Control your costs]() - For details on how you can control your costs in case unexpected high usage occurs
- [Control your costs](/docs/guides/platform/cost-control) - For details on how you can control your costs in case unexpected high usage occurs

### Compute costs for projects

Expand Down
6 changes: 6 additions & 0 deletions apps/docs/content/guides/realtime/presence.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ When any client subscribes, disconnects, or updates their presence payload, Supa
- **`join`** — a new client has started tracking presence
- **`leave`** — a client has stopped tracking presence

<Admonition type="note" title="Sync event behavior">

During a `sync` event, you may receive `join` and `leave` events simultaneously, even though no users are actually joining or leaving. This is expected behavior—Presence reconciles its local state with the server state, which can trigger these events as part of the synchronization process. This reflects state reconciliation, not real user movement.

</Admonition>

The complete presence state returned by `presenceState()` looks like this:

```json
Expand Down
4 changes: 2 additions & 2 deletions apps/docs/content/guides/telemetry/log-drains.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -220,12 +220,12 @@ with timestamp modified to be parsed by ingestion endpoint.

To set up the Axiom log drain, you have to:

1. Create a dataset for ingestion in Axiom dashboard -> Datasets
1. Create a dataset for ingestion in Axiom Console -> Datasets
2. Generate an Axiom API Token with permission to ingest into the created dataset (see [Axiom docs](https://axiom.co/docs/reference/tokens#create-basic-api-token))
3. Create log drain in [Supabase dashboard](/dashboard/project/_/settings/log-drains), providing:
- Name of the dataset
- API token
4. Watch for events in the Stream panel of Axiom dashboard
4. Watch for events in the Stream panel of Axiom Console

## Amazon S3

Expand Down
2 changes: 2 additions & 0 deletions apps/studio/components/grid/SupabaseGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export const SupabaseGrid = ({
const { data: project } = useSelectedProjectQuery()
const tableEditorSnap = useTableEditorStateSnapshot()
const snap = useTableEditorTableStateSnapshot()
const preflightCheck = !tableEditorSnap.tablesToIgnorePreflightCheck.includes(tableId ?? -1)

const gridRef = useRef<DataGridHandle>(null)
const [mounted, setMounted] = useState(false)
Expand Down Expand Up @@ -81,6 +82,7 @@ export const SupabaseGrid = ({
sorts,
filters,
page: snap.page,
preflightCheck,
limit: tableEditorSnap.rowsPerPage,
roleImpersonationState: roleImpersonationState as RoleImpersonationState,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import { THRESHOLD_COUNT } from '@supabase/pg-meta/src/sql/studio/get-count-estimate'
import { ArrowLeft, ArrowRight, HelpCircle, Loader2 } from 'lucide-react'
import { useEffect, useState } from 'react'

import { keepPreviousData } from '@tanstack/react-query'
import { useParams } from 'common'
import { useTableFilter } from 'components/grid/hooks/useTableFilter'
Expand All @@ -12,12 +9,15 @@ import { useTableRowsCountQuery } from 'data/table-rows/table-rows-count-query'
import { useTableRowsQuery } from 'data/table-rows/table-rows-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { RoleImpersonationState } from 'lib/role-impersonation'
import { ArrowLeft, ArrowRight, HelpCircle, Loader2 } from 'lucide-react'
import { useEffect, useState } from 'react'
import { useRoleImpersonationStateSnapshot } from 'state/role-impersonation-state'
import { useTableEditorStateSnapshot } from 'state/table-editor'
import { useTableEditorTableStateSnapshot } from 'state/table-editor-table'
import { Button, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
import { Input } from 'ui-patterns/DataInputs/Input'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'

import { DropdownControl } from '../../common/DropdownControl'
import { formatEstimatedCount } from './Pagination.utils'

Expand Down Expand Up @@ -106,6 +106,8 @@ export const Pagination = ({ enableForeignRowsQuery = true }: PaginationProps) =
const maxPages = Math.ceil(count / tableEditorSnap.rowsPerPage)
const totalPages = count > 0 ? maxPages : 1

const preflightCheck = !tableEditorSnap.tablesToIgnorePreflightCheck.includes(id ?? -1)

// [Joshen] This is only applicable for foreign tables, as we use the number of rows on the page to determine
// if we've reached the last page (and hence disable the next button)
const { data: rowsData, isPending: isLoadingRows } = useTableRowsQuery(
Expand All @@ -116,6 +118,7 @@ export const Pagination = ({ enableForeignRowsQuery = true }: PaginationProps) =
sorts,
filters,
page: snap.page,
preflightCheck,
limit: tableEditorSnap.rowsPerPage,
roleImpersonationState: roleImpersonationState as RoleImpersonationState,
},
Expand Down
9 changes: 9 additions & 0 deletions apps/studio/components/grid/components/grid/GridError.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,18 @@ import { Admonition } from 'ui-patterns'

import { HighCostError } from '@/components/ui/HighQueryCost'
import { COST_THRESHOLD_ERROR } from '@/data/sql/execute-sql-query'
import { useTableEditorStateSnapshot } from '@/state/table-editor'
import { ResponseError } from '@/types'

export const GridError = ({ error }: { error?: ResponseError | null }) => {
const { id: _id } = useParams()
const tableId = _id ? Number(_id) : undefined

const { filters } = useTableFilter()
const { sorts } = useTableSort()

const snap = useTableEditorTableStateSnapshot()
const tableEditorSnap = useTableEditorStateSnapshot()

if (!error) return null

Expand All @@ -42,6 +48,9 @@ export const GridError = ({ error }: { error?: ResponseError | null }) => {
'Remove any sorts or filters on unindexed columns, or',
'Create indexes for columns that you want to filter or sort on',
]}
onSelectLoadData={() => {
if (!!tableId) tableEditorSnap.setTableToIgnorePreflightCheck(tableId)
}}
/>
)
} else if (isForeignTableMissingVaultKeyError) {
Expand Down
12 changes: 9 additions & 3 deletions apps/studio/components/grid/components/header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,18 @@ import { useTableEditorStateSnapshot } from 'state/table-editor'
import { useTableEditorTableStateSnapshot } from 'state/table-editor-table'
import {
Button,
cn,
copyToClipboard,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
Separator,
cn,
copyToClipboard,
} from 'ui'

import { ExportDialog } from './ExportDialog'
import { formatRowsForCSV } from './Header.utils'
import { FilterPopover } from './filter/FilterPopover'
import { formatRowsForCSV } from './Header.utils'
import { SortPopover } from './sort/SortPopover'

export type HeaderProps = {
Expand Down Expand Up @@ -222,6 +222,9 @@ type RowHeaderProps = {
}

const RowHeader = ({ tableQueriesEnabled = true }: RowHeaderProps) => {
const { id: _id } = useParams()
const tableId = _id ? Number(_id) : undefined

const queryClient = useQueryClient()
const { data: project } = useSelectedProjectQuery()
const tableEditorSnap = useTableEditorStateSnapshot()
Expand All @@ -237,6 +240,8 @@ const RowHeader = ({ tableQueriesEnabled = true }: RowHeaderProps) => {
const [isExporting, setIsExporting] = useState(false)
const [showExportModal, setShowExportModal] = useState(false)

const preflightCheck = !tableEditorSnap.tablesToIgnorePreflightCheck.includes(tableId ?? -1)

const { data } = useTableRowsQuery(
{
projectRef: project?.ref,
Expand All @@ -245,6 +250,7 @@ const RowHeader = ({ tableQueriesEnabled = true }: RowHeaderProps) => {
sorts,
filters,
page: snap.page,
preflightCheck,
limit: tableEditorSnap.rowsPerPage,
roleImpersonationState: roleImpersonationState as RoleImpersonationState,
},
Expand Down
6 changes: 6 additions & 0 deletions apps/studio/components/grid/components/header/HeaderNew.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,9 @@ type RowHeaderProps = {
}

const RowHeader = ({ tableQueriesEnabled = true }: RowHeaderProps) => {
const { id: _id } = useParams()
const tableId = _id ? Number(_id) : undefined

const queryClient = useQueryClient()
const { data: project } = useSelectedProjectQuery()
const tableEditorSnap = useTableEditorStateSnapshot()
Expand All @@ -238,6 +241,8 @@ const RowHeader = ({ tableQueriesEnabled = true }: RowHeaderProps) => {
const [isExporting, setIsExporting] = useState(false)
const [showExportModal, setShowExportModal] = useState(false)

const preflightCheck = !tableEditorSnap.tablesToIgnorePreflightCheck.includes(tableId ?? -1)

const { data } = useTableRowsQuery(
{
projectRef: project?.ref,
Expand All @@ -246,6 +251,7 @@ const RowHeader = ({ tableQueriesEnabled = true }: RowHeaderProps) => {
sorts,
filters,
page: snap.page,
preflightCheck,
limit: tableEditorSnap.rowsPerPage,
roleImpersonationState: roleImpersonationState as RoleImpersonationState,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,17 @@ export const CreateBranchModal = () => {
{
onSuccess: () => {
if (form.getValues('gitBranchName') !== branchName) return

// Check if another branch is already linked to this git branch
const existingBranch = (branches ?? []).find((b) => b.git_branch === branchName)
if (existingBranch) {
setIsGitBranchValid(false)
form.setError('gitBranchName', {
message: `Branch "${existingBranch.name}" is already linked to git branch "${branchName}"`,
})
return
}

setIsGitBranchValid(true)
form.clearErrors('gitBranchName')
},
Expand All @@ -250,7 +261,7 @@ export const CreateBranchModal = () => {
}
)
},
[githubConnection, form, checkGithubBranchValidity, repoOwner, repoName]
[githubConnection, form, checkGithubBranchValidity, repoOwner, repoName, branches]
)

const onSubmit = (data: z.infer<typeof FormSchema>) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,19 @@ export const EditBranchModal = ({ branch, visible, onClose }: EditBranchModalPro
{
onSuccess: () => {
if (form.getValues('gitBranchName') !== requested) return

// Check if another branch is already linked to this git branch
const existingBranch = (branches ?? []).find(
(b) => b.git_branch === branchName && b.id !== branch?.id
)
if (existingBranch) {
setIsGitBranchValid(false)
form.setError('gitBranchName', {
message: `Branch "${existingBranch.name}" is already linked to git branch "${branchName}"`,
})
return
}

setIsGitBranchValid(true)
form.clearErrors('gitBranchName')
},
Expand All @@ -181,7 +194,7 @@ export const EditBranchModal = ({ branch, visible, onClose }: EditBranchModalPro
}
)
},
[githubConnection, form, checkGithubBranchValidity, repoOwner, repoName]
[githubConnection, form, checkGithubBranchValidity, repoOwner, repoName, branches, branch]
)

// Pre-fill form when the modal becomes visible and branch data is available
Expand Down

This file was deleted.

Loading
Loading