npm run build
This commit is contained in:
Vendored
+4
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"IDX.aI.enableInlineCompletion": true,
|
||||||
|
"IDX.aI.enableCodebaseIndexing": true
|
||||||
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
|
|
||||||
|
'use client';
|
||||||
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { getFlows, type Flow } from '@/lib/actions/flows';
|
import { useEffect, useState, useTransition } from 'react';
|
||||||
|
import { getFlows, deleteFlow, type Flow } from '@/lib/actions/flows';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
||||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
|
||||||
@@ -15,9 +18,89 @@ import {
|
|||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
|
import {
|
||||||
|
AlertDialog,
|
||||||
|
AlertDialogAction,
|
||||||
|
AlertDialogCancel,
|
||||||
|
AlertDialogContent,
|
||||||
|
AlertDialogDescription,
|
||||||
|
AlertDialogFooter,
|
||||||
|
AlertDialogHeader,
|
||||||
|
AlertDialogTitle,
|
||||||
|
} from "@/components/ui/alert-dialog"
|
||||||
|
import { useToast } from '@/hooks/use-toast';
|
||||||
|
import { Skeleton } from '@/components/ui/skeleton';
|
||||||
|
|
||||||
export default async function FlowsPage() {
|
|
||||||
const flows = await getFlows();
|
function DeleteFlowButton({ flowId, onFlowDeleted }: { flowId: number, onFlowDeleted: (id: number) => void }) {
|
||||||
|
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||||
|
const [isPending, startTransition] = useTransition();
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
const handleDelete = () => {
|
||||||
|
startTransition(async () => {
|
||||||
|
const result = await deleteFlow(flowId);
|
||||||
|
if (result.success) {
|
||||||
|
toast({
|
||||||
|
title: 'Success!',
|
||||||
|
description: result.message,
|
||||||
|
});
|
||||||
|
onFlowDeleted(flowId);
|
||||||
|
setIsDialogOpen(false);
|
||||||
|
} else {
|
||||||
|
toast({
|
||||||
|
variant: 'destructive',
|
||||||
|
title: 'Error',
|
||||||
|
description: result.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<AlertDialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
|
||||||
|
<AlertDialogContent>
|
||||||
|
<AlertDialogHeader>
|
||||||
|
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
||||||
|
<AlertDialogDescription>
|
||||||
|
This action cannot be undone. This will permanently delete the flow.
|
||||||
|
</AlertDialogDescription>
|
||||||
|
</AlertDialogHeader>
|
||||||
|
<AlertDialogFooter>
|
||||||
|
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||||
|
<AlertDialogAction onClick={handleDelete} disabled={isPending}>
|
||||||
|
{isPending ? 'Deleting...' : 'Continue'}
|
||||||
|
</AlertDialogAction>
|
||||||
|
</AlertDialogFooter>
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialog>
|
||||||
|
<DropdownMenuItem
|
||||||
|
className="text-destructive focus:bg-destructive/10 focus:text-destructive"
|
||||||
|
onSelect={() => setIsDialogOpen(true)}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default function FlowsPage() {
|
||||||
|
const [flows, setFlows] = useState<Flow[]>([]);
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIsLoading(true);
|
||||||
|
getFlows().then(data => {
|
||||||
|
setFlows(data);
|
||||||
|
setIsLoading(false);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleFlowDeleted = (deletedFlowId: number) => {
|
||||||
|
setFlows(currentFlows => currentFlows.filter(flow => flow.id !== deletedFlowId));
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-8">
|
<div className="space-y-8">
|
||||||
@@ -55,7 +138,19 @@ export default async function FlowsPage() {
|
|||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{flows.length > 0 ? (
|
{isLoading ? (
|
||||||
|
<>
|
||||||
|
{[...Array(3)].map((_, i) => (
|
||||||
|
<TableRow key={i}>
|
||||||
|
<TableCell><Skeleton className="h-5 w-24" /></TableCell>
|
||||||
|
<TableCell><Skeleton className="h-5 w-48" /></TableCell>
|
||||||
|
<TableCell><Skeleton className="h-5 w-16" /></TableCell>
|
||||||
|
<TableCell><Skeleton className="h-5 w-32" /></TableCell>
|
||||||
|
<TableCell className="text-right"><Skeleton className="h-8 w-8 ml-auto" /></TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
) : flows.length > 0 ? (
|
||||||
flows.map((flow) => (
|
flows.map((flow) => (
|
||||||
<TableRow key={flow.id}>
|
<TableRow key={flow.id}>
|
||||||
<TableCell className="font-medium">{flow.name}</TableCell>
|
<TableCell className="font-medium">{flow.name}</TableCell>
|
||||||
@@ -83,9 +178,7 @@ export default async function FlowsPage() {
|
|||||||
<DropdownMenuItem>View</DropdownMenuItem>
|
<DropdownMenuItem>View</DropdownMenuItem>
|
||||||
</Link>
|
</Link>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem className="text-destructive focus:bg-destructive/10 focus:text-destructive">
|
<DeleteFlowButton flowId={flow.id} onFlowDeleted={handleFlowDeleted} />
|
||||||
Delete
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|||||||
@@ -67,6 +67,16 @@ export async function saveFlow(prevState: State, formData: FormData): Promise<St
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (id) {
|
if (id) {
|
||||||
|
// Check if path is unique before updating
|
||||||
|
const checkStmt = db.prepare('SELECT id FROM flows WHERE path = ? AND id != ?');
|
||||||
|
const existing = checkStmt.get(path, id);
|
||||||
|
if (existing) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: 'A flow with this path already exists.',
|
||||||
|
errors: [{ path: ['path'], message: 'A flow with this path already exists.', code: 'custom' }],
|
||||||
|
};
|
||||||
|
}
|
||||||
// Update existing flow
|
// Update existing flow
|
||||||
const stmt = db.prepare(
|
const stmt = db.prepare(
|
||||||
'UPDATE flows SET name = ?, description = ?, path = ?, updatedAt = CURRENT_TIMESTAMP WHERE id = ?'
|
'UPDATE flows SET name = ?, description = ?, path = ?, updatedAt = CURRENT_TIMESTAMP WHERE id = ?'
|
||||||
@@ -101,6 +111,10 @@ export async function saveFlow(prevState: State, formData: FormData): Promise<St
|
|||||||
|
|
||||||
export async function deleteFlow(id: number): Promise<{ success: boolean, message: string }> {
|
export async function deleteFlow(id: number): Promise<{ success: boolean, message: string }> {
|
||||||
try {
|
try {
|
||||||
|
// Prevent deletion of the default flow (ID 1)
|
||||||
|
if (id === 1) {
|
||||||
|
return { success: false, message: "The default flow cannot be deleted." };
|
||||||
|
}
|
||||||
const stmt = db.prepare('DELETE FROM flows WHERE id = ?');
|
const stmt = db.prepare('DELETE FROM flows WHERE id = ?');
|
||||||
stmt.run(id);
|
stmt.run(id);
|
||||||
revalidatePath('/admin/flows');
|
revalidatePath('/admin/flows');
|
||||||
|
|||||||
Reference in New Issue
Block a user