feat: add billing_interval to products supporting one-time, monthly and yearly subscription pricing
This commit is contained in:
@@ -53,6 +53,7 @@ const productSchema = z.object({
|
||||
base_price: z.coerce.number().min(0, 'Preis darf nicht negativ sein'),
|
||||
category_id: z.string().min(1, 'Bitte wählen Sie eine Kategorie'),
|
||||
is_active: z.boolean().default(true),
|
||||
billing_interval: z.enum(['one_time', 'monthly', 'yearly']).default('monthly'),
|
||||
modules: z.array(moduleSchema).default([]),
|
||||
})
|
||||
|
||||
@@ -69,6 +70,7 @@ export function CreateProductDialog({ children, categories, product }: { childre
|
||||
base_price: product?.base_price || 0,
|
||||
category_id: product?.category_id || '',
|
||||
is_active: product?.is_active ?? true,
|
||||
billing_interval: product?.billing_interval || 'monthly',
|
||||
modules: product?.modules?.map(m => ({
|
||||
name: m.name,
|
||||
description: m.description || '',
|
||||
@@ -121,7 +123,7 @@ export function CreateProductDialog({ children, categories, product }: { childre
|
||||
<DialogTitle className="text-2xl font-bold">
|
||||
{product ? 'Produkt bearbeiten' : 'Neues Produkt erstellen'}
|
||||
</DialogTitle>
|
||||
<DialogDescription className="text-muted-foreground">
|
||||
<DialogDescription className="text-slate-300">
|
||||
Definieren Sie das Basisprodukt und fügen Sie optionale oder erforderliche Module hinzu.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
@@ -136,9 +138,9 @@ export function CreateProductDialog({ children, categories, product }: { childre
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Produktname</FormLabel>
|
||||
<FormLabel className="text-white">Produktname</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="z.B. Basic ERP" className="bg-white/5 border-white/10" {...field} />
|
||||
<Input placeholder="z.B. Basic ERP" className="bg-white/5 border-white/10 text-white" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -149,9 +151,9 @@ export function CreateProductDialog({ children, categories, product }: { childre
|
||||
name="base_price"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Basispreis (€)</FormLabel>
|
||||
<FormLabel className="text-white">Basispreis (€)</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="number" step="0.01" className="bg-white/5 border-white/10" {...field} />
|
||||
<Input type="number" step="0.01" className="bg-white/5 border-white/10 text-white" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -164,16 +166,16 @@ export function CreateProductDialog({ children, categories, product }: { childre
|
||||
name="category_id"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Kategorie</FormLabel>
|
||||
<FormLabel className="text-white">Kategorie</FormLabel>
|
||||
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger className="bg-white/5 border-white/10">
|
||||
<SelectValue placeholder="Kategorie wählen" />
|
||||
<SelectTrigger className="bg-white/5 border-white/10 text-white">
|
||||
<SelectValue placeholder="Kategorie wählen" className="text-white" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent className="glass-dark border-white/10 text-white">
|
||||
{categories.map(cat => (
|
||||
<SelectItem key={cat.id} value={cat.id}>{cat.name}</SelectItem>
|
||||
<SelectItem key={cat.id} value={cat.id} className="text-white">{cat.name}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
@@ -182,14 +184,46 @@ export function CreateProductDialog({ children, categories, product }: { childre
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="billing_interval"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel className="text-white">Abrechnungsmodell</FormLabel>
|
||||
<div className="grid grid-cols-3 gap-2 pt-1">
|
||||
{[
|
||||
{ value: 'one_time', label: 'Einmalig', icon: '💳' },
|
||||
{ value: 'monthly', label: 'Monatlich', icon: '🔄' },
|
||||
{ value: 'yearly', label: 'Jährlich', icon: '📅' },
|
||||
].map(opt => (
|
||||
<button
|
||||
key={opt.value}
|
||||
type="button"
|
||||
onClick={() => field.onChange(opt.value)}
|
||||
className={`flex flex-col items-center gap-1 p-3 rounded-xl border-2 text-sm font-medium transition-all ${
|
||||
field.value === opt.value
|
||||
? 'border-primary bg-primary/10 text-white'
|
||||
: 'border-white/10 bg-white/5 text-slate-400 hover:bg-white/10'
|
||||
}`}
|
||||
>
|
||||
<span className="text-lg">{opt.icon}</span>
|
||||
{opt.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="description"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Beschreibung</FormLabel>
|
||||
<FormLabel className="text-white">Beschreibung</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="Kurze Beschreibung des Produkts" className="bg-white/5 border-white/10" {...field} />
|
||||
<Input placeholder="Kurze Beschreibung des Produkts" className="bg-white/5 border-white/10 text-white placeholder:text-slate-400" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -233,9 +267,9 @@ export function CreateProductDialog({ children, categories, product }: { childre
|
||||
name={`modules.${index}.name`}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Modulname</FormLabel>
|
||||
<FormLabel className="text-white">Modulname</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="z.B. Cloud Storage" className="bg-white/10 border-white/10" {...field} />
|
||||
<Input placeholder="z.B. Cloud Storage" className="bg-white/10 border-white/10 text-white placeholder:text-slate-400" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -246,9 +280,9 @@ export function CreateProductDialog({ children, categories, product }: { childre
|
||||
name={`modules.${index}.price`}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Aufpreis (€)</FormLabel>
|
||||
<FormLabel className="text-white">Aufpreis (€)</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="number" step="0.01" className="bg-white/10 border-white/10" {...field} />
|
||||
<Input type="number" step="0.01" className="bg-white/10 border-white/10 text-white" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -262,9 +296,9 @@ export function CreateProductDialog({ children, categories, product }: { childre
|
||||
name={`modules.${index}.requirements`}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Benötigt (IDs, kommagetrennt)</FormLabel>
|
||||
<FormLabel className="text-white">Benötigt (IDs, kommagetrennt)</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="UUIDs..." className="bg-white/10 border-white/10 text-xs" {...field} />
|
||||
<Input placeholder="UUIDs..." className="bg-white/10 border-white/10 text-xs text-white" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -275,9 +309,9 @@ export function CreateProductDialog({ children, categories, product }: { childre
|
||||
name={`modules.${index}.exclusions`}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Schließt aus (IDs, kommagetrennt)</FormLabel>
|
||||
<FormLabel className="text-white">Schließt aus (IDs, kommagetrennt)</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="UUIDs..." className="bg-white/10 border-white/10 text-xs" {...field} />
|
||||
<Input placeholder="UUIDs..." className="bg-white/10 border-white/10 text-xs text-white" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -298,7 +332,7 @@ export function CreateProductDialog({ children, categories, product }: { childre
|
||||
/>
|
||||
</FormControl>
|
||||
<div className="space-y-1 leading-none">
|
||||
<FormLabel>Erforderlich</FormLabel>
|
||||
<FormLabel className="text-white">Erforderlich</FormLabel>
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
@@ -308,7 +342,7 @@ export function CreateProductDialog({ children, categories, product }: { childre
|
||||
))}
|
||||
|
||||
{fields.length === 0 && (
|
||||
<div className="text-center py-6 border-2 border-dashed border-white/5 rounded-lg text-muted-foreground text-sm">
|
||||
<div className="text-center py-6 border-2 border-dashed border-white/5 rounded-lg text-slate-400 text-sm">
|
||||
Noch keine Module hinzugefügt.
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user