import { LOCAL_STORAGE_KEYS } from 'common' import { useRouter } from 'next/router' import { useEffect, useRef, type ReactNode } from 'react' import { Button, cn, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogSection, DialogSectionSeparator, DialogTitle, DialogTrigger, } from 'ui' import { HeaderBanner } from '@/components/interfaces/Organization/HeaderBanner' import { InlineLink, InlineLinkClassName } from '@/components/ui/InlineLink' import { useFlyDeprecationProjects, type FlyDeprecationProject, } from '@/hooks/misc/useFlyDeprecationProjects' import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage' import { useTrack } from '@/lib/telemetry/track' const BANNER_EXPIRES_AT = new Date('2026-06-01T00:00:00Z') const BACKUP_RESTORE_CLI_URL = 'https://supabase.com/docs/guides/platform/migrating-within-supabase/backup-restore' const DASHBOARD_RESTORE_URL = 'https://supabase.com/docs/guides/platform/migrating-within-supabase/dashboard-restore' const BRANCHING_DASHBOARD_URL = 'https://supabase.com/docs/guides/deployment/branching/dashboard' const SUPPORT_EMAIL = 'success@supabase.io' export const FlyDeprecationBanner = () => { const router = useRouter() const [acknowledged, setAcknowledged, { isSuccess }] = useLocalStorageQuery( LOCAL_STORAGE_KEYS.FLY_DEPRECATION_2026_05_31, false ) const isExpired = Date.now() >= BANNER_EXPIRES_AT.getTime() const onSignIn = router.pathname.startsWith('/sign-in') const shouldEvaluate = !isExpired && !onSignIn && isSuccess && !acknowledged const { isReady, primaries, branches } = useFlyDeprecationProjects({ enabled: shouldEvaluate }) const hasFlyResources = primaries.length > 0 || branches.length > 0 const track = useTrack() const exposedRef = useRef(false) useEffect(() => { if (!shouldEvaluate || !isReady || !hasFlyResources || exposedRef.current) return exposedRef.current = true track('fly_deprecation_banner_exposed', { primaryCount: primaries.length, branchCount: branches.length, }) }, [shouldEvaluate, isReady, hasFlyResources, primaries.length, branches.length, track]) if (!shouldEvaluate || !isReady || !hasFlyResources) return null const onDismiss = () => { track('fly_deprecation_banner_dismissed', { primaryCount: primaries.length, branchCount: branches.length, }) setAcknowledged(true) } const title = primaries.length > 0 && branches.length > 0 ? 'Action required: Fly.io project and branch suspensions begin May 31' : primaries.length > 0 ? 'Action required: Fly.io project suspensions begin May 31' : 'Action required: Fly.io branch suspensions begin May 31' return ( } onDismiss={onDismiss} /> ) } const FlyDeprecationDialog = ({ primaries, branches, }: { primaries: FlyDeprecationProject[] branches: FlyDeprecationProject[] }) => { return ( View affected projects Fly.io deprecation: suspensions begin May 31, 2026 Briven will begin suspending projects and branches still running on Fly.io infrastructure on May 31, 2026.

To preserve your data, migrate each project to Briven's general infrastructure:

  1. Back up your database using the{' '} Briven CLI (or take a{' '} Dashboard backup).
  2. Create a new project on Briven's general infrastructure.
  3. Restore the backup into the new project.
} />

Merge preview branches before May 31. For persistent branches:

  1. Take a{' '} snapshot of the branch database {' '} before shutting it down.
  2. Deploy a new persistent branch {' '} on Briven's general infrastructure.
  3. Restore your data manually from the snapshot.
} />

Questions or need an extension? Email{' '} {SUPPORT_EMAIL} .

) } const MAX_LISTED = 5 const ProjectList = ({ label, items, instructions, }: { label: string items: FlyDeprecationProject[] instructions: ReactNode }) => { if (items.length === 0) return null const visible = items.slice(0, MAX_LISTED) const remaining = items.length - visible.length return ( <>

{label} ({items.length})

{items.length === 1 ? (

{items[0].name} ({items[0].orgName})

) : ( )}
{instructions} ) }