'use client' import { useMemo } from 'react' import dynamic from 'next/dynamic' import { IS_PLATFORM, useFlag } from 'common' import { Code2, KeyRound, ListChecks, LockKeyhole, Plus, Rows, Vault, ShieldPlus, UserCog, UserPlus, Webhook, Zap, Table2, MessageCircle, Mail, Lock, Telescope, Clock5, Layers, } from 'lucide-react' import { PageType, useRegisterCommands, useRegisterPage, useSetCommandMenuOpen, } from 'ui-patterns/CommandMenu' import { COMMAND_MENU_SECTIONS } from './CommandMenu.utils' import { SIDEBAR_KEYS } from 'components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider' import { useCreateCommandsConfig, getIntegrationRoute, getIntegrationCommandName, } from './CreateCommands.utils' import type { CommandOptions } from 'ui-patterns/CommandMenu' import type { ICommand } from 'ui-patterns/CommandMenu' const AiIconAnimation = dynamic(() => import('ui').then((mod) => mod.AiIconAnimation)) const Badge = dynamic(() => import('ui').then((mod) => mod.Badge)) const EdgeFunctions = dynamic(() => import('icons').then((mod) => mod.EdgeFunctions)) const AnalyticsBucket = dynamic(() => import('icons').then((mod) => mod.AnalyticsBucket)) const FilesBucket = dynamic(() => import('icons').then((mod) => mod.FilesBucket)) const VectorBucket = dynamic(() => import('icons').then((mod) => mod.VectorBucket)) const Graphql = dynamic(() => import('icons').then((mod) => mod.Graphql)) const CREATE_STUDIO_ENTITY = 'Create Studio Entity' export function useCreateCommands(options?: CommandOptions) { const enableCreateCommands = useFlag('enablecreatecommands') const setIsOpen = useSetCommandMenuOpen() const { ref, setPage, openSidebar, snap, authenticationOauth21, authEnabled, edgeFunctionsEnabled, storageEnabled, sendSmsHook, sendEmailHook, customAccessTokenHook, mfaVerificationHook, mfaVerificationHookEnabled, passwordVerificationHook, passwordVerificationHookEnabled, beforeUserCreatedHook, isFreePlan, isVectorBucketsEnabled, isAnalyticsBucketsEnabled, installedIntegrationIds, allIntegrations, reportsEnabled, } = useCreateCommandsConfig() const databaseCommands = useMemo( () => [ { id: 'create-db-table', name: 'Create Table', route: `/project/${ref}/editor?create=table`, icon: () => , }, { id: 'create-db-index', name: 'Create Index', route: `/project/${ref}/database/indexes?new=true`, icon: () => , }, { id: 'create-db-function', name: 'Create Database Function', route: `/project/${ref}/database/functions?new=true`, icon: () => , }, { id: 'create-db-enum', name: 'Create Enumerated Type', route: `/project/${ref}/database/types?new=true`, icon: () => , }, { id: 'create-db-trigger', name: 'Create Database Trigger', route: `/project/${ref}/database/triggers?new=true`, icon: () => , }, { id: 'create-db-role', name: 'Create Database Role', route: `/project/${ref}/database/roles?new=true`, icon: () => , }, ].filter(Boolean) as ICommand[], [ref] ) const authCommands = useMemo( () => authEnabled ? ([ { id: 'create-auth-user', name: 'Create Auth User', route: `/project/${ref}/auth/users?new=true`, icon: () => , }, { id: 'create-rls-policy', name: 'Create RLS Policy', route: `/project/${ref}/auth/policies?new=true`, icon: () => , }, ...(IS_PLATFORM ? [ { id: 'create-auth-hook-sms', name: 'Create Auth Hook (SMS)', route: `/project/${ref}/auth/hooks?hook=${sendSmsHook?.id}`, icon: () => , }, { id: 'create-auth-hook-email', name: 'Create Auth Hook (Email)', route: `/project/${ref}/auth/hooks?hook=${sendEmailHook?.id}`, icon: () => , }, { id: 'create-auth-hook-custom-access-token', name: 'Create Auth Hook (Custom Access Token)', route: `/project/${ref}/auth/hooks?hook=${customAccessTokenHook?.id}`, icon: () => , }, { id: 'create-auth-hook-mfa-verification', name: 'Create Auth Hook (MFA Verification Attempt)', route: `/project/${ref}/auth/hooks?hook=${mfaVerificationHook?.id}`, icon: () => , badge: () => (mfaVerificationHookEnabled ? Team : null), className: mfaVerificationHookEnabled ? 'opacity-50 cursor-not-allowed pointer-events-none' : '', }, { id: 'create-auth-hook-password-verification', name: 'Create Auth Hook (Password Verification Attempt)', route: `/project/${ref}/auth/hooks?hook=${passwordVerificationHook?.id}`, icon: () => , badge: () => (passwordVerificationHookEnabled ? Team : null), className: passwordVerificationHookEnabled ? 'opacity-50 cursor-not-allowed pointer-events-none' : '', }, { id: 'create-auth-hook-before-user-created', name: 'Create Auth Hook (Before User Created)', route: `/project/${ref}/auth/hooks?hook=${beforeUserCreatedHook?.id}`, icon: () => , }, ] : []), ...(IS_PLATFORM && authenticationOauth21 ? [ { id: 'create-oauth-app', name: 'Create OAuth App', route: `/project/${ref}/auth/oauth-apps?new=true`, icon: () => , }, ] : []), ].filter(Boolean) as ICommand[]) : [], [ ref, authEnabled, sendSmsHook, sendEmailHook, customAccessTokenHook, mfaVerificationHook, mfaVerificationHookEnabled, passwordVerificationHook, passwordVerificationHookEnabled, beforeUserCreatedHook, authenticationOauth21, ] ) const edgeFunctionsCommands = useMemo( () => edgeFunctionsEnabled ? ([ { id: 'create-edge-function-editor', name: 'Create Edge Function via Editor', route: `/project/${ref}/functions/new`, icon: () => , }, { id: 'create-edge-function-cli', name: 'Create Edge Function via CLI', route: `/project/${ref}/functions?create=cli`, icon: () => , }, { id: 'create-edge-function-ai', name: 'Create Edge Function via AI', action: () => { openSidebar(SIDEBAR_KEYS.AI_ASSISTANT) snap.newChat({ name: 'Create new edge function', initialInput: `Create a new edge function that ...`, suggestions: { title: 'I can help you create a new edge function. Here are a few example prompts to get you started:', prompts: [ { label: 'Stripe Payments', description: 'Create a new edge function that processes payments with Stripe', }, { label: 'Email with Resend', description: 'Create a new edge function that sends emails with Resend', }, { label: 'PDF Generator', description: 'Create a new edge function that generates PDFs from HTML templates', }, ], }, }) setIsOpen(false) }, icon: () => ( ), }, { id: 'create-edge-function-secret', name: 'Create Edge Function Secret', route: `/project/${ref}/functions/secrets`, icon: () => , }, ].filter(Boolean) as ICommand[]) : [], [ref, edgeFunctionsEnabled, openSidebar, snap, setIsOpen] ) const storageCommands = useMemo( () => storageEnabled ? ([ { id: 'create-storage-bucket-files', name: 'Create Storage Bucket (Files)', route: `/project/${ref}/storage/files?new=true`, icon: () => , }, { id: 'create-storage-bucket-analytics', name: 'Create Storage Bucket (Analytics)', route: `/project/${ref}/storage/analytics?new=true`, icon: () => , badge: () => (isFreePlan ? Pro : null), className: !isAnalyticsBucketsEnabled ? 'opacity-50 cursor-not-allowed pointer-events-none' : '', }, { id: 'create-storage-bucket-vectors', name: 'Create Storage Bucket (Vectors)', route: `/project/${ref}/storage/vectors?new=true`, icon: () => , badge: () => (isFreePlan ? Pro : null), className: !isVectorBucketsEnabled ? 'opacity-50 cursor-not-allowed pointer-events-none' : '', }, ].filter(Boolean) as ICommand[]) : [], [ref, storageEnabled, isFreePlan, isAnalyticsBucketsEnabled, isVectorBucketsEnabled] ) const integrationsCommands = useMemo(() => { // Sort integrations: Postgres modules (non-wrappers) first, then wrappers const sortedIntegrations = [...allIntegrations].sort((a, b) => { const aIsWrapper = a.type === 'wrapper' ? 1 : 0 const bIsWrapper = b.type === 'wrapper' ? 1 : 0 return aIsWrapper - bIsWrapper }) return sortedIntegrations .map((integration) => { const route = getIntegrationRoute(integration, ref, installedIntegrationIds) if (!route) return null const isWrapper = integration.type === 'wrapper' // For wrappers, use the integration icon with wrapper styling // For Postgres modules, use plain icons const getIcon = () => { if (isWrapper) { return (
{integration.icon()}
) } // Use plain icons for Postgres modules switch (integration.id) { case 'vault': return case 'cron': return case 'webhooks': return case 'queues': return case 'graphiql': return default: // Fallback to integration icon for other Postgres modules return integration.icon() } } return { id: `create-integration-${integration.id}`, name: getIntegrationCommandName(integration), route, icon: getIcon, } }) .filter(Boolean) as ICommand[] }, [ref, allIntegrations, installedIntegrationIds]) const observabilityCommands = useMemo( () => [ ...(IS_PLATFORM && reportsEnabled ? ([ { id: 'create-observability-report', name: 'Create Custom Report', route: `/project/${ref}/observability/api-overview?newReport=true`, icon: () => , }, ].filter(Boolean) as ICommand[]) : []), ], [ref, reportsEnabled] ) const sections = useMemo( () => [ { id: 'create-database', name: 'Database', commands: databaseCommands, }, ...(authCommands.length > 0 ? [ { id: 'create-auth', name: 'Auth', commands: authCommands, }, ] : []), ...(edgeFunctionsCommands.length > 0 ? [ { id: 'create-edge-functions', name: 'Edge Functions', commands: edgeFunctionsCommands, }, ] : []), ...(storageCommands.length > 0 ? [ { id: 'create-storage', name: 'Storage', commands: storageCommands, }, ] : []), ...(observabilityCommands.length > 0 ? [ { id: 'create-observability', name: 'Observability', commands: observabilityCommands, }, ] : []), ...(integrationsCommands.length > 0 ? [ { id: 'create-integrations', name: 'Integrations', commands: integrationsCommands, }, ] : []), ], [ databaseCommands, authCommands, edgeFunctionsCommands, storageCommands, integrationsCommands, observabilityCommands, ] ) useRegisterPage( CREATE_STUDIO_ENTITY, { type: PageType.Commands, sections, }, { deps: [sections], enabled: enableCreateCommands, } ) useRegisterCommands( COMMAND_MENU_SECTIONS.ACTIONS, [ { id: 'create-studio-entity', name: 'Create...', action: () => setPage(CREATE_STUDIO_ENTITY), icon: () => , }, ], { ...options, orderSection: (sections) => sections, sectionMeta: { priority: 3 }, enabled: enableCreateCommands, } ) }