Files
reui-blocks/blocks/onboarding1.tsx
T

1126 lines
32 KiB
TypeScript

"use client";
import { Check, ChevronRight, Plus } from "lucide-react";
import { motion } from "motion/react";
import React, { useRef, useState } from "react";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Switch } from "@/components/ui/switch";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Textarea } from "@/components/ui/textarea";
import { cn } from "@/lib/utils";
const toFaDigits = (value: number) => value.toLocaleString("fa-IR");
interface Hyperlink {
label: string;
href: string;
}
interface StepComponentProps {
onSubmit: () => void;
goBack?: () => void;
currentStep: number;
totalSteps: number;
}
interface OnboardingStep {
component: React.ComponentType<StepComponentProps>;
}
interface OnboardingStepHeaderProps {
title: string;
stepIndex: number;
totalSteps: number;
goBack?: () => void;
}
const OnboardingStepHeader = ({
title,
stepIndex,
totalSteps,
goBack,
}: OnboardingStepHeaderProps) => {
return (
<div className="relative">
{goBack && stepIndex > 0 && (
<Button
variant="ghost"
size="icon"
onClick={goBack}
className="absolute top-1/2 inset-s-full -translate-y-1/2 translate-x-1/2"
aria-label="بازگشت به مرحله قبل"
>
<ChevronRight className="size-4" />
</Button>
)}
<div>
<p className="text-sm font-medium text-muted-foreground">
{toFaDigits(stepIndex + 1)}/{toFaDigits(totalSteps)}
</p>
<h3 className="text-xl font-semibold tracking-tight md:whitespace-nowrap">
{title}
</h3>
</div>
</div>
);
};
interface OnboardingStepProps {
children: React.ReactNode;
}
const OnboardingStep = ({ children }: OnboardingStepProps) => {
return (
<div className="flex w-full flex-col-reverse gap-10 sm:rounded-2xl sm:border md:min-h-[85dvh] md:flex-row lg:rounded-3xl">
{children}
</div>
);
};
interface OnboardingStepLeftWrapperProps {
title: string;
currentStep: number;
totalSteps: number;
children: React.ReactNode;
goBack?: () => void;
}
const OnboardingStepLeftWrapper = ({
title,
currentStep,
totalSteps,
goBack,
children,
}: OnboardingStepLeftWrapperProps) => {
return (
<div className="flex flex-1/2 justify-center pe-10 sm:py-10 sm:pb-10 sm:ps-10 md:py-20 lg:justify-start lg:pe-0 lg:ps-24">
<div className="flex h-full w-full max-w-sm shrink-0 flex-col gap-6 md:max-w-md">
<OnboardingStepHeader
title={title}
stepIndex={currentStep}
totalSteps={totalSteps}
goBack={goBack}
/>
{children}
</div>
</div>
);
};
interface OnboardingStepRightWrapperProps {
children: React.ReactNode;
className?: string;
}
const OnboardingStepRightWrapper = ({
children,
className,
}: OnboardingStepRightWrapperProps) => {
return (
<div
className={cn(
"hidden flex-1/2 overflow-hidden sm:pt-10 md:pt-20 lg:block",
className,
)}
>
{children}
</div>
);
};
interface FileInputProps {
label: string;
name: string;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
onRemove: () => void;
value: string | null;
description?: string;
}
const FileInput = ({
label,
name,
onChange,
onRemove,
value,
description,
}: FileInputProps) => {
const fileInputRef = useRef<HTMLInputElement>(null);
const handleUploadFile = () => {
fileInputRef.current?.click();
};
return (
<div className="flex items-center gap-4">
<div
onClick={handleUploadFile}
className="flex size-18 shrink-0 cursor-pointer items-center justify-center overflow-hidden rounded-full border bg-muted"
>
{value ? (
<img
src={value}
alt="تصویر پروفایل"
className="size-full object-cover"
/>
) : (
<Plus />
)}
</div>
<div className="space-y-3">
<Label>{label}</Label>
<div className="flex items-center gap-2">
<input
type="file"
name={name}
ref={fileInputRef}
className="hidden"
onChange={onChange}
/>
<Button
variant="outline"
type="button"
size="sm"
onClick={handleUploadFile}
>
بارگذاری تصویر
</Button>
{value && (
<Button
variant="outline"
type="button"
size="sm"
onClick={onRemove}
>
حذف
</Button>
)}
</div>
<p className="text-xs text-muted-foreground">{description}</p>
</div>
</div>
);
};
interface DashboardIllustrationProps {
image?: string | null;
variant?: "zoomed-in" | "zoomed-out";
title?: string;
transformOrigin?: string;
className?: string;
}
const DashboardIllustration = ({
image,
variant = "zoomed-out",
title = "عنوان فضای کار",
transformOrigin = "-20% -10%",
className,
}: DashboardIllustrationProps) => {
return (
<motion.div
style={{
transformOrigin,
}}
animate={{
scale: variant === "zoomed-in" ? 1.5 : 1,
}}
transition={{
type: "spring",
stiffness: 300,
damping: 40,
}}
className={cn(
"flex h-full w-5xl overflow-hidden rounded-xl border",
className,
)}
>
<div className="h-full flex-2/7 shrink-0 overflow-hidden bg-muted">
<div className="flex items-center justify-between gap-2 border-b p-4">
<div className="flex items-center gap-2 overflow-hidden">
<div
className={cn(
"size-8 shrink-0 overflow-hidden rounded-md",
!image && "bg-blue-500",
)}
>
{image && (
<img
src={image}
alt="تصویر فضای کار"
className="size-full object-cover"
/>
)}
</div>
<p className="truncate overflow-hidden font-semibold">{title}</p>
</div>
<ChevronRight className="size-4 rtl:rotate-180" />
</div>
<ul className="space-y-2 p-4">
{Array.from({ length: 10 }).map((_, index) => (
<li
key={`sidebar-tab-${index}`}
className="h-9 rounded-lg border bg-background/50 hover:shadow-md"
/>
))}
</ul>
</div>
<div className="flex flex-5/7 shrink-0 flex-col justify-between p-4">
<div className="space-y-4">
<div className="flex items-center justify-between gap-4">
<div className="flex items-center gap-4">
<div className="size-9 rounded-lg border bg-muted/50" />
<div className="h-9 w-64 rounded-lg border bg-muted/50" />
<div className="flex items-center gap-2">
{Array.from({ length: 2 }).map((_, index) => (
<div
key={`icon-btn-${index}`}
className="size-9 rounded-lg border"
/>
))}
</div>
</div>
<Button variant="outline">
<span className="block h-5 w-20 rounded-md bg-muted/50" />
<Plus />
</Button>
</div>
<div className="overflow-hidden rounded-lg border">
<Table>
<TableHeader>
<TableRow className="bg-muted/50">
{[10, 40, 30, 60].map((width, index) => (
<TableHead
key={`th-${index}`}
style={{ width }}
className="h-9 border-e last:border-e-0"
/>
))}
</TableRow>
</TableHeader>
<TableBody>
{Array.from({ length: 8 }).map((_, rowIndex) => (
<TableRow
key={`row-${rowIndex}`}
className="even:bg-muted/20"
>
{Array.from({ length: 4 }).map((_, colIndex) => (
<TableCell
key={`cell-${rowIndex}-${colIndex}`}
className="h-9 border-e last:border-e-0"
/>
))}
</TableRow>
))}
</TableBody>
</Table>
</div>
</div>
<div className="flex items-center gap-2">
{Array.from({ length: 5 }).map((_, index) => (
<div
key={`icon-btn-${index}`}
className="size-9 rounded-lg border bg-muted/50"
/>
))}
</div>
</div>
</motion.div>
);
};
interface FormFieldProps {
label: string;
placeholder: string;
name: string;
type?: string;
input?: string;
onChange?: React.ChangeEventHandler;
options?: { label: string; value: string }[];
}
const FormField = ({
label,
placeholder,
name,
type,
input = "input",
onChange,
options,
}: FormFieldProps) => {
return (
<div className="space-y-2">
<Label htmlFor={name}>{label}</Label>
{input === "textarea" && (
<Textarea
id={name}
placeholder={placeholder}
name={name}
className="w-full"
style={{
resize: "none",
}}
onChange={onChange}
/>
)}
{input === "input" && (
<Input
type={type}
id={name}
placeholder={placeholder}
name={name}
dir={
type === "email" || name === "workspaceHandle" ? "ltr" : undefined
}
className={cn(
"w-full",
(type === "email" || name === "workspaceHandle") && "text-end",
)}
onChange={onChange}
/>
)}
{input === "select" && (
<Select>
<SelectTrigger className="w-full">
<SelectValue placeholder={placeholder} />
</SelectTrigger>
<SelectContent>
{options?.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
)}
</div>
);
};
interface SkipOnboardingButtonProps {
onSubmit: () => void;
label: string;
description: string;
}
const SkipOnboardingButton = ({
onSubmit,
label,
description,
}: SkipOnboardingButtonProps) => {
const [isDialogOpen, setIsDialogOpen] = useState(false);
return (
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
<DialogTrigger asChild>
<Button className="w-full" variant="ghost">
{label}
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>مطمئن هستید؟</DialogTitle>
<DialogDescription>{description}</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button
onClick={() => {
setIsDialogOpen(false);
onSubmit();
}}
>
تأیید
</Button>
<Button variant="outline" onClick={() => setIsDialogOpen(false)}>
انصراف
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
};
const StepOneComponent = ({
onSubmit,
currentStep,
totalSteps,
}: StepComponentProps) => {
const [profilePicture, setProfilePicture] = useState<string | null>(null);
const [productUpdates, setProductUpdates] = useState(true);
const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files[0]) {
setProfilePicture(URL.createObjectURL(e.target.files[0]));
}
};
const handleRemoveImage = () => {
setProfilePicture(null);
};
const TEXT_FIELDS = [
{
label: "نام و نام خانوادگی",
placeholder: "مریم احمدی‌زاده",
name: "name",
type: "text",
},
{
label: "بیوگرافی",
placeholder: "چند جمله درباره خودتان...",
name: "about",
type: "text",
input: "textarea",
},
{
label: "ایمیل کاری",
placeholder: "maryam@example.ir",
name: "email",
type: "email",
},
];
return (
<OnboardingStep>
<OnboardingStepLeftWrapper
title="تنظیم پروفایل"
currentStep={currentStep}
totalSteps={totalSteps}
>
<form
onSubmit={(e) => e.preventDefault()}
className="space-y-6 py-4 text-start"
>
<FileInput
label="تصویر پروفایل"
name="profilePicture"
onChange={handleFileUpload}
onRemove={handleRemoveImage}
value={profilePicture}
description="فایل‌های تصویر و jpeg تا ۱۰ مگابایت"
/>
<div className="space-y-6 border-b pt-4 pb-6">
{TEXT_FIELDS.map((field, i) => {
return (
<FormField
key={`text-field-${i}-${field.name}`}
label={field.label}
placeholder={field.placeholder}
name={field.name}
type={field.type}
input={field.input}
/>
);
})}
</div>
<div className="inline-flex w-full items-center gap-3 text-xs">
<span dir="rtl" className="inline-flex shrink-0 items-center">
<Switch
checked={productUpdates}
onCheckedChange={setProductUpdates}
aria-label="دریافت به‌روزرسانی‌های محصول"
/>
</span>
<div className="space-y-1">
<p
className={cn(
"font-medium transition-colors",
productUpdates
? "text-foreground"
: "text-muted-foreground",
)}
>
به‌روزرسانی‌های محصول را برایم بفرستید
</p>
<p className="text-muted-foreground">
از ویژگی‌ها و بهبودهای جدید مطلع بمانید.
</p>
</div>
</div>
<Button type="submit" onClick={onSubmit} className="mt-4 w-full">
ادامه
</Button>
</form>
</OnboardingStepLeftWrapper>
<OnboardingStepRightWrapper className="sm:pb-10 md:pb-20">
<DashboardIllustration />
</OnboardingStepRightWrapper>
</OnboardingStep>
);
};
const StepTwoComponent = ({
currentStep,
totalSteps,
onSubmit,
goBack,
}: StepComponentProps) => {
const [profilePicture, setProfilePicture] = useState<string | null>(null);
const [formValue, setFormValue] = useState<Record<string, string>>({});
const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files[0]) {
setProfilePicture(URL.createObjectURL(e.target.files[0]));
}
};
const handleRemoveImage = () => {
setProfilePicture(null);
};
const handleTextAreaChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
setFormValue({ ...formValue, [e.target.name]: e.target.value });
};
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setFormValue({ ...formValue, [e.target.name]: e.target.value });
};
const TEXT_FIELDS = [
{
label: "نام سازمان",
placeholder: "آکمه",
name: "companyName",
type: "text",
onChange: handleInputChange,
},
{
label: "آدرس فضای کار",
placeholder: "akme-workspace",
name: "workspaceHandle",
type: "text",
onChange: handleInputChange,
},
{
label: "کشور",
placeholder: "کشور خود را انتخاب کنید",
name: "billingCountry",
input: "select",
options: [
{ label: "ایران", value: "ir" },
{ label: "آمریکا", value: "us" },
{ label: "کانادا", value: "ca" },
{ label: "بریتانیا", value: "uk" },
{ label: "استرالیا", value: "au" },
],
},
{
label: "چطور با ما آشنا شدید؟",
placeholder: "موتور جستجو، معرفی دوست، شبکه اجتماعی...",
name: "referralSource",
type: "text",
input: "textarea",
onChange: handleTextAreaChange,
},
];
return (
<OnboardingStep>
<OnboardingStepLeftWrapper
title="تنظیم فضای کار"
currentStep={currentStep}
totalSteps={totalSteps}
goBack={goBack}
>
<form
onSubmit={(e) => e.preventDefault()}
className="space-y-6 py-4 text-start"
>
<FileInput
label="لوگوی فضای کار"
name="workspaceLogo"
onChange={handleFileUpload}
onRemove={handleRemoveImage}
value={profilePicture}
description="فایل‌های تصویر و jpeg تا ۱۰ مگابایت"
/>
<div className="space-y-6 pt-4">
{TEXT_FIELDS.map((field, i) => {
return (
<FormField
key={`text-field-${i}-${field.name}`}
label={field.label}
placeholder={field.placeholder}
name={field.name}
type={field.type}
input={field.input}
onChange={field.onChange}
/>
);
})}
</div>
<Button type="submit" onClick={onSubmit} className="mt-4 w-full">
ادامه
</Button>
</form>
</OnboardingStepLeftWrapper>
<OnboardingStepRightWrapper className="bg-gradient-to-b from-background to-muted">
<DashboardIllustration
variant="zoomed-in"
image={profilePicture}
title={formValue?.companyName}
/>
</OnboardingStepRightWrapper>
</OnboardingStep>
);
};
const StepThreeComponent = ({
currentStep,
totalSteps,
onSubmit,
goBack,
}: StepComponentProps) => {
const [mode, setMode] = useState<string>("pro");
const modes = [
{
title: "همگام‌سازی کامل",
value: "pro",
description:
"همه مخاطبین، رویدادها و پیام‌ها همگام می‌شوند. تصویر کامل ارتباطات شما.",
image:
"/block-media/shadcnblocks/block/block-1.svg",
},
{
title: "فقط مخاطبین",
value: "lite",
description:
"فقط اطلاعات تماس همگام می‌شود. مناسب کاربران حساس به حریم خصوصی.",
image:
"/block-media/shadcnblocks/block/block-2.svg",
},
];
const features = [
"وارد کردن خودکار مخاطبین موجود",
"همگام‌سازی رویدادهای تقویم بین دستگاه‌ها",
"یادآورهای هوشمند بر اساس برنامه شما",
];
return (
<OnboardingStep>
<OnboardingStepLeftWrapper
title="اتصال حساب‌ها"
currentStep={currentStep}
totalSteps={totalSteps}
goBack={goBack}
>
<div className="space-y-8 text-start">
<div className="space-y-3 text-sm">
<p>
با اتصال حساب‌ها پتانسیل کامل فضای کار را آزاد کنید. منظم
بمانید و هیچ موقعیت مهمی را از دست ندهید.
</p>
<ul className="space-y-1">
{features.map((feature, i) => {
return (
<li key={`feature-${i}`} className="flex items-center gap-2">
<Check className="size-4" />
<p>{feature}</p>
</li>
);
})}
</ul>
</div>
<div className="space-y-3">
{modes.map((m, i) => {
const isActive = m.value === mode;
return (
<div
role="button"
key={`mode-${i}`}
tabIndex={0}
onClick={() => setMode(m.value)}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
setMode(m.value);
}
}}
className={cn(
"flex cursor-pointer items-center gap-3 rounded-lg border px-3 py-4 text-start text-xs transition-colors",
isActive
? "border-primary bg-primary/5"
: "hover:bg-muted/50",
)}
>
<img
src={m.image}
alt={m.title}
className="size-10 shrink-0 dark:invert"
/>
<div className="min-w-0 flex-1">
<p
className={cn(
"font-medium transition-colors",
isActive
? "text-foreground"
: "text-muted-foreground",
)}
>
{m.title}
</p>
<p className="text-muted-foreground">{m.description}</p>
</div>
</div>
);
})}
</div>
<div className="w-full space-y-3">
<Button className="w-full gap-2" onClick={onSubmit}>
<img
src="/block-media/shadcnblocks/block/logos/google-icon.svg"
alt="گوگل"
className="size-4"
/>
اتصال با گوگل
</Button>
<Button className="w-full gap-2" onClick={onSubmit}>
<img
src="/block-media/shadcnblocks/block/logos/facebook-icon.svg"
alt="فیسبوک"
className="size-4"
/>
اتصال با فیسبوک
</Button>
<SkipOnboardingButton
onSubmit={onSubmit}
label="رد کردن این مرحله"
description="می‌توانید بعداً از تنظیمات حساب‌ها را متصل کنید."
/>
</div>
<p className="text-center text-xs text-muted-foreground">
داده‌های شما امن هستند و هرگز با اشخاص ثالث به اشتراک گذاشته نمی‌شوند.
</p>
</div>
</OnboardingStepLeftWrapper>
<OnboardingStepRightWrapper className="sm:pb-10 md:pb-20">
<DashboardIllustration />
</OnboardingStepRightWrapper>
</OnboardingStep>
);
};
const StepFourComponent = ({
currentStep,
totalSteps,
onSubmit,
goBack,
}: StepComponentProps) => {
const [usecase, setUsecase] = useState<string | null>(null);
const toggleUsecase = (i: string) => {
setUsecase((prev) => (prev === i ? null : i));
};
const descriptions = [
"می‌خواهیم تجربه را مطابق شیوه کار شما شخصی‌سازی کنیم.",
"کاربرد اصلی خود را انتخاب کنید تا قالب‌ها، نماها و میانبرها را برای شما بهینه کنیم.",
];
const usecases = [
"فروش",
"استخدام",
"بازاریابی",
"موفقیت مشتری",
"پشتیبانی",
"مهندسی",
"محصول",
"طراحی",
"منابع انسانی",
"حقوقی",
"مالی",
"عملیات",
"سایر",
];
return (
<OnboardingStep>
<OnboardingStepLeftWrapper
title="نقش خود را بگویید"
currentStep={currentStep}
totalSteps={totalSteps}
goBack={goBack}
>
<div className="flex h-full flex-col justify-between text-start">
<div className="space-y-6">
<div className="space-y-2">
{descriptions.map((description, i) => {
return (
<p key={`description-${i}`} className="text-sm">
{description}
</p>
);
})}
</div>
<div className="space-y-2">
<p className="text-sm">کدام مورد بیشترین شباهت را به نقش شما دارد؟</p>
<div className="flex flex-wrap items-center gap-2">
{usecases.map((u, i) => {
const isActive = usecase === u;
return (
<div
key={`usecase-${i}`}
role="button"
tabIndex={0}
className={cn(
"cursor-pointer rounded-lg border px-2 py-1 text-xs transition-colors",
isActive
? "border-primary bg-primary/5 font-medium text-foreground"
: "text-muted-foreground hover:bg-muted/50",
)}
onClick={() => toggleUsecase(u)}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
toggleUsecase(u);
}
}}
>
{u}
</div>
);
})}
</div>
</div>
{usecase === "سایر" && (
<div className="space-y-2">
<Label>نقش خود را مشخص کنید</Label>
<Input
type="text"
placeholder="مثلاً پژوهش، اداری..."
/>
</div>
)}
</div>
<Button onClick={onSubmit} className="w-full">
ادامه
</Button>
</div>
</OnboardingStepLeftWrapper>
<OnboardingStepRightWrapper className="sm:pb-10 md:pb-20">
<DashboardIllustration />
</OnboardingStepRightWrapper>
</OnboardingStep>
);
};
const StepFiveComponent = ({
currentStep,
totalSteps,
onSubmit,
goBack,
}: StepComponentProps) => {
const descriptions = [
"وقتی تیم‌ها با هم کار می‌کنند، اتفاقات بزرگی رخ می‌دهد.",
"همکاران را به فضای کار دعوت کنید. پروژه‌ها را به اشتراک بگذارید، هم‌زمان همکاری کنید و همه را هم‌سو نگه دارید.",
];
return (
<OnboardingStep>
<OnboardingStepLeftWrapper
title="دعوت تیم"
currentStep={currentStep}
totalSteps={totalSteps}
goBack={goBack}
>
<div className="flex h-full flex-col justify-between text-start">
<div className="space-y-8">
<div className="space-y-2">
{descriptions.map((description, i) => {
return (
<p key={`description-${i}`} className="text-sm">
{description}
</p>
);
})}
</div>
<form onSubmit={(e) => e.preventDefault()} className="space-y-8">
<div className="space-y-4">
<Label>آدرس ایمیل را وارد کنید</Label>
<div className="space-y-2">
{Array.from({ length: 2 }).map((_, i) => (
<div
key={`input-${i}`}
className="flex flex-col gap-2 sm:flex-row sm:items-center"
>
<Input
type="email"
dir="rtl"
placeholder="ali.rezaei@example.ir"
className="flex-1 text-end"
/>
<Select defaultValue="viewer">
<SelectTrigger className="w-full sm:w-36">
<SelectValue placeholder="انتخاب نقش" />
</SelectTrigger>
<SelectContent>
<SelectItem value="admin">مدیر</SelectItem>
<SelectItem value="editor">ویرایشگر</SelectItem>
<SelectItem value="viewer">بیننده</SelectItem>
</SelectContent>
</Select>
</div>
))}
</div>
</div>
<div className="space-y-2">
<Button type="submit" className="w-full">
ارسال دعوت‌نامه
</Button>
<SkipOnboardingButton
onSubmit={onSubmit}
label="فعلاً رد شوید"
description="هر زمان می‌توانید از تنظیمات فضای کار اعضا را دعوت کنید."
/>
</div>
</form>
</div>
<p className="text-xs text-muted-foreground">
با ادامه، با شرایط استفاده و حریم خصوصی ما موافقت می‌کنید. اعضای
دعوت‌شده ایمیلی با دستورالعمل پیوستن به فضای کار دریافت می‌کنند.
</p>
</div>
</OnboardingStepLeftWrapper>
<OnboardingStepRightWrapper className="sm:pb-10 md:pb-20">
<DashboardIllustration
transformOrigin="-20% -10%"
variant="zoomed-in"
className="translate-x-1/4 xl:translate-x-1/5 2xl:translate-x-0"
/>
</OnboardingStepRightWrapper>
</OnboardingStep>
);
};
interface Onboarding1Props {
className?: string;
companyLogo?: string;
copyright?: string;
footerLinks?: Hyperlink[];
steps?: OnboardingStep[];
}
const Onboarding1 = ({
className,
companyLogo = "/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo-word.svg",
copyright = `© ${new Date().getFullYear().toLocaleString("fa-IR")} شاد‌سی‌ان بلاکس`,
footerLinks = [
{ label: "حریم خصوصی", href: "https://ShadcnBlocks" },
{
label: "پشتیبانی",
href: "https://ShadcnBlocks",
},
{
label: "خروج",
href: "https://ShadcnBlocks",
},
],
steps = [
{
component: StepOneComponent,
},
{
component: StepTwoComponent,
},
{
component: StepThreeComponent,
},
{
component: StepFourComponent,
},
{
component: StepFiveComponent,
},
],
}: Onboarding1Props) => {
const [currentStep, setCurrentStep] = useState(0);
const step = steps[currentStep];
const handleSubmit = () => {
if (currentStep !== steps.length - 1) setCurrentStep(currentStep + 1);
};
const handleGoBack = () => {
if (currentStep > 0) setCurrentStep(currentStep - 1);
};
return (
<section dir="rtl" lang="fa" className="py-20">
<div
className={cn(
"container flex flex-col items-center gap-20 text-start",
className,
)}
>
<img
src={companyLogo}
alt="لوگوی شرکت"
className="w-54 dark:invert"
/>
<step.component
onSubmit={handleSubmit}
currentStep={currentStep}
totalSteps={steps.length}
goBack={handleGoBack}
/>
<div className="flex flex-wrap items-center justify-center gap-4 text-xs text-muted-foreground">
<p>{copyright}</p>
{footerLinks.map((link) => (
<a key={link.label} href={link.href} className="underline">
{link.label}
</a>
))}
</div>
</div>
</section>
);
};
export { Onboarding1 };