import { useState, useEffect, createContext, useContext, ReactNode } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { X, ChevronRight, ChevronLeft, CheckCircle, Sparkles } from "lucide-react"; export interface TutorialStep { id: string; title: string; content: string; target?: string; position?: "top" | "bottom" | "left" | "right" | "center"; action?: string; } interface TutorialContextType { isActive: boolean; currentStep: number; steps: TutorialStep[]; startTutorial: (steps: TutorialStep[]) => void; endTutorial: () => void; nextStep: () => void; prevStep: () => void; skipTutorial: () => void; hasCompletedTutorial: boolean; } const TutorialContext = createContext(null); export function useTutorial() { const context = useContext(TutorialContext); if (!context) { throw new Error("useTutorial must be used within TutorialProvider"); } return context; } export function TutorialProvider({ children }: { children: ReactNode }) { const [isActive, setIsActive] = useState(false); const [currentStep, setCurrentStep] = useState(0); const [steps, setSteps] = useState([]); const [hasCompletedTutorial, setHasCompletedTutorial] = useState(false); useEffect(() => { if (typeof window !== "undefined") { setHasCompletedTutorial(localStorage.getItem("aethex_tutorial_completed") === "true"); } }, []); const startTutorial = (newSteps: TutorialStep[]) => { setSteps(newSteps); setCurrentStep(0); setIsActive(true); }; const endTutorial = () => { setIsActive(false); setCurrentStep(0); setSteps([]); setHasCompletedTutorial(true); if (typeof window !== "undefined") { localStorage.setItem("aethex_tutorial_completed", "true"); } }; const nextStep = () => { if (currentStep < steps.length - 1) { setCurrentStep(currentStep + 1); } else { endTutorial(); } }; const prevStep = () => { if (currentStep > 0) { setCurrentStep(currentStep - 1); } }; const skipTutorial = () => { endTutorial(); }; return ( {children} ); } function TutorialOverlay() { const { isActive, currentStep, steps, nextStep, prevStep, skipTutorial } = useTutorial(); const [targetRect, setTargetRect] = useState(null); const step = steps[currentStep]; useEffect(() => { if (!isActive || !step?.target) { setTargetRect(null); return; } const findTarget = () => { const element = document.querySelector(`[data-tutorial="${step.target}"]`); if (element) { const rect = element.getBoundingClientRect(); setTargetRect(rect); element.scrollIntoView({ behavior: "smooth", block: "center" }); } else { setTargetRect(null); } }; findTarget(); const interval = setInterval(findTarget, 500); return () => clearInterval(interval); }, [isActive, step]); if (!isActive || !step) return null; const getTooltipPosition = () => { const viewportWidth = typeof window !== "undefined" ? window.innerWidth : 800; const viewportHeight = typeof window !== "undefined" ? window.innerHeight : 600; const isMobile = viewportWidth < 640; const padding = 16; const tooltipWidth = isMobile ? Math.min(320, viewportWidth - 32) : 360; const tooltipHeight = 200; if (!targetRect || step.position === "center" || isMobile) { return { top: "50%", left: "50%", transform: "translate(-50%, -50%)", maxWidth: `${viewportWidth - 32}px`, }; } let top = 0; let left = 0; switch (step.position || "bottom") { case "top": top = targetRect.top - tooltipHeight - padding; left = targetRect.left + targetRect.width / 2 - tooltipWidth / 2; break; case "bottom": top = targetRect.bottom + padding; left = targetRect.left + targetRect.width / 2 - tooltipWidth / 2; break; case "left": top = targetRect.top + targetRect.height / 2 - tooltipHeight / 2; left = targetRect.left - tooltipWidth - padding; break; case "right": top = targetRect.top + targetRect.height / 2 - tooltipHeight / 2; left = targetRect.right + padding; break; default: top = targetRect.bottom + padding; left = targetRect.left + targetRect.width / 2 - tooltipWidth / 2; } left = Math.max(padding, Math.min(left, viewportWidth - tooltipWidth - padding)); top = Math.max(padding, Math.min(top, viewportHeight - tooltipHeight - padding)); return { top: `${top}px`, left: `${left}px`, maxWidth: `${tooltipWidth}px`, }; }; const progress = ((currentStep + 1) / steps.length) * 100; return ( {/* Dark overlay with cutout */}