diff --git a/apps/docs/content/guides/platform/credits.mdx b/apps/docs/content/guides/platform/credits.mdx index 6197fe1423c1a..dc667e1b55c75 100644 --- a/apps/docs/content/guides/platform/credits.mdx +++ b/apps/docs/content/guides/platform/credits.mdx @@ -28,9 +28,6 @@ As an example, if you start a Pro Plan subscription on January 1 and downgrade t ## Credit top-ups You can top up credits at any time, with a maximum of per top-up. These credits do not expire and are non-refundable. - -If you have any outstanding invoices, we’ll automatically use your credits to pay them off. Any remaining credits will be applied to future invoices. - You may want to consider this option to avoid issues with recurring payments, gain more control over how often your credit card is charged, and potentially make things easier for your accounting department. diff --git a/apps/studio/components/grid/SupabaseGrid.tsx b/apps/studio/components/grid/SupabaseGrid.tsx index 3bf86dd9ee2fe..b468f085d191d 100644 --- a/apps/studio/components/grid/SupabaseGrid.tsx +++ b/apps/studio/components/grid/SupabaseGrid.tsx @@ -60,17 +60,10 @@ export const SupabaseGrid = ({ }, { keepPreviousData: true, - retryDelay: (retryAttempt, error: any) => { + retry: (_, error: any) => { const doesNotExistError = error && error.message?.includes('does not exist') - const tooManyRequestsError = error.message?.includes('Too Many Requests') - const vaultError = error.message?.includes('query vault failed') - if (doesNotExistError) onApplySorts([]) - - if (retryAttempt > 3 || doesNotExistError || tooManyRequestsError || vaultError) { - return Infinity - } - return 5000 + return false }, } ) diff --git a/apps/studio/components/grid/components/common/DropdownControl.tsx b/apps/studio/components/grid/components/common/DropdownControl.tsx index 502f688651a1d..a5f9fe0913e9c 100644 --- a/apps/studio/components/grid/components/common/DropdownControl.tsx +++ b/apps/studio/components/grid/components/common/DropdownControl.tsx @@ -1,5 +1,6 @@ +import { DropdownMenuItemTooltip } from 'components/ui/DropdownMenuItemTooltip' import { PropsWithChildren } from 'react' -import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from 'ui' +import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from 'ui' interface DropdownControlProps { options: { @@ -7,6 +8,8 @@ interface DropdownControlProps { label: string postLabel?: string preLabel?: string + disabled?: boolean + tooltip?: string }[] onSelect: (value: string | number) => void side?: 'bottom' | 'left' | 'top' | 'right' | undefined @@ -30,13 +33,18 @@ export const DropdownControl = ({ {options.length === 0 &&

No more items

} {options.map((x) => { return ( - onSelect(x.value)}> + onSelect(x.value)} + >
{x.preLabel && {x.preLabel}} {x.label} {x.postLabel && {x.postLabel}}
-
+ ) })} diff --git a/apps/studio/components/grid/components/grid/Grid.tsx b/apps/studio/components/grid/components/grid/Grid.tsx index 4b5e9142a7e1d..5608e5b97cae4 100644 --- a/apps/studio/components/grid/components/grid/Grid.tsx +++ b/apps/studio/components/grid/components/grid/Grid.tsx @@ -4,8 +4,6 @@ import { ref as valtioRef } from 'valtio' import { handleCopyCell } from 'components/grid/SupabaseGrid.utils' import { formatForeignKeys } from 'components/interfaces/TableGridEditor/SidePanelEditor/ForeignKeySelector/ForeignKeySelector.utils' -import AlertError from 'components/ui/AlertError' -import { InlineLink } from 'components/ui/InlineLink' import { useForeignKeyConstraintsQuery } from 'data/database/foreign-key-constraints-query' import { ENTITY_TYPE } from 'data/entity-types/entity-type-constants' import { useSendEventMutation } from 'data/telemetry/send-event-mutation' @@ -15,10 +13,10 @@ import { useCsvFileDrop } from 'hooks/ui/useCsvFileDrop' import { useTableEditorStateSnapshot } from 'state/table-editor' import { useTableEditorTableStateSnapshot } from 'state/table-editor-table' import { Button, cn } from 'ui' -import { Admonition } from 'ui-patterns' import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader' import type { Filter, GridProps, SupaRow } from '../../types' import { useOnRowsChange } from './Grid.utils' +import { GridError } from './GridError' import RowRenderer from './RowRenderer' const rowKeyGetter = (row: SupaRow) => { @@ -60,7 +58,6 @@ export const Grid = memo( const { data: org } = useSelectedOrganizationQuery() const { data: project } = useSelectedProjectQuery() - const isBranch = project?.parent_project_ref !== undefined const onRowsChange = useOnRowsChange(rows) @@ -80,9 +77,6 @@ export const Grid = memo( const isForeignTable = tableEntityType === ENTITY_TYPE.FOREIGN_TABLE const isTableEmpty = (rows ?? []).length === 0 - const isForeignTableMissingVaultKeyError = - isForeignTable && isError && error.message.includes('query vault failed') - const { mutate: sendEvent } = useSendEventMutation() const { isDraggedOver, onDragOver, onFileDrop } = useCsvFileDrop({ @@ -155,51 +149,9 @@ export const Grid = memo( className="absolute top-9 p-2 w-full z-[1] pointer-events-none" > {isLoading && } - {isError ? ( - isForeignTableMissingVaultKeyError ? ( - -

- The key that's used to retrieve data from your foreign table is either - incorrect or missing. Verify the key in your{' '} - - wrapper's settings - {' '} - or in{' '} - - Vault - - . -

- {isBranch && ( -

- Note: Vault keys from the main project do not sync to branches. You may add - them manually into{' '} - - Vault - {' '} - if you want to query foreign tables while on a branch. -

- )} -
- ) : ( - - {filters.length > 0 && ( -

- Verify that the filter values are correct, as the error may stem from an - incorrectly applied filter -

- )} -
- ) - ) : null} + + {isError && } + {isSuccess && ( <> {(filters ?? []).length === 0 ? ( diff --git a/apps/studio/components/grid/components/grid/GridError.tsx b/apps/studio/components/grid/components/grid/GridError.tsx new file mode 100644 index 0000000000000..9999466ef57a7 --- /dev/null +++ b/apps/studio/components/grid/components/grid/GridError.tsx @@ -0,0 +1,147 @@ +import { useParams } from 'common' +import { useTableFilter } from 'components/grid/hooks/useTableFilter' +import { useTableSort } from 'components/grid/hooks/useTableSort' +import AlertError from 'components/ui/AlertError' +import { InlineLink } from 'components/ui/InlineLink' +import { ENTITY_TYPE } from 'data/entity-types/entity-type-constants' +import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' +import { useTableEditorTableStateSnapshot } from 'state/table-editor-table' +import { Button } from 'ui' +import { Admonition } from 'ui-patterns' + +export const GridError = ({ error }: { error?: any }) => { + const { filters } = useTableFilter() + const { sorts } = useTableSort() + const snap = useTableEditorTableStateSnapshot() + + const tableEntityType = snap.originalTable?.entity_type + const isForeignTable = tableEntityType === ENTITY_TYPE.FOREIGN_TABLE + + const isForeignTableMissingVaultKeyError = + isForeignTable && error?.message?.includes('query vault failed') + + const isInvalidSyntaxError = + filters.length > 0 && error?.message?.includes('invalid input syntax') + + const isInvalidOrderingOperatorError = + sorts.length > 0 && error?.message?.includes('identify an ordering operator') + + if (isForeignTableMissingVaultKeyError) { + return + } else if (isInvalidSyntaxError) { + return + } else if (isInvalidOrderingOperatorError) { + return + } + + return +} + +const ForeignTableMissingVaultKeyError = () => { + const { ref } = useParams() + const { data: project } = useSelectedProjectQuery() + const isBranch = project?.parent_project_ref !== undefined + + return ( + +

+ The key that's used to retrieve data from your foreign table is either incorrect or missing. + Verify the key in your{' '} + + wrapper's settings + {' '} + or in Vault. +

+ {isBranch && ( +

+ Note: Vault keys from the main project do not sync to branches. You may add them manually + into Vault{' '} + if you want to query foreign tables while on a branch. +

+ )} +
+ ) +} + +const InvalidSyntaxError = ({ error }: { error?: any }) => { + const { onApplyFilters } = useTableFilter() + + return ( + +

+ Unable to retrieve results as the provided value in your filter(s) doesn't match it's column + data type. +

+

+ Verify that your filter values are correct before applying the filters again. +

+

+ Error: {error.message} +

+ + +
+ ) +} + +const InvalidOrderingOperatorError = ({ error }: { error: any }) => { + const { sorts, onApplySorts } = useTableSort() + const invalidDataType = (error?.message ?? '').split('type ').pop() + const formattedInvalidDataType = invalidDataType.includes('json') + ? invalidDataType.toUpperCase() + : invalidDataType + + return ( + 1 ? 'one of the selected columns' : 'the selected column'}`} + > +

+ Unable to retrieve results as sorting is not supported on{' '} + {sorts.length > 1 ? 'one of the selected columns' : 'the selected column'} due to its data + type. ({formattedInvalidDataType}) +

+

+ Remove any sorts on columns with the data type {formattedInvalidDataType} applying the sorts + again. +

+

+ Error: {error.message} +

+ + +
+ ) +} + +const GeneralError = ({ error }: { error: any }) => { + const { filters } = useTableFilter() + + return ( + + {filters.length > 0 && ( +

+ Verify that the filter values are correct, as the error may stem from an incorrectly + applied filter +

+ )} +
+ ) +} diff --git a/apps/studio/components/grid/components/header/filter/FilterPopoverPrimitive.tsx b/apps/studio/components/grid/components/header/filter/FilterPopoverPrimitive.tsx index a36027b46108d..868b2e2715ef6 100644 --- a/apps/studio/components/grid/components/header/filter/FilterPopoverPrimitive.tsx +++ b/apps/studio/components/grid/components/header/filter/FilterPopoverPrimitive.tsx @@ -109,7 +109,7 @@ export const FilterPopoverPrimitive = ({ ))} {localFilters.length == 0 && (
-
No filters applied to this view
+
No filters applied to this view

Add a column below to filter the view

@@ -118,7 +118,7 @@ export const FilterPopoverPrimitive = ({
-
-
+
- onToggle(columnName, e)} + onCheckedChange={(e: boolean) => onToggle(columnName, e)} />