2025-07-24 14:41:34 -07:00
"use client" ;
2025-07-29 23:21:02 +02:00
import { Dialog , DialogContent , DialogTitle } from "./ui/dialog" ;
2025-07-24 14:41:34 -07:00
import { Button } from "./ui/button" ;
import { ArrowRightIcon } from "lucide-react" ;
import { useState , useEffect } from "react" ;
import ReactMarkdown from "react-markdown" ;
export function Onboarding() {
const [ step , setStep ] = useState ( 0 );
const [ isOpen , setIsOpen ] = useState ( false );
useEffect (() => {
const hasSeenOnboarding = localStorage . getItem ( "hasSeenOnboarding" );
if ( ! hasSeenOnboarding ) {
setIsOpen ( true );
}
}, []);
const handleNext = () => {
setStep ( step + 1 );
};
const handleClose = () => {
setIsOpen ( false );
localStorage . setItem ( "hasSeenOnboarding" , "true" );
};
2025-07-29 23:21:02 +02:00
const getStepTitle = () => {
switch ( step ) {
case 0 :
return "Welcome to OpenCut Beta! 🎉" ;
case 1 :
return "⚠️ This is a super early beta!" ;
case 2 :
return "🦋 Have fun testing!" ;
default :
return "OpenCut Onboarding" ;
}
};
2025-07-24 14:41:34 -07:00
const renderStepContent = () => {
switch ( step ) {
case 0 :
return (
< div className = "space-y-5" >
< div className = "space-y-3" >
< Title title = "Welcome to OpenCut Beta! 🎉" />
< Description description = "You're among the first to try OpenCut - the fully open source CapCut alternative." />
</ div >
< NextButton onClick = { handleNext }> Next </ NextButton >
</ div >
);
case 1 :
return (
< div className = "space-y-5" >
< div className = "space-y-3" >
2025-07-29 23:21:02 +02:00
< Title title = { getStepTitle ()} />
2025-07-24 14:41:34 -07:00
< Description description = "OpenCut started just one month ago. There's still a ton of things to do to make this editor amazing." />
< Description description = "If you're curious, check out our roadmap [here](https://opencut.app/roadmap)" />
</ div >
< NextButton onClick = { handleNext }> Next </ NextButton >
</ div >
);
case 2 :
return (
< div className = "space-y-5" >
< div className = "space-y-3" >
2025-07-29 23:21:02 +02:00
< Title title = { getStepTitle ()} />
2025-07-24 14:41:34 -07:00
< Description description = "Join our [Discord](https://discord.gg/zmR9N35cjK), chat with cool people and share feedback to help make OpenCut the best editor ever." />
</ div >
< NextButton onClick = { handleClose }> Finish </ NextButton >
</ div >
);
default :
return null ;
}
};
return (
< Dialog open = { isOpen } onOpenChange = { handleClose }>
2025-07-30 13:15:09 -04:00
< DialogContent className = "sm:max-w-[425px] outline-hidden!" >
2025-07-29 23:21:02 +02:00
< DialogTitle >
< span className = "sr-only" >{ getStepTitle ()}</ span >
</ DialogTitle >
2025-07-24 14:41:34 -07:00
{ renderStepContent ()}
</ DialogContent >
</ Dialog >
);
}
function Title ({ title } : { title : string }) {
return < h2 className = "text-lg md:text-xl font-bold" >{ title }</ h2 >;
}
function Subtitle ({ subtitle } : { subtitle : string }) {
return < h3 className = "text-lg font-medium" >{ subtitle }</ h3 >;
}
function Description ({ description } : { description : string }) {
return (
< div className = "text-muted-foreground" >
< ReactMarkdown
components = {{
p : ({ children }) => < p className = "mb-0" >{ children }</ p >,
a : ({ href , children }) => (
< a
href = { href }
target = "_blank"
rel = "noopener noreferrer"
className = "text-foreground hover:text-foreground/80 underline"
>
{ children }
</ a >
),
}}
>
{ description }
</ ReactMarkdown >
</ div >
);
}
function NextButton ({
children ,
onClick ,
} : {
children : React.ReactNode ;
onClick : () => void ;
}) {
return (
< Button onClick = { onClick } variant = "default" className = "w-full" >
{ children }
< ArrowRightIcon className = "w-4 h-4" />
</ Button >
);
}