Initial OSS scaffold from Songs2VID (pre-strip)

This commit is contained in:
Songs2VID Support
2026-08-03 06:15:05 +02:00
commit 506d56fa92
195 changed files with 25023 additions and 0 deletions
+93
View File
@@ -0,0 +1,93 @@
"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>
);
}