the flow we just created is not showing up
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useFormState } from 'react-dom';
|
import { useFormState, useFormStatus } from 'react-dom';
|
||||||
import { getFlow, saveFlow, type Flow } from '@/lib/actions/flows';
|
import { getFlow, saveFlow, type Flow } from '@/lib/actions/flows';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
@@ -19,9 +19,10 @@ interface FlowFormPageProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function SubmitButton({ isNew }: { isNew: boolean }) {
|
function SubmitButton({ isNew }: { isNew: boolean }) {
|
||||||
|
const { pending } = useFormStatus();
|
||||||
return (
|
return (
|
||||||
<Button type="submit">
|
<Button type="submit" disabled={pending}>
|
||||||
{isNew ? 'Create Flow' : 'Save Changes'}
|
{pending ? (isNew ? 'Creating...' : 'Saving...') : (isNew ? 'Create Flow' : 'Save Changes')}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -49,11 +50,11 @@ export default function FlowFormPage({ params }: FlowFormPageProps) {
|
|||||||
}, [params.id, isNew]);
|
}, [params.id, isNew]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (state.message) {
|
if (state.message && !state.success) {
|
||||||
toast({
|
toast({
|
||||||
title: state.success ? 'Success!' : 'Error',
|
title: 'Error',
|
||||||
description: state.message,
|
description: state.message,
|
||||||
variant: state.success ? 'default' : 'destructive',
|
variant: 'destructive',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [state, toast]);
|
}, [state, toast]);
|
||||||
@@ -87,6 +88,10 @@ export default function FlowFormPage({ params }: FlowFormPageProps) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getError = (field: string) => {
|
||||||
|
return state.errors?.find(e => e.path?.[0] === field)?.message;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form action={formAction} className="space-y-8">
|
<form action={formAction} className="space-y-8">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
@@ -113,17 +118,17 @@ export default function FlowFormPage({ params }: FlowFormPageProps) {
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="name">Flow Name</Label>
|
<Label htmlFor="name">Flow Name</Label>
|
||||||
<Input id="name" name="name" defaultValue={flow?.name} required />
|
<Input id="name" name="name" defaultValue={flow?.name} required />
|
||||||
{state.errors?.name && <p className="text-sm text-destructive">{state.errors.name[0]}</p>}
|
{getError('name') && <p className="text-sm text-destructive">{getError('name')}</p>}
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="path">Path</Label>
|
<Label htmlFor="path">Path</Label>
|
||||||
<Input id="path" name="path" defaultValue={flow?.path} placeholder="/my-cool-flow" required />
|
<Input id="path" name="path" defaultValue={flow?.path} placeholder="/my-cool-flow" required />
|
||||||
{state.errors?.path && <p className="text-sm text-destructive">{state.errors.path[0]}</p>}
|
{getError('path') && <p className="text-sm text-destructive">{getError('path')}</p>}
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="description">Description</Label>
|
<Label htmlFor="description">Description</Label>
|
||||||
<Textarea id="description" name="description" defaultValue={flow?.description || ''} placeholder="A brief description of what this flow does." />
|
<Textarea id="description" name="description" defaultValue={flow?.description || ''} placeholder="A brief description of what this flow does." />
|
||||||
{state.errors?.description && <p className="text-sm text-destructive">{state.errors.description[0]}</p>}
|
{getError('description') && <p className="text-sm text-destructive">{getError('description')}</p>}
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -22,6 +22,12 @@ const flowSchema = z.object({
|
|||||||
path: z.string().min(1, 'Path is required').startsWith('/', { message: 'Path must start with /' }),
|
path: z.string().min(1, 'Path is required').startsWith('/', { message: 'Path must start with /' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
success: boolean;
|
||||||
|
message: string;
|
||||||
|
errors?: z.ZodIssue[] | null;
|
||||||
|
}
|
||||||
|
|
||||||
export async function getFlows(): Promise<Flow[]> {
|
export async function getFlows(): Promise<Flow[]> {
|
||||||
try {
|
try {
|
||||||
const stmt = db.prepare(
|
const stmt = db.prepare(
|
||||||
@@ -46,14 +52,14 @@ export async function getFlow(id: number): Promise<Flow | null> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function saveFlow(formData: FormData) {
|
export async function saveFlow(prevState: State, formData: FormData): Promise<State> {
|
||||||
const validatedFields = flowSchema.safeParse(Object.fromEntries(formData.entries()));
|
const validatedFields = flowSchema.safeParse(Object.fromEntries(formData.entries()));
|
||||||
|
|
||||||
if (!validatedFields.success) {
|
if (!validatedFields.success) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Invalid fields.',
|
message: 'Invalid fields.',
|
||||||
errors: validatedFields.error.flatten().fieldErrors,
|
errors: validatedFields.error.issues,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,9 +81,16 @@ export async function saveFlow(formData: FormData) {
|
|||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Failed to save flow:', error);
|
console.error('Failed to save flow:', error);
|
||||||
|
if (error.code === 'SQLITE_CONSTRAINT_UNIQUE') {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: 'A flow with this path already exists.',
|
||||||
|
errors: [{ path: ['path'], message: 'A flow with this path already exists.', code: 'custom' }],
|
||||||
|
};
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: error.code === 'SQLITE_CONSTRAINT_UNIQUE' ? 'A flow with this path already exists.' : 'An internal error occurred.',
|
message: 'An internal error occurred.',
|
||||||
errors: null,
|
errors: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-2
@@ -4,7 +4,7 @@ import Database from 'better-sqlite3';
|
|||||||
// Use a file-based database in development
|
// Use a file-based database in development
|
||||||
const db = new Database('local.db');
|
const db = new Database('local.db');
|
||||||
|
|
||||||
// Create the tables if they don't exist
|
// --- SCHEMA CREATION ---
|
||||||
db.exec(`
|
db.exec(`
|
||||||
CREATE TABLE IF NOT EXISTS users (
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
@@ -44,11 +44,37 @@ db.exec(`
|
|||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
description TEXT,
|
description TEXT,
|
||||||
path TEXT NOT NULL,
|
path TEXT NOT NULL UNIQUE,
|
||||||
createdAt DATETIME DEFAULT CURRENT_TIMESTAMP,
|
createdAt DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
updatedAt DATETIME DEFAULT CURRENT_TIMESTAMP
|
updatedAt DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
)
|
)
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
// --- SEEDING LOGIC ---
|
||||||
|
console.log('Running database checks and seeding if necessary...');
|
||||||
|
|
||||||
|
// Seed default user
|
||||||
|
const userStmt = db.prepare('SELECT id FROM users WHERE id = ?');
|
||||||
|
const defaultUser = userStmt.get(1);
|
||||||
|
if (!defaultUser) {
|
||||||
|
const insertUser = db.prepare(
|
||||||
|
"INSERT INTO users (id, email, password, name) VALUES (?, ?, ?, ?)"
|
||||||
|
);
|
||||||
|
insertUser.run(1, 'admin@example.com', 'password', 'Admin User');
|
||||||
|
console.log('Default user created.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seed default flow
|
||||||
|
const flowStmt = db.prepare("SELECT id FROM flows WHERE path = ?");
|
||||||
|
const defaultFlow = flowStmt.get('/');
|
||||||
|
if (!defaultFlow) {
|
||||||
|
const insertFlow = db.prepare(
|
||||||
|
"INSERT INTO flows (name, description, path) VALUES (?, ?, ?)"
|
||||||
|
);
|
||||||
|
insertFlow.run('Cost Estimator', 'The main cost estimation tool for clients.', '/');
|
||||||
|
console.log('Default flow created.');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Database setup complete.');
|
||||||
|
|
||||||
export default db;
|
export default db;
|
||||||
|
|||||||
Reference in New Issue
Block a user