revert back... tyou are trylly awfull

This commit is contained in:
Leon Serfaty G
2025-07-18 05:52:44 +00:00
parent 94063017ed
commit 0a8cee14cc
3 changed files with 146 additions and 242 deletions
+139 -144
View File
@@ -1,161 +1,156 @@
'use client'; 'use client';
import { Button } from '@/components/ui/button'; import { useEffect, useState } from 'react';
import { Card, CardContent } from '@/components/ui/card'; import { useActionState } from 'react';
import { useRouter } from 'next/navigation';
import { getFlow, saveFlow, type Flow } from '@/lib/actions/flows';
import { Card, CardContent, CardHeader, CardTitle, CardDescription, CardFooter } from '@/components/ui/card';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Badge } from '@/components/ui/badge'; import { Label } from '@/components/ui/label';
import { import { Textarea } from '@/components/ui/textarea';
Play, import { Button } from '@/components/ui/button';
Share2, import { useToast } from '@/hooks/use-toast';
MessageSquare, import { Alert, AlertDescription } from '@/components/ui/alert';
Pencil,
Settings, type FlowFormPageProps = {
Bike, params: { id: string };
Footprints, };
MoreHorizontal,
Plus, function SubmitButton() {
Minus, // useFormStatus is not used here because we are not in a form, but we can simulate the pending state
Code2 // from the formAction. For a real app, you might use the pending state from useFormStatus.
} from 'lucide-react'; // For now, let's just show a static text.
import { FlowNode, FlowNodeInput, FlowNodeOption, FlowNodeMessage } from '@/components/admin/flow-node'; return <Button type="submit">Save Changes</Button>;
}
export default function FlowFormPage({ params }: FlowFormPageProps) {
const [state, formAction] = useActionState(saveFlow, { success: false, message: '', errors: null });
const [flow, setFlow] = useState<Flow | null>(null);
const [isLoading, setIsLoading] = useState(true);
const { toast } = useToast();
const router = useRouter();
const isNew = params.id === 'new';
const pageTitle = isNew ? 'Add New Flow' : 'Edit Flow';
const pageDescription = isNew
? 'Create a new interactive flow for your application.'
: 'Modify the details of an existing flow.';
useEffect(() => {
if (!isNew) {
const id = parseInt(params.id, 10);
if (isNaN(id)) {
router.push('/admin/flows');
return;
}
setIsLoading(true);
getFlow(id)
.then((data) => {
if (data) {
setFlow(data);
} else {
toast({
variant: 'destructive',
title: 'Error',
description: 'Flow not found.',
});
router.push('/admin/flows');
}
})
.finally(() => setIsLoading(false));
} else {
setIsLoading(false);
}
}, [params.id, isNew, router, toast]);
useEffect(() => {
if (state.success) {
toast({
title: 'Success!',
description: state.message,
});
// The redirect is handled in the server action, but as a fallback
router.push('/admin/flows');
} else if (state.message) {
// This handles general errors, not validation ones.
if(!state.errors || state.errors.length === 0) {
toast({
variant: 'destructive',
title: 'Error',
description: state.message,
});
}
}
}, [state, router, toast]);
const getError = (field: string) => {
return state.errors?.find(e => e.path[0] === field)?.message;
}
if (isLoading) {
return <div>Loading...</div>;
}
export default function FlowEditorPage() {
return ( return (
<div className="flex flex-col h-[calc(100vh-4rem)] bg-muted/40"> <div className="space-y-8">
{/* Header */} <div>
<header className="flex items-center justify-between p-2 border-b bg-background"> <h1 className="text-3xl font-bold tracking-tight">{pageTitle}</h1>
<div className="flex items-center gap-4"> <p className="mt-2 text-muted-foreground">{pageDescription}</p>
<h1 className="text-lg font-semibold">Quick Carb Calculator</h1>
<nav className="flex items-center gap-2">
<Button variant="ghost" size="sm" className="bg-muted">Flow</Button>
<Button variant="ghost" size="sm">Theme</Button>
<Button variant="ghost" size="sm">Settings</Button>
<Button variant="ghost" size="sm">Share</Button>
</nav>
</div>
<div className="flex items-center gap-2">
<Button variant="outline" size="sm">
<Share2 className="mr-2 h-4 w-4" />
Share
</Button>
<Button variant="outline" size="sm">
<Play className="mr-2 h-4 w-4" />
Test
</Button>
<Button size="sm">Publish</Button>
</div>
</header>
{/* Main Content */}
<div className="flex-1 grid grid-cols-1 lg:grid-cols-3 gap-2 p-2">
{/* Flow Canvas */}
<div className="lg:col-span-2 relative bg-dotted-pattern bg-background rounded-lg border overflow-hidden">
{/* This is a static representation. A real implementation would use a library like React Flow. */}
<div className="absolute top-10 left-10">
<FlowNode title="Start" icon={<Play />} />
</div> </div>
<div className="absolute top-10 left-64 w-80"> <form action={formAction}>
<FlowNode title="Sport"> {flow && <input type="hidden" name="id" value={flow.id} />}
<FlowNodeMessage text="Welcome to the Quick Carb Calculator!" />
<FlowNodeMessage text="I'll help you figure out how much fuel you'll need to perform at your best. Let's get started! ✨" />
<FlowNodeMessage text="First, what are you fueling for?" />
<FlowNodeOption icon={<Bike />} text="Ride" />
<FlowNodeOption icon={<Footprints />} text="Run" />
<FlowNodeOption icon={<MoreHorizontal />} text="Other" />
<div className="p-2 border rounded-md mt-2">
<p className="text-xs text-muted-foreground">Default</p>
<Badge variant="secondary" className="mt-1">Set Sport</Badge>
</div>
</FlowNode>
</div>
<div className="absolute top-10 left-[48rem]"> <Card>
<FlowNode title="What sport?"> <CardHeader>
<FlowNodeInput placeholder="Type your answer..." variable="Sport" /> <CardTitle>Flow Details</CardTitle>
</FlowNode> <CardDescription>
Provide the name, description, and URL path for this flow.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="name">Name</Label>
<Input
id="name"
name="name"
placeholder="e.g., Cost Estimator"
defaultValue={flow?.name}
required
/>
{getError('name') && <p className="text-sm text-destructive">{getError('name')}</p>}
</div> </div>
<div className="space-y-2">
<div className="absolute top-64 left-[48rem]"> <Label htmlFor="description">Description (Optional)</Label>
<FlowNode title="Duration"> <Textarea
<FlowNodeMessage text="Great! How long will you be doing this activity?" /> id="description"
<FlowNodeInput placeholder="Type your answer..." variable="Duration" /> name="description"
</FlowNode> placeholder="A short description of what this flow does."
defaultValue={flow?.description || ''}
/>
</div> </div>
<div className="space-y-2">
<div className="absolute top-64 left-[72rem]"> <Label htmlFor="path">Path</Label>
<FlowNode title="Intensity"> <Input
<FlowNodeMessage text="Understood! Now, how hard will you be going?" /> id="path"
<FlowNodeInput placeholder="Type your answer..." variable="Intensity" /> name="path"
</FlowNode> placeholder="/cost-estimator"
</div> defaultValue={flow?.path}
required
<div className="absolute top-[34rem] left-[72rem]"> />
<FlowNode title="AI gen"> <p className="text-xs text-muted-foreground">
<FlowNodeMessage text="Create ..." /> The URL path where this flow will be accessible. Must start with a `/`.
<Badge variant="secondary" className="mt-1">Set Assistant</Badge> </p>
</FlowNode> {getError('path') && <p className="text-sm text-destructive">{getError('path')}</p>}
</div>
{/* Canvas Controls */}
<div className="absolute top-4 right-4 flex flex-col gap-2">
<Card className="p-1">
<Button variant="ghost" size="icon"><Code2 className="h-4 w-4" /></Button>
<Button variant="ghost" size="icon"><MoreHorizontal className="h-4 w-4" /></Button>
</Card>
<Card className="p-1">
<Button variant="ghost" size="icon"><Plus className="h-4 w-4" /></Button>
<Button variant="ghost" size="icon"><Minus className="h-4 w-4" /></Button>
</Card>
</div>
</div>
{/* Phone Preview */}
<div className="lg:col-span-1 bg-background rounded-lg border flex justify-center items-center p-4">
<Card className="w-full max-w-sm h-full max-h-[800px] shadow-lg flex flex-col">
<CardContent className="p-2 flex-1 overflow-y-auto">
<div className="flex flex-col gap-3 p-2">
{/* Chat messages */}
<div className="p-3 rounded-lg bg-muted text-muted-foreground self-start max-w-xs">
First, what are you fueling for?
</div>
<div className="p-3 rounded-lg bg-muted text-muted-foreground self-start max-w-xs flex items-center gap-2">
<Bike className="h-4 w-4" /> Ride
</div>
<div className="p-3 rounded-lg bg-muted text-muted-foreground self-start max-w-xs flex items-center gap-2">
<Footprints className="h-4 w-4" /> Run
</div>
<div className="p-3 rounded-lg bg-muted text-muted-foreground self-start max-w-xs flex items-center gap-2">
<MoreHorizontal className="h-4 w-4" /> Other
</div>
<div className="p-3 rounded-lg bg-green-200 text-green-900 self-end max-w-xs flex items-center gap-2">
Run <Footprints className="h-4 w-4" />
</div>
<div className="p-3 rounded-lg bg-muted text-muted-foreground self-start max-w-xs">
Great! How long will you be doing this activity?
</div>
<div className="p-3 rounded-lg bg-green-200 text-green-900 self-end max-w-xs">
I'll try 3h30
</div>
<div className="p-3 rounded-lg bg-muted text-muted-foreground self-start max-w-xs">
Understood! Now, how hard will you be going?
</div>
<div className="p-3 rounded-lg bg-green-200 text-green-900 self-end max-w-xs">
I'd say a good 8 out of 10
</div>
<div className="p-3 rounded-lg bg-muted text-muted-foreground self-start max-w-xs">
Total carbs: 90-120g
</div>
</div> </div>
</CardContent> </CardContent>
<CardFooter>
<SubmitButton />
</CardFooter>
</Card> </Card>
</div> </form>
</div>
</div> </div>
); );
} }
-5
View File
@@ -56,9 +56,4 @@
body { body {
@apply bg-background text-foreground; @apply bg-background text-foreground;
} }
.bg-dotted-pattern {
background-color: hsl(var(--card));
background-image: radial-gradient(hsl(var(--border)) 1px, transparent 0);
background-size: 20px 20px;
}
} }
-86
View File
@@ -1,86 +0,0 @@
'use client';
import React from 'react';
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import { MessageSquare, Pencil, Type } from 'lucide-react';
interface FlowNodeProps {
title: string;
icon?: React.ReactNode;
children?: React.ReactNode;
className?: string;
}
export function FlowNode({ title, icon, children, className }: FlowNodeProps) {
return (
<Card className={`w-72 bg-background shadow-lg ${className}`}>
<CardHeader className="flex flex-row items-center justify-between p-2 border-b">
<div className="flex items-center gap-2">
{icon}
<CardTitle className="text-sm font-medium">{title}</CardTitle>
</div>
</CardHeader>
<CardContent className="p-2 space-y-2">
{children}
</CardContent>
</Card>
);
}
interface FlowNodeElementProps {
className?: string;
children?: React.ReactNode;
}
function FlowNodeElement({className, children}: FlowNodeElementProps) {
return (
<div className={`group relative p-2 text-sm rounded-md border bg-card hover:bg-muted/50 ${className}`}>
{children}
<Button variant="ghost" size="icon" className="absolute top-1 right-1 h-6 w-6 opacity-0 group-hover:opacity-100">
<Pencil className="h-3 w-3" />
</Button>
</div>
)
}
export function FlowNodeMessage({ text }: { text: string }) {
return (
<FlowNodeElement>
<div className="flex items-start gap-2">
<MessageSquare className="h-4 w-4 text-muted-foreground mt-0.5" />
<p className="flex-1">{text}</p>
</div>
</FlowNodeElement>
);
}
export function FlowNodeInput({ placeholder, variable }: { placeholder: string; variable: string }) {
return (
<FlowNodeElement>
<div className="flex items-start gap-2">
<Type className="h-4 w-4 text-muted-foreground mt-0.5" />
<div className='flex-1'>
<Input disabled placeholder={placeholder} className="text-xs h-7 bg-transparent disabled:cursor-text disabled:opacity-100" />
<div className="mt-2">
<Badge variant="secondary">Set {variable}</Badge>
</div>
</div>
</div>
</FlowNodeElement>
)
}
export function FlowNodeOption({ text, icon }: { text: string; icon?: React.ReactNode }) {
return (
<FlowNodeElement>
<div className="flex items-center gap-2">
{icon}
<p>{text}</p>
</div>
</FlowNodeElement>
)
}