Files
songs2vid/components/PaygPriceCalculator.tsx
T
Atakan Doğan Özban 9404efd86c Align preview typography with FFmpeg output, add video/song title split, and ship OSS updates.
Separate YouTube video titles from on-video song/artist fields with Pro gating, serve curated fonts and watermark assets for 1:1 preview parity, and include billing/API/docs/deploy stack for self-hosted release.
2026-07-27 16:26:19 +02:00

94 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
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 { useState } from "react";
import {
CREDIT_PRICE_CENTS,
CREDIT_PURCHASE_MAX,
CREDIT_PURCHASE_MIN,
formatCreditPrice,
} from "@/lib/credits";
export function PaygPriceCalculator() {
const [videos, setVideos] = useState(CREDIT_PURCHASE_MIN);
const total = formatCreditPrice(videos);
const unit = formatCreditPrice(1);
return (
<div className="mx-auto w-full max-w-xl rounded-2xl border border-gray-700/60 bg-surface/80 p-6 sm:p-8">
<p className="text-xs font-semibold uppercase tracking-wider text-amber-400">
Pay as you go
</p>
<h3 className="mt-2 text-xl font-semibold text-white sm:text-2xl">
Choose how many videos you need
</h3>
<p className="mt-2 text-sm text-gray-400">
{unit} per video · minimum {CREDIT_PURCHASE_MIN} videos (
{formatCreditPrice(CREDIT_PURCHASE_MIN)})
</p>
<div className="mt-8">
<div className="flex items-end justify-between gap-4">
<label htmlFor="payg-videos" className="text-sm text-gray-300">
Videos
</label>
<div className="flex items-center gap-2">
<input
id="payg-videos-number"
type="number"
min={CREDIT_PURCHASE_MIN}
max={CREDIT_PURCHASE_MAX}
value={videos}
onChange={(e) => {
const n = Math.floor(Number(e.target.value));
if (!Number.isFinite(n)) return;
setVideos(
Math.min(CREDIT_PURCHASE_MAX, Math.max(CREDIT_PURCHASE_MIN, n)),
);
}}
className="w-20 rounded border border-gray-600 bg-surface-dark px-3 py-2 text-center text-lg font-semibold text-white focus:border-accent focus:outline-none"
/>
</div>
</div>
<input
id="payg-videos"
type="range"
min={CREDIT_PURCHASE_MIN}
max={CREDIT_PURCHASE_MAX}
value={videos}
onChange={(e) => setVideos(Number(e.target.value))}
className="mt-4 w-full accent-amber-400"
aria-valuemin={CREDIT_PURCHASE_MIN}
aria-valuemax={CREDIT_PURCHASE_MAX}
aria-valuenow={videos}
aria-label="Number of videos"
/>
<div className="mt-1 flex justify-between text-xs text-gray-500">
<span>{CREDIT_PURCHASE_MIN}</span>
<span>{CREDIT_PURCHASE_MAX}</span>
</div>
</div>
<div className="mt-8 flex flex-wrap items-end justify-between gap-4 border-t border-gray-800 pt-6">
<div>
<p className="text-xs uppercase tracking-wider text-gray-500">Total</p>
<p className="mt-1 text-3xl font-bold text-white sm:text-4xl">{total}</p>
<p className="mt-1 text-sm text-gray-400">
{videos} × {unit}{" "}
<span className="text-gray-500">
({(CREDIT_PRICE_CENTS / 100).toFixed(2)} each)
</span>
</p>
</div>
<Link
href="/dashboard/settings"
className="inline-flex rounded border border-amber-500/40 bg-amber-500/10 px-5 py-3 text-sm font-medium text-amber-300 transition-colors hover:border-amber-500/60 hover:bg-amber-500/20"
>
Buy credits
</Link>
</div>
</div>
);
}