+
Noch keine Module hinzugefügt.
)}
diff --git a/shop/components/admin/product-list.tsx b/shop/components/admin/product-list.tsx
index 43bf2c7..49f8158 100644
--- a/shop/components/admin/product-list.tsx
+++ b/shop/components/admin/product-list.tsx
@@ -32,7 +32,7 @@ export function ProductList({ initialProducts, categories }: { initialProducts:
Produktübersicht
-
+
Alle konfigurierten Software-Lösungen und Erweiterungen.
@@ -40,17 +40,17 @@ export function ProductList({ initialProducts, categories }: { initialProducts:
- Name
- Basispreis
- Module
- Status
- Aktionen
+ Name
+ Basispreis
+ Module
+ Status
+ Aktionen
{products.map((product) => (
-
+
{product.name}
@@ -59,15 +59,24 @@ export function ProductList({ initialProducts, categories }: { initialProducts:
{product.category.name}
)}
+ {product.billing_interval === 'monthly' && (
+ Abo/Monat
+ )}
+ {product.billing_interval === 'yearly' && (
+ Abo/Jahr
+ )}
+ {product.billing_interval === 'one_time' && (
+ Einmalig
+ )}
-
{product.description}
+
{product.description}
- {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(product.base_price)}
+ {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(product.base_price)}
- {product.modules?.length || 0} Module
+ {product.modules?.length || 0} Module
@@ -96,7 +105,7 @@ export function ProductList({ initialProducts, categories }: { initialProducts:
))}
{products.length === 0 && (
-
+
Keine Produkte gefunden. Erstellen Sie Ihr erstes Produkt!
diff --git a/shop/components/order-wizard.tsx b/shop/components/order-wizard.tsx
index a2b84f5..e8dd73d 100644
--- a/shop/components/order-wizard.tsx
+++ b/shop/components/order-wizard.tsx
@@ -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"
>
-
{product.name}
+
+ {product.name}
+
+ {product.billing_interval === 'one_time' ? 'Einmalig' : product.billing_interval === 'yearly' ? 'Abo/Jahr' : 'Abo/Monat'}
+
+
{new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR',
- }).format(product.base_price)}
+ }).format(product.base_price)}{' '}
+ {billingLabel(product.billing_interval)}
{product.description && (
@@ -374,6 +393,7 @@ export function OrderWizard({
{prod.name}
{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(prod.base_price)}
+ {' '}{billingLabel(prod.billing_interval)}
{sel.moduleIds.map(mId => {
@@ -511,6 +531,7 @@ export function OrderWizard({
{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(catTotal)}
+ {' '}{billingLabel(prod.billing_interval)}
{sel.moduleIds.length > 0 && (
diff --git a/shop/lib/types.ts b/shop/lib/types.ts
index b5acaae..510cccb 100644
--- a/shop/lib/types.ts
+++ b/shop/lib/types.ts
@@ -4,6 +4,7 @@ export type Product = {
description?: string | null | undefined;
base_price: number;
is_active: boolean;
+ billing_interval: 'one_time' | 'monthly' | 'yearly';
created_at: string;
updated_at: string;
category_id?: string | null;
diff --git a/shop/supabase/migrations/20260501003000_add_billing_interval_to_products.sql b/shop/supabase/migrations/20260501003000_add_billing_interval_to_products.sql
new file mode 100644
index 0000000..62ef514
--- /dev/null
+++ b/shop/supabase/migrations/20260501003000_add_billing_interval_to_products.sql
@@ -0,0 +1,4 @@
+-- Add billing_interval to products to support subscription and one-time products
+-- Values: 'one_time', 'monthly', 'yearly'
+ALTER TABLE public.products ADD COLUMN IF NOT EXISTS billing_interval TEXT DEFAULT 'monthly' NOT NULL
+ CHECK (billing_interval IN ('one_time', 'monthly', 'yearly'));