diff --git a/apps/studio/components/interfaces/BranchManagement/OutOfDateNotice.tsx b/apps/studio/components/interfaces/BranchManagement/OutOfDateNotice.tsx index ca8db9e7c74..73444da1e93 100644 --- a/apps/studio/components/interfaces/BranchManagement/OutOfDateNotice.tsx +++ b/apps/studio/components/interfaces/BranchManagement/OutOfDateNotice.tsx @@ -17,10 +17,10 @@ import { Admonition } from 'ui-patterns' interface OutOfDateNoticeProps { isBranchOutOfDateMigrations: boolean missingMigrationsCount: number - hasNewerRemovedFunctions: boolean - newerRemovedFunctionsCount: number - hasNewerModifiedFunctions: boolean - newerModifiedFunctionsCount: number + hasMissingFunctions: boolean + missingFunctionsCount: number + hasOutOfDateFunctions: boolean + outOfDateFunctionsCount: number hasEdgeFunctionModifications: boolean modifiedFunctionsCount: number isPushing: boolean @@ -30,10 +30,10 @@ interface OutOfDateNoticeProps { export const OutOfDateNotice = ({ isBranchOutOfDateMigrations, missingMigrationsCount, - hasNewerRemovedFunctions, - newerRemovedFunctionsCount, - hasNewerModifiedFunctions, - newerModifiedFunctionsCount, + hasMissingFunctions, + missingFunctionsCount, + hasOutOfDateFunctions, + outOfDateFunctionsCount, hasEdgeFunctionModifications, modifiedFunctionsCount, isPushing, @@ -43,29 +43,18 @@ export const OutOfDateNotice = ({ const hasOutdatedMigrations = isBranchOutOfDateMigrations && missingMigrationsCount > 0 const getTitle = () => { - if (hasOutdatedMigrations && (hasNewerRemovedFunctions || hasNewerModifiedFunctions)) { - const functionCount = newerRemovedFunctionsCount + newerModifiedFunctionsCount - return `Missing ${missingMigrationsCount} migration${missingMigrationsCount !== 1 ? 's' : ''} and ${functionCount} updated function${functionCount !== 1 ? 's' : ''} from main branch` + if (hasOutdatedMigrations && (hasMissingFunctions || hasOutOfDateFunctions)) { + return 'Your database schema and edge functions are out of date' } else if (hasOutdatedMigrations) { - return `Missing ${missingMigrationsCount} migration${missingMigrationsCount !== 1 ? 's' : ''} from main branch` - } else if (hasNewerRemovedFunctions && hasNewerModifiedFunctions) { - const functionCount = newerRemovedFunctionsCount + newerModifiedFunctionsCount - return `Main branch has ${functionCount} updated function${functionCount !== 1 ? 's' : ''}` - } else if (hasNewerRemovedFunctions) { - return `Main branch has ${newerRemovedFunctionsCount} updated function${newerRemovedFunctionsCount !== 1 ? 's' : ''} that were removed in this branch` - } else if (hasNewerModifiedFunctions) { - return `Main branch has ${newerModifiedFunctionsCount} updated function${newerModifiedFunctionsCount !== 1 ? 's' : ''}` + return 'Your database schema is out of date' + } else if (hasMissingFunctions || hasOutOfDateFunctions) { + return 'Your functions are out of date' } return 'Branch is out of date' } const getDescription = () => { - if (hasOutdatedMigrations && (hasNewerRemovedFunctions || hasNewerModifiedFunctions)) { - return `The main branch has migrations and functions that were updated after this branch was created.` - } else if (hasNewerRemovedFunctions || hasNewerModifiedFunctions) { - return `The main branch has functions that were created or updated after this branch was created.` - } - return 'Update this branch to get the latest changes from the main branch.' + return 'Update this branch to get the latest changes from the production branch.' } const handleUpdateClick = () => { @@ -103,7 +92,8 @@ export const OutOfDateNotice = ({ This branch has {modifiedFunctionsCount} modified edge function {modifiedFunctionsCount !== 1 ? 's' : ''} that will be overwritten when updating - with the latest functions from the main branch, and this action cannot be undone. + with the latest functions from the production branch. This action cannot be + undone. diff --git a/apps/studio/components/interfaces/BranchManagement/WorkflowLogsCard.tsx b/apps/studio/components/interfaces/BranchManagement/WorkflowLogsCard.tsx index 9643fd6d1c8..75fb81e00b5 100644 --- a/apps/studio/components/interfaces/BranchManagement/WorkflowLogsCard.tsx +++ b/apps/studio/components/interfaces/BranchManagement/WorkflowLogsCard.tsx @@ -2,128 +2,97 @@ import React from 'react' import { Card, CardHeader, CardContent, CardTitle, Button } from 'ui' import { motion } from 'framer-motion' import { CircleDotDashed, GitMerge, X, AlertTriangle } from 'lucide-react' -import Link from 'next/link' -import { useWorkflowRunQuery } from 'data/workflow-runs/workflow-run-query' -import { useWorkflowRunsQuery } from 'data/workflow-runs/workflow-runs-query' + +interface WorkflowRun { + id: string + status: string + branch_id?: string + check_run_id?: number | null + created_at?: string + updated_at?: string + workdir?: string | null + git_config?: unknown +} interface WorkflowLogsCardProps { - workflowRunId: string - projectRef: string + workflowRun: WorkflowRun | null | undefined + logs: string | undefined + isLoading?: boolean onClose?: () => void - onStatusChange?: (status: string, workflowRunId: string) => void - statusComplete?: string // Status that indicates completion (e.g., 'FUNCTIONS_DEPLOYED') - statusFailed?: string[] // Statuses that indicate failure (e.g., ['MIGRATIONS_FAILED', 'FUNCTIONS_FAILED']) - headerTitle?: string // Additional header title - headerDescription?: string // Additional header description - headerIcon?: React.ReactNode // Additional header icon - headerAction?: React.ReactNode // Additional header action button + // Override props for failed workflows + overrideTitle?: string + overrideDescription?: string + overrideIcon?: React.ReactNode + overrideAction?: React.ReactNode } const WorkflowLogsCard: React.FC = ({ - workflowRunId, - projectRef, + workflowRun, + logs, + isLoading = false, onClose, - onStatusChange, - statusComplete = 'FUNCTIONS_DEPLOYED', - statusFailed = ['MIGRATIONS_FAILED', 'FUNCTIONS_FAILED'], - headerTitle, - headerDescription, - headerIcon, - headerAction, + overrideTitle, + overrideDescription, + overrideIcon, + overrideAction, }) => { const scrollRef = React.useRef(null) - // Get workflow runs for the specified project to find the workflow run - const { data: workflowRuns } = useWorkflowRunsQuery( - { projectRef }, - { - enabled: !!projectRef, - refetchInterval: 3000, // Always poll - refetchOnMount: 'always', - refetchOnWindowFocus: true, - staleTime: 0, - } - ) - - // Find the current workflow run - const currentWorkflowRun = React.useMemo(() => { - if (!workflowRunId) return null - return workflowRuns?.find((run) => run.id === workflowRunId) - }, [workflowRuns, workflowRunId]) - - // Query for workflow run logs with continuous polling - const { data: workflowRunLogs } = useWorkflowRunQuery( - { workflowRunId }, - { - enabled: !!workflowRunId, - refetchInterval: 2000, // Always poll every 2 seconds - refetchOnMount: 'always', - refetchOnWindowFocus: true, - staleTime: 0, // Always fetch fresh data - } - ) - // Auto-scroll to bottom when logs change React.useEffect(() => { - if (scrollRef.current && workflowRunLogs?.logs) { + if (scrollRef.current && logs) { scrollRef.current.scrollTop = scrollRef.current.scrollHeight } - }, [workflowRunLogs?.logs]) + }, [logs]) - // Call onStatusChange when status changes - React.useEffect(() => { - if (currentWorkflowRun?.status && onStatusChange) { - onStatusChange(currentWorkflowRun.status, workflowRunId) - } - }, [currentWorkflowRun?.status, onStatusChange, workflowRunId]) - - const showSuccessIcon = currentWorkflowRun?.status === statusComplete - const isFailed = currentWorkflowRun?.status && statusFailed.includes(currentWorkflowRun.status) + const showSuccessIcon = workflowRun?.status === 'FUNCTIONS_DEPLOYED' + const isFailed = + workflowRun?.status && ['MIGRATIONS_FAILED', 'FUNCTIONS_FAILED'].includes(workflowRun.status) const isPolling = - currentWorkflowRun?.status !== statusComplete && - (!currentWorkflowRun?.status || !statusFailed.includes(currentWorkflowRun.status)) + workflowRun?.status !== 'FUNCTIONS_DEPLOYED' && + (!workflowRun?.status || + !['MIGRATIONS_FAILED', 'FUNCTIONS_FAILED'].includes(workflowRun.status)) + + const displayTitle = + overrideTitle || + (isPolling + ? 'Processing...' + : showSuccessIcon + ? 'Workflow completed successfully' + : isFailed + ? 'Workflow failed' + : 'Workflow completed') + + const displayIcon = + overrideIcon || + (isPolling ? ( + + + + ) : showSuccessIcon ? ( + + ) : null) return (
- {/* Custom icon or activity / success indicator */} - {headerIcon ? ( - headerIcon - ) : isPolling ? ( - - - - ) : ( - showSuccessIcon && - )} + {displayIcon}
- {/* Custom title or default status text */} - - {headerTitle || - (isPolling - ? 'Processing...' - : showSuccessIcon - ? 'Workflow completed successfully' - : isFailed - ? 'Workflow failed' - : 'Workflow completed')} - - {/* Custom description */} - {headerDescription && ( + {displayTitle} + {overrideDescription && (
- {headerDescription} + {overrideDescription}
)}
- {/* Custom action or default close button */} - {headerAction && headerAction} + {overrideAction && overrideAction} {onClose && (