Files
supabase/apps/studio/components/interfaces/HomeNew/TableQuickstart/useQuickstartData.ts
Sean Oliver ec4bc204b1 feat: Add TableQuickstart experiment for onboarding flow
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.
2025-09-17 16:01:40 -07:00

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
}