npm run build

This commit is contained in:
Leon Serfaty G
2025-07-19 15:10:27 +00:00
parent caff416826
commit f5e94bf039
3 changed files with 118 additions and 7 deletions
+100 -7
View File
@@ -1,6 +1,9 @@
'use client';
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 { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
@@ -15,9 +18,89 @@ import {
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
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 (
<div className="space-y-8">
@@ -55,7 +138,19 @@ export default async function FlowsPage() {
</TableRow>
</TableHeader>
<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) => (
<TableRow key={flow.id}>
<TableCell className="font-medium">{flow.name}</TableCell>
@@ -83,9 +178,7 @@ export default async function FlowsPage() {
<DropdownMenuItem>View</DropdownMenuItem>
</Link>
<DropdownMenuSeparator />
<DropdownMenuItem className="text-destructive focus:bg-destructive/10 focus:text-destructive">
Delete
</DropdownMenuItem>
<DeleteFlowButton flowId={flow.id} onFlowDeleted={handleFlowDeleted} />
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
+14
View File
@@ -67,6 +67,16 @@ export async function saveFlow(prevState: State, formData: FormData): Promise<St
try {
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
const stmt = db.prepare(
'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 }> {
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 = ?');
stmt.run(id);
revalidatePath('/admin/flows');