feat: add billing_interval to products supporting one-time, monthly and yearly subscription pricing
This commit is contained in:
@@ -21,6 +21,19 @@ type CategorySelection = {
|
||||
moduleIds: string[] // selected module IDs for this product
|
||||
}
|
||||
|
||||
// ─── Billing interval helpers ─────────────────────────────────────────────────
|
||||
function billingLabel(interval?: string) {
|
||||
if (interval === 'one_time') return 'einmalig'
|
||||
if (interval === 'yearly') return '/ Jahr'
|
||||
return '/ Monat'
|
||||
}
|
||||
|
||||
function billingBadgeClass(interval?: string) {
|
||||
if (interval === 'one_time') return 'bg-slate-500/20 text-slate-300 border-slate-500/30'
|
||||
if (interval === 'yearly') return 'bg-purple-500/20 text-purple-400 border-purple-500/30'
|
||||
return 'bg-blue-500/20 text-blue-400 border-blue-500/30'
|
||||
}
|
||||
|
||||
// ─── Icon helper ──────────────────────────────────────────────────────────────
|
||||
function CategoryIcon({ icon, className }: { icon?: string | null; className?: string }) {
|
||||
const Icon = icon === 'Cloud' ? Cloud : icon === 'Utensils' ? Utensils : icon === 'HardDrive' ? HardDrive : Package
|
||||
@@ -267,12 +280,18 @@ export function OrderWizard({
|
||||
className="flex flex-col items-start p-4 rounded-xl border-2 border-white/5 bg-white/5 hover:bg-white/10 peer-data-[state=checked]:border-primary peer-data-[state=checked]:bg-primary/5 cursor-pointer transition-all"
|
||||
>
|
||||
<div className="flex justify-between w-full items-center">
|
||||
<span className="font-bold text-base text-white">{product.name}</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-bold text-base text-white">{product.name}</span>
|
||||
<span className={`text-[10px] px-1.5 py-0.5 rounded border ${billingBadgeClass(product.billing_interval)}`}>
|
||||
{product.billing_interval === 'one_time' ? 'Einmalig' : product.billing_interval === 'yearly' ? 'Abo/Jahr' : 'Abo/Monat'}
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-primary font-semibold text-sm">
|
||||
{new Intl.NumberFormat('de-DE', {
|
||||
style: 'currency',
|
||||
currency: 'EUR',
|
||||
}).format(product.base_price)}
|
||||
}).format(product.base_price)}{' '}
|
||||
<span className="text-xs text-slate-400">{billingLabel(product.billing_interval)}</span>
|
||||
</span>
|
||||
</div>
|
||||
{product.description && (
|
||||
@@ -374,6 +393,7 @@ export function OrderWizard({
|
||||
<span className="text-white">{prod.name}</span>
|
||||
<span className="text-white">
|
||||
{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(prod.base_price)}
|
||||
{' '}<span className="text-slate-400 text-xs">{billingLabel(prod.billing_interval)}</span>
|
||||
</span>
|
||||
</div>
|
||||
{sel.moduleIds.map(mId => {
|
||||
@@ -511,6 +531,7 @@ export function OrderWizard({
|
||||
</span>
|
||||
<span>
|
||||
{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(catTotal)}
|
||||
{' '}<span className="text-sm font-normal text-slate-400">{billingLabel(prod.billing_interval)}</span>
|
||||
</span>
|
||||
</div>
|
||||
{sel.moduleIds.length > 0 && (
|
||||
|
||||
Reference in New Issue
Block a user