mirror of
https://github.com/vndangkhoa/Sys-Arc-Visl.git
synced 2026-04-05 01:17:57 +07:00
194 lines
9.3 KiB
TypeScript
194 lines
9.3 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { Button } from './ui/Button';
|
|
import { Card } from './ui/Card';
|
|
import { useVisualOrganizer } from '../hooks/useVisualOrganizer';
|
|
import { useDiagramStore } from '../store';
|
|
import { Sparkles, Wand2, Layout, Scan, CheckCircle2, RotateCcw } from 'lucide-react';
|
|
import type { LayoutSuggestion } from '../types/visualOrganization';
|
|
|
|
export const VisualOrganizerPanel: React.FC = () => {
|
|
const { analyzeLayout, generateSuggestions, applySuggestion } = useVisualOrganizer();
|
|
const { nodes, edges, setNodes, setEdges } = useDiagramStore();
|
|
|
|
// UI States
|
|
const [status, setStatus] = useState<'idle' | 'analyzing' | 'ready' | 'applied'>('idle');
|
|
const [bestSuggestion, setBestSuggestion] = useState<LayoutSuggestion | null>(null);
|
|
const [snapshot, setSnapshot] = useState<{ nodes: any[], edges: any[] } | null>(null);
|
|
|
|
// AI Organize Handler
|
|
const handleAIOrganize = async () => {
|
|
setStatus('analyzing');
|
|
|
|
// 1. Analyze (Simulate brief delay for effect)
|
|
analyzeLayout();
|
|
|
|
// 2. Generate Suggestions
|
|
try {
|
|
// Artificial delay for "Scanning" animation effect
|
|
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
|
|
const results = await generateSuggestions();
|
|
|
|
// Pick best suggestion (or default to first non-current)
|
|
if (results.length > 0) {
|
|
setBestSuggestion(results[0]);
|
|
setStatus('ready');
|
|
} else {
|
|
// Fallback if no suggestions
|
|
setStatus('idle');
|
|
}
|
|
} catch (error) {
|
|
console.error(error);
|
|
setStatus('idle');
|
|
}
|
|
};
|
|
|
|
const handleApply = () => {
|
|
if (!bestSuggestion) return;
|
|
|
|
// Take snapshot before applying
|
|
setSnapshot({ nodes: [...nodes], edges: [...edges] });
|
|
|
|
applySuggestion(bestSuggestion);
|
|
setStatus('applied');
|
|
};
|
|
|
|
const handleUndo = () => {
|
|
if (snapshot) {
|
|
setNodes(snapshot.nodes);
|
|
setEdges(snapshot.edges);
|
|
setSnapshot(null);
|
|
setStatus('ready');
|
|
}
|
|
};
|
|
|
|
const handleReset = () => {
|
|
setStatus('idle');
|
|
setSnapshot(null);
|
|
setBestSuggestion(null);
|
|
};
|
|
|
|
return (
|
|
<div className="visual-organizer-panel w-full">
|
|
<Card className="p-0 overflow-hidden border-none shadow-none bg-transparent">
|
|
|
|
{/* IDLE STATE: Main AI Button */}
|
|
{status === 'idle' && (
|
|
<div className="flex flex-col items-center justify-center p-8 text-center space-y-6">
|
|
<div className="relative group cursor-pointer" onClick={handleAIOrganize}>
|
|
<div className="absolute inset-0 bg-blue-500 rounded-full blur-xl opacity-20 group-hover:opacity-40 transition-opacity duration-500 animate-pulse"></div>
|
|
<div className="relative w-24 h-24 bg-gradient-to-br from-blue-600 to-indigo-600 rounded-2xl flex items-center justify-center shadow-2xl transform group-hover:scale-105 transition-all duration-300 border border-white/20">
|
|
<Sparkles className="w-10 h-10 text-white animate-pulse" />
|
|
</div>
|
|
<div className="absolute -bottom-2 -right-2 bg-white dark:bg-slate-800 p-2 rounded-full shadow-lg border border-slate-100 dark:border-slate-700">
|
|
<Wand2 className="w-4 h-4 text-purple-500" />
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<h3 className="text-xl font-display font-bold text-slate-800 dark:text-white">AI Visual Organizer</h3>
|
|
<p className="text-sm text-slate-500 dark:text-slate-400 max-w-[200px] mx-auto leading-relaxed">
|
|
Click to instantly analyze and reorganize your flow for maximum clarity.
|
|
</p>
|
|
</div>
|
|
|
|
<Button
|
|
onClick={handleAIOrganize}
|
|
className="bg-slate-900 dark:bg-white text-white dark:text-slate-900 hover:bg-slate-800 hover:dark:bg-slate-100 px-8 py-6 rounded-xl shadow-xl hover:shadow-2xl transition-all font-bold tracking-wide"
|
|
>
|
|
<Scan className="w-4 h-4 mr-2" />
|
|
Start Organization
|
|
</Button>
|
|
</div>
|
|
)}
|
|
|
|
{/* ANALYZING STATE: Scanning Animation */}
|
|
{status === 'analyzing' && (
|
|
<div className="flex flex-col items-center justify-center p-12 text-center space-y-6 animate-in fade-in zoom-in duration-300">
|
|
<div className="relative w-20 h-20">
|
|
<div className="absolute inset-0 border-4 border-blue-500/20 rounded-full"></div>
|
|
<div className="absolute inset-0 border-4 border-t-blue-500 rounded-full animate-spin"></div>
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
<Scan className="w-8 h-8 text-blue-500 animate-pulse" />
|
|
</div>
|
|
</div>
|
|
<h3 className="text-lg font-bold text-blue-600 dark:text-blue-400 animate-pulse">
|
|
Analyzing Layout Logic...
|
|
</h3>
|
|
</div>
|
|
)}
|
|
|
|
{/* READY STATE: Suggestion Found */}
|
|
{status === 'ready' && bestSuggestion && (
|
|
<div className="flex flex-col p-6 space-y-6 animate-in slide-in-from-bottom-4 duration-500">
|
|
<div className="flex items-center gap-3 text-green-600 dark:text-green-400 mb-2">
|
|
<CheckCircle2 className="w-6 h-6" />
|
|
<span className="font-bold text-lg">Optimization Found!</span>
|
|
</div>
|
|
|
|
<div className="p-4 bg-slate-50 dark:bg-white/5 rounded-xl border border-slate-200 dark:border-white/10">
|
|
<div className="flex items-center gap-3 mb-2">
|
|
<Layout className="w-5 h-5 text-indigo-500" />
|
|
<h4 className="font-bold">{bestSuggestion.title}</h4>
|
|
</div>
|
|
<p className="text-sm text-slate-600 dark:text-slate-400 leading-relaxed">
|
|
{bestSuggestion.description}
|
|
</p>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-3">
|
|
<Button
|
|
onClick={handleReset}
|
|
variant="secondary"
|
|
className="h-12"
|
|
>
|
|
Cancel
|
|
</Button>
|
|
<Button
|
|
onClick={handleApply}
|
|
className="h-12 bg-indigo-600 hover:bg-indigo-500 text-white shadow-lg hover:shadow-indigo-500/25"
|
|
>
|
|
<Wand2 className="w-4 h-4 mr-2" />
|
|
Apply Magic
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* APPLIED STATE: Success & Undo */}
|
|
{status === 'applied' && (
|
|
<div className="flex flex-col items-center justify-center p-8 text-center space-y-6 animate-in zoom-in duration-300">
|
|
<div className="w-16 h-16 bg-green-100 dark:bg-green-900/30 rounded-full flex items-center justify-center mb-2">
|
|
<CheckCircle2 className="w-8 h-8 text-green-600 dark:text-green-400" />
|
|
</div>
|
|
|
|
<div>
|
|
<h3 className="text-xl font-bold text-slate-800 dark:text-white mb-2">Beautifully Organized!</h3>
|
|
<p className="text-sm text-slate-500 dark:text-slate-400">
|
|
Your graph has been transformed.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="flex gap-3 w-full">
|
|
<Button
|
|
onClick={handleUndo}
|
|
variant="secondary"
|
|
className="flex-1 h-12 border-slate-200 dark:border-white/10 hover:bg-red-50 hover:text-red-600 dark:hover:bg-red-900/10 dark:hover:text-red-400 transition-colors"
|
|
>
|
|
<RotateCcw className="w-4 h-4 mr-2" />
|
|
Undo
|
|
</Button>
|
|
<Button
|
|
onClick={handleReset} // Goes back to idle
|
|
className="flex-1 h-12"
|
|
>
|
|
Done
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
</Card>
|
|
</div>
|
|
);
|
|
};
|