import { useParams } from 'common' import { useTablesQuery } from 'data/tables/tables-query' import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' import { GettingStarted } from './GettingStarted' import { Code, Database, Table, User, Upload, UserPlus, BarChart3, Shield, Table2, GitBranch, } from 'lucide-react' import { useBranchesQuery } from 'data/branches/branches-query' import { useAiAssistantStateSnapshot } from 'state/ai-assistant-state' import { useRouter } from 'next/router' import { useCallback, useMemo, useState } from 'react' import { FrameworkSelector } from './FrameworkSelector' import { FRAMEWORKS } from 'components/interfaces/Connect/Connect.constants' import { AiIconAnimation, Button, Card, CardContent, CodeBlock, ToggleGroup, ToggleGroupItem, } from 'ui' import { BASE_PATH } from 'lib/constants' import { usePHFlag } from 'hooks/ui/useFlag' import { InlineTableQuickstart } from '../TableQuickstart/inline/InlineTableQuickstart' export type GettingStartedAction = { label: string href?: string onClick?: () => void variant?: React.ComponentProps['type'] icon?: React.ReactNode component?: React.ReactNode } export type GettingStartedStep = { key: string status: 'complete' | 'incomplete' icon?: React.ReactNode title: string description: string image?: React.ReactNode actions: GettingStartedAction[] } export type GettingStartedState = 'empty' | 'code' | 'no-code' | 'hidden' export function GettingStartedSection({ value, onChange, }: { value: GettingStartedState onChange: (v: GettingStartedState) => void }) { const { data: project } = useSelectedProjectQuery() const { ref } = useParams() const aiSnap = useAiAssistantStateSnapshot() const router = useRouter() // Feature flag for table quickstart variant const tableQuickstartVariant = usePHFlag('tableQuickstart') as | 'control' | 'ai' | 'templates' | false // false = flag is disabled | undefined // undefined = flags are loading // Local state for framework selector preview const [selectedFramework, setSelectedFramework] = useState(FRAMEWORKS[0]?.key ?? 'nextjs') const workflow: 'no-code' | 'code' | null = value === 'code' || value === 'no-code' ? value : null const { data: tablesData } = useTablesQuery({ projectRef: project?.ref, connectionString: project?.connectionString, schema: 'public', }) const tablesCount = Math.max(0, tablesData?.length ?? 0) const { data: branchesData } = useBranchesQuery({ projectRef: project?.parent_project_ref ?? project?.ref, }) const isDefaultProject = project?.parent_project_ref === undefined const hasNonDefaultBranch = (branchesData ?? []).some((b) => !b.is_default) || isDefaultProject === false // Helpers const openAiChat = useCallback( (name: string, initialInput: string) => aiSnap.newChat({ name, open: true, initialInput }), [aiSnap] ) const openConnect = useCallback(() => { router.push( { pathname: router.pathname, query: { ...router.query, showConnect: true, connectTab: 'frameworks', framework: selectedFramework, }, }, undefined, { shallow: true } ) }, [router, selectedFramework]) const connectActions: GettingStartedAction[] = useMemo( () => [ { label: 'Framework selector', component: ( ), }, { label: 'Connect', variant: 'default', onClick: openConnect, }, ], [openConnect, selectedFramework] ) const codeSteps: GettingStartedStep[] = useMemo( () => [ { key: 'install-cli', status: 'incomplete', title: 'Install the Supabase CLI', icon: , description: 'To get started, install the Supabase CLI to manage your project locally, handle migrations, and seed data.', actions: [ { label: 'Install via npm', component: ( npm install supabase --save-dev ), }, ], }, { key: 'design-db', status: tablesCount > 0 ? 'complete' : 'incomplete', title: 'Design your database schema', icon: , description: 'Next, create a schema file that defines the structure of your database.', actions: [ { label: 'Create schema file', href: 'https://supabase.com/docs/guides/local-development/declarative-database-schemas', variant: 'default', }, { label: 'Generate it', variant: 'default', icon: , onClick: () => openAiChat( 'Design my database', 'Help me create a schema file for my database. We will be using Supabase declarative schemas which you can learn about by searching docs for declarative schema.' ), }, ], }, { key: 'add-data', status: 'incomplete', title: 'Seed your database with data', icon: , description: 'Now, create a seed file to populate your database with initial data.', actions: [ { label: 'Create a seed file', href: 'https://supabase.com/docs/guides/local-development/seeding-your-database', variant: 'default', }, { label: 'Generate data', variant: 'default', icon: , onClick: () => openAiChat( 'Generate seed data', 'Generate SQL INSERT statements for realistic seed data that I can run via the Supabase CLI.' ), }, ], }, { key: 'add-rls-policies', status: 'incomplete', title: 'Secure your data with RLS policies', icon: , description: "Let's secure your data by enabling Row Level Security and defining access policies in a migration file.", actions: [ { label: 'Create a migration file', href: `/project/${ref}/auth/policies`, variant: 'default', }, { label: 'Create policies for me', variant: 'default', icon: , onClick: () => openAiChat( 'Generate RLS policies', 'Generate RLS policies for my existing tables in the public schema and guide me through the process of adding them as migration files to my codebase ' ), }, ], }, { key: 'setup-auth', status: 'incomplete', title: 'Configure authentication', icon: , description: "It's time to configure your authentication providers and settings for Supabase Auth.", actions: [ { label: 'Configure', href: `/project/${ref}/auth/providers`, variant: 'default' }, ], }, { key: 'connect-app', status: 'incomplete', title: 'Connect your application', icon: , description: 'Your project is ready. Connect your app using one of our client libraries.', actions: connectActions, }, { key: 'signup-first-user', status: 'incomplete', title: 'Sign up your first user', icon: , description: 'Test your authentication setup by creating the first user account.', actions: [ { label: 'Read docs', href: 'https://supabase.com/docs/guides/auth', variant: 'default', }, ], }, { key: 'upload-file', status: 'incomplete', title: 'Upload a file', icon: , description: 'Integrate file storage by creating a bucket and uploading a file.', actions: [ { label: 'Buckets', href: `/project/${ref}/storage/buckets`, variant: 'default' }, ], }, { key: 'create-edge-function', status: 'incomplete', title: 'Deploy an Edge Function', icon: , description: 'Add server-side logic by creating and deploying your first Edge Function.', actions: [ { label: 'Create a function', href: `/project/${ref}/functions/new`, variant: 'default', }, { label: 'View functions', href: `/project/${ref}/functions`, variant: 'default' }, ], }, { key: 'monitor-progress', status: 'incomplete', title: "Monitor your project's usage", icon: , description: "Track your project's activity by creating custom reports for API, database, and auth events.", actions: [{ label: 'Reports', href: `/project/${ref}/reports`, variant: 'default' }], }, { key: 'create-first-branch', status: hasNonDefaultBranch ? 'complete' : 'incomplete', title: 'Connect to GitHub', icon: , description: 'Streamline your development workflow by connecting your project to GitHub to automatically manage branches.', actions: [ { label: 'Connect to GitHub', href: `/project/${ref}/settings/integrations`, variant: 'default', }, ], }, ], [tablesCount, ref, openAiChat, connectActions, hasNonDefaultBranch] ) const noCodeSteps: GettingStartedStep[] = useMemo( () => [ { key: 'design-db', status: tablesCount > 0 ? 'complete' : 'incomplete', title: 'Create your first table', icon: , description: "To kick off your new project, let's start by creating your very first database table using either the table editor or AI Assistant.", actions: tableQuickstartVariant === 'control' || tableQuickstartVariant === false || tableQuickstartVariant === undefined ? [ { label: 'Create a table', href: `/project/${ref}/editor`, variant: 'default' }, { label: 'Do it for me', variant: 'default', icon: , onClick: () => openAiChat( 'Design my database', 'I want to design my database schema. Please propose tables, relationships, and SQL to create them for my app. Ask clarifying questions if needed.' ), }, ] : [ { label: 'inline-quickstart', component: ( { // Optionally refresh tables or update status }} /> ), }, ], }, { key: 'add-data', status: 'incomplete', title: 'Add sample data', icon:
, description: "Next, let's add some sample data that you can play with once you connect your app.", actions: [ { label: 'Add data', href: `/project/${ref}/editor`, variant: 'default' }, { label: 'Do it for me', variant: 'default', icon: , onClick: () => openAiChat( 'Generate sample data', 'Generate SQL INSERT statements to add realistic sample data to my existing tables. Use safe defaults and avoid overwriting data.' ), }, ], }, { key: 'add-rls-policies', status: 'incomplete', title: 'Secure your data with Row Level Security', icon: , description: "Now that you have some data, let's secure it by enabling Row Level Security and creating policies.", actions: [ { label: 'Create a policy', href: `/project/${ref}/auth/policies`, variant: 'default', }, { label: 'Do it for me', variant: 'default', icon: , onClick: () => openAiChat( 'Generate RLS policies', 'Generate RLS policies for my existing tables in the public schema. ' ), }, ], }, { key: 'setup-auth', status: 'incomplete', title: 'Set up authentication', icon: , description: "It's time to set up authentication so you can start signing up users.", actions: [ { label: 'Configure auth', href: `/project/${ref}/auth/providers`, variant: 'default', }, ], }, { key: 'connect-app', status: 'incomplete', title: 'Connect your application', icon: , description: "Your project is ready. Let's connect your application to Supabase.", actions: connectActions, }, { key: 'signup-first-user', status: 'incomplete', title: 'Sign up your first user', icon: , description: 'Test your authentication by signing up your first user.', actions: [ { label: 'Read docs', href: 'https://supabase.com/docs/guides/auth', variant: 'default', }, ], }, { key: 'upload-file', status: 'incomplete', title: 'Upload a file', icon: , description: "Let's add file storage to your app by creating a bucket and uploading your first file.", actions: [ { label: 'Buckets', href: `/project/${ref}/storage/buckets`, variant: 'default' }, ], }, { key: 'create-edge-function', status: 'incomplete', title: 'Add server-side logic', icon: , description: "Extend your app's functionality by creating an Edge Function for server-side logic.", actions: [ { label: 'Create a function', href: `/project/${ref}/functions/new`, variant: 'default', }, ], }, { key: 'monitor-progress', status: 'incomplete', title: "Monitor your project's health", icon: , description: "Keep an eye on your project's performance and usage by setting up custom reports.", actions: [ { label: 'Create a report', href: `/project/${ref}/reports`, variant: 'default' }, ], }, { key: 'create-first-branch', status: hasNonDefaultBranch ? 'complete' : 'incomplete', title: 'Create a branch to test changes', icon: , description: 'Safely test changes by creating a preview branch before deploying to production.', actions: [ { label: 'Create a branch', href: `/project/${ref}/branches`, variant: 'default' }, ], }, ], [tablesCount, ref, openAiChat, connectActions, hasNonDefaultBranch, tableQuickstartVariant] ) const steps = workflow === 'code' ? codeSteps : workflow === 'no-code' ? noCodeSteps : [] return (

Getting started

v && onChange(v as 'no-code' | 'code')} >
{steps.length === 0 ? (
Supabase Grafana Supabase Grafana

Choose a preferred workflow

With Supabase, you have the flexibility to adopt a workflow that works for you. You can do everything via the dashboard, or manage your entire project within your own codebase.

) : ( )}
) }