feat(packages): implement package-based business model

This commit is contained in:
Codex Agent
2025-09-26 22:13:56 +02:00
parent 6fc36ebaf4
commit 0a643c3e4d
54 changed files with 3301 additions and 282 deletions

View File

@@ -1,6 +1,7 @@
import React from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { ArrowLeft, Loader2, Save, Sparkles } from 'lucide-react';
import { ArrowLeft, Loader2, Save, Sparkles, Package as PackageIcon } from 'lucide-react';
import { useQuery } from '@tanstack/react-query';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { Button } from '@/components/ui/button';
@@ -8,15 +9,18 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/com
import { Checkbox } from '@/components/ui/checkbox';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
import { AdminLayout } from '../components/AdminLayout';
import { createEvent, getEvent, updateEvent } from '../api';
import { createEvent, getEvent, updateEvent, getPackages } from '../api';
import { isAuthError } from '../auth/tokens';
interface EventFormState {
name: string;
slug: string;
date: string;
package_id: number;
isPublished: boolean;
}
@@ -30,6 +34,7 @@ export default function EventFormPage() {
name: '',
slug: '',
date: '',
package_id: 1, // Default Free package
isPublished: false,
});
const [autoSlug, setAutoSlug] = React.useState(true);
@@ -38,6 +43,11 @@ export default function EventFormPage() {
const [saving, setSaving] = React.useState(false);
const [error, setError] = React.useState<string | null>(null);
const { data: packages, isLoading: packagesLoading } = useQuery({
queryKey: ['packages', 'endcustomer'],
queryFn: () => getPackages('endcustomer'),
});
React.useEffect(() => {
let cancelled = false;
if (!isEdit || !slugParam) {
@@ -109,6 +119,7 @@ export default function EventFormPage() {
const payload = {
name: trimmedName,
slug: trimmedSlug,
package_id: form.package_id,
date: form.date || undefined,
status: form.isPublished ? 'published' : 'draft',
};
@@ -199,6 +210,50 @@ export default function EventFormPage() {
onChange={(e) => setForm((prev) => ({ ...prev, date: e.target.value }))}
/>
</div>
<div className="space-y-2">
<Label htmlFor="package_id">Package</Label>
<Select value={form.package_id.toString()} onValueChange={(value) => setForm((prev) => ({ ...prev, package_id: parseInt(value) }))}>
<SelectTrigger>
<SelectValue placeholder="Wählen Sie ein Package" />
</SelectTrigger>
<SelectContent>
{packagesLoading ? (
<SelectItem value="">Laden...</SelectItem>
) : (
packages?.map((pkg) => (
<SelectItem key={pkg.id} value={pkg.id.toString()}>
{pkg.name} - {pkg.price} ({pkg.max_photos} Fotos)
</SelectItem>
))
)}
</SelectContent>
</Select>
<Dialog>
<DialogTrigger asChild>
<Button variant="outline" size="sm">Package-Details</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Package auswählen</DialogTitle>
<DialogDescription>Wählen Sie das Package für Ihr Event. Höhere Packages bieten mehr Limits und Features.</DialogDescription>
</DialogHeader>
<div className="space-y-2">
{packages?.map((pkg) => (
<div key={pkg.id} className="p-4 border rounded">
<h3 className="font-semibold">{pkg.name}</h3>
<p>{pkg.price} </p>
<ul className="text-sm">
<li>Max Fotos: {pkg.max_photos}</li>
<li>Max Gäste: {pkg.max_guests}</li>
<li>Galerie: {pkg.gallery_days} Tage</li>
<li>Features: {Object.keys(pkg.features).filter(k => pkg.features[k]).join(', ')}</li>
</ul>
</div>
))}
</div>
</DialogContent>
</Dialog>
</div>
</div>
<div className="flex items-start gap-3 rounded-xl bg-pink-50/60 p-4">