Files
fotospiel-app/resources/js/pages/marketing/Packages.tsx

526 lines
26 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState } from 'react';
import { Head, Link, usePage } from '@inertiajs/react';
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "@/components/ui/carousel"
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { Badge } from "@/components/ui/badge"
import { Progress } from "@/components/ui/progress"
import { Button } from "@/components/ui/button"
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion"
import MarketingLayout from '@/layouts/marketing/MarketingLayout';
import { ArrowRight, ShoppingCart, Check, X, Users, Image, Calendar, Shield, Star } from 'lucide-react';
interface Package {
id: number;
name: string;
description: string;
price: number;
events: number;
features: string[];
limits?: {
max_photos?: number;
max_guests?: number;
max_tenants?: number;
max_events?: number;
gallery_days?: number;
};
watermark_allowed?: boolean;
branding_allowed?: boolean;
}
interface PackagesProps {
endcustomerPackages: Package[];
resellerPackages: Package[];
}
const Packages: React.FC<PackagesProps> = ({ endcustomerPackages, resellerPackages }) => {
const [open, setOpen] = useState(false);
const [selectedPackage, setSelectedPackage] = useState<Package | null>(null);
const [currentStep, setCurrentStep] = useState('step1');
const { props } = usePage();
const { auth } = props as any;
const testimonials = [
{ name: 'Anna M.', text: 'Das Starter-Paket war perfekt für unsere Hochzeit einfach und günstig!', rating: 5 },
{ name: 'Max B.', text: 'Pro-Paket mit Analytics hat uns geholfen, die besten Momente zu finden.', rating: 5 },
{ name: 'Lisa K.', text: 'Als Reseller spare ich Zeit mit dem M-Paket super Support!', rating: 5 },
];
const allPackages = [...endcustomerPackages, ...resellerPackages];
const handleCardClick = (pkg: Package) => {
setSelectedPackage(pkg);
setCurrentStep('step1');
setOpen(true);
};
// nextStep entfernt, da Tabs nun parallel sind
const getFeatureIcon = (feature: string) => {
switch (feature) {
case 'basic_uploads': return <Image className="w-4 h-4" />;
case 'unlimited_sharing': return <ArrowRight className="w-4 h-4" />;
case 'no_watermark': return <Shield className="w-4 h-4" />;
case 'custom_tasks': return <Check className="w-4 h-4" />;
case 'advanced_analytics': return <Star className="w-4 h-4" />;
case 'priority_support': return <Users className="w-4 h-4" />;
case 'reseller_dashboard': return <ShoppingCart className="w-4 h-4" />;
case 'custom_branding': return <Image className="w-4 h-4" />;
default: return <Check className="w-4 h-4" />;
}
};
return (
<MarketingLayout title="Packages">
{/* Hero Section */}
<section className="bg-aurora-enhanced text-white py-20 px-4">
<div className="container mx-auto text-center">
<h1 className="text-4xl md:text-6xl font-bold mb-4 font-display">Unsere Packages</h1>
<p className="text-xl md:text-2xl mb-8 max-w-3xl mx-auto font-sans-marketing">Wählen Sie das passende Paket für Ihr Event von kostenlos bis premium.</p>
<Link href="#endcustomer" className="bg-white text-[#FFB6C1] px-8 py-4 rounded-full font-semibold text-lg font-sans-marketing hover:bg-gray-100 transition">
Jetzt entdecken
</Link>
</div>
</section>
<section id="endcustomer" className="py-20 px-4">
<div className="container mx-auto">
<h2 className="text-3xl font-bold text-center mb-12 font-display">Für Endkunden</h2>
{/* Mobile Carousel for Endcustomer Packages */}
<div className="block md:hidden">
<Carousel className="w-full max-w-md mx-auto">
<CarouselContent className="-ml-1">
{endcustomerPackages.map((pkg) => (
<CarouselItem key={pkg.id} className="pl-1 basis-full">
<div
className="bg-white p-6 rounded-lg shadow-md border hover:shadow-lg transition-all duration-300"
>
<div className="text-center mb-4">
<ShoppingCart className="w-12 h-12 text-[#FFB6C1] mx-auto" />
</div>
<h3 className="text-xl font-semibold mb-2 font-sans-marketing">{pkg.name}</h3>
<p className="text-gray-600 mb-4 font-serif-custom">{pkg.description}</p>
<p className="text-2xl font-bold text-[#FFB6C1] mb-4 font-sans-marketing">
{pkg.price === 0 ? 'Kostenlos' : `${pkg.price}`}
</p>
<ul className="text-sm text-gray-600 mb-6 space-y-1 font-sans-marketing">
<li> {pkg.events} Events</li>
{pkg.features.map((feature, index) => (
<li key={index} className="flex items-center">
{getFeatureIcon(feature)} {feature}
</li>
))}
{pkg.limits?.max_photos && <li> Max. {pkg.limits.max_photos} Fotos</li>}
{pkg.limits?.gallery_days && <li> Galerie {pkg.limits.gallery_days} Tage</li>}
{pkg.limits?.max_guests && <li> Max. {pkg.limits.max_guests} Gäste</li>}
{pkg.watermark_allowed === false && <li><Badge variant="secondary">Kein Watermark</Badge></li>}
{pkg.branding_allowed && <li><Badge variant="secondary">Custom Branding</Badge></li>}
</ul>
<Button
variant="outline"
onClick={() => handleCardClick(pkg)}
className="w-full mt-4 font-sans-marketing"
>
Details anzeigen
</Button>
</div>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
</div>
{/* Desktop Grid for Endcustomer Packages */}
<div className="hidden md:block">
<div className="grid md:grid-cols-3 gap-8">
{endcustomerPackages.map((pkg) => (
<div
key={pkg.id}
className="bg-white p-6 rounded-lg shadow-md border hover:shadow-lg transition-all duration-300"
>
<div className="text-center mb-4">
<ShoppingCart className="w-12 h-12 text-[#FFB6C1] mx-auto" />
</div>
<h3 className="text-xl font-semibold mb-2 font-sans-marketing">{pkg.name}</h3>
<p className="text-gray-600 mb-4 font-serif-custom">{pkg.description}</p>
<p className="text-2xl font-bold text-[#FFB6C1] mb-4 font-sans-marketing">
{pkg.price === 0 ? 'Kostenlos' : `${pkg.price}`}
</p>
<ul className="text-sm text-gray-600 mb-6 space-y-1 font-sans-marketing">
<li> {pkg.events} Events</li>
{pkg.features.map((feature, index) => (
<li key={index} className="flex items-center">
{getFeatureIcon(feature)} {feature}
</li>
))}
{pkg.limits?.max_photos && <li> Max. {pkg.limits.max_photos} Fotos</li>}
{pkg.limits?.gallery_days && <li> Galerie {pkg.limits.gallery_days} Tage</li>}
{pkg.limits?.max_guests && <li> Max. {pkg.limits.max_guests} Gäste</li>}
{pkg.watermark_allowed === false && <li><Badge variant="secondary">Kein Watermark</Badge></li>}
{pkg.branding_allowed && <li><Badge variant="secondary">Custom Branding</Badge></li>}
</ul>
<Button
variant="outline"
onClick={() => handleCardClick(pkg)}
className="w-full mt-4 font-sans-marketing"
>
Details anzeigen
</Button>
</div>
))}
</div>
</div>
</div>
{/* Comparison Section for Endcustomer */}
<div className="mt-12">
<h3 className="text-2xl font-bold text-center mb-6 font-display">Endkunden-Pakete vergleichen</h3>
<div className="block md:hidden">
<Accordion type="single" collapsible className="w-full">
<AccordionItem value="price">
<AccordionTrigger className="font-sans-marketing">Preis</AccordionTrigger>
<AccordionContent>
<div className="grid grid-cols-3 gap-4 p-4">
{endcustomerPackages.map((pkg) => (
<div key={pkg.id} className="text-center">
<p className="font-bold">{pkg.name}</p>
<p>{pkg.price === 0 ? 'Kostenlos' : `${pkg.price}`}</p>
</div>
))}
</div>
</AccordionContent>
</AccordionItem>
<AccordionItem value="max-photos">
<AccordionTrigger className="font-sans-marketing">Max. Fotos {getFeatureIcon('max_photos')}</AccordionTrigger>
<AccordionContent>
<div className="grid grid-cols-3 gap-4 p-4">
{endcustomerPackages.map((pkg) => (
<div key={pkg.id} className="text-center">
<p className="font-bold">{pkg.name}</p>
<p>{pkg.limits?.max_photos || 'Unbegrenzt'}</p>
</div>
))}
</div>
</AccordionContent>
</AccordionItem>
<AccordionItem value="max-guests">
<AccordionTrigger className="font-sans-marketing">Max. Gäste {getFeatureIcon('max_guests')}</AccordionTrigger>
<AccordionContent>
<div className="grid grid-cols-3 gap-4 p-4">
{endcustomerPackages.map((pkg) => (
<div key={pkg.id} className="text-center">
<p className="font-bold">{pkg.name}</p>
<p>{pkg.limits?.max_guests || 'Unbegrenzt'}</p>
</div>
))}
</div>
</AccordionContent>
</AccordionItem>
<AccordionItem value="gallery-days">
<AccordionTrigger className="font-sans-marketing">Galerie Tage {getFeatureIcon('gallery_days')}</AccordionTrigger>
<AccordionContent>
<div className="grid grid-cols-3 gap-4 p-4">
{endcustomerPackages.map((pkg) => (
<div key={pkg.id} className="text-center">
<p className="font-bold">{pkg.name}</p>
<p>{pkg.limits?.gallery_days || 'Unbegrenzt'}</p>
</div>
))}
</div>
</AccordionContent>
</AccordionItem>
<AccordionItem value="watermark">
<AccordionTrigger className="font-sans-marketing">Watermark {getFeatureIcon('no_watermark')}</AccordionTrigger>
<AccordionContent>
<div className="grid grid-cols-3 gap-4 p-4">
{endcustomerPackages.map((pkg) => (
<div key={pkg.id} className="text-center">
<p className="font-bold">{pkg.name}</p>
{pkg.watermark_allowed === false ? <Check className="w-4 h-4 text-green-500 mx-auto" /> : <X className="w-4 h-4 text-red-500 mx-auto" />}
</div>
))}
</div>
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
<div className="hidden md:block">
<Table>
<TableHeader>
<TableRow>
<TableHead>Feature</TableHead>
{endcustomerPackages.map((pkg) => (
<TableHead key={pkg.id} className="text-center">
{pkg.name}
</TableHead>
))}
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell className="font-semibold">Preis</TableCell>
{endcustomerPackages.map((pkg) => (
<TableCell key={pkg.id} className="text-center">
{pkg.price === 0 ? 'Kostenlos' : `${pkg.price}`}
</TableCell>
))}
</TableRow>
<TableRow>
<TableCell className="font-semibold">Max. Fotos {getFeatureIcon('max_photos')}</TableCell>
{endcustomerPackages.map((pkg) => (
<TableCell key={pkg.id} className="text-center">
{pkg.limits?.max_photos || 'Unbegrenzt'}
</TableCell>
))}
</TableRow>
<TableRow>
<TableCell className="font-semibold">Max. Gäste {getFeatureIcon('max_guests')}</TableCell>
{endcustomerPackages.map((pkg) => (
<TableCell key={pkg.id} className="text-center">
{pkg.limits?.max_guests || 'Unbegrenzt'}
</TableCell>
))}
</TableRow>
<TableRow>
<TableCell className="font-semibold">Galerie Tage {getFeatureIcon('gallery_days')}</TableCell>
{endcustomerPackages.map((pkg) => (
<TableCell key={pkg.id} className="text-center">
{pkg.limits?.gallery_days || 'Unbegrenzt'}
</TableCell>
))}
</TableRow>
<TableRow>
<TableCell className="font-semibold">Watermark {getFeatureIcon('no_watermark')}</TableCell>
{endcustomerPackages.map((pkg) => (
<TableCell key={pkg.id} className="text-center">
{pkg.watermark_allowed === false ? <Check className="w-4 h-4 text-green-500" /> : <X className="w-4 h-4 text-red-500" />}
</TableCell>
))}
</TableRow>
</TableBody>
</Table>
</div>
</div>
</section>
<section className="py-20 px-4 bg-gray-50">
<div className="container mx-auto">
<h2 className="text-3xl font-bold text-center mb-12 font-display">Für Reseller</h2>
{/* Mobile Carousel for Reseller Packages */}
<div className="block md:hidden">
<Carousel className="w-full max-w-md mx-auto">
<CarouselContent className="-ml-1">
{resellerPackages.map((pkg) => (
<CarouselItem key={pkg.id} className="pl-1 basis-full">
<div
className="bg-white p-6 rounded-lg shadow-md border hover:shadow-lg transition-all duration-300"
>
<div className="text-center mb-4">
<ShoppingCart className="w-12 h-12 text-[#FFD700] mx-auto" />
</div>
<h3 className="text-xl font-semibold mb-2 font-sans-marketing">{pkg.name}</h3>
<p className="text-gray-600 mb-4 font-serif-custom">{pkg.description}</p>
<p className="text-2xl font-bold text-[#FFD700] mb-4 font-sans-marketing">
{pkg.price} / Jahr
</p>
<ul className="text-sm text-gray-600 mb-6 space-y-1 font-sans-marketing">
{pkg.features.map((feature, index) => (
<li key={index} className="flex items-center">
{getFeatureIcon(feature)} {feature}
</li>
))}
{pkg.limits?.max_tenants && <li> Max. {pkg.limits.max_tenants} Tenants</li>}
{pkg.limits?.max_events && <li> Max. {pkg.limits.max_events} Events/Jahr</li>}
{pkg.branding_allowed && <li><Badge variant="secondary">Custom Branding</Badge></li>}
</ul>
<Button
variant="outline"
onClick={() => handleCardClick(pkg)}
className="w-full mt-4 font-sans-marketing"
>
Details anzeigen
</Button>
</div>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
</div>
{/* Desktop Grid for Reseller Packages */}
<div className="hidden md:block">
<div className="grid md:grid-cols-2 gap-8">
{resellerPackages.map((pkg) => (
<div
key={pkg.id}
className="bg-white p-6 rounded-lg shadow-md border hover:shadow-lg transition-all duration-300"
>
<div className="text-center mb-4">
<ShoppingCart className="w-12 h-12 text-[#FFD700] mx-auto" />
</div>
<h3 className="text-xl font-semibold mb-2 font-sans-marketing">{pkg.name}</h3>
<p className="text-gray-600 mb-4 font-serif-custom">{pkg.description}</p>
<p className="text-2xl font-bold text-[#FFD700] mb-4 font-sans-marketing">
{pkg.price} / Jahr
</p>
<ul className="text-sm text-gray-600 mb-6 space-y-1 font-sans-marketing">
{pkg.features.map((feature, index) => (
<li key={index} className="flex items-center">
{getFeatureIcon(feature)} {feature}
</li>
))}
{pkg.limits?.max_tenants && <li> Max. {pkg.limits.max_tenants} Tenants</li>}
{pkg.limits?.max_events && <li> Max. {pkg.limits.max_events} Events/Jahr</li>}
{pkg.branding_allowed && <li><Badge variant="secondary">Custom Branding</Badge></li>}
</ul>
<Button
variant="outline"
onClick={() => handleCardClick(pkg)}
className="w-full mt-4 font-sans-marketing"
>
Details anzeigen
</Button>
</div>
))}
</div>
</div>
</div>
</section>
{/* FAQ Section */}
<section className="py-20 px-4">
<div className="container mx-auto">
<h2 className="text-3xl font-bold text-center mb-12 font-display">Häufige Fragen</h2>
<div className="grid md:grid-cols-2 gap-8">
<div className="bg-white p-6 rounded-lg shadow-md">
<h3 className="text-xl font-semibold mb-2 font-display">Was ist das Free-Paket?</h3>
<p className="text-gray-600 font-sans-marketing">Ideal für Tests: 30 Fotos, 7 Tage Galerie, mit Watermark.</p>
</div>
<div className="bg-white p-6 rounded-lg shadow-md">
<h3 className="text-xl font-semibold mb-2 font-display">Kann ich upgraden?</h3>
<p className="text-gray-600 font-sans-marketing">Ja, jederzeit im Dashboard Limits werden sofort erweitert.</p>
</div>
<div className="bg-white p-6 rounded-lg shadow-md">
<h3 className="text-xl font-semibold mb-2 font-display">Was für Reseller?</h3>
<p className="text-gray-600 font-sans-marketing">Jährliche Subscriptions mit Dashboard, Branding und Support.</p>
</div>
<div className="bg-white p-6 rounded-lg shadow-md">
<h3 className="text-xl font-semibold mb-2 font-display">Zahlungssicher?</h3>
<p className="text-gray-600 font-sans-marketing">Sichere Zahlung via Stripe/PayPal, 14 Tage Rückgaberecht.</p>
</div>
</div>
</div>
</section>
{/* Modal */}
{selectedPackage && (
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle className="text-2xl font-display">{selectedPackage.name} - Details</DialogTitle>
</DialogHeader>
<Tabs value={currentStep} onValueChange={setCurrentStep} className="w-full">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="step1">Details</TabsTrigger>
<TabsTrigger value="step2">Kundenmeinungen</TabsTrigger>
</TabsList>
<TabsContent value="step1" className="mt-4">
<div className="space-y-4">
<div className="text-center">
<h2 className="text-3xl font-bold font-display">{selectedPackage.name}</h2>
<p className="text-2xl font-bold text-[#FFB6C1] mt-2">
{selectedPackage.price === 0 ? 'Kostenlos' : `${selectedPackage.price}`}
</p>
</div>
<p className="text-gray-600 font-sans-marketing">{selectedPackage.description}</p>
<div className="grid grid-cols-2 gap-4">
{selectedPackage.features.map((feature, index) => (
<Badge key={`feature-${index}`} variant="secondary" className="flex items-center justify-center gap-1">
{getFeatureIcon(feature)} {feature}
</Badge>
))}
{selectedPackage.limits?.max_photos && (
<Badge variant="outline" className="flex items-center justify-center gap-1">
<Image className="w-4 h-4" /> Max. {selectedPackage.limits.max_photos} Fotos
</Badge>
)}
{selectedPackage.limits?.max_guests && (
<Badge variant="outline" className="flex items-center justify-center gap-1">
<Users className="w-4 h-4" /> Max. {selectedPackage.limits.max_guests} Gäste
</Badge>
)}
{selectedPackage.limits?.gallery_days && (
<Badge variant="outline" className="flex items-center justify-center gap-1">
<Calendar className="w-4 h-4" /> {selectedPackage.limits.gallery_days} Tage Galerie
</Badge>
)}
{selectedPackage.watermark_allowed === false && (
<Badge variant="secondary" className="flex items-center justify-center gap-1">
<Shield className="w-4 h-4" /> Kein Watermark
</Badge>
)}
{selectedPackage.branding_allowed && (
<Badge variant="secondary" className="flex items-center justify-center gap-1">
<Image className="w-4 h-4" /> Custom Branding
</Badge>
)}
</div>
<div className="text-center">
{auth.user ? (
<Link
href={`/buy-packages/${selectedPackage.id}`}
className="w-full block bg-[#FFB6C1] text-white py-3 rounded-md font-semibold font-sans-marketing hover:bg-[#FF69B4] transition text-center"
>
Zur Bestellung
</Link>
) : (
<Link
href={`/register?package_id=${selectedPackage.id}`}
className="w-full block bg-[#FFB6C1] text-white py-3 rounded-md font-semibold font-sans-marketing hover:bg-[#FF69B4] transition text-center"
onClick={() => {
localStorage.setItem('preferred_package', JSON.stringify(selectedPackage));
}}
>
Zur Bestellung
</Link>
)}
</div>
</div>
</TabsContent>
<TabsContent value="step2" className="mt-4">
<div className="space-y-4">
<h3 className="text-xl font-semibold mb-4 font-display">Was Kunden sagen</h3>
<div className="grid md:grid-cols-3 gap-4">
{testimonials.map((testimonial, index) => (
<div key={index} className="bg-white p-4 rounded-lg shadow-md">
<p className="text-gray-600 font-sans-marketing mb-2">"{testimonial.text}"</p>
<p className="font-semibold font-sans-marketing">{testimonial.name}</p>
<div className="flex">
{[...Array(testimonial.rating)].map((_, i) => <Star key={i} className="w-4 h-4 text-yellow-400 fill-current" />)}
</div>
</div>
))}
</div>
<button onClick={() => setOpen(false)} className="w-full mt-4 text-gray-500 underline">
Schließen
</button>
</div>
</TabsContent>
</Tabs>
</DialogContent>
</Dialog>
)}
{/* Testimonials Section entfernt, da nun im Dialog */}
</MarketingLayout>
);
};
export default Packages;