mirror of
https://github.com/supabase/supabase.git
synced 2026-10-14 21:59:16 +03:00
Implements a growth engineering experiment to improve new user onboarding by providing AI-powered and template-based table creation options. Features: - AI prompt interface for describing app requirements - Pre-built templates for common app types (social media, e-commerce, blog, etc.) - Seamless integration with existing table editor - State management using Valtio for reliable data flow - PostHog feature flag controlled (tableQuickstart) Technical implementation: - Uses Valtio proxy-based state management instead of sessionStorage - Properly handles quickstart flow to prevent race conditions - Integrates with TableEditor via useQuickstartData hook - Cleans up state appropriately to prevent panel re-opening issues - Full TypeScript support with proper type definitions The experiment aims to reduce time-to-value for new users by providing smart defaults and reducing the SQL knowledge barrier for initial setup.
113 lines
3.9 KiB
TypeScript
113 lines
3.9 KiB
TypeScript
import { IS_PLATFORM } from 'common'
|
|
import { useEffect, useState } from 'react'
|
|
import { toast } from 'sonner'
|
|
import type { TableField } from 'components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableEditor.types'
|
|
import { generateTableField } from 'components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableEditor.utils'
|
|
import { useTableEditorStateSnapshot } from 'state/table-editor'
|
|
import type { TableField as QuickstartTableField } from './types'
|
|
import { QUICKSTART_DEFAULT_SCHEMA } from './constants'
|
|
import { useQuickstartSnapshot, useQuickstartStore } from './quickstartStore'
|
|
|
|
interface UseQuickstartDataOptions {
|
|
isNewRecord: boolean
|
|
visible: boolean
|
|
}
|
|
|
|
export const useQuickstartData = ({
|
|
isNewRecord,
|
|
visible,
|
|
}: UseQuickstartDataOptions): TableField | null => {
|
|
const tableEditorSnap = useTableEditorStateSnapshot()
|
|
const quickstartSnap = useQuickstartSnapshot()
|
|
const quickstartStore = useQuickstartStore()
|
|
const [tableFields, setTableFields] = useState<TableField | null>(null)
|
|
const [hasProcessedData, setHasProcessedData] = useState(false)
|
|
|
|
useEffect(() => {
|
|
if (!IS_PLATFORM) return
|
|
if (quickstartSnap.isQuickstartFlow && !visible) {
|
|
tableEditorSnap.onAddTable()
|
|
}
|
|
}, [quickstartSnap.isQuickstartFlow, visible, tableEditorSnap])
|
|
|
|
useEffect(() => {
|
|
if (!visible && hasProcessedData) {
|
|
quickstartStore.clearQuickstartData()
|
|
setHasProcessedData(false)
|
|
setTableFields(null)
|
|
}
|
|
}, [visible, hasProcessedData, quickstartStore])
|
|
|
|
useEffect(() => {
|
|
if (!visible || !isNewRecord) {
|
|
return
|
|
}
|
|
|
|
if (quickstartSnap.selectedTableData) {
|
|
try {
|
|
const { tableName, fields } = quickstartSnap.selectedTableData
|
|
|
|
if (!tableName || !Array.isArray(fields) || fields.length === 0) {
|
|
throw new Error('Invalid quickstart data structure')
|
|
}
|
|
|
|
const columns: TableField['columns'] = fields.map(
|
|
(field: QuickstartTableField, index: number) => {
|
|
const looksLikePrimaryKey =
|
|
field.name === 'id' &&
|
|
(field.type === 'uuid' || field.type.toLowerCase().includes('int'))
|
|
|
|
return {
|
|
id: `column-${index}`,
|
|
name: field.name,
|
|
format: field.type,
|
|
defaultValue: field.default ? String(field.default) : null,
|
|
isNullable: field.nullable !== false,
|
|
isUnique: field.unique ?? false,
|
|
isIdentity:
|
|
looksLikePrimaryKey && field.type.toLowerCase().includes('int') && !field.default,
|
|
isPrimaryKey: looksLikePrimaryKey,
|
|
comment: field.description || '',
|
|
isNewColumn: true,
|
|
table: tableName,
|
|
schema: QUICKSTART_DEFAULT_SCHEMA,
|
|
check: null,
|
|
isArray: false,
|
|
isEncrypted: false,
|
|
}
|
|
}
|
|
)
|
|
|
|
const tableField = {
|
|
id: 0,
|
|
name: tableName,
|
|
schema: QUICKSTART_DEFAULT_SCHEMA,
|
|
comment: '',
|
|
columns: columns,
|
|
isRLSEnabled: false,
|
|
isRealtimeEnabled: false,
|
|
} as TableField
|
|
|
|
setTableFields(tableField)
|
|
setHasProcessedData(true)
|
|
quickstartStore.endQuickstartFlow()
|
|
} catch (error) {
|
|
console.error('Error processing quickstart data:', error)
|
|
toast.error('Unable to load template data. Starting with default table instead.')
|
|
|
|
const defaultTable = generateTableField()
|
|
const defaultWithSchema = {
|
|
...defaultTable,
|
|
schema: QUICKSTART_DEFAULT_SCHEMA,
|
|
} as TableField
|
|
setTableFields(defaultWithSchema)
|
|
quickstartStore.clearQuickstartData()
|
|
}
|
|
} else if (!quickstartSnap.selectedTableData) {
|
|
setTableFields(null)
|
|
}
|
|
}, [visible, isNewRecord, quickstartSnap.selectedTableData, quickstartStore])
|
|
|
|
return tableFields
|
|
}
|