import React, { useState } from 'react';
export default function PipelineVisualizer() {
const [input, setInput] = useState(" user_session_token_v2 ");
const [activeStep, setActiveStep] = useState(0);
const [isAutoPlaying, setIsAutoPlaying] = useState(false);
const operations = {
trim: (s) => s.trim(),
normalize: (s) => s.toLowerCase().replace(/_/g, '-'),
validate: (s) => s.length > 5 ?`${s} (Valid)` : `${s} (Invalid)`,
encrypt: (s) => `🔒 ${btoa(s).substring(0, 10)}...`,
wrap: (s) => `{ "token": "${s}" }`
};
const steps = [
{ id: 0, fn: 'raw', name: 'Raw Input', desc: 'Data Entry', icon: <span>🗄️</span>, color: 'gray' },
{ id: 1, fn: 'trim', name: '|> trim(%)', desc: 'Remove whitespace', icon: <span>⬇️</span>, color: 'blue' },
{ id: 2, fn: 'normalize', name: '|> normalize(%)', desc: 'Snake to Kebab Case', icon: <span>🔄</span>, color: 'indigo' },
{ id: 3, fn: 'validate', name: '|> validate(%)', desc: 'Length Check', icon: <span>✅</span>, color: 'green' },
{ id: 4, fn: 'encrypt', name: '|> encrypt(%)', desc: 'Base64 Encoding', icon: <span>🛡️</span>, color: 'purple' },
{ id: 5, fn: 'wrap', name: '|> wrapDTO(%)', desc: 'JSON Structure', icon: <span>🧱</span>, color: 'cyan' },
];
const getStepValue = (stepIndex) => {
let val = input;
if (stepIndex >= 1) val = operations.trim(val);
if (stepIndex >= 2) val = operations.normalize(val);
if (stepIndex >= 3) val = operations.validate(val);
if (stepIndex >= 4) val = operations.encrypt(val);
if (stepIndex >= 5) val = operations.wrap(val);
return val;
};
const handleNext = () => setActiveStep(prev => Math.min(prev + 1, steps.length - 1));
const handleReset = () => { setActiveStep(0); setIsAutoPlaying(false); };
React.useEffect(() => {
if (!isAutoPlaying) return;
const interval = setInterval(() => {
setActiveStep(prev => {
if (prev >= steps.length - 1) {
setIsAutoPlaying(false);
return prev;
}
return prev + 1;
});
}, 800);
return () => clearInterval(interval);
}, [isAutoPlaying]);
const currentValue = getStepValue(activeStep);
const getColor = (color, intensity = 500) => {
const map = {
gray: `text-gray-${intensity} bg-gray-${intensity === 500 ? 100 : 50}`,
blue: `text-blue-${intensity} bg-blue-${intensity === 500 ? 100 : 50}`,
indigo: `text-indigo-${intensity} bg-indigo-${intensity === 500 ? 100 : 50}`,
green: `text-green-${intensity} bg-green-${intensity === 500 ? 100 : 50}`,
purple: `text-purple-${intensity} bg-purple-${intensity === 500 ? 100 : 50}`,
cyan: `text-cyan-${intensity} bg-cyan-${intensity === 500 ? 100 : 50}`,
};
if (color === 'gray') return intensity === 500 ? 'text-gray-600 bg-gray-100 dark:bg-gray-900 border-gray-200' : 'bg-gray-50';
if (color === 'blue') return intensity === 500 ? 'text-blue-600 bg-blue-100 dark:bg-blue-900 border-blue-200' : 'bg-blue-50';
if (color === 'indigo') return intensity === 500 ? 'text-indigo-600 bg-indigo-100 dark:bg-indigo-900 border-indigo-200' : 'bg-indigo-50';
if (color === 'green') return intensity === 500 ? 'text-green-600 bg-green-100 dark:bg-green-900 border-green-200' : 'bg-green-50';
if (color === 'purple') return intensity === 500 ? 'text-purple-600 bg-purple-100 dark:bg-purple-900 border-purple-200' : 'bg-purple-50';
if (color === 'cyan') return intensity === 500 ? 'text-cyan-600 bg-cyan-100 dark:bg-cyan-900 border-cyan-200' : 'bg-cyan-50';
return 'text-gray-600 bg-gray-100';
};
return (
<div className="bg-slate-50 dark:bg-[#0f1115] p-6 lg:p-12 rounded-3xl border border-slate-200 dark:border-white/5 shadow-2xl font-sans min-h-[800px] flex flex-col">
{}
<div className="flex justify-between items-center mb-12">
<div>
<h3 className="text-3xl font-black text-slate-900 dark:text-white flex items-center gap-3">
<span className="text-cyan-500 font-mono text-4xl">|></span> Data Pipeline
</h3>
<p className="text-slate-500 mt-2 font-medium">Visualizing Functional Transformations</p>
</div>
<div className="hidden md:block bg-black/5 dark:bg-white/5 px-4 py-2 rounded-lg text-xs font-mono text-slate-500">
STATUS: <span className={isAutoPlaying ? "text-green-500 font-bold" : "text-slate-500"}>{isAutoPlaying ? "RUNNING" : "PAUSED"}</span>
</div>
</div>
<div className="flex-1 flex flex-col xl:flex-row gap-12">
{}
<div className="flex-1 relative">
{}
<div className="absolute left-8 top-0 bottom-0 w-1 bg-slate-200 dark:bg-slate-800 z-0 rounded-full"></div>
<div className="absolute left-8 top-0 bottom-0 w-1 bg-cyan-500 z-0 rounded-full transition-all duration-500 ease-out" style={{ height: `${(activeStep / (steps.length - 1)) * 100}%` }}></div>
<div className="space-y-6 relative z-10 pb-10">
{steps.map((step, i) => (
<div
key={i}
onClick={() => { setActiveStep(i); setIsAutoPlaying(false); }}
className={`group flex items-center gap-6 cursor-pointer transition-all duration-300 ${i > activeStep ? 'opacity-40 grayscale blur-[1px]' : 'opacity-100'}`}
>
{}
<div className={`w-16 h-16 rounded-2xl flex items-center justify-center border-4 shadow-xl transition-all duration-300 ${
i === activeStep ? 'scale-110 border-cyan-500 bg-white dark:bg-slate-800 z-20' :
i < activeStep ? 'border-cyan-500/50 bg-cyan-500 text-white scale-90' : 'border-slate-200 dark:border-slate-800 bg-slate-100 dark:bg-slate-900'
} ${getColor(step.color)}`}>
{step.icon}
</div>
{}
<div className={`flex-1 p-5 rounded-2xl border transition-all duration-300 ${
i === activeStep
? 'bg-white dark:bg-[#1a1c20] border-cyan-500/50 shadow-2xl shadow-cyan-500/10 translate-x-2'
: 'bg-transparent border-transparent'
}`}>
<div className="flex justify-between items-center">
<div>
<code className={`text-sm font-bold block mb-1 ${i==activeStep ? 'text-cyan-600 dark:text-cyan-400' : 'text-slate-500'}`}>{step.name}</code>
<span className="text-xs text-slate-400 uppercase tracking-wider">{step.desc}</span>
</div>
{i === activeStep && <span className="text-cyan-500 animate-pulse text-xl">➡️</span>}
</div>
</div>
</div>
))}
</div>
</div>
{}
<div className="w-full xl:w-[450px] flex flex-col gap-6 sticky top-6 self-start">
{}
<div className="bg-white dark:bg-[#1a1c20] p-6 rounded-3xl border border-slate-200 dark:border-white/5 shadow-xl">
<label className="text-xs font-bold text-slate-400 uppercase mb-3 block">Input Data Source</label>
<input
type="text"
value={input}
onChange={(e) => { setInput(e.target.value); setActiveStep(0); setIsAutoPlaying(false); }}
className="w-full bg-slate-100 dark:bg-black/50 border-0 rounded-xl p-4 font-mono text-slate-700 dark:text-slate-200 outline-none focus:ring-2 focus:ring-cyan-500 mb-6 transition-all"
/>
<div className="grid grid-cols-2 gap-3">
<button
onClick={() => setIsAutoPlaying(!isAutoPlaying)}
className={`col-span-1 py-4 rounded-xl font-bold flex items-center justify-center gap-2 transition-all ${isAutoPlaying ? 'bg-orange-500 text-white hover:bg-orange-600 shadow-orange-500/20 shadow-lg' : 'bg-green-500 text-white hover:bg-green-600 shadow-green-500/20 shadow-lg'}`}
>
{isAutoPlaying ? 'Pause' : 'Auto Play'}
</button>
<button
onClick={handleNext}
disabled={activeStep === steps.length - 1}
className="col-span-1 bg-slate-100 dark:bg-slate-800 hover:bg-slate-200 dark:hover:bg-slate-700 text-slate-600 dark:text-slate-300 font-bold py-4 rounded-xl transition-all disabled:opacity-50"
>
Next Step
</button>
</div>
</div>
{}
<div className="flex-1 bg-slate-900 rounded-3xl p-8 border border-slate-800 shadow-inner relative overflow-hidden min-h-[300px] flex flex-col justify-center">
<div className="absolute top-0 right-0 left-0 h-1 bg-cyan-500 shadow-[0_0_20px_rgba(6,182,212,0.5)]"></div>
<div className="text-center">
<div className="text-xs font-bold text-slate-500 uppercase tracking-[0.2em] mb-6">Current Value</div>
<div className="text-3xl md:text-4xl font-black text-white font-mono break-all animate-in zoom-in duration-300 key={activeStep}">
"{getStepValue(activeStep)}"
</div>
<div className="mt-8 inline-flex items-center gap-2 px-3 py-1 bg-purple-500/10 border border-purple-500/20 rounded-full text-purple-300 text-xs font-mono">
<span>⚡</span>
Type: {typeof getStepValue(activeStep)}
</div>
</div>
</div>
</div>
</div>
</div>
);
}