Files
flatrender/src/components/sections/SecondsCalculator.tsx
T

121 lines
3.8 KiB
TypeScript
Raw Normal View History

"use client";
import { useMemo, useState } from "react";
import { useTranslations } from "next-intl";
import {
RESOLUTION_ORDER,
renderSecondsCost,
type SecondsPlan,
} from "@/lib/plans-catalog";
interface Props {
plans: SecondsPlan[];
}
/**
* Interactive "how many seconds do I need" helper. The user picks a video length
* and resolution; we show the per-render cost (length × resolution multiplier)
* and how many such videos each paid plan's monthly seconds would cover.
*/
export function SecondsCalculator({ plans }: Props) {
const t = useTranslations("pricing");
const [length, setLength] = useState(15);
const [resolution, setResolution] = useState("720p");
const cost = useMemo(
() => renderSecondsCost(length, resolution),
[length, resolution]
);
const paidPlans = plans.filter((p) => p.priceTomans > 0);
return (
<div className="rounded-2xl border border-neutral-200 bg-white p-6 shadow-sm sm:p-8">
<h3 className="font-heading text-xl font-bold text-neutral-900">
{t("calcTitle")}
</h3>
<p className="mt-1 text-sm text-neutral-500">{t("calcDesc")}</p>
<div className="mt-6 grid gap-6 sm:grid-cols-2">
{/* Length */}
<div>
<label className="mb-2 flex items-center justify-between text-sm font-medium text-neutral-700">
<span>{t("calcLength")}</span>
<span className="font-bold text-neutral-900">
{length} {t("calcSecondsUnit")}
</span>
</label>
<input
type="range"
min={1}
max={120}
value={length}
onChange={(e) => setLength(Number(e.target.value))}
className="w-full accent-indigo-600"
/>
</div>
{/* Resolution */}
<div>
<label className="mb-2 block text-sm font-medium text-neutral-700">
{t("calcResolution")}
</label>
<div className="flex flex-wrap gap-1.5">
{RESOLUTION_ORDER.map((r) => (
<button
key={r}
type="button"
onClick={() => setResolution(r)}
className={`rounded-lg border px-2.5 py-1.5 text-xs font-medium transition ${
resolution === r
? "border-indigo-600 bg-indigo-600 text-white"
: "border-neutral-200 bg-white text-neutral-600 hover:border-neutral-300"
}`}
>
{r}
</button>
))}
</div>
</div>
</div>
{/* Cost */}
<div className="mt-6 flex flex-wrap items-end justify-between gap-4 rounded-xl bg-neutral-50 p-5">
<div>
<p className="text-sm text-neutral-500">{t("calcCost")}</p>
<p className="mt-1 text-3xl font-extrabold text-neutral-900">
{cost}{" "}
<span className="text-base font-medium text-neutral-500">
{t("calcSecondsUnit")}
</span>
</p>
</div>
{paidPlans.length > 0 && (
<div className="text-end">
<p className="mb-1 text-sm text-neutral-500">
{t("calcRendersWith")}
</p>
<div className="flex flex-wrap justify-end gap-2">
{paidPlans.map((p) => (
<span
key={p.id}
className="rounded-lg border border-neutral-200 bg-white px-2.5 py-1 text-xs text-neutral-700"
>
<span className="font-semibold text-neutral-900">
{p.name}
</span>
{": "}
{t("calcVideosFmt", {
count: Math.floor(p.secondsCharge / Math.max(cost, 1)),
})}
</span>
))}
</div>
</div>
)}
</div>
</div>
);
}