Initial import of 1407 block sources under blocks/
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
"use client";
|
||||
|
||||
import { motion } from "framer-motion";
|
||||
import { Calendar } from "lucide-react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface Waitlist2Props {
|
||||
logo?: string;
|
||||
badge?: string;
|
||||
heading?: string;
|
||||
description?: string;
|
||||
avatars?: string[];
|
||||
joinedPeople?: number;
|
||||
launchDate?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const COUNTDOWN_UNITS = [
|
||||
{ key: "day", label: "روز" },
|
||||
{ key: "hour", label: "ساعت" },
|
||||
{ key: "minute", label: "دقیقه" },
|
||||
{ key: "second", label: "ثانیه" },
|
||||
] as const;
|
||||
|
||||
type CountdownTimeLeft = Record<
|
||||
(typeof COUNTDOWN_UNITS)[number]["key"],
|
||||
number
|
||||
>;
|
||||
|
||||
const formatCountdownValue = (value: number) =>
|
||||
value.toLocaleString("fa-IR", { minimumIntegerDigits: 2 });
|
||||
|
||||
const SignupForm = () => {
|
||||
return (
|
||||
<div className="relative mt-8 w-full max-w-md overflow-hidden rounded-xl p-px md:rounded-lg">
|
||||
<div
|
||||
style={{
|
||||
background:
|
||||
"conic-gradient(transparent 70%, var(--color-blue-500) 90%)",
|
||||
animationDuration: "4s",
|
||||
}}
|
||||
className="absolute top-1/2 aspect-square w-full -translate-y-1/2 scale-200 animate-spin opacity-70"
|
||||
/>
|
||||
<form className="relative z-10 flex w-full flex-col items-center gap-2 rounded-xl bg-background p-2 md:flex-row md:rounded-lg md:p-1">
|
||||
<Input
|
||||
className="w-full"
|
||||
required
|
||||
type="email"
|
||||
placeholder="ایمیل خود را وارد کنید"
|
||||
/>
|
||||
<Button type="submit" className="w-full shrink-0 md:w-auto">
|
||||
پیوستن به لیست انتظار
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface JoinedPeopleProps {
|
||||
joinedPeople: number;
|
||||
avatars: string[];
|
||||
}
|
||||
|
||||
const JoinedPeople = ({ joinedPeople, avatars }: JoinedPeopleProps) => {
|
||||
return (
|
||||
<div className="mt-4 flex items-center justify-center">
|
||||
<div className="relative flex flex-col items-center gap-2 md:flex-row md:gap-4">
|
||||
<div className="flex items-center">
|
||||
{avatars.map((avatar, index) => {
|
||||
return (
|
||||
<div key={`waitlist-avatar-${index}`} className="h-8 w-6">
|
||||
<div className="size-8">
|
||||
<img
|
||||
src={avatar}
|
||||
alt={`عضو ${(index + 1).toLocaleString("fa-IR")}`}
|
||||
className="size-full shrink-0 rounded-full bg-background object-cover object-top p-0.5"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
بیش از {joinedPeople.toLocaleString("fa-IR")} نفر قبلاً پیوستهاند
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface CountdownTimerProps {
|
||||
targetDate: string;
|
||||
}
|
||||
|
||||
const CountdownTimer = ({ targetDate }: CountdownTimerProps) => {
|
||||
const [timeLeft, setTimeLeft] = useState<CountdownTimeLeft>({
|
||||
day: 0,
|
||||
hour: 0,
|
||||
minute: 0,
|
||||
second: 0,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const calculateTimeLeft = () => {
|
||||
const target = new Date(targetDate).getTime();
|
||||
const now = new Date().getTime();
|
||||
const difference = target - now;
|
||||
|
||||
if (difference > 0) {
|
||||
const days = Math.floor(difference / (1000 * 60 * 60 * 24));
|
||||
const hours = Math.floor(
|
||||
(difference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60),
|
||||
);
|
||||
const minutes = Math.floor(
|
||||
(difference % (1000 * 60 * 60)) / (1000 * 60),
|
||||
);
|
||||
const seconds = Math.floor((difference % (1000 * 60)) / 1000);
|
||||
|
||||
setTimeLeft({
|
||||
day: days,
|
||||
hour: hours,
|
||||
minute: minutes,
|
||||
second: seconds,
|
||||
});
|
||||
} else {
|
||||
setTimeLeft({ day: 0, hour: 0, minute: 0, second: 0 });
|
||||
}
|
||||
};
|
||||
|
||||
calculateTimeLeft();
|
||||
|
||||
const timer = setInterval(calculateTimeLeft, 1000);
|
||||
|
||||
return () => clearInterval(timer);
|
||||
}, [targetDate]);
|
||||
|
||||
return (
|
||||
<div className="mt-6 flex flex-col items-center gap-6">
|
||||
<div className="flex justify-center gap-6">
|
||||
{COUNTDOWN_UNITS.map(({ key, label }, index) => {
|
||||
const value = timeLeft[key];
|
||||
|
||||
return (
|
||||
<React.Fragment key={`waitlist-timer-${key}`}>
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<span className="text-xl">{formatCountdownValue(value)}</span>
|
||||
<span className="text-xs text-muted-foreground">{label}</span>
|
||||
</div>
|
||||
{index < COUNTDOWN_UNITS.length - 1 ? (
|
||||
<span className="text-muted-foreground">:</span>
|
||||
) : null}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Calendar className="text-muted-foreground" size={14} />
|
||||
<span className="text-xs leading-0">
|
||||
زمان باقیمانده تا راهاندازی
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Waitlist2 = ({
|
||||
logo = "/block-media/shadcnblocks/block/block-white-1.svg",
|
||||
badge = `بهزودی ${(new Date().getFullYear() + 1).toLocaleString("fa-IR")}`,
|
||||
heading = "به لیست انتظار بپیوندید",
|
||||
description = "زودتر از همه از راهاندازی مطلع شوید. با ثبت ایمیل، دسترسی زودهنگام و مزایای ویژه دریافت کنید.",
|
||||
avatars = [
|
||||
"/block-media/shadcnblocks/block/portraits/joseph-gonzalez-iFgRcqHznqg-unsplash.jpg",
|
||||
"/block-media/shadcnblocks/block/portraits/michael-dam-mEZ3PoFGs_k-unsplash.jpg",
|
||||
"/block-media/shadcnblocks/block/portraits/nima-motaghian-nejad-_omdf_EgRUo-unsplash.jpg",
|
||||
"/block-media/shadcnblocks/block/portraits/good-faces-xmSWVeGEnJw-unsplash.jpg",
|
||||
"/block-media/shadcnblocks/block/portraits/christian-buehner-DItYlc26zVI-unsplash 1.png",
|
||||
],
|
||||
joinedPeople = 12500,
|
||||
launchDate = `01/01/${new Date().getFullYear() + 1}`,
|
||||
className,
|
||||
}: Waitlist2Props) => {
|
||||
return (
|
||||
<section dir="rtl" lang="fa" className={cn("py-32", className)}>
|
||||
<div className="container">
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="flex flex-col items-center gap-6">
|
||||
<div className="flex size-12 items-center justify-center rounded-2xl bg-primary p-2">
|
||||
<img src={logo} alt="لوگو" className="size-full" />
|
||||
</div>
|
||||
<Badge variant="outline">
|
||||
<div className="relative flex items-center justify-center">
|
||||
<span className="size-1.5 rounded-full bg-primary" />
|
||||
<motion.span
|
||||
initial={{
|
||||
scale: 1,
|
||||
opacity: 1,
|
||||
}}
|
||||
animate={{
|
||||
scale: 2,
|
||||
opacity: 0,
|
||||
}}
|
||||
transition={{
|
||||
duration: 1,
|
||||
ease: "easeInOut",
|
||||
repeat: Infinity,
|
||||
repeatType: "loop",
|
||||
repeatDelay: 0.5,
|
||||
}}
|
||||
className="absolute size-1.5 rounded-full bg-primary"
|
||||
/>
|
||||
</div>
|
||||
{badge}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 flex flex-col items-center gap-2">
|
||||
<h3 className="text-center text-3xl font-medium lg:text-xl">
|
||||
{heading}
|
||||
</h3>
|
||||
<p className="max-w-md text-center text-muted-foreground">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<SignupForm />
|
||||
|
||||
<JoinedPeople joinedPeople={joinedPeople} avatars={avatars} />
|
||||
|
||||
<CountdownTimer targetDate={launchDate} />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export { Waitlist2 };
|
||||
Reference in New Issue
Block a user