Bit more consolidation to reduce duplication

This commit is contained in:
Joshen Lim committed 2025-07-24 17:23:28 +08:00
1 parent 3ecbcb4268
commit 6c79cdcd4d
6 files changed
+196 -210

No files matched your search

@@ -1,3 +1,4 @@
import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold'
import Table from 'components/to-be-cleaned/Table'
import { motion } from 'framer-motion'
import { BASE_PATH } from 'lib/constants'
@@ -62,6 +63,14 @@ const STATIC_EDGES = [
{ id: 'e1-4', source: '1', target: '4', type: 'smoothstep', animated: true },
]
export const ReplicationComingSoon = () => {
return (
<ReactFlowProvider>
<ReplicationStaticMockup />
</ReactFlowProvider>
)
}
const ReplicationStaticMockup = () => {
const nodes = useMemo(() => STATIC_NODES, [])
const edges = useMemo(() => STATIC_EDGES, [])
@@ -108,16 +117,6 @@ const ReplicationStaticMockup = () => {
)
}
const ReplicationComingSoon = () => {
return (
<ReactFlowProvider>
<ReplicationStaticMockup />
</ReactFlowProvider>
)
}
export default ReplicationComingSoon
const PrimaryNode = ({
data,
}: {
@@ -251,58 +250,68 @@ const StaticDestinations = () => {
return (
<>
<div className="flex flex-col bg-surface-100 px-6 pt-6 border-t relative h-full">
<div className="bg-surface-300 w-full h-full absolute top-0 left-0 opacity-30"></div>
<div className="flex items-center justify-between">
<div className="relative w-52">
<Search
className="absolute left-3 top-1/2 transform -translate-y-1/2 text-foreground-lighter"
size={14}
/>
<Input_Shadcn_ className="pl-9 bg-transparent h-8" placeholder="Search..." />
</div>
<Button
icon={<Plus size={16} />}
type="primary"
className="flex items-center"
onClick={() => {}}
>
New destination
</Button>
</div>
<Table
head={[
<Table.th key="name">Name</Table.th>,
<Table.th key="publication">Publication</Table.th>,
<Table.th key="lag">Lag</Table.th>,
<Table.th key="status">Status</Table.th>,
<Table.th key="actions"></Table.th>,
]}
className="mt-4"
body={mockRows.map((row, i) => (
<Table.tr key={i}>
<Table.td>{row.name}</Table.td>
<Table.td>
<span className="flex items-center gap-2">
<span className="font-bold">All</span>
<span className="text-sm text-foreground-lighter">{row.tables} tables</span>
</span>
</Table.td>
<Table.td>{row.lag}</Table.td>
<Table.td>
<span className="flex items-center gap-3">
<Circle size={10} className="bg-brand-500 stroke-none rounded-full" />
{row.status}
</span>
</Table.td>
<Table.td className="text-right">
<button className="p-1">
<MoreVertical size={18} />
</button>
</Table.td>
</Table.tr>
))}
/>
<div className="flex flex-col bg-surface-100 px-6 py-6 border-t relative ">
<div className="z-10 bg-surface-300 w-full h-full absolute top-0 left-0 opacity-30" />
<ScaffoldContainer>
<ScaffoldSection className="!py-0">
<div className="col-span-12">
<div className="flex items-center justify-between">
<div className="relative w-52">
<Search
size={14}
className="absolute left-3 top-1/2 transform -translate-y-1/2 text-foreground-lighter"
/>
<Input_Shadcn_
className="pl-9 bg-transparent h-8 pointer-events-none"
placeholder="Search..."
/>
</div>
<Button
disabled
type="primary"
icon={<Plus size={16} />}
className="flex items-center pointer-events-none"
>
New destination
</Button>
</div>
<Table
head={[
<Table.th key="name">Name</Table.th>,
<Table.th key="publication">Publication</Table.th>,
<Table.th key="lag">Lag</Table.th>,
<Table.th key="status">Status</Table.th>,
<Table.th key="actions"></Table.th>,
]}
className="mt-4"
body={mockRows.map((row, i) => (
<Table.tr key={i}>
<Table.td>{row.name}</Table.td>
<Table.td>
<span className="flex items-center gap-2">
<span className="font-bold">All</span>
<span className="text-sm text-foreground-lighter">{row.tables} tables</span>
</span>
</Table.td>
<Table.td>{row.lag}</Table.td>
<Table.td>
<span className="flex items-center gap-3">
<Circle size={10} className="bg-brand-500 stroke-none rounded-full" />
{row.status}
</span>
</Table.td>
<Table.td className="text-right">
<button className="p-1">
<MoreVertical size={18} />
</button>
</Table.td>
</Table.tr>
))}
/>
</div>
</ScaffoldSection>
</ScaffoldContainer>
</div>
</>
)
@@ -136,10 +136,11 @@ export const DestinationRow = ({
)}
</Table.td>
<Table.td>
<div className="flex items-center justify-end gap-1">
<div className="flex items-center justify-end gap-x-1">
{pipeline && (
<ButtonTooltip
size="tiny"
type="default"
icon={<Info className="w-3 h-3" />}
onClick={() => onSelectPipeline?.(pipeline.id, destinationName)}
className="px-1.5"
@@ -55,8 +55,8 @@ export const PipelineStatus = ({
<span>Failed</span>
</div>
</TooltipTrigger>
<TooltipContent>
<p>{getPipelineStateMessages(requestStatus, 'failed').message}</p>
<TooltipContent side="bottom">
{getPipelineStateMessages(requestStatus, 'failed').message}
</TooltipContent>
</Tooltip>
)
@@ -165,9 +165,7 @@ export const PipelineStatus = ({
<span>{statusConfig.label}</span>
</div>
</TooltipTrigger>
<TooltipContent>
<p>{statusConfig.tooltip}</p>
</TooltipContent>
<TooltipContent side="bottom">{statusConfig.tooltip}</TooltipContent>
</Tooltip>
)}
</>
@@ -18,7 +18,6 @@ import { useParams } from 'common'
import Table from 'components/to-be-cleaned/Table'
import AlertError from 'components/ui/AlertError'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { InlineLink } from 'components/ui/InlineLink'
import { useReplicationPipelineByIdQuery } from 'data/replication/pipeline-by-id-query'
import { useReplicationPipelineReplicationStatusQuery } from 'data/replication/pipeline-replication-status-query'
import { useReplicationPipelineStatusQuery } from 'data/replication/pipeline-status-query'
@@ -28,7 +27,16 @@ import {
PipelineStatusRequestStatus,
usePipelineRequestStatus,
} from 'state/replication-pipeline-request-status'
import { Badge, Button, cn, copyToClipboard, Input_Shadcn_ } from 'ui'
import {
Badge,
Button,
cn,
copyToClipboard,
Input_Shadcn_,
Tooltip,
TooltipContent,
TooltipTrigger,
} from 'ui'
import { GenericSkeletonLoader } from 'ui-patterns'
import { getPipelineStateMessages, getStatusName, PIPELINE_ERROR_MESSAGES } from './Pipeline.utils'
import { PipelineStatus } from './PipelineStatus'
@@ -298,7 +306,6 @@ export const ReplicationPipelineStatus = ({
requestStatus={requestStatus}
/>
</div>
<p className="text-sm text-foreground-light">Table replication status</p>
</div>
</div>
<div className="flex items-center gap-x-2">
@@ -314,6 +321,14 @@ export const ReplicationPipelineStatus = ({
onChange={(e) => setFilterString(e.target.value)}
/>
</div>
<Button
type={statusName === 'stopped' ? 'primary' : 'default'}
onClick={() => onTogglePipeline()}
loading={isStartingPipeline || isStoppingPipeline}
disabled={!['started', 'stopped'].includes(statusName ?? '')}
>
{statusName === 'stopped' ? 'Enable' : 'Disable'} pipeline
</Button>
</div>
</div>
@@ -358,39 +373,31 @@ export const ReplicationPipelineStatus = ({
</div>
)}
{/* Pipeline not running state */}
{showDisabledState && hasTableData && (
<div className="space-y-4">
<div
className={cn(
'p-4 border border-default rounded-lg flex items-center justify-between',
config.colors.bg
)}
>
<div className="flex items-center gap-x-3">
<div
className={cn(
'w-10 h-10 rounded-full flex items-center justify-center',
config.colors.iconBg
)}
>
<div className={config.colors.icon}>{config.icon}</div>
</div>
<div className="flex-1">
<h4 className={`text-sm font-medium ${config.colors.text}`}>{config.title}</h4>
<p className={`text-sm ${config.colors.subtext}`}>{config.message}</p>
{hasTableData && (
<div className="flex flex-col gap-y-4">
{showDisabledState && (
<div
className={cn(
'p-4 border border-default rounded-lg flex items-center justify-between',
config.colors.bg
)}
>
<div className="flex items-center gap-x-3">
<div
className={cn(
'w-10 h-10 rounded-full flex items-center justify-center',
config.colors.iconBg
)}
>
<div className={config.colors.icon}>{config.icon}</div>
</div>
<div className="flex-1">
<h4 className={`text-sm font-medium ${config.colors.text}`}>{config.title}</h4>
<p className={`text-sm ${config.colors.subtext}`}>{config.message}</p>
</div>
</div>
</div>
{['stopped', 'started'].includes(statusName ?? '') && (
<Button
type={statusName === 'stopped' ? 'primary' : 'default'}
onClick={() => onTogglePipeline()}
loading={isStartingPipeline || isStoppingPipeline}
>
{statusName === 'stopped' ? 'Enable' : 'Disable'} pipeline
</Button>
)}
</div>
)}
<div className="w-full overflow-hidden overflow-x-auto">
<Table
@@ -402,105 +409,73 @@ export const ReplicationPipelineStatus = ({
Actions
</Table.th>,
]}
body={filteredTableStatuses.map((table: TableState, index: number) => (
<Table.tr key={`${table.table_name}-${index}`} className="border-t opacity-50">
<Table.td>
<InlineLink href={`/project/${projectRef}/editor/${table.table_id}`}>
{table.table_name}
</InlineLink>
</Table.td>
<Table.td>
<Badge
variant="default"
className="text-foreground-lighter bg-surface-100 border-border-muted"
>
Not Available
</Badge>
</Table.td>
<Table.td>
<div className="text-sm text-foreground-lighter">
Status unavailable while pipeline is {config.badge.toLowerCase()}
</div>
</Table.td>
<Table.td className="text-center">
<ButtonTooltip
type="text"
size="tiny"
icon={<Copy className="w-3 h-3" />}
className="px-1.5"
disabled
tooltip={{
content: {
side: 'bottom',
text: `Copy unavailable while pipeline is ${config.badge.toLowerCase()}`,
},
}}
/>
</Table.td>
</Table.tr>
))}
/>
</div>
</div>
)}
{/* Tables list - active/running pipeline */}
{showActiveTable && !isStatusLoading && hasTableData && (
<div className="w-full overflow-hidden overflow-x-auto">
<Table
head={[
<Table.th key="table">Table</Table.th>,
<Table.th key="status">Status</Table.th>,
<Table.th key="details">Details</Table.th>,
<Table.th key="actions" className="w-16 text-center">
Actions
</Table.th>,
]}
body={filteredTableStatuses.map((table: TableState, index: number) => {
const statusConfig = getStatusConfig(table.state)
return (
<Table.tr
key={`${table.table_name}-${index}`}
className="border-t hover:bg-surface-100"
>
<Table.td>
<Link
href={`/project/${projectRef}/editor/${table.table_id}`}
className="font-mono text-sm font-medium text-foreground hover:text-foreground-light transition-colors cursor-pointer underline underline-offset-2"
>
{table.table_name}
</Link>
</Table.td>
<Table.td>{statusConfig.badge}</Table.td>
<Table.td>
<div className="space-y-1">
<div className="text-sm text-foreground">{statusConfig.description}</div>
{'lag' in table.state && (
<div className="text-xs text-foreground-light">
Lag: {table.state.lag}ms
body={filteredTableStatuses.map((table: TableState, index: number) => {
const statusConfig = getStatusConfig(table.state)
return (
<Table.tr key={`${table.table_name}-${index}`} className="border-t opacity-50">
<Table.td>
<div className="flex items-center gap-x-2">
<p>{table.table_name}</p>
<Tooltip>
<TooltipTrigger>
<a
target="_blank"
rel="noopener noreferrer"
href={`/project/${projectRef}/editor/${table.table_id}`}
>
<ExternalLink size={14} />
</a>
</TooltipTrigger>
<TooltipContent>Open in Table Editor</TooltipContent>
</Tooltip>
</div>
</Table.td>
<Table.td>
{showDisabledState ? (
<Badge variant="default">Not Available</Badge>
) : (
statusConfig.badge
)}
</Table.td>
<Table.td>
{showDisabledState ? (
<p className="text-sm text-foreground-lighter">
Status unavailable while pipeline is {config.badge.toLowerCase()}
</p>
) : (
<div className="space-y-1">
<div className="text-sm text-foreground">{statusConfig.description}</div>
{'lag' in table.state && (
<div className="text-xs text-foreground-light">
Lag: {table.state.lag}ms
</div>
)}
</div>
)}
</div>
</Table.td>
<Table.td className="text-center">
<ButtonTooltip
type="text"
size="tiny"
icon={<Copy className="w-3 h-3" />}
className="px-1.5"
onClick={() => handleCopyTableStatus(table.table_name, table.state)}
tooltip={{
content: {
side: 'bottom',
text: 'Copy status details',
},
}}
/>
</Table.td>
</Table.tr>
)
})}
/>
</Table.td>
<Table.td className="text-center">
<ButtonTooltip
type="text"
size="tiny"
icon={<Copy className="w-3 h-3" />}
className="px-1.5"
disabled={showDisabledState}
onClick={() => handleCopyTableStatus(table.table_name, table.state)}
tooltip={{
content: {
side: 'bottom',
text: showDisabledState
? `Copy unavailable while pipeline is ${config.badge.toLowerCase()}`
: 'Copy status details',
},
}}
/>
</Table.td>
</Table.tr>
)
})}
/>
</div>
</div>
)}
@@ -1,4 +1,6 @@
import ReplicationComingSoon from 'components/interfaces/Database/Replication/ComingSoon'
import { useState } from 'react'
import { ReplicationComingSoon } from 'components/interfaces/Database/Replication/ComingSoon'
import { Destinations } from 'components/interfaces/Database/Replication/Destinations'
import { ReplicationPipelineStatus } from 'components/interfaces/Database/Replication/ReplicationPipelineStatus'
import DatabaseLayout from 'components/layouts/DatabaseLayout/DatabaseLayout'
@@ -6,7 +8,6 @@ import DefaultLayout from 'components/layouts/DefaultLayout'
import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold'
import { FormHeader } from 'components/ui/Forms/FormHeader'
import { useFlag } from 'hooks/ui/useFlag'
import { useState } from 'react'
import { PipelineRequestStatusProvider } from 'state/replication-pipeline-request-status'
import type { NextPageWithLayout } from 'types'
@@ -51,14 +52,16 @@ const DatabaseReplicationPage: NextPageWithLayout = () => {
</ScaffoldContainer>
</PipelineRequestStatusProvider>
) : (
<ScaffoldContainer>
<ScaffoldSection>
<div className="col-span-12">
<FormHeader title="Replication" description="Send data to other destinations" />
<ReplicationComingSoon />
</div>
</ScaffoldSection>
</ScaffoldContainer>
<>
<ScaffoldContainer>
<ScaffoldSection>
<div className="col-span-12">
<FormHeader title="Replication" description="Send data to other destinations" />
</div>
</ScaffoldSection>
</ScaffoldContainer>
<ReplicationComingSoon />
</>
)}
</>
)
+1 -1
View File
@@ -119,7 +119,7 @@
}
.table-container thead th:first-child {
@apply pl-6 rounded rounded-r-none rounded-b-none;
@apply rounded rounded-r-none rounded-b-none;
@apply border-l;
}