TorbatYar/frontend/modules/commercial/components/TrialBanner.tsx
2026-07-28 20:39:10 +03:30

124 lines
4.9 KiB
TypeScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import Link from "next/link";
import type { TrialState } from "../types";
export function TrialBanner({ trial }: { trial: TrialState | null | undefined }) {
if (!trial) return null;
if (!trial.active) {
const expired =
(typeof trial.days_remaining === "number" && trial.days_remaining <= 0) ||
(trial.ends_at && new Date(trial.ends_at).getTime() < Date.now());
if (!expired) return null;
return (
<div
className="rounded-2xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-900 dark:border-red-900 dark:bg-red-950/40 dark:text-red-100"
role="alert"
>
<div className="flex flex-wrap items-center justify-between gap-2">
<p className="font-semibold">دوره آزمایشی منقضی شده است</p>
<Link href="/billing" className="font-semibold text-primary hover:underline">
ارتقا فوری
</Link>
</div>
<p className="mt-1 text-xs">برای ادامه قابلیتهای قفلشده، اشتراک را فعال کنید.</p>
</div>
);
}
const days = trial.days_remaining;
const urgent = typeof days === "number" && days <= 3;
const tone = urgent
? "border-orange-300 bg-orange-50 text-orange-950 dark:border-orange-800 dark:bg-orange-950/30 dark:text-orange-100"
: "border-amber-200 bg-amber-50 text-amber-900 dark:border-amber-800 dark:bg-amber-950/30 dark:text-amber-100";
return (
<div className={`rounded-2xl border px-4 py-3 text-sm ${tone}`} role="status">
<div className="flex flex-wrap items-center justify-between gap-2">
<p>
{urgent ? "هشدار: " : ""}
دوره آزمایشی فعال است
{typeof days === "number" ? (
<span className="font-semibold"> {days} روز باقیمانده</span>
) : null}
{trial.ends_at ? (
<span className="mr-2 text-xs opacity-80" dir="ltr">
({new Date(trial.ends_at).toLocaleDateString("fa-IR")})
</span>
) : null}
</p>
<Link href="/billing" className="font-semibold text-primary hover:underline">
ارتقا به اشتراک
</Link>
</div>
{urgent ? (
<p className="mt-1 text-xs">قبل از انقضا ارتقا دهید تا قفل قابلیتها فعال نشود.</p>
) : null}
{trial.quotas && trial.quotas.length > 0 ? (
<ul className="mt-2 grid gap-1 text-xs sm:grid-cols-2">
{trial.quotas.map((q) => {
const nearLimit =
typeof q.used === "number" &&
typeof q.limit === "number" &&
q.limit > 0 &&
q.used / q.limit >= 0.8;
return (
<li key={q.key} className={nearLimit ? "font-semibold text-orange-800 dark:text-orange-200" : ""}>
{q.label || q.key}: {q.used ?? "—"} / {q.limit ?? "∞"}
{nearLimit ? " · نزدیک سقف" : ""}
</li>
);
})}
</ul>
) : null}
</div>
);
}
export function SubscriptionBanner({
status,
planName,
}: {
status?: string | null;
planName?: string | null;
}) {
if (!status) {
return (
<div className="rounded-2xl border border-gray-200 bg-gray-50 px-4 py-3 text-sm text-gray-700 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300">
اشتراک ثبت نشده است.{" "}
<Link href="/billing" className="font-semibold text-primary hover:underline">
شروع trial یا انتخاب پلن
</Link>
</div>
);
}
const tone =
status === "active"
? "border-emerald-200 bg-emerald-50 text-emerald-900 dark:border-emerald-800 dark:bg-emerald-950/30 dark:text-emerald-100"
: status === "trialing"
? "border-amber-200 bg-amber-50 text-amber-900 dark:border-amber-800 dark:bg-amber-950/30 dark:text-amber-100"
: status === "past_due" || status === "expired" || status === "suspended"
? "border-red-200 bg-red-50 text-red-900 dark:border-red-900 dark:bg-red-950/40 dark:text-red-100"
: "border-gray-200 bg-gray-50 text-gray-800 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-200";
return (
<div className={`rounded-2xl border px-4 py-3 text-sm ${tone}`} role="status">
وضعیت اشتراک: <span className="font-semibold">{status}</span>
{planName ? (
<>
{" "}
· پلن: <span className="font-semibold">{planName}</span>
</>
) : null}
{(status === "past_due" || status === "expired" || status === "suspended") && (
<>
{" "}
<Link href="/billing" className="font-semibold text-primary hover:underline">
رفع مشکل / ارتقا
</Link>
</>
)}
</div>
);
}