Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -133,15 +133,15 @@ export const Destinations = () => {
'flex flex-col px-10 rounded-lg justify-center items-center py-8 mt-4'
)}
>
<h4 className="text-lg">Send data to your first destination</h4>
<p className="prose text-sm text-center mt-2">
<h4>Send data to your first destination</h4>
<p className="prose text-sm text-center mt-1 max-w-full">
Use destinations to improve performance or run analysis on your data via
integrations like BigQuery
</p>
<Button
icon={<Plus />}
onClick={() => setShowNewDestinationPanel(true)}
className="mt-6"
className="mt-4"
>
Add destination
</Button>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { useParams } from 'common'
import { InlineLink } from 'components/ui/InlineLink'
import { TableState } from './ReplicationPipelineStatus.types'
import { isValidRetryPolicy } from './ReplicationPipelineStatus.utils'
import { RetryCountdown } from './RetryCountdown'
import { RetryOptionsDropdown } from './RetryOptionsDropdown'

interface ErroredTableDetailsProps {
state: Extract<TableState['state'], { name: 'error' }>
tableName: string
tableId: number
}

export const ErroredTableDetails = ({ state, tableName, tableId }: ErroredTableDetailsProps) => {
const { ref: projectRef } = useParams()
const retryPolicy = state.retry_policy.policy

if (!isValidRetryPolicy(state.retry_policy)) {
return (
<div
role="region"
className="flex flex-col gap-y-3"
aria-label={`Error details for table ${tableName}`}
>
{state.solution && <div className="text-xs text-foreground-light">{state.solution}</div>}
<div className="text-xs text-foreground-lighter">Invalid retry policy configuration</div>
</div>
)
}

return (
<div role="region" aria-label={`Error details for table ${tableName}`}>
{retryPolicy === 'no_retry' ? (
<p className="text-xs text-foreground-lighter">
This error requires manual intervention from our{' '}
<InlineLink
className="text-foreground-lighter hover:text-foreground"
href={`/support?projectRef=${projectRef}&category=dashboard_bug&subject=Database%20replication%20error&error=${state.reason}`}
>
support
</InlineLink>
. Alternatively, you may also recreate the pipeline.
</p>
) : retryPolicy === 'manual_retry' ? (
<div className="flex flex-col gap-y-2 text-foreground-lighter">
<p className="text-xs">{state.solution}. You may thereafter rollback the pipeline.</p>
<RetryOptionsDropdown tableId={tableId} tableName={tableName} />
</div>
) : retryPolicy === 'timed_retry' ? (
<div className="flex flex-col text-foreground-lighter">
<p className="text-xs">
A retry will be triggered automatically by restarting the pipeline on this table.
</p>
<RetryCountdown nextRetryTime={state.retry_policy.next_retry} />
</div>
) : null}
</div>
)
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import AlertError from 'components/ui/AlertError'
import { ReplicationPipelineStatusData } from 'data/replication/pipeline-status-query'
import { AlertTriangle, Loader2 } from 'lucide-react'
import { PipelineStatusRequestStatus } from 'state/replication-pipeline-request-status'
import { ResponseError } from 'types'
import { cn, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
import { cn, Tooltip, TooltipContent, TooltipTrigger, WarningIcon } from 'ui'
import ShimmeringLoader from 'ui-patterns/ShimmeringLoader'
import { getPipelineStateMessages, PIPELINE_ERROR_MESSAGES } from './Pipeline.utils'
import { getPipelineStateMessages } from './Pipeline.utils'

export enum PipelineStatusName {
FAILED = 'failed',
Expand All @@ -15,9 +14,6 @@ export enum PipelineStatusName {
UNKNOWN = 'unknown',
}

// Type alias for better readability
type FailedStatus = Extract<ReplicationPipelineStatusData['status'], { name: 'failed' }>

interface PipelineStatusProps {
pipelineStatus: ReplicationPipelineStatusData['status'] | undefined
error: ResponseError | null
Expand All @@ -35,32 +31,6 @@ export const PipelineStatus = ({
isSuccess,
requestStatus,
}: PipelineStatusProps) => {
const isFailedStatus = (
status: ReplicationPipelineStatusData['status'] | undefined
): status is FailedStatus => {
return (
status !== null &&
status !== undefined &&
typeof status === 'object' &&
status.name === PipelineStatusName.FAILED
)
}

const renderFailedStatus = () => {
return (
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center gap-2 text-sm text-destructive">
<AlertTriangle className="w-3 h-3" />
<span>Failed</span>
</div>
</TooltipTrigger>
<TooltipContent side="bottom">
{getPipelineStateMessages(requestStatus, 'failed').message}
</TooltipContent>
</Tooltip>
)
}
// Map backend statuses to UX-friendly display
const getStatusConfig = () => {
const statusName =
Expand Down Expand Up @@ -89,15 +59,15 @@ export const PipelineStatus = ({
}
}

// Handle Failed status object
if (isFailedStatus(pipelineStatus)) {
return {
isFailedStatus: true,
}
}

if (pipelineStatus && typeof pipelineStatus === 'object' && 'name' in pipelineStatus) {
switch (pipelineStatus.name) {
case PipelineStatusName.FAILED:
return {
label: 'Failed',
dot: <AlertTriangle className="w-3 h-3 text-destructive-600" />,
color: 'text-destructive-600',
tooltip: stateMessages.message,
}
case PipelineStatusName.STARTING:
return {
label: 'Starting',
Expand Down Expand Up @@ -151,24 +121,25 @@ export const PipelineStatus = ({
<>
{isLoading && <ShimmeringLoader />}
{isError && (
<AlertError error={error} subject={PIPELINE_ERROR_MESSAGES.RETRIEVE_PIPELINE_STATUS} />
<Tooltip>
<TooltipTrigger>
<WarningIcon />
</TooltipTrigger>
<TooltipContent side="bottom" className="w-64 text-center">
Unable to retrieve status: {error?.message}
</TooltipContent>
</Tooltip>
)}
{isSuccess && (
<>
{statusConfig.isFailedStatus ? (
<div className="relative">{renderFailedStatus()}</div>
) : (
<Tooltip>
<TooltipTrigger asChild>
<div className={cn('flex items-center gap-2 text-sm w-min', statusConfig.color)}>
{statusConfig.dot}
<span>{statusConfig.label}</span>
</div>
</TooltipTrigger>
<TooltipContent side="bottom">{statusConfig.tooltip}</TooltipContent>
</Tooltip>
)}
</>
<Tooltip>
<TooltipTrigger asChild>
<div className={cn('flex items-center gap-2 text-sm w-min', statusConfig.color)}>
{statusConfig.dot}
<span>{statusConfig.label}</span>
</div>
</TooltipTrigger>
<TooltipContent side="bottom">{statusConfig.tooltip}</TooltipContent>
</Tooltip>
)}
</>
)
Expand Down
Loading
Loading