2025-10-30 21:08:08 +01:00
"use client"
import type React from "react"
import { useState , useEffect } from "react"
import { Button } from "./ui/button"
import { Dialog , DialogContent } from "./ui/dialog"
import {
ChevronLeft ,
ChevronRight ,
X ,
Sparkles ,
LayoutDashboard ,
HardDrive ,
Network ,
Box ,
Cpu ,
FileText ,
Rocket ,
} from "lucide-react"
import Image from "next/image"
interface OnboardingSlide {
id : number
title : string
description : string
image? : string
icon : React.ReactNode
gradient : string
}
const slides : OnboardingSlide [ ] = [
{
id : 0 ,
2025-10-30 23:14:00 +01:00
title : "Welcome to ProxMenux Monitor!" ,
2025-10-30 21:08:08 +01:00
description :
2025-10-30 23:14:00 +01:00
"Your new monitoring tool for Proxmox. Discover all the features that will help you manage and supervise your infrastructure efficiently." ,
2025-10-30 21:08:08 +01:00
icon : < Sparkles className = "h-16 w-16" / > ,
gradient : "from-blue-500 via-purple-500 to-pink-500" ,
} ,
{
id : 1 ,
2025-10-30 23:14:00 +01:00
title : "System Overview" ,
2025-10-30 21:08:08 +01:00
description :
2025-10-30 23:14:00 +01:00
"Monitor your server's status in real-time: CPU, memory, temperature, system load and more. Everything in an intuitive and easy-to-understand dashboard." ,
2025-10-30 21:08:08 +01:00
image : "/images/onboarding/imagen1.png" ,
icon : < LayoutDashboard className = "h-12 w-12" / > ,
gradient : "from-blue-500 to-cyan-500" ,
} ,
{
id : 2 ,
2025-10-30 23:14:00 +01:00
title : "Storage Management" ,
2025-10-30 21:08:08 +01:00
description :
2025-10-30 23:14:00 +01:00
"Visualize the status of all your disks and volumes. Detailed information on capacity, usage, SMART health, temperature and performance of each storage device." ,
2025-10-30 21:08:08 +01:00
image : "/images/onboarding/imagen2.png" ,
icon : < HardDrive className = "h-12 w-12" / > ,
gradient : "from-cyan-500 to-teal-500" ,
} ,
{
id : 3 ,
2025-10-30 23:14:00 +01:00
title : "Network Metrics" ,
2025-10-30 21:08:08 +01:00
description :
2025-10-30 23:14:00 +01:00
"Monitor network traffic in real-time. Bandwidth statistics, active interfaces, transfer speeds and historical usage graphs." ,
2025-10-30 21:08:08 +01:00
image : "/images/onboarding/imagen3.png" ,
icon : < Network className = "h-12 w-12" / > ,
gradient : "from-teal-500 to-green-500" ,
} ,
{
id : 4 ,
2025-10-30 23:14:00 +01:00
title : "Virtual Machines & Containers" ,
2025-10-30 21:08:08 +01:00
description :
2025-10-30 23:14:00 +01:00
"Manage all your VMs and LXC containers from one place. Status, allocated resources, current usage and quick controls for each virtual machine." ,
2025-10-30 21:08:08 +01:00
image : "/images/onboarding/imagen4.png" ,
icon : < Box className = "h-12 w-12" / > ,
gradient : "from-green-500 to-emerald-500" ,
} ,
{
id : 5 ,
2025-10-30 23:14:00 +01:00
title : "Hardware Information" ,
2025-10-30 21:08:08 +01:00
description :
2025-10-30 23:14:00 +01:00
"Complete details of your server hardware: CPU, RAM, GPU, disks, network, UPS and more. Technical specifications, models, serial numbers and status of each component." ,
2025-10-30 21:08:08 +01:00
image : "/images/onboarding/imagen5.png" ,
icon : < Cpu className = "h-12 w-12" / > ,
gradient : "from-emerald-500 to-blue-500" ,
} ,
{
id : 6 ,
2025-10-30 23:14:00 +01:00
title : "System Logs" ,
2025-10-30 21:08:08 +01:00
description :
2025-10-30 23:14:00 +01:00
"Access system logs in real-time. Filter by event type, search for specific errors and keep complete track of your server activity. Download the displayed logs for further analysis." ,
2025-10-30 21:08:08 +01:00
image : "/images/onboarding/imagen6.png" ,
icon : < FileText className = "h-12 w-12" / > ,
gradient : "from-blue-500 to-indigo-500" ,
} ,
{
id : 7 ,
2025-10-30 23:14:00 +01:00
title : "Ready for the Future!" ,
2025-10-30 21:08:08 +01:00
description :
2025-10-30 23:14:00 +01:00
"ProxMenux Monitor is prepared to receive updates and improvements that will be added gradually, improving the user experience and being able to execute ProxMenux functions from the web panel." ,
2025-10-30 21:08:08 +01:00
icon : < Rocket className = "h-16 w-16" / > ,
gradient : "from-indigo-500 via-purple-500 to-pink-500" ,
} ,
]
export function OnboardingCarousel() {
const [ open , setOpen ] = useState ( false )
const [ currentSlide , setCurrentSlide ] = useState ( 0 )
const [ direction , setDirection ] = useState < "next" | "prev" > ( "next" )
useEffect ( ( ) = > {
const hasSeenOnboarding = localStorage . getItem ( "proxmenux-onboarding-seen" )
if ( ! hasSeenOnboarding ) {
setOpen ( true )
}
} , [ ] )
const handleNext = ( ) = > {
if ( currentSlide < slides . length - 1 ) {
setDirection ( "next" )
setCurrentSlide ( currentSlide + 1 )
} else {
handleComplete ( )
}
}
const handlePrev = ( ) = > {
if ( currentSlide > 0 ) {
setDirection ( "prev" )
setCurrentSlide ( currentSlide - 1 )
}
}
const handleSkip = ( ) = > {
setOpen ( false )
}
const handleComplete = ( ) = > {
localStorage . setItem ( "proxmenux-onboarding-seen" , "true" )
setOpen ( false )
}
const handleDotClick = ( index : number ) = > {
setDirection ( index > currentSlide ? "next" : "prev" )
setCurrentSlide ( index )
}
const slide = slides [ currentSlide ]
return (
< Dialog open = { open } onOpenChange = { setOpen } >
< DialogContent className = "max-w-4xl p-0 gap-0 overflow-hidden border-0 bg-transparent" >
< div className = "relative bg-card rounded-lg overflow-hidden shadow-2xl" >
{ /* Close button */ }
< Button
variant = "ghost"
size = "icon"
className = "absolute top-4 right-4 z-50 h-8 w-8 rounded-full bg-background/80 backdrop-blur-sm hover:bg-background"
onClick = { handleSkip }
>
< X className = "h-4 w-4" / >
< / Button >
< div
2025-10-30 23:30:58 +01:00
className = { ` relative h-48 md:h-64 bg-gradient-to-br ${ slide . gradient } flex items-center justify-center overflow-hidden ` }
2025-10-30 21:08:08 +01:00
>
< div className = "absolute inset-0 bg-black/10" / >
< div className = "absolute inset-0 bg-[radial-gradient(circle_at_50%_120%,rgba(255,255,255,0.1),transparent)]" / >
{ /* Icon or Image */ }
< div className = "relative z-10 text-white" >
{ slide . image ? (
2025-10-30 23:30:58 +01:00
< div className = "relative w-full h-36 md:h-48 flex items-center justify-center px-4" >
2025-10-30 21:08:08 +01:00
< Image
src = { slide . image || "/placeholder.svg" }
alt = { slide . title }
width = { 600 }
height = { 400 }
2025-10-30 23:30:58 +01:00
className = "rounded-lg shadow-2xl object-cover max-h-36 md:max-h-48"
2025-10-30 21:08:08 +01:00
onError = { ( e ) = > {
const target = e . target as HTMLImageElement
target . style . display = "none"
const fallback = target . parentElement ? . querySelector ( ".fallback-icon" )
if ( fallback ) {
fallback . classList . remove ( "hidden" )
}
} }
/ >
< div className = "fallback-icon hidden" > { slide . icon } < / div >
< / div >
) : (
< div className = "animate-pulse" > { slide . icon } < / div >
) }
< / div >
{ /* Decorative elements */ }
< div className = "absolute top-10 left-10 w-20 h-20 bg-white/10 rounded-full blur-2xl" / >
< div className = "absolute bottom-10 right-10 w-32 h-32 bg-white/10 rounded-full blur-3xl" / >
< / div >
2025-10-30 23:30:58 +01:00
< div className = "p-4 md:p-8 space-y-4 md:space-y-6" >
< div className = "space-y-2 md:space-y-3" >
< h2 className = "text-2xl md:text-3xl font-bold text-foreground text-balance" > { slide . title } < / h2 >
< p className = "text-base md:text-lg text-muted-foreground leading-relaxed text-pretty" >
{ slide . description }
< / p >
2025-10-30 21:08:08 +01:00
< / div >
{ /* Progress dots */ }
2025-10-30 23:30:58 +01:00
< div className = "flex items-center justify-center gap-2 py-2 md:py-4" >
2025-10-30 21:08:08 +01:00
{ slides . map ( ( _ , index ) = > (
< button
key = { index }
onClick = { ( ) = > handleDotClick ( index ) }
className = { ` transition-all duration-300 rounded-full ${
index === currentSlide
2025-10-30 23:30:58 +01:00
? "w-8 h-2.5 bg-blue-500 shadow-lg shadow-blue-500/50"
: "w-2.5 h-2.5 bg-muted-foreground/60 hover:bg-muted-foreground/80 border border-muted-foreground/40"
2025-10-30 21:08:08 +01:00
} ` }
aria - label = { ` Go to slide ${ index + 1 } ` }
/ >
) ) }
< / div >
2025-10-30 23:30:58 +01:00
< div className = "flex flex-col sm:flex-row items-center justify-between gap-3 md:gap-4" >
< Button
variant = "ghost"
onClick = { handlePrev }
disabled = { currentSlide === 0 }
className = "gap-2 w-full sm:w-auto"
>
2025-10-30 21:08:08 +01:00
< ChevronLeft className = "h-4 w-4" / >
2025-10-30 23:14:00 +01:00
Previous
2025-10-30 21:08:08 +01:00
< / Button >
2025-10-30 23:30:58 +01:00
< div className = "flex gap-2 w-full sm:w-auto" >
2025-10-30 21:08:08 +01:00
{ currentSlide < slides . length - 1 ? (
< >
2025-10-30 23:30:58 +01:00
< Button variant = "outline" onClick = { handleSkip } className = "flex-1 sm:flex-none bg-transparent" >
2025-10-30 23:14:00 +01:00
Skip
2025-10-30 21:08:08 +01:00
< / Button >
2025-10-30 23:30:58 +01:00
< Button onClick = { handleNext } className = "gap-2 bg-blue-500 hover:bg-blue-600 flex-1 sm:flex-none" >
2025-10-30 23:14:00 +01:00
Next
2025-10-30 21:08:08 +01:00
< ChevronRight className = "h-4 w-4" / >
< / Button >
< / >
) : (
< Button
onClick = { handleComplete }
2025-10-30 23:30:58 +01:00
className = "gap-2 bg-gradient-to-r from-blue-500 to-purple-500 hover:from-blue-600 hover:to-purple-600 w-full sm:w-auto"
2025-10-30 21:08:08 +01:00
>
2025-10-30 23:14:00 +01:00
Get Started !
2025-10-30 21:08:08 +01:00
< Sparkles className = "h-4 w-4" / >
< / Button >
) }
< / div >
< / div >
{ /* Don't show again */ }
{ currentSlide === slides . length - 1 && (
< div className = "text-center pt-2" >
< button
onClick = { handleComplete }
className = "text-sm text-muted-foreground hover:text-foreground transition-colors underline"
>
2025-10-30 23:14:00 +01:00
Don ' t show again
2025-10-30 21:08:08 +01:00
< / button >
< / div >
) }
< / div >
< / div >
< / DialogContent >
< / Dialog >
)
}