5ba09c2ef1
CI/CD / CI · API (dotnet build + test) (push) Successful in 40s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 30s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m3s
CI/CD / CI · Admin Web (tsc) (push) Successful in 35s
CI/CD / CI · Website (tsc) (push) Successful in 43s
CI/CD / CI · Koja (tsc) (push) Successful in 48s
CI/CD / Deploy · all services (push) Failing after 1m27s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
129 lines
4.7 KiB
TypeScript
129 lines
4.7 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { X, Rocket } from "lucide-react";
|
|
import { useLocale } from "next-intl";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
// 1 Tir 1405 = June 22, 2026 (Tehran, UTC+3:30)
|
|
const LAUNCH_DATE = new Date("2026-06-22T00:00:00+03:30");
|
|
const DISMISS_KEY = "meezi_launch_banner_v3";
|
|
|
|
interface TimeLeft {
|
|
days: number;
|
|
hours: number;
|
|
minutes: number;
|
|
seconds: number;
|
|
expired: boolean;
|
|
}
|
|
|
|
function calcTimeLeft(): TimeLeft {
|
|
const diff = LAUNCH_DATE.getTime() - Date.now();
|
|
if (diff <= 0) return { days: 0, hours: 0, minutes: 0, seconds: 0, expired: true };
|
|
const days = Math.floor(diff / 86_400_000);
|
|
const hours = Math.floor((diff % 86_400_000) / 3_600_000);
|
|
const minutes = Math.floor((diff % 3_600_000) / 60_000);
|
|
const seconds = Math.floor((diff % 60_000) / 1_000);
|
|
return { days, hours, minutes, seconds, expired: false };
|
|
}
|
|
|
|
function pad(n: number) {
|
|
return String(n).padStart(2, "0");
|
|
}
|
|
|
|
export function LaunchCountdownSection() {
|
|
const locale = useLocale();
|
|
const isFa = locale === "fa";
|
|
|
|
const [visible, setVisible] = useState(false);
|
|
const [timeLeft, setTimeLeft] = useState<TimeLeft>(calcTimeLeft);
|
|
|
|
useEffect(() => {
|
|
if (localStorage.getItem(DISMISS_KEY) === "1") return;
|
|
if (calcTimeLeft().expired) return;
|
|
setVisible(true);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (!visible) return;
|
|
const id = setInterval(() => {
|
|
const tl = calcTimeLeft();
|
|
setTimeLeft(tl);
|
|
if (tl.expired) setVisible(false);
|
|
}, 1_000);
|
|
return () => clearInterval(id);
|
|
}, [visible]);
|
|
|
|
if (!visible) return null;
|
|
|
|
function dismiss() {
|
|
localStorage.setItem(DISMISS_KEY, "1");
|
|
setVisible(false);
|
|
}
|
|
|
|
const { days, hours, minutes, seconds } = timeLeft;
|
|
|
|
return (
|
|
<section
|
|
dir={isFa ? "rtl" : "ltr"}
|
|
data-launch-countdown
|
|
aria-label={isFa ? "شمارش معکوس راهاندازی" : "Launch countdown"}
|
|
className="border-b border-gray-100 bg-gradient-to-b from-brand-50/80 to-white pt-24 pb-10 sm:pt-28 sm:pb-12"
|
|
>
|
|
<div className="mx-auto max-w-4xl px-4 sm:px-6 lg:px-8">
|
|
<div className="relative overflow-hidden rounded-2xl border border-brand-700/15 bg-white p-6 shadow-sm sm:p-8">
|
|
<button
|
|
onClick={dismiss}
|
|
aria-label={isFa ? "بستن" : "Dismiss"}
|
|
className="absolute end-4 top-4 rounded-lg p-1.5 text-gray-400 transition hover:bg-gray-100 hover:text-gray-600 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-700"
|
|
>
|
|
<X className="h-4 w-4" />
|
|
</button>
|
|
|
|
<div className="flex flex-col items-center gap-6 text-center">
|
|
<div className="flex flex-col items-center gap-2">
|
|
<span className="inline-flex items-center gap-1.5 rounded-full bg-brand-50 px-3 py-1 text-xs font-semibold uppercase tracking-wider text-brand-700">
|
|
<Rocket className="h-3.5 w-3.5" />
|
|
{isFa ? "راهاندازی رسمی" : "Official launch"}
|
|
</span>
|
|
<p className="max-w-2xl text-base font-medium text-gray-900 sm:text-lg">
|
|
{isFa
|
|
? "میزی رسماً ۱ تیر ۱۴۰۵ برای همه کاربران راهاندازی میشود"
|
|
: "Meezi officially launches for all users on June 22, 2026"}
|
|
</p>
|
|
</div>
|
|
|
|
<div className="grid w-full max-w-md grid-cols-4 gap-3 sm:gap-4">
|
|
<CountdownUnit value={days} label={isFa ? "روز" : "Days"} />
|
|
<CountdownUnit value={hours} label={isFa ? "ساعت" : "Hours"} />
|
|
<CountdownUnit value={minutes} label={isFa ? "دقیقه" : "Min"} />
|
|
<CountdownUnit value={seconds} label={isFa ? "ثانیه" : "Sec"} />
|
|
</div>
|
|
|
|
<a
|
|
href="https://app.meezi.ir/register"
|
|
className={cn(
|
|
"inline-flex items-center justify-center rounded-xl bg-brand-700 px-6 py-3 text-sm font-semibold text-white",
|
|
"transition hover:bg-brand-800 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-700 focus-visible:ring-offset-2"
|
|
)}
|
|
>
|
|
{isFa ? "ثبتنام رایگان" : "Register free"}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
function CountdownUnit({ value, label }: { value: number; label: string }) {
|
|
return (
|
|
<div className="flex flex-col items-center rounded-xl border border-gray-100 bg-gray-50 px-2 py-3 sm:px-3 sm:py-4">
|
|
<span className="font-mono text-2xl font-bold tabular-nums text-brand-700 sm:text-3xl">
|
|
{pad(value)}
|
|
</span>
|
|
<span className="mt-1 text-[11px] font-medium text-gray-500">{label}</span>
|
|
</div>
|
|
);
|
|
}
|