import { useFlag } from 'common' import { useRouter } from 'next/router' import { useEffect, useState } from 'react' import { UseFormReturn } from 'react-hook-form' import { Badge, Button, cn, PopoverSeparator, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from 'ui' import { InfoTooltip } from 'ui-patterns/info-tooltip' import { CreateProjectForm } from './ProjectCreation.schema' import { instanceLabel, monthlyInstancePrice } from './ProjectCreation.utils' import { InlineLink } from '@/components/ui/InlineLink' import { OrgProject } from '@/data/projects/org-projects-infinite-query' import { useLastVisitedOrganization } from '@/hooks/misc/useLastVisitedOrganization' import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization' import { DOCS_URL } from '@/lib/constants' /** `close` = close the popup (Vercel interstitial); `studio` = navigate into Studio. */ export type ProjectCreationCancelAction = 'studio' | 'close' | 'hidden' interface ProjectCreationFooterProps { form: UseFormReturn canCreateProject: boolean instanceSize?: string highAvailability?: boolean organizationProjects: OrgProject[] isCreatingNewProject: boolean isSuccessNewProject: boolean isLoadingAvailableRegions?: boolean cancelAction?: ProjectCreationCancelAction } export const ProjectCreationFooter = ({ form, canCreateProject, instanceSize, highAvailability = false, organizationProjects, isCreatingNewProject, isSuccessNewProject, isLoadingAvailableRegions = false, cancelAction = 'studio', }: ProjectCreationFooterProps) => { const router = useRouter() const { data: currentOrg } = useSelectedOrganizationQuery() const isFreePlan = currentOrg?.plan?.id === 'free' const { lastVisitedOrganization } = useLastVisitedOrganization() const [showCloseWindowHint, setShowCloseWindowHint] = useState(false) const projectCreationDisabled = useFlag('disableProjectCreationAndUpdate') const availableComputeCredits = organizationProjects.length === 0 ? 10 : 0 const additionalMonthlySpend = isFreePlan ? 0 : monthlyInstancePrice(instanceSize) - availableComputeCredits const showAdditionalCosts = !isFreePlan && !projectCreationDisabled && canCreateProject && additionalMonthlySpend > 0 // High availability is free during Alpha, so the new project costs $0 and is excluded from // the totals below — the tooltip table still shows the usual compute price struck through. const displayedAdditionalMonthlySpend = highAvailability ? 0 : additionalMonthlySpend const newProjectComputeCosts = highAvailability ? 0 : monthlyInstancePrice(instanceSize) // [kevin] This will eventually all be provided by a new API endpoint to preview and validate project creation, this is just for kaizen now // Clamped since a free HA project can leave the compute credits larger than the costs const monthlyComputeCosts = Math.max( 0, // current project costs organizationProjects.reduce((prev, acc) => { const primaryDatabase = acc.databases.find((db) => db.identifier === acc.ref) const cost = !!primaryDatabase ? monthlyInstancePrice(primaryDatabase.infra_compute_size) : 0 return prev + cost }, 0) + // selected compute size newProjectComputeCosts - // compute credits 10 ) const onCancel = () => { if (cancelAction === 'close') { window.close() return } if (!!lastVisitedOrganization) router.push(`/org/${lastVisitedOrganization}`) else router.push('/organizations') } useEffect(() => { // Browsers only allow closing windows that were opened by script (i.e. have a live `opener`). // Detect this upfront so we don't need to attempt-and-check after the user clicks cancel. if (cancelAction === 'close' && !window.opener) setShowCloseWindowHint(true) }, [cancelAction]) return (
{showAdditionalCosts && (
Additional costs
${displayedAdditionalMonthlySpend}/m

Each project includes a dedicated Postgres instance running on its own server. You are charged for the{' '} Compute resource {' '} of that server, independent of your database usage.

{highAvailability && (

High availability projects are free during Alpha for up to 2 projects.

)} {monthlyComputeCosts > 0 && (

Compute costs are applied on top of your subscription plan costs.

)}
Project Compute Size Monthly Costs {organizationProjects.map((project) => { const primaryDb = project.databases.find( (db) => db.identifier === project.ref ) return ( {project.name} {instanceLabel(primaryDb?.infra_compute_size)} ${monthlyInstancePrice(primaryDb?.infra_compute_size)} ) })} {form.getValues('projectName') || 'New project'} New {instanceLabel(instanceSize)} ${monthlyInstancePrice(instanceSize)} {highAvailability && (

Free during Alpha

)}
Compute Credits -$10 Total Monthly Compute Costs {/** * API currently doesnt output replica information on the projects list endpoint. Until then, we cannot correctly calculate the costs including RRs. * Will be adjusted in the future [kevin] */} {organizationProjects.length > 0 && (

Excluding Read replicas

)}
${monthlyComputeCosts}
)}
{cancelAction === 'hidden' ? null : showCloseWindowHint ? (

Close window to cancel

) : ( )}
) }