mirror of
https://github.com/ruvnet/RuVector.git
synced 2026-05-24 13:54:31 +00:00
Major additions: - Complete Next.js studio application with 1600+ components - Docker support (Dockerfile.combined, docker-compose.yml) - GCP deployment documentation and benchmarks - SQL benchmark scripts for performance testing - Sentry integration for monitoring - Comprehensive test suite and mocks Studio features: - Dashboard and admin interfaces - Data visualization components - Authentication and user management - API integration with RuVector backend - Static data and public assets 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
125 lines
4 KiB
TypeScript
125 lines
4 KiB
TypeScript
import { ChevronDown, RotateCcw, Undo2 } from 'lucide-react'
|
|
import { useState } from 'react'
|
|
import { toast } from 'sonner'
|
|
|
|
import { useParams } from 'common'
|
|
import { useRestartPipelineHelper } from 'data/replication/restart-pipeline-helper'
|
|
import { RollbackType, useRollbackTableMutation } from 'data/replication/rollback-table-mutation'
|
|
import {
|
|
Button,
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuTrigger,
|
|
} from 'ui'
|
|
|
|
const RETRY_OPTIONS = [
|
|
{
|
|
type: 'individual' as RollbackType,
|
|
icon: <Undo2 className="w-3 h-3 mt-0.5" />,
|
|
title: 'Rollback to previous state',
|
|
description: 'Restart from the last working state (may not be enough in some cases)',
|
|
},
|
|
{
|
|
type: 'full' as RollbackType,
|
|
icon: <RotateCcw className="w-3 h-3 mt-0.5" />,
|
|
title: 'Reset from scratch',
|
|
description: 'Completely restart replicating this table',
|
|
},
|
|
] as const
|
|
|
|
interface RetryOptionsDropdownProps {
|
|
tableId: number
|
|
tableName: string
|
|
}
|
|
|
|
export const RetryOptionsDropdown = ({ tableId, tableName }: RetryOptionsDropdownProps) => {
|
|
const { ref: projectRef, pipelineId: _pipelineId } = useParams()
|
|
const [isOpen, setIsOpen] = useState(false)
|
|
const [isRestartingPipeline, setIsRestartingPipeline] = useState(false)
|
|
|
|
const { restartPipeline } = useRestartPipelineHelper()
|
|
|
|
const { mutate: rollbackTable, isPending: isRollingBack } = useRollbackTableMutation({
|
|
onSuccess: async (_, vars) => {
|
|
const { projectRef, pipelineId, rollbackType } = vars
|
|
toast.success(
|
|
`Table "${tableName}" ${rollbackType === 'full' ? 'reset' : 'rolled back'} successfully and pipeline is being restarted`
|
|
)
|
|
|
|
setIsRestartingPipeline(true)
|
|
try {
|
|
await restartPipeline({ projectRef, pipelineId })
|
|
toast.success('Pipeline restarted successfully')
|
|
} catch (error: any) {
|
|
toast.error(`Failed to restart pipeline: ${error.message}`)
|
|
} finally {
|
|
setIsRestartingPipeline(false)
|
|
setIsOpen(false)
|
|
}
|
|
},
|
|
onError: (error, vars) => {
|
|
const { rollbackType } = vars
|
|
toast.error(
|
|
`Failed to ${rollbackType === 'full' ? 'reset' : 'rollback'} table: ${error.message}`
|
|
)
|
|
},
|
|
})
|
|
|
|
const isLoading = isRollingBack || isRestartingPipeline
|
|
|
|
const handleRollback = async (rollbackType: RollbackType) => {
|
|
if (!projectRef) return toast.error('Project ref is required')
|
|
if (!_pipelineId) return toast.error('Pipeline ID is required')
|
|
|
|
const pipelineId = Number(_pipelineId)
|
|
|
|
rollbackTable({
|
|
projectRef,
|
|
pipelineId,
|
|
tableId,
|
|
rollbackType,
|
|
})
|
|
}
|
|
|
|
return (
|
|
<DropdownMenu open={isOpen} onOpenChange={setIsOpen}>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button
|
|
size="tiny"
|
|
type="default"
|
|
loading={isLoading}
|
|
disabled={isLoading}
|
|
className="w-min"
|
|
iconRight={<ChevronDown />}
|
|
aria-label={`Rollback ${tableName}`}
|
|
>
|
|
Rollback
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
|
|
<DropdownMenuContent align="start" className="w-72" aria-label="Rollback options">
|
|
{RETRY_OPTIONS.map((option) => (
|
|
<DropdownMenuItem
|
|
key={option.type}
|
|
disabled={isLoading}
|
|
className="flex flex-col items-start px-3 py-2 cursor-pointer disabled:cursor-not-allowed disabled:opacity-50"
|
|
aria-describedby={`rollback-${option.type}-description`}
|
|
onClick={() => handleRollback(option.type)}
|
|
>
|
|
<div className="flex items-start gap-x-2 w-full">
|
|
<div className="min-w-4">{option.icon}</div>
|
|
<div
|
|
id={`rollback-${option.type}-description`}
|
|
className="flex flex-col gap-y-1 text-xs"
|
|
>
|
|
<p>{option.title}</p>
|
|
<p className="text-foreground-light">{option.description}</p>
|
|
</div>
|
|
</div>
|
|
</DropdownMenuItem>
|
|
))}
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
)
|
|
}
|