feat(addons): finalize event addon catalog and ai styling upgrade flow
This commit is contained in:
@@ -5,14 +5,20 @@ namespace App\Filament\Resources;
|
||||
use App\Filament\Clusters\WeeklyOps\WeeklyOpsCluster;
|
||||
use App\Filament\Resources\PackageAddonResource\Pages;
|
||||
use App\Jobs\SyncPackageAddonToLemonSqueezy;
|
||||
use App\Models\CheckoutSession;
|
||||
use App\Models\PackageAddon;
|
||||
use App\Services\Audit\SuperAdminAuditLogger;
|
||||
use Filament\Actions;
|
||||
use Filament\Forms\Components\DateTimePicker;
|
||||
use Filament\Forms\Components\Placeholder;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TagsInput;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Toggle;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Filament\Schemas\Components\Utilities\Get;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Columns\BadgeColumn;
|
||||
@@ -53,7 +59,15 @@ class PackageAddonResource extends Resource
|
||||
TextInput::make('variant_id')
|
||||
->label('Lemon Squeezy Variant-ID')
|
||||
->helperText('Variant-ID aus Lemon Squeezy für dieses Add-on')
|
||||
->required(fn (Get $get): bool => (bool) $get('active') && ! is_numeric($get('metadata.price_eur')))
|
||||
->maxLength(191),
|
||||
TextInput::make('metadata.price_eur')
|
||||
->label('PayPal Preis (EUR)')
|
||||
->helperText('Für PayPal-Checkout erforderlich (z. B. 9.90).')
|
||||
->numeric()
|
||||
->step(0.01)
|
||||
->minValue(0.01)
|
||||
->required(fn (Get $get): bool => (bool) $get('active') && blank($get('variant_id'))),
|
||||
TextInput::make('sort')
|
||||
->label('Sortierung')
|
||||
->numeric()
|
||||
@@ -61,6 +75,23 @@ class PackageAddonResource extends Resource
|
||||
Toggle::make('active')
|
||||
->label('Aktiv')
|
||||
->default(true),
|
||||
Placeholder::make('sellable_state')
|
||||
->label('Verfügbarkeits-Check')
|
||||
->content(function (Get $get): string {
|
||||
$isActive = (bool) $get('active');
|
||||
$hasVariant = filled($get('variant_id'));
|
||||
$hasPayPalPrice = is_numeric($get('metadata.price_eur'));
|
||||
|
||||
if (! $isActive) {
|
||||
return 'Inaktiv';
|
||||
}
|
||||
|
||||
if (! $hasVariant && ! $hasPayPalPrice) {
|
||||
return 'Nicht verkäuflich: Variant-ID oder PayPal Preis fehlt.';
|
||||
}
|
||||
|
||||
return 'Verkäuflich';
|
||||
}),
|
||||
]),
|
||||
Section::make('Limits-Inkremente')
|
||||
->columns(3)
|
||||
@@ -81,6 +112,30 @@ class PackageAddonResource extends Resource
|
||||
->minValue(0)
|
||||
->default(0),
|
||||
]),
|
||||
Section::make('Feature-Entitlements')
|
||||
->columns(2)
|
||||
->schema([
|
||||
Select::make('metadata.scope')
|
||||
->label('Scope')
|
||||
->options([
|
||||
'photos' => 'Fotos',
|
||||
'guests' => 'Gäste',
|
||||
'gallery' => 'Galerie',
|
||||
'feature' => 'Feature',
|
||||
'bundle' => 'Bundle',
|
||||
])
|
||||
->native(false)
|
||||
->searchable(),
|
||||
TagsInput::make('metadata.entitlements.features')
|
||||
->label('Freigeschaltete Features')
|
||||
->helperText('Feature-Keys für Freischaltungen, z. B. ai_styling')
|
||||
->placeholder('z. B. ai_styling')
|
||||
->columnSpanFull(),
|
||||
DateTimePicker::make('metadata.entitlements.expires_at')
|
||||
->label('Entitlement gültig bis')
|
||||
->seconds(false)
|
||||
->nullable(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -100,6 +155,29 @@ class PackageAddonResource extends Resource
|
||||
->label('Lemon Squeezy Variant-ID')
|
||||
->toggleable()
|
||||
->copyable(),
|
||||
TextColumn::make('metadata.price_eur')
|
||||
->label('PayPal Preis (EUR)')
|
||||
->formatStateUsing(fn (mixed $state): string => is_numeric($state) ? number_format((float) $state, 2, ',', '.').' €' : '—')
|
||||
->toggleable(),
|
||||
TextColumn::make('metadata.scope')
|
||||
->label('Scope')
|
||||
->badge()
|
||||
->toggleable(),
|
||||
TextColumn::make('metadata.entitlements.features')
|
||||
->label('Features')
|
||||
->formatStateUsing(function (mixed $state): string {
|
||||
if (! is_array($state)) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
$features = array_values(array_filter(array_map(
|
||||
static fn (mixed $feature): string => trim((string) $feature),
|
||||
$state,
|
||||
)));
|
||||
|
||||
return $features === [] ? '—' : implode(', ', $features);
|
||||
})
|
||||
->toggleable(),
|
||||
TextColumn::make('extra_photos')->label('Fotos +'),
|
||||
TextColumn::make('extra_guests')->label('Gäste +'),
|
||||
TextColumn::make('extra_gallery_days')->label('Galerietage +'),
|
||||
@@ -110,6 +188,14 @@ class PackageAddonResource extends Resource
|
||||
'danger' => false,
|
||||
])
|
||||
->formatStateUsing(fn (bool $state) => $state ? 'Aktiv' : 'Inaktiv'),
|
||||
BadgeColumn::make('sellability')
|
||||
->label('Checkout')
|
||||
->state(fn (PackageAddon $record): string => static::sellabilityLabel($record))
|
||||
->colors([
|
||||
'success' => fn (string $state): bool => $state === 'Verkäuflich',
|
||||
'warning' => fn (string $state): bool => $state === 'Unvollständig',
|
||||
'gray' => fn (string $state): bool => $state === 'Inaktiv',
|
||||
]),
|
||||
TextColumn::make('sort')
|
||||
->label('Sort')
|
||||
->sortable()
|
||||
@@ -166,4 +252,21 @@ class PackageAddonResource extends Resource
|
||||
'edit' => Pages\EditPackageAddon::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
|
||||
protected static function sellabilityLabel(PackageAddon $record): string
|
||||
{
|
||||
if (! $record->active) {
|
||||
return 'Inaktiv';
|
||||
}
|
||||
|
||||
return $record->isSellableForProvider(static::addonProvider()) ? 'Verkäuflich' : 'Unvollständig';
|
||||
}
|
||||
|
||||
protected static function addonProvider(): string
|
||||
{
|
||||
return (string) (
|
||||
config('package-addons.provider')
|
||||
?? config('checkout.default_provider', CheckoutSession::PROVIDER_PAYPAL)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user