Datavision / frontend /src /components /ContextualHelp.tsx
DataVision CI/CD Bot
release: clean production build for HuggingFace Space
09801ca
Raw
History Blame Contribute Delete
8.24 kB
import React, { useState, useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { motion, AnimatePresence } from 'framer-motion';
import { Sparkles, X, ChevronRight, BookOpen, Lightbulb, HelpCircle, MessageSquare } from 'lucide-react';
import { useUserStore } from '@/store/userStore';
const helpContent: Record<string, any> = {
'/dashboard': {
title: 'Autonomous Dashboard',
description: 'This is your central command center. AI generates these widgets automatically based on your connected data.',
tips: [
'Click on any chart to expand it for a deeper analysis.',
'Use the filter panel on the right to drill down into specific timeframes.',
'The AI insights at the top update in real-time as new data flows in.'
],
action: 'Ask AI about these metrics'
},
'/data-hub': {
title: 'Data Hub',
description: 'Manage all your datasets here. You can upload CSV, Excel, or PDF files, or connect directly to Google Sheets.',
tips: [
'Try the "Synthetic Data Generator" if you need realistic test data.',
'You can train ML models directly from your uploaded files.',
'Click the trash icon to delete files you no longer need.'
],
action: 'How to structure my data?'
},
'/ml-predictions': {
title: 'ML Predictions',
description: 'Explore automated machine learning models trained on your data to forecast trends and detect patterns.',
tips: [
'Toggle between Regression (numbers) and Classification (categories) models.',
'Hover over the prediction charts to see confidence intervals.',
'Use the "Deploy Model" button to create an API endpoint.'
],
action: 'Explain these ML metrics'
},
'/chat': {
title: 'AI Analyst Chat',
description: 'Your personal data scientist. Ask questions in plain English and it will query your datasets, generate charts, and provide insights.',
tips: [
'Try asking for specific aggregations, e.g., "Show total revenue by month".',
'The AI remembers past conversations for continuous context.',
'You can attach files directly in the chat to analyze them on the fly.'
],
action: 'Show me example questions'
},
'/anomalies': {
title: 'Anomaly Monitor',
description: 'Real-time detection of outliers and unusual patterns in your live data streams.',
tips: [
'Red dots indicate critical anomalies that require immediate attention.',
'Adjust the sensitivity slider to reduce false positives.',
'Set up webhook alerts in Settings to get notified instantly.'
],
action: 'What causes these anomalies?'
},
'/decisions': {
title: 'Decision Intelligence',
description: 'Actionable business recommendations generated by AI based on deep pattern recognition across your data.',
tips: [
'Review the confidence score before approving an automated action.',
'Click "Approve & Execute" to trigger the associated API workflow.',
'Dismissed recommendations help train the AI to better understand your preferences.'
],
action: 'How are these generated?'
}
};
const defaultHelp = {
title: 'Need Help?',
description: 'Navigate to different pages in the application to see contextual tips and AI guidance specific to that feature.',
tips: [
'Use the left sidebar to navigate between modules.',
'Press Ctrl+K (or Cmd+K) to open the quick search palette.',
'Check Settings to customize your theme and preferences.'
],
action: 'Go to Help Center'
};
export const ContextualHelp: React.FC = () => {
const [isOpen, setIsOpen] = useState(false);
const [hasPulse, setHasPulse] = useState(true);
const location = useLocation();
const { isDark } = useUserStore();
// Get content based on current path
const currentPath = Object.keys(helpContent).find(path => location.pathname.startsWith(path));
const content = currentPath ? helpContent[currentPath] : defaultHelp;
useEffect(() => {
// Pulse animation triggers when route changes to draw attention
setHasPulse(true);
const timer = setTimeout(() => setHasPulse(false), 5000);
return () => clearTimeout(timer);
}, [location.pathname]);
return (
<>
{/* Floating Action Button */}
<motion.button
initial={{ scale: 0 }}
animate={{ scale: 1 }}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
onClick={() => {
setIsOpen(true);
setHasPulse(false);
}}
className={`fixed bottom-6 right-6 z-50 p-4 rounded-full shadow-2xl flex items-center justify-center transition-colors ${
isDark ? 'bg-indigo-600 hover:bg-indigo-500 text-white shadow-indigo-900/50' : 'bg-indigo-600 hover:bg-indigo-500 text-white shadow-indigo-500/30'
}`}
>
<div className="relative">
<HelpCircle className="w-6 h-6" />
{hasPulse && (
<span className="absolute top-0 right-0 w-3 h-3 bg-rose-500 border-2 border-indigo-600 rounded-full animate-pulse"></span>
)}
</div>
</motion.button>
{/* Slide-out Panel */}
<AnimatePresence>
{isOpen && (
<motion.div
initial={{ opacity: 0, x: 400 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: 400 }}
transition={{ type: 'spring', damping: 25, stiffness: 200 }}
className={`fixed bottom-24 right-6 w-80 sm:w-96 z-50 rounded-3xl border shadow-2xl overflow-hidden ${
isDark ? 'bg-zinc-900/90 border-white/10 backdrop-blur-xl shadow-black/50' : 'bg-white/95 border-gray-200 backdrop-blur-xl shadow-gray-300/50'
}`}
>
{/* Header */}
<div className={`p-5 border-b flex justify-between items-center ${isDark ? 'border-white/10' : 'border-gray-100'}`}>
<div className="flex items-center gap-2 text-indigo-500">
<Sparkles className="w-5 h-5" />
<h3 className="font-bold text-lg">AI Assistant</h3>
</div>
<button
onClick={() => setIsOpen(false)}
className={`p-1.5 rounded-full transition-colors ${isDark ? 'hover:bg-white/10 text-gray-400' : 'hover:bg-gray-100 text-gray-500'}`}
>
<X className="w-5 h-5" />
</button>
</div>
{/* Body */}
<div className="p-6 space-y-6">
<div>
<h4 className="text-xl font-bold mb-2" style={{ color: isDark ? 'white' : 'black' }}>
{content.title}
</h4>
<p className="text-sm leading-relaxed" style={{ color: isDark ? '#a1a1aa' : '#52525b' }}>
{content.description}
</p>
</div>
<div className="space-y-3">
<h5 className="text-xs font-bold uppercase tracking-wider flex items-center gap-2" style={{ color: isDark ? '#71717a' : '#a1a1aa' }}>
<Lightbulb className="w-3.5 h-3.5" /> Pro Tips
</h5>
<ul className="space-y-3">
{content.tips.map((tip: string, idx: number) => (
<li key={idx} className="flex items-start gap-3 text-sm">
<div className="mt-0.5 w-1.5 h-1.5 rounded-full bg-indigo-500 shrink-0"></div>
<span style={{ color: isDark ? '#d4d4d8' : '#3f3f46' }}>{tip}</span>
</li>
))}
</ul>
</div>
</div>
{/* Footer Action */}
<div className={`p-4 border-t ${isDark ? 'bg-black/20 border-white/5' : 'bg-gray-50/50 border-gray-100'}`}>
<button className="w-full py-3 px-4 rounded-xl font-semibold text-sm bg-gradient-to-r from-indigo-500 to-purple-600 hover:from-indigo-600 hover:to-purple-700 text-white shadow-lg shadow-indigo-500/25 flex items-center justify-center gap-2 transition-all">
<MessageSquare className="w-4 h-4" />
{content.action}
</button>
</div>
</motion.div>
)}
</AnimatePresence>
</>
);
};