mirror of
https://github.com/ruvnet/RuVector.git
synced 2026-06-01 06:10: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>
173 lines
6 KiB
TypeScript
173 lines
6 KiB
TypeScript
import { zodResolver } from '@hookform/resolvers/zod'
|
|
import { X } from 'lucide-react'
|
|
import { useForm } from 'react-hook-form'
|
|
import { toast } from 'sonner'
|
|
import { z } from 'zod'
|
|
|
|
import { useParams } from 'common'
|
|
import { useCreatePublicationMutation } from 'data/replication/publication-create-mutation'
|
|
import { useReplicationTablesQuery } from 'data/replication/tables-query'
|
|
import {
|
|
Button,
|
|
cn,
|
|
Form_Shadcn_,
|
|
FormControl_Shadcn_,
|
|
FormField_Shadcn_,
|
|
Input_Shadcn_,
|
|
Sheet,
|
|
SheetClose,
|
|
SheetContent,
|
|
SheetDescription,
|
|
SheetFooter,
|
|
SheetHeader,
|
|
SheetSection,
|
|
SheetTitle,
|
|
} from 'ui'
|
|
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
|
|
import { MultiSelector } from 'ui-patterns/multi-select'
|
|
|
|
interface NewPublicationPanelProps {
|
|
visible: boolean
|
|
sourceId?: number
|
|
onClose: () => void
|
|
}
|
|
|
|
export const NewPublicationPanel = ({ visible, sourceId, onClose }: NewPublicationPanelProps) => {
|
|
const { ref: projectRef } = useParams()
|
|
const { mutateAsync: createPublication, isPending: creatingPublication } =
|
|
useCreatePublicationMutation()
|
|
const { data: tables } = useReplicationTablesQuery({
|
|
projectRef,
|
|
sourceId,
|
|
})
|
|
const formId = 'publication-editor'
|
|
const FormSchema = z.object({
|
|
name: z.string().min(1, 'Name is required'),
|
|
tables: z.array(z.string()).min(1, 'At least one table is required'),
|
|
})
|
|
const defaultValues = {
|
|
name: '',
|
|
tables: [],
|
|
}
|
|
const form = useForm<z.infer<typeof FormSchema>>({
|
|
mode: 'onBlur',
|
|
reValidateMode: 'onBlur',
|
|
resolver: zodResolver(FormSchema),
|
|
defaultValues,
|
|
})
|
|
|
|
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
|
if (!projectRef) return console.error('Project ref is required')
|
|
if (!sourceId) return console.error('Source id is required')
|
|
try {
|
|
await createPublication({
|
|
projectRef,
|
|
sourceId,
|
|
name: data.name,
|
|
tables: data.tables.map((table) => {
|
|
const [schema, name] = table.split('.')
|
|
return { schema, name }
|
|
}),
|
|
})
|
|
toast.success('Successfully created publication')
|
|
onClose()
|
|
} catch (error) {
|
|
toast.error('Failed to create publication')
|
|
}
|
|
form.reset(defaultValues)
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<Sheet open={visible} onOpenChange={onClose}>
|
|
<SheetContent showClose={false} size="default">
|
|
<div className="flex flex-col h-full">
|
|
<SheetHeader>
|
|
<div className="flex flex-row justify-between items-center">
|
|
<div>
|
|
<SheetTitle>New Publication</SheetTitle>
|
|
<SheetDescription>
|
|
Create a new publication to replicate table changes to destinations
|
|
</SheetDescription>
|
|
</div>
|
|
<SheetClose
|
|
className={cn(
|
|
'text-muted hover:opacity-100',
|
|
'focus:outline-none focus:ring-2',
|
|
'disabled:pointer-events-none'
|
|
)}
|
|
>
|
|
<X className="h-3 w-3" />
|
|
<span className="sr-only">Close</span>
|
|
</SheetClose>
|
|
</div>
|
|
</SheetHeader>
|
|
<SheetSection className="flex-grow overflow-auto">
|
|
<Form_Shadcn_ {...form}>
|
|
<form
|
|
id={formId}
|
|
onSubmit={form.handleSubmit(onSubmit)}
|
|
className="flex flex-col gap-y-4"
|
|
>
|
|
<FormField_Shadcn_
|
|
control={form.control}
|
|
name="name"
|
|
render={({ field }) => (
|
|
<FormItemLayout label="Name" layout="vertical">
|
|
<FormControl_Shadcn_>
|
|
<Input_Shadcn_ {...field} placeholder="Name" />
|
|
</FormControl_Shadcn_>
|
|
</FormItemLayout>
|
|
)}
|
|
/>
|
|
<FormField_Shadcn_
|
|
control={form.control}
|
|
name="tables"
|
|
render={({ field }) => (
|
|
<FormItemLayout
|
|
label="Tables"
|
|
description="Which tables to replicate to destinations"
|
|
>
|
|
<FormControl_Shadcn_>
|
|
<MultiSelector
|
|
values={field.value}
|
|
onValuesChange={field.onChange}
|
|
disabled={creatingPublication}
|
|
>
|
|
<MultiSelector.Trigger>
|
|
<MultiSelector.Input placeholder="Select tables" />
|
|
</MultiSelector.Trigger>
|
|
<MultiSelector.Content>
|
|
<MultiSelector.List>
|
|
{tables?.map((table) => (
|
|
<MultiSelector.Item
|
|
key={`${table.schema}.${table.name}`}
|
|
value={`${table.schema}.${table.name}`}
|
|
>
|
|
{`${table.schema}.${table.name}`}
|
|
</MultiSelector.Item>
|
|
))}
|
|
</MultiSelector.List>
|
|
</MultiSelector.Content>
|
|
</MultiSelector>
|
|
</FormControl_Shadcn_>
|
|
</FormItemLayout>
|
|
)}
|
|
/>
|
|
</form>
|
|
</Form_Shadcn_>
|
|
</SheetSection>
|
|
<SheetFooter>
|
|
<Button type="default" disabled={creatingPublication} onClick={onClose}>
|
|
Cancel
|
|
</Button>
|
|
<Button type="primary" disabled={creatingPublication} form={formId} htmlType="submit">
|
|
Create publication
|
|
</Button>
|
|
</SheetFooter>
|
|
</div>
|
|
</SheetContent>
|
|
</Sheet>
|
|
</>
|
|
)
|
|
}
|