diff --git a/apps/studio/components/grid/components/header/Header.tsx b/apps/studio/components/grid/components/header/Header.tsx index 45c69218f9981..1b414f79ff1eb 100644 --- a/apps/studio/components/grid/components/header/Header.tsx +++ b/apps/studio/components/grid/components/header/Header.tsx @@ -7,7 +7,6 @@ import { toast } from 'sonner' import { useDispatch, useTrackedState } from 'components/grid/store/Store' import type { Filter, Sort, SupaTable } from 'components/grid/types' -import { Markdown } from 'components/interfaces/Markdown' import { formatTableRowsToSQL } from 'components/interfaces/TableGridEditor/TableEntity.utils' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import { ButtonTooltip } from 'components/ui/ButtonTooltip' @@ -31,12 +30,22 @@ import { } from 'ui' import FilterPopover from './filter/FilterPopover' import { SortPopover } from './sort' +import Link from 'next/link' // [Joshen] CSV exports require this guard as a fail-safe if the table is // just too large for a browser to keep all the rows in memory before // exporting. Either that or export as multiple CSV sheets with max n rows each export const MAX_EXPORT_ROW_COUNT = 500000 -export const MAX_EXPORT_ROW_COUNT_MESSAGE = `Sorry! We're unable to support exporting row counts larger than ${MAX_EXPORT_ROW_COUNT.toLocaleString()} at the moment. Alternatively, you may consider using [pg_dump](https://supabase.com/docs/reference/cli/supabase-db-dump) via our CLI instead.` +export const MAX_EXPORT_ROW_COUNT_MESSAGE = ( + <> + Sorry! We're unable to support exporting row counts larger than $ + {MAX_EXPORT_ROW_COUNT.toLocaleString()} at the moment. Alternatively, you may consider using + + pg_dump + {' '} + via our CLI instead. + +) export type HeaderProps = { table: SupaTable @@ -290,7 +299,9 @@ const RowHeader = ({ table, sorts, filters }: RowHeaderProps) => { setIsExporting(true) if (allRowsSelected && totalRows > MAX_EXPORT_ROW_COUNT) { - toast.error() + toast.error( +
{MAX_EXPORT_ROW_COUNT_MESSAGE}
+ ) return setIsExporting(false) } @@ -331,7 +342,9 @@ const RowHeader = ({ table, sorts, filters }: RowHeaderProps) => { setIsExporting(true) if (allRowsSelected && totalRows > MAX_EXPORT_ROW_COUNT) { - toast.error() + toast.error( +
{MAX_EXPORT_ROW_COUNT_MESSAGE}
+ ) return setIsExporting(false) } diff --git a/apps/studio/components/interfaces/Auth/Policies/PolicyTableRow/PolicyRow.tsx b/apps/studio/components/interfaces/Auth/Policies/PolicyTableRow/PolicyRow.tsx index eb02e72693e94..c8d7abb4295db 100644 --- a/apps/studio/components/interfaces/Auth/Policies/PolicyTableRow/PolicyRow.tsx +++ b/apps/studio/components/interfaces/Auth/Policies/PolicyTableRow/PolicyRow.tsx @@ -118,6 +118,14 @@ const PolicyRow = ({ open: true, sqlSnippets: [sql], initialInput: `Update the policy with name "${policy.name}" in the ${policy.schema} schema on the ${policy.table} table. It should...`, + suggestions: { + title: `I can help you make a change to the policy "${policy.name}" in the ${policy.schema} schema on the ${policy.table} table, here are a few example prompts to get you started:`, + prompts: [ + 'Tell me how I can improve this policy...', + 'Duplicate this policy for another table...', + 'Add extra conditions to this policy...', + ], + }, }) }} > diff --git a/apps/studio/components/layouts/TableEditorLayout/EntityListItem.tsx b/apps/studio/components/layouts/TableEditorLayout/EntityListItem.tsx index 6b265070ee236..57b63d892f8cd 100644 --- a/apps/studio/components/layouts/TableEditorLayout/EntityListItem.tsx +++ b/apps/studio/components/layouts/TableEditorLayout/EntityListItem.tsx @@ -21,7 +21,6 @@ import { MAX_EXPORT_ROW_COUNT_MESSAGE, } from 'components/grid/components/header/Header' import { parseSupaTable } from 'components/grid/SupabaseGrid.utils' -import { Markdown } from 'components/interfaces/Markdown' import { formatTableRowsToSQL, getEntityLintDetails, @@ -117,7 +116,7 @@ const EntityListItem: ItemRenderer = ({ }) if (isTableLike(table) && table.live_rows_estimate > MAX_EXPORT_ROW_COUNT) { return toast.error( - , +
{MAX_EXPORT_ROW_COUNT_MESSAGE}
, { id: toastId } ) } @@ -171,7 +170,7 @@ const EntityListItem: ItemRenderer = ({ if (isTableLike(table) && table.live_rows_estimate > MAX_EXPORT_ROW_COUNT) { return toast.error( - , +
{MAX_EXPORT_ROW_COUNT_MESSAGE}
, { id: toastId } ) } diff --git a/apps/studio/components/ui/AIAssistantPanel/AIAssistant.tsx b/apps/studio/components/ui/AIAssistantPanel/AIAssistant.tsx index 0d032e777c942..74c2d00d3014e 100644 --- a/apps/studio/components/ui/AIAssistantPanel/AIAssistant.tsx +++ b/apps/studio/components/ui/AIAssistantPanel/AIAssistant.tsx @@ -40,6 +40,10 @@ import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' import AIOnboarding from './AIOnboarding' import CollapsibleCodeBlock from './CollapsibleCodeBlock' import { Message } from './Message' +import { useParams } from 'common/hooks' +import { useSearchParamsShallow } from 'common/hooks' +import { useSqlEditorV2StateSnapshot } from 'state/sql-editor-v2' +import { useRouter } from 'next/router' const MemoizedMessage = memo( ({ message, isLoading }: { message: MessageType; isLoading: boolean }) => { @@ -71,14 +75,18 @@ export const AIAssistant = ({ className, onResetConversation, }: AIAssistantProps) => { + const router = useRouter() const project = useSelectedProject() const isOptedInToAI = useOrgOptedIntoAi() const selectedOrganization = useSelectedOrganization() + const { id: entityId } = useParams() + const searchParams = useSearchParamsShallow() const includeSchemaMetadata = isOptedInToAI || !IS_PLATFORM const disablePrompts = useFlag('disableAssistantPrompts') + const { snippets } = useSqlEditorV2StateSnapshot() const { aiAssistantPanel, setAiAssistantPanel } = useAppStateSnapshot() - const { initialInput, sqlSnippets, suggestions } = aiAssistantPanel + const { open, initialInput, sqlSnippets, suggestions } = aiAssistantPanel const inputRef = useRef(null) const bottomRef = useRef(null) @@ -93,6 +101,10 @@ export const AIAssistant = ({ const { data: check } = useCheckOpenAIKeyQuery() const isApiKeySet = IS_PLATFORM || !!check?.hasKey + const isInSQLEditor = router.pathname.includes('/sql/[id]') + const snippet = snippets[entityId ?? ''] + const snippetContent = snippet?.snippet?.content?.sql + const { data: subscription } = useOrgSubscriptionQuery({ orgSlug: selectedOrganization?.slug }) const hasHipaaAddon = subscriptionHasHipaaAddon(subscription) @@ -102,6 +114,9 @@ export const AIAssistant = ({ schema: 'public', }) + const currentTable = tables?.find((t) => t.id.toString() === entityId) + const currentSchema = searchParams?.get('schema') ?? 'public' + const { mutate: sendEvent } = useSendEventMutation() const sendTelemetryEvent = (value: string) => { sendEvent({ @@ -126,6 +141,8 @@ export const AIAssistant = ({ includeSchemaMetadata, projectRef: project?.ref, connectionString: project?.connectionString, + schema: currentSchema, + table: currentTable?.name, }, }) @@ -264,6 +281,20 @@ export const AIAssistant = ({ } }, [messages, isChatLoading, setAiAssistantPanel]) + // Remove suggestions if sqlSnippets were removed + useEffect(() => { + if (!sqlSnippets || sqlSnippets.length === 0) { + setAiAssistantPanel({ suggestions: undefined }) + } + }, [sqlSnippets, suggestions, setAiAssistantPanel]) + + useEffect(() => { + if (open && isInSQLEditor && !!snippetContent) { + setAiAssistantPanel({ sqlSnippets: [snippetContent] }) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [open, isInSQLEditor, snippetContent]) + if (isLoadingTables) { return (
@@ -329,7 +360,7 @@ export const AIAssistant = ({ })}
{renderedMessages} - {last(messages)?.role === 'user' && ( + {(last(messages)?.role === 'user' || last(messages)?.content?.length === 0) && ( Thinking
@@ -363,7 +394,7 @@ export const AIAssistant = ({ {suggestions.title &&

{suggestions.title}

}
- {suggestions?.prompts.map((prompt) => ( + {suggestions?.prompts?.map((prompt) => (