import { PermissionAction } from '@supabase/shared-types/out/constants' import { useQueryClient } from '@tanstack/react-query' import { useParams } from 'common' import { partition } from 'lodash' import { Clock, ExternalLink, Infinity, MoreVertical, Pencil, RefreshCw, Shield, Trash2, } from 'lucide-react' import Link from 'next/link' import { useState } from 'react' import { toast } from 'sonner' import { Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from 'ui' import { ConfirmationModal } from 'ui-patterns/Dialogs/ConfirmationModal' import { BranchLoader, BranchManagementSection, BranchRow, BranchRowLoader } from './BranchPanels' import { EditBranchModal } from './EditBranchModal' import { PreviewBranchesEmptyState } from './EmptyStates' import { DropdownMenuItemTooltip } from '@/components/ui/DropdownMenuItemTooltip' import { TextConfirmModal } from '@/components/ui/TextConfirmModalWrapper' import { useBranchQuery } from '@/data/branches/branch-query' import { useBranchResetMutation } from '@/data/branches/branch-reset-mutation' import { useBranchRestoreMutation } from '@/data/branches/branch-restore-mutation' import { useBranchUpdateMutation } from '@/data/branches/branch-update-mutation' import type { Branch } from '@/data/branches/branches-query' import { branchKeys } from '@/data/branches/keys' import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements' import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization' import { IS_PLATFORM } from '@/lib/constants' interface OverviewProps { isGithubConnected: boolean isLoading: boolean isSuccess: boolean repo: string mainBranch: Branch previewBranches: Branch[] onSelectCreateBranch: () => void onSelectDeleteBranch: (branch: Branch) => void generateCreatePullRequestURL: (branchName?: string) => string } export const Overview = ({ isGithubConnected, isLoading, isSuccess, repo, mainBranch, previewBranches, onSelectCreateBranch, onSelectDeleteBranch, generateCreatePullRequestURL, }: OverviewProps) => { const [scheduledForDeletionBranches, aliveBranches] = partition( previewBranches, (branch) => branch.deletion_scheduled_at !== undefined ) const [persistentBranches, ephemeralBranches] = partition( aliveBranches, (branch) => branch.persistent ) const { ref: projectRef } = useParams() const { data: selectedOrg } = useSelectedOrganizationQuery() const { hasAccess: hasAccessToPersistentBranching, isLoading: isLoadingEntitlement } = useCheckEntitlements('branching_persistent') return ( <> {isLoading && } {isSuccess && mainBranch !== undefined && ( {mainBranch.name} } repo={repo} rowActions={} /> )} {isSuccess && mainBranch === undefined && (
main
)}
{/* Persistent Branches Section */} {(isLoading || isLoadingEntitlement) && } {isSuccess && !isLoadingEntitlement && !hasAccessToPersistentBranching && IS_PLATFORM && persistentBranches.length === 0 && (

Upgrade to unlock persistent branches

Persistent branches are long-lived, cannot be reset, and are ideal for staging environments.

)} {isSuccess && !isLoadingEntitlement && hasAccessToPersistentBranching && persistentBranches.length === 0 && (

No persistent branches

Persistent branches are long-lived, cannot be reset, and are ideal for staging environments.

)} {isSuccess && !isLoadingEntitlement && persistentBranches.map((branch) => { return ( onSelectDeleteBranch(branch)} generateCreatePullRequestURL={generateCreatePullRequestURL} /> } /> ) })}
{/* Ephemeral/Preview Branches Section */} {isLoading && } {isSuccess && ephemeralBranches.length === 0 && ( )} {isSuccess && ephemeralBranches.map((branch) => { return ( onSelectDeleteBranch(branch)} generateCreatePullRequestURL={generateCreatePullRequestURL} /> } /> ) })} {/* Scheduled for deletion branches section */} {isLoading && } {isSuccess && scheduledForDeletionBranches.length === 0 && (

No branches scheduled for deletion

)} {isSuccess && scheduledForDeletionBranches.map((branch) => { return ( onSelectDeleteBranch(branch)} generateCreatePullRequestURL={generateCreatePullRequestURL} /> } /> ) })}
) } // Row actions for preview branches (non-main) const PreviewBranchActions = ({ branch, onSelectDeleteBranch, generateCreatePullRequestURL, }: { branch: Branch repo: string onSelectDeleteBranch: () => void generateCreatePullRequestURL: (branchName?: string) => string }) => { const queryClient = useQueryClient() const { project_ref: branchRef, parent_project_ref: projectRef } = branch const { can: canDeleteBranches } = useAsyncCheckPermissions( PermissionAction.DELETE, 'preview_branches' ) const { can: canUpdateBranches } = useAsyncCheckPermissions( PermissionAction.UPDATE, 'preview_branches' ) // If user can update branches, they can restore branches const canRestoreBranches = canUpdateBranches const { data } = useBranchQuery({ projectRef, branchRef }) const isBranchActiveHealthy = data?.status === 'ACTIVE_HEALTHY' const isPersistentBranch = branch.persistent const { hasAccess: hasAccessToPersistentBranching } = useCheckEntitlements('branching_persistent') const [showConfirmResetModal, setShowConfirmResetModal] = useState(false) const [showBranchModeSwitch, setShowBranchModeSwitch] = useState(false) const [ showPersistentBranchDeleteConfirmationModal, setShowPersistentBranchDeleteConfirmationModal, ] = useState(false) const [showEditBranchModal, setShowEditBranchModal] = useState(false) const { mutate: resetBranch, isPending: isResetting } = useBranchResetMutation({ onSuccess() { toast.success('Success! Please allow a few seconds for the branch to reset.') setShowConfirmResetModal(false) }, }) const { mutate: updateBranch, isPending: isUpdatingBranch } = useBranchUpdateMutation({ onSuccess() { toast.success('Successfully updated branch') setShowBranchModeSwitch(false) if (projectRef) { queryClient.invalidateQueries({ queryKey: branchKeys.list(projectRef) }) } }, }) const { mutate: restoreBranch } = useBranchRestoreMutation({ onSuccess() { toast.success('Success! Please allow a few minutes for the branch to restore.') setShowBranchModeSwitch(false) }, }) const onRestoreBranch = () => { restoreBranch({ branchRef, projectRef }) } const onConfirmReset = () => { resetBranch({ branchRef, projectRef }) } const onTogglePersistent = () => { updateBranch({ branchRef, projectRef, persistent: !branch.persistent }) } const onDeleteBranch = (e: Event | React.MouseEvent) => { if (isPersistentBranch) { setShowPersistentBranchDeleteConfirmationModal(true) } else { e.stopPropagation() onSelectDeleteBranch() } } return ( <>