event photo wasserzeichen umgesetzt. Event admins können eigene einsetzen (als branding) falls das Paket es erlaubt. der Super Admin kann für die günstigen Pakete eigene Wasserzeichen erzwingen
This commit is contained in:
@@ -14,7 +14,7 @@ import MarketingLayout from '@/layouts/mainWebsite';
|
||||
import { useAnalytics } from '@/hooks/useAnalytics';
|
||||
import { useCtaExperiment } from '@/hooks/useCtaExperiment';
|
||||
import { useLocalizedRoutes } from '@/hooks/useLocalizedRoutes';
|
||||
import { ArrowRight, Check, Shield, Star, Sparkles } from 'lucide-react';
|
||||
import { ArrowRight, Check, Star } from 'lucide-react';
|
||||
|
||||
interface Package {
|
||||
id: number;
|
||||
@@ -46,6 +46,39 @@ interface PackageComparisonProps {
|
||||
variant: 'endcustomer' | 'reseller';
|
||||
}
|
||||
|
||||
const buildDisplayFeatures = (pkg: Package): string[] => {
|
||||
const features = [...pkg.features];
|
||||
|
||||
const removeFeature = (key: string) => {
|
||||
const index = features.indexOf(key);
|
||||
if (index !== -1) {
|
||||
features.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
const addFeature = (key: string) => {
|
||||
if (!features.includes(key)) {
|
||||
features.push(key);
|
||||
}
|
||||
};
|
||||
|
||||
if (pkg.watermark_allowed === false) {
|
||||
removeFeature('watermark');
|
||||
addFeature('no_watermark');
|
||||
} else {
|
||||
removeFeature('no_watermark');
|
||||
addFeature('watermark');
|
||||
}
|
||||
|
||||
if (pkg.branding_allowed) {
|
||||
addFeature('custom_branding');
|
||||
} else {
|
||||
removeFeature('custom_branding');
|
||||
}
|
||||
|
||||
return Array.from(new Set(features));
|
||||
};
|
||||
|
||||
function PackageComparison({ packages, variant }: PackageComparisonProps) {
|
||||
const { t } = useTranslation('marketing');
|
||||
const { t: tCommon } = useTranslation('common');
|
||||
@@ -547,7 +580,9 @@ function PackageCard({
|
||||
? t('packages.badge_starter')
|
||||
: null;
|
||||
|
||||
const keyFeatures = pkg.features.slice(0, 3);
|
||||
const displayFeatures = buildDisplayFeatures(pkg);
|
||||
const keyFeatures = displayFeatures.slice(0, 3);
|
||||
const visibleFeatures = compact ? displayFeatures.slice(0, 3) : displayFeatures.slice(0, 5);
|
||||
const metrics = resolvePackageMetrics(pkg, variant, t, tCommon);
|
||||
|
||||
const metricList = compact ? (
|
||||
@@ -572,45 +607,21 @@ function PackageCard({
|
||||
|
||||
const featureList = compact ? (
|
||||
<ul className="space-y-1 text-sm text-gray-700">
|
||||
{keyFeatures.map((feature) => (
|
||||
{visibleFeatures.map((feature) => (
|
||||
<li key={feature} className="flex items-start gap-2 text-xs">
|
||||
<Check className="mt-0.5 h-3.5 w-3.5 text-gray-900" />
|
||||
<span>{t(`packages.feature_${feature}`)}</span>
|
||||
</li>
|
||||
))}
|
||||
{pkg.watermark_allowed === false && (
|
||||
<li className="flex items-start gap-2 text-xs">
|
||||
<Shield className="mt-0.5 h-3.5 w-3.5 text-gray-900" />
|
||||
<span>{t('packages.no_watermark')}</span>
|
||||
</li>
|
||||
)}
|
||||
{pkg.branding_allowed && (
|
||||
<li className="flex items-start gap-2 text-xs">
|
||||
<Sparkles className="mt-0.5 h-3.5 w-3.5 text-gray-900" />
|
||||
<span>{t('packages.custom_branding')}</span>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
) : (
|
||||
<ul className="space-y-2 text-sm text-gray-700">
|
||||
{keyFeatures.map((feature) => (
|
||||
{visibleFeatures.map((feature) => (
|
||||
<li key={feature} className="flex items-center gap-2">
|
||||
<Check className="h-4 w-4 text-gray-900" />
|
||||
<span>{t(`packages.feature_${feature}`)}</span>
|
||||
</li>
|
||||
))}
|
||||
{pkg.watermark_allowed === false && (
|
||||
<li className="flex items-center gap-2">
|
||||
<Shield className="h-4 w-4 text-gray-900" />
|
||||
<span>{t('packages.no_watermark')}</span>
|
||||
</li>
|
||||
)}
|
||||
{pkg.branding_allowed && (
|
||||
<li className="flex items-center gap-2">
|
||||
<Sparkles className="h-4 w-4 text-gray-900" />
|
||||
<span>{t('packages.custom_branding')}</span>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
);
|
||||
|
||||
@@ -703,6 +714,10 @@ const PackageDetailGrid: React.FC<PackageDetailGridProps> = ({
|
||||
close,
|
||||
}) => {
|
||||
const metrics = resolvePackageMetrics(packageData, variant, t, tCommon);
|
||||
const highlightFeatures = useMemo(
|
||||
() => buildDisplayFeatures(packageData).slice(0, 5),
|
||||
[packageData],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="grid gap-8 lg:grid-cols-[320px,1fr]">
|
||||
@@ -757,24 +772,12 @@ const PackageDetailGrid: React.FC<PackageDetailGridProps> = ({
|
||||
<div className="rounded-2xl border border-gray-100 bg-white p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900">{t('packages.feature_highlights')}</h3>
|
||||
<ul className="mt-4 space-y-3 text-sm text-gray-700">
|
||||
{packageData.features.slice(0, 5).map((feature) => (
|
||||
{highlightFeatures.map((feature) => (
|
||||
<li key={feature} className="flex items-start gap-2">
|
||||
<Check className="mt-1 h-4 w-4 text-gray-900" />
|
||||
<span>{t(`packages.feature_${feature}`)}</span>
|
||||
</li>
|
||||
))}
|
||||
{packageData.watermark_allowed === false && (
|
||||
<li className="flex items-start gap-2">
|
||||
<Shield className="mt-1 h-4 w-4 text-gray-900" />
|
||||
<span>{t('packages.no_watermark')}</span>
|
||||
</li>
|
||||
)}
|
||||
{packageData.branding_allowed && (
|
||||
<li className="flex items-start gap-2">
|
||||
<Sparkles className="mt-1 h-4 w-4 text-gray-900" />
|
||||
<span>{t('packages.custom_branding')}</span>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="rounded-2xl border border-gray-100 bg-white p-6">
|
||||
|
||||
20
resources/lang/de/filament-watermark.php
Normal file
20
resources/lang/de/filament-watermark.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'heading' => 'Wasserzeichen',
|
||||
'description' => 'Branding- und Wasserzeichen-Optionen für dieses Event.',
|
||||
'mode' => [
|
||||
'label' => 'Wasserzeichen-Modus',
|
||||
'base' => 'Standard-Fotospiel-Wasserzeichen',
|
||||
'custom' => 'Eigenes Wasserzeichen (Logo)',
|
||||
'off' => 'Kein Wasserzeichen',
|
||||
],
|
||||
'asset' => 'Logo/Wasserzeichen-Datei',
|
||||
'position' => 'Position',
|
||||
'opacity' => 'Deckkraft (0-1)',
|
||||
'scale' => 'Skalierung (0-1)',
|
||||
'padding' => 'Abstand (px)',
|
||||
'serve_originals' => 'Originale statt watermarked ausliefern (nur Admin/Owner)',
|
||||
'save' => 'Speichern',
|
||||
'saved' => 'Branding/Wasserzeichen gespeichert.',
|
||||
];
|
||||
20
resources/lang/en/filament-watermark.php
Normal file
20
resources/lang/en/filament-watermark.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'heading' => 'Watermark',
|
||||
'description' => 'Branding and watermark options for this event.',
|
||||
'mode' => [
|
||||
'label' => 'Watermark mode',
|
||||
'base' => 'Standard Fotospiel watermark',
|
||||
'custom' => 'Custom watermark (logo)',
|
||||
'off' => 'No watermark',
|
||||
],
|
||||
'asset' => 'Logo/Watermark file',
|
||||
'position' => 'Position',
|
||||
'opacity' => 'Opacity (0-1)',
|
||||
'scale' => 'Scale (0-1)',
|
||||
'padding' => 'Padding (px)',
|
||||
'serve_originals' => 'Serve originals instead of watermarked (admin/owner only)',
|
||||
'save' => 'Save',
|
||||
'saved' => 'Branding/Watermark saved.',
|
||||
];
|
||||
@@ -0,0 +1,13 @@
|
||||
<x-filament::page>
|
||||
<div class="space-y-6">
|
||||
<div class="text-sm text-gray-600 dark:text-gray-300">
|
||||
{{ __('filament-watermark.description') }}
|
||||
</div>
|
||||
{{ $this->form }}
|
||||
<div class="mt-4 flex justify-end">
|
||||
<x-filament::button wire:click="save" color="primary">
|
||||
{{ __('filament-watermark.save') }}
|
||||
</x-filament::button>
|
||||
</div>
|
||||
</div>
|
||||
</x-filament::page>
|
||||
@@ -0,0 +1,22 @@
|
||||
@php
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
@endphp
|
||||
|
||||
<x-filament::page>
|
||||
<div class="space-y-6">
|
||||
<div class="rounded-xl bg-white p-6 shadow-sm">
|
||||
{{ $this->form }}
|
||||
<div class="mt-4 flex justify-end">
|
||||
<x-filament::button wire:click="save" color="primary">
|
||||
Speichern
|
||||
</x-filament::button>
|
||||
</div>
|
||||
</div>
|
||||
@if($asset)
|
||||
<div class="rounded-xl bg-white p-4 shadow-sm">
|
||||
<p class="text-sm font-medium mb-2">Aktuelles Basis-Wasserzeichen</p>
|
||||
<img src="{{ Storage::disk('public')->url($asset) }}" alt="Watermark" class="max-h-32 object-contain">
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-filament::page>
|
||||
Reference in New Issue
Block a user