Add order CTA links on packages overview
This commit is contained in:
@@ -673,6 +673,7 @@ interface PackageCardProps {
|
|||||||
variant: 'endcustomer' | 'reseller';
|
variant: 'endcustomer' | 'reseller';
|
||||||
highlight?: boolean;
|
highlight?: boolean;
|
||||||
onSelect?: (pkg: Package) => void;
|
onSelect?: (pkg: Package) => void;
|
||||||
|
onCtaClick?: (pkg: Package, variant: 'endcustomer' | 'reseller') => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
showCTA?: boolean;
|
showCTA?: boolean;
|
||||||
ctaLabel?: string;
|
ctaLabel?: string;
|
||||||
@@ -684,6 +685,7 @@ function PackageCard({
|
|||||||
variant,
|
variant,
|
||||||
highlight = false,
|
highlight = false,
|
||||||
onSelect,
|
onSelect,
|
||||||
|
onCtaClick,
|
||||||
className,
|
className,
|
||||||
showCTA = true,
|
showCTA = true,
|
||||||
ctaLabel,
|
ctaLabel,
|
||||||
@@ -691,9 +693,12 @@ function PackageCard({
|
|||||||
}: PackageCardProps) {
|
}: PackageCardProps) {
|
||||||
const { t } = useTranslation('marketing');
|
const { t } = useTranslation('marketing');
|
||||||
const { t: tCommon } = useTranslation('common');
|
const { t: tCommon } = useTranslation('common');
|
||||||
|
const { localizedPath } = useLocalizedRoutes();
|
||||||
|
|
||||||
const accent = getAccentTheme(variant);
|
const accent = getAccentTheme(variant);
|
||||||
|
|
||||||
|
const purchaseUrl = localizedPath(`/bestellen/${pkg.id}`);
|
||||||
|
|
||||||
const numericPrice = Number(pkg.price);
|
const numericPrice = Number(pkg.price);
|
||||||
const priceLabel =
|
const priceLabel =
|
||||||
numericPrice === 0
|
numericPrice === 0
|
||||||
@@ -805,19 +810,42 @@ function PackageCard({
|
|||||||
{featureList}
|
{featureList}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
{showCTA && onSelect && (
|
{showCTA && onSelect && (
|
||||||
<CardFooter className={cn('mt-auto', compact && 'pt-4')}>
|
<CardFooter className={cn('mt-auto', compact && 'pt-4')}>
|
||||||
<Button
|
<div className="grid w-full grid-cols-[2fr_1fr] gap-2">
|
||||||
onClick={() => onSelect(pkg)}
|
<Button
|
||||||
className={cn(
|
asChild
|
||||||
'w-full justify-center rounded-full text-sm font-semibold',
|
className={cn(
|
||||||
highlight ? accent.buttonHighlight : accent.buttonDefault,
|
'w-full justify-center rounded-full text-sm font-semibold',
|
||||||
compact && 'py-4 text-base',
|
highlight
|
||||||
)}
|
? accent.buttonHighlight
|
||||||
variant={highlight ? 'default' : 'outline'}
|
: 'bg-gray-900 text-white hover:bg-gray-800 dark:bg-gray-100 dark:text-gray-900 dark:hover:bg-white',
|
||||||
>
|
compact && 'py-4 text-base',
|
||||||
{ctaLabel ?? t('packages.view_details')}
|
)}
|
||||||
<ArrowRight className="h-4 w-4" aria-hidden />
|
variant="default"
|
||||||
</Button>
|
>
|
||||||
|
<Link
|
||||||
|
href={purchaseUrl}
|
||||||
|
onClick={() => {
|
||||||
|
onCtaClick?.(pkg, variant);
|
||||||
|
localStorage.setItem('preferred_package', JSON.stringify(pkg));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('packages.to_order')}
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => onSelect(pkg)}
|
||||||
|
className={cn(
|
||||||
|
'w-full justify-center rounded-full text-sm font-semibold',
|
||||||
|
accent.buttonDefault,
|
||||||
|
compact && 'py-4 text-base',
|
||||||
|
)}
|
||||||
|
variant="outline"
|
||||||
|
>
|
||||||
|
{ctaLabel ?? t('packages.view_details')}
|
||||||
|
<ArrowRight className="h-4 w-4" aria-hidden />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
@@ -1072,6 +1100,7 @@ const PackageDetailGrid: React.FC<PackageDetailGridProps> = ({
|
|||||||
variant="endcustomer"
|
variant="endcustomer"
|
||||||
highlight={pkg.id === highlightEndcustomerId}
|
highlight={pkg.id === highlightEndcustomerId}
|
||||||
onSelect={(selected) => handleCardClick(selected, 'endcustomer')}
|
onSelect={(selected) => handleCardClick(selected, 'endcustomer')}
|
||||||
|
onCtaClick={handleCtaClick}
|
||||||
className="h-full"
|
className="h-full"
|
||||||
compact
|
compact
|
||||||
/>
|
/>
|
||||||
@@ -1095,6 +1124,7 @@ const PackageDetailGrid: React.FC<PackageDetailGridProps> = ({
|
|||||||
variant="endcustomer"
|
variant="endcustomer"
|
||||||
highlight={pkg.id === highlightEndcustomerId}
|
highlight={pkg.id === highlightEndcustomerId}
|
||||||
onSelect={(selected) => handleCardClick(selected, 'endcustomer')}
|
onSelect={(selected) => handleCardClick(selected, 'endcustomer')}
|
||||||
|
onCtaClick={handleCtaClick}
|
||||||
className="h-full"
|
className="h-full"
|
||||||
compact
|
compact
|
||||||
/>
|
/>
|
||||||
@@ -1124,6 +1154,7 @@ const PackageDetailGrid: React.FC<PackageDetailGridProps> = ({
|
|||||||
variant="reseller"
|
variant="reseller"
|
||||||
highlight={pkg.id === highlightResellerId}
|
highlight={pkg.id === highlightResellerId}
|
||||||
onSelect={(selected) => handleCardClick(selected, 'reseller')}
|
onSelect={(selected) => handleCardClick(selected, 'reseller')}
|
||||||
|
onCtaClick={handleCtaClick}
|
||||||
className="h-full"
|
className="h-full"
|
||||||
compact
|
compact
|
||||||
/>
|
/>
|
||||||
@@ -1147,6 +1178,7 @@ const PackageDetailGrid: React.FC<PackageDetailGridProps> = ({
|
|||||||
variant="reseller"
|
variant="reseller"
|
||||||
highlight={pkg.id === highlightResellerId}
|
highlight={pkg.id === highlightResellerId}
|
||||||
onSelect={(selected) => handleCardClick(selected, 'reseller')}
|
onSelect={(selected) => handleCardClick(selected, 'reseller')}
|
||||||
|
onCtaClick={handleCtaClick}
|
||||||
className="h-full"
|
className="h-full"
|
||||||
compact
|
compact
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -71,6 +71,7 @@
|
|||||||
"gallery_days": "Tage Galerie",
|
"gallery_days": "Tage Galerie",
|
||||||
"max_events_year": "Events enthalten",
|
"max_events_year": "Events enthalten",
|
||||||
"recommended_usage_window": "Empfohlen innerhalb von 24 Monaten zu nutzen.",
|
"recommended_usage_window": "Empfohlen innerhalb von 24 Monaten zu nutzen.",
|
||||||
|
"to_order": "Bestellen",
|
||||||
"buy_now": "Jetzt kaufen",
|
"buy_now": "Jetzt kaufen",
|
||||||
"subscribe_now": "Jetzt kaufen",
|
"subscribe_now": "Jetzt kaufen",
|
||||||
"register_buy": "Registrieren und kaufen",
|
"register_buy": "Registrieren und kaufen",
|
||||||
|
|||||||
@@ -71,6 +71,7 @@
|
|||||||
"gallery_days": "Gallery Days",
|
"gallery_days": "Gallery Days",
|
||||||
"max_events_year": "Events included",
|
"max_events_year": "Events included",
|
||||||
"recommended_usage_window": "Recommended to use within 24 months.",
|
"recommended_usage_window": "Recommended to use within 24 months.",
|
||||||
|
"to_order": "Order now",
|
||||||
"buy_now": "Buy Now",
|
"buy_now": "Buy Now",
|
||||||
"subscribe_now": "Buy Now",
|
"subscribe_now": "Buy Now",
|
||||||
"register_buy": "Register and Buy",
|
"register_buy": "Register and Buy",
|
||||||
|
|||||||
28
tests/Feature/Marketing/PackagesPageTest.php
Normal file
28
tests/Feature/Marketing/PackagesPageTest.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature\Marketing;
|
||||||
|
|
||||||
|
use App\Models\Package;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Inertia\Testing\AssertableInertia as Assert;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class PackagesPageTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
public function test_packages_page_renders_available_packages(): void
|
||||||
|
{
|
||||||
|
Package::factory()->count(2)->endcustomer()->create();
|
||||||
|
Package::factory()->count(1)->reseller()->create();
|
||||||
|
|
||||||
|
$response = $this->get('/de/packages');
|
||||||
|
|
||||||
|
$response->assertOk();
|
||||||
|
$response->assertInertia(fn (Assert $page) => $page
|
||||||
|
->component('marketing/Packages')
|
||||||
|
->has('endcustomerPackages', 2)
|
||||||
|
->has('resellerPackages', 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user