feat(packages): implement package-based business model
This commit is contained in:
@@ -15,12 +15,14 @@ use Filament\Forms\Form;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\DatePicker;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\Toggle;
|
||||
use Filament\Forms\Components\KeyValue;
|
||||
use Filament\Forms\Components\Select;
|
||||
use UnitEnum;
|
||||
use BackedEnum;
|
||||
|
||||
use App\Filament\Resources\EventResource\RelationManagers\EventPackagesRelationManager;
|
||||
|
||||
class EventResource extends Resource
|
||||
{
|
||||
protected static ?string $model = Event::class;
|
||||
@@ -57,6 +59,12 @@ class EventResource extends Resource
|
||||
->label(__('admin.events.fields.type'))
|
||||
->options(EventType::all()->pluck('name', 'id'))
|
||||
->searchable(),
|
||||
Select::make('package_id')
|
||||
->label(__('admin.events.fields.package'))
|
||||
->options(\App\Models\Package::where('type', 'endcustomer')->pluck('name', 'id'))
|
||||
->searchable()
|
||||
->preload()
|
||||
->required(),
|
||||
TextInput::make('default_locale')
|
||||
->label(__('admin.events.fields.default_locale'))
|
||||
->default('de')
|
||||
@@ -82,6 +90,18 @@ class EventResource extends Resource
|
||||
Tables\Columns\TextColumn::make('date')->date(),
|
||||
Tables\Columns\IconColumn::make('is_active')->boolean(),
|
||||
Tables\Columns\TextColumn::make('default_locale'),
|
||||
Tables\Columns\TextColumn::make('eventPackage.package.name')
|
||||
->label(__('admin.events.table.package'))
|
||||
->badge()
|
||||
->color('success'),
|
||||
Tables\Columns\TextColumn::make('eventPackage.used_photos')
|
||||
->label(__('admin.events.table.used_photos'))
|
||||
->badge(),
|
||||
Tables\Columns\TextColumn::make('eventPackage.remaining_photos')
|
||||
->label(__('admin.events.table.remaining_photos'))
|
||||
->badge()
|
||||
->color(fn ($state) => $state < 1 ? 'danger' : 'success')
|
||||
->getStateUsing(fn ($record) => $record->eventPackage?->remaining_photos ?? 0),
|
||||
Tables\Columns\TextColumn::make('join')->label(__('admin.events.table.join'))
|
||||
->getStateUsing(fn($record) => url("/e/{$record->slug}"))
|
||||
->copyable()
|
||||
@@ -117,4 +137,11 @@ class EventResource extends Resource
|
||||
'edit' => Pages\EditEvent::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
EventPackagesRelationManager::class,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\EventResource\RelationManagers;
|
||||
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\RelationManagers\RelationManager;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Actions\BulkActionGroup;
|
||||
use Filament\Actions\DeleteBulkAction;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use App\Models\EventPackage;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\DateTimePicker;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Schemas\Schema;
|
||||
|
||||
class EventPackagesRelationManager extends RelationManager
|
||||
{
|
||||
protected static string $relationship = 'eventPackages';
|
||||
|
||||
public function form(Schema $schema): Schema
|
||||
{
|
||||
return $schema->schema([
|
||||
Select::make('package_id')
|
||||
->label('Package')
|
||||
->relationship('package', 'name')
|
||||
->searchable()
|
||||
->preload()
|
||||
->required(),
|
||||
TextInput::make('purchased_price')
|
||||
->label('Kaufpreis')
|
||||
->prefix('€')
|
||||
->numeric()
|
||||
->step(0.01)
|
||||
->required(),
|
||||
TextInput::make('used_photos')
|
||||
->label('Verwendete Fotos')
|
||||
->numeric()
|
||||
->default(0)
|
||||
->readOnly(),
|
||||
TextInput::make('used_guests')
|
||||
->label('Verwendete Gäste')
|
||||
->numeric()
|
||||
->default(0)
|
||||
->readOnly(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->recordTitleAttribute('package.name')
|
||||
->columns([
|
||||
TextColumn::make('package.name')
|
||||
->label('Package')
|
||||
->badge()
|
||||
->color('success'),
|
||||
TextColumn::make('used_photos')
|
||||
->label('Verwendete Fotos')
|
||||
->badge(),
|
||||
TextColumn::make('remaining_photos')
|
||||
->label('Verbleibende Fotos')
|
||||
->badge()
|
||||
->color(fn ($state) => $state < 1 ? 'danger' : 'success')
|
||||
->getStateUsing(fn (EventPackage $record) => $record->remaining_photos),
|
||||
TextColumn::make('used_guests')
|
||||
->label('Verwendete Gäste')
|
||||
->badge(),
|
||||
TextColumn::make('remaining_guests')
|
||||
->label('Verbleibende Gäste')
|
||||
->badge()
|
||||
->color(fn ($state) => $state < 1 ? 'danger' : 'success')
|
||||
->getStateUsing(fn (EventPackage $record) => $record->remaining_guests),
|
||||
TextColumn::make('expires_at')
|
||||
->label('Ablauf')
|
||||
->dateTime()
|
||||
->badge()
|
||||
->color(fn ($state) => $state && $state->isPast() ? 'danger' : 'success'),
|
||||
TextColumn::make('purchased_price')
|
||||
->label('Preis')
|
||||
->money('EUR')
|
||||
->sortable(),
|
||||
])
|
||||
->filters([
|
||||
//
|
||||
])
|
||||
->headerActions([
|
||||
CreateAction::make(),
|
||||
])
|
||||
->actions([
|
||||
EditAction::make(),
|
||||
DeleteAction::make(),
|
||||
])
|
||||
->bulkActions([
|
||||
BulkActionGroup::make([
|
||||
DeleteBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
public function getRelationExistenceQuery(
|
||||
Builder $query,
|
||||
string $relationshipName,
|
||||
?string $ownerKeyName,
|
||||
mixed $ownerKeyValue,
|
||||
): Builder {
|
||||
return $query;
|
||||
}
|
||||
|
||||
public static function getTitle(Model $ownerRecord, string $pageClass): string
|
||||
{
|
||||
return __('admin.events.relation_managers.event_packages.title');
|
||||
}
|
||||
|
||||
public function getTableQuery(): Builder | Relation
|
||||
{
|
||||
return parent::getTableQuery()
|
||||
->with('package');
|
||||
}
|
||||
}
|
||||
153
app/Filament/Resources/PackageResource.php
Normal file
153
app/Filament/Resources/PackageResource.php
Normal file
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources;
|
||||
|
||||
use App\Filament\Resources\PackageResource\Pages;
|
||||
use App\Models\Package;
|
||||
use Filament\Forms;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\Toggle;
|
||||
use Filament\Forms\Components\Repeater;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Actions\BulkActionGroup;
|
||||
use Filament\Actions\DeleteBulkAction;
|
||||
use UnitEnum;
|
||||
use BackedEnum;
|
||||
|
||||
class PackageResource extends Resource
|
||||
{
|
||||
protected static ?string $model = Package::class;
|
||||
|
||||
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-cube';
|
||||
|
||||
protected static string|UnitEnum|null $navigationGroup = null;
|
||||
|
||||
protected static ?int $navigationSort = 5;
|
||||
|
||||
public static function form(Schema $schema): Schema
|
||||
{
|
||||
return $schema->schema([
|
||||
TextInput::make('name')
|
||||
->label('Name')
|
||||
->required()
|
||||
->maxLength(255),
|
||||
Select::make('type')
|
||||
->label('Type')
|
||||
->options([
|
||||
'endcustomer' => 'Endcustomer',
|
||||
'reseller' => 'Reseller',
|
||||
])
|
||||
->required(),
|
||||
TextInput::make('price')
|
||||
->label('Price')
|
||||
->prefix('€')
|
||||
->numeric()
|
||||
->step(0.01)
|
||||
->required()
|
||||
->default(0),
|
||||
TextInput::make('max_photos')
|
||||
->label('Max Photos')
|
||||
->numeric()
|
||||
->nullable(),
|
||||
TextInput::make('max_guests')
|
||||
->label('Max Guests')
|
||||
->numeric()
|
||||
->nullable(),
|
||||
TextInput::make('gallery_days')
|
||||
->label('Gallery Days')
|
||||
->numeric()
|
||||
->nullable(),
|
||||
TextInput::make('max_tasks')
|
||||
->label('Max Tasks')
|
||||
->numeric()
|
||||
->nullable(),
|
||||
Toggle::make('watermark_allowed')
|
||||
->label('Watermark Allowed')
|
||||
->default(true),
|
||||
Toggle::make('branding_allowed')
|
||||
->label('Branding Allowed')
|
||||
->default(false),
|
||||
TextInput::make('max_events_per_year')
|
||||
->label('Max Events per Year')
|
||||
->numeric()
|
||||
->nullable(),
|
||||
Repeater::make('features')
|
||||
->label('Features')
|
||||
->schema([
|
||||
TextInput::make('key')
|
||||
->label('Feature Key'),
|
||||
TextInput::make('value')
|
||||
->label('Feature Value'),
|
||||
])
|
||||
->columns(2)
|
||||
->defaultItems(0),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
TextColumn::make('name')
|
||||
->label('Name')
|
||||
->searchable()
|
||||
->sortable(),
|
||||
TextColumn::make('type')
|
||||
->label('Type')
|
||||
->badge()
|
||||
->color(fn (string $state): string => match ($state) {
|
||||
'endcustomer' => 'info',
|
||||
'reseller' => 'warning',
|
||||
default => 'gray',
|
||||
}),
|
||||
TextColumn::make('price')
|
||||
->label('Price')
|
||||
->money('EUR')
|
||||
->sortable(),
|
||||
IconColumn::make('max_photos')
|
||||
->label('Max Photos')
|
||||
->icon('heroicon-o-photo')
|
||||
->color('primary'),
|
||||
TextColumn::make('features')
|
||||
->label('Features')
|
||||
->limit(50),
|
||||
])
|
||||
->filters([
|
||||
//
|
||||
])
|
||||
->actions([
|
||||
EditAction::make(),
|
||||
DeleteAction::make(),
|
||||
])
|
||||
->bulkActions([
|
||||
BulkActionGroup::make([
|
||||
DeleteBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListPackages::route('/'),
|
||||
'create' => Pages\CreatePackage::route('/create'),
|
||||
'edit' => Pages\EditPackage::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,8 @@ use Filament\Forms\Components\KeyValue;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\DateTimePicker;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use App\Filament\Resources\TenantResource\RelationManagers\PurchasesRelationManager;
|
||||
use App\Filament\Resources\TenantResource\RelationManagers\PackagePurchasesRelationManager;
|
||||
use App\Filament\Resources\TenantResource\RelationManagers\TenantPackagesRelationManager;
|
||||
use Filament\Resources\RelationManagers\RelationGroup;
|
||||
use UnitEnum;
|
||||
use BackedEnum;
|
||||
@@ -52,28 +53,23 @@ class TenantResource extends Resource
|
||||
->email()
|
||||
->required()
|
||||
->maxLength(255),
|
||||
TextInput::make('event_credits_balance')
|
||||
->label(__('admin.tenants.fields.event_credits_balance'))
|
||||
->numeric()
|
||||
->default(0),
|
||||
Select::make('subscription_tier')
|
||||
->label(__('admin.tenants.fields.subscription_tier'))
|
||||
->options([
|
||||
'free' => 'Free',
|
||||
'starter' => 'Starter (€4.99/mo)',
|
||||
'pro' => 'Pro (€14.99/mo)',
|
||||
'agency' => 'Agency (€19.99/mo)',
|
||||
'lifetime' => 'Lifetime (€49.99)'
|
||||
])
|
||||
->default('free'),
|
||||
DateTimePicker::make('subscription_expires_at')
|
||||
->label(__('admin.tenants.fields.subscription_expires_at')),
|
||||
TextInput::make('total_revenue')
|
||||
->label(__('admin.tenants.fields.total_revenue'))
|
||||
->prefix('€')
|
||||
->numeric()
|
||||
->step(0.01)
|
||||
->readOnly(),
|
||||
Select::make('active_reseller_package_id')
|
||||
->label(__('admin.tenants.fields.active_reseller_package'))
|
||||
->relationship('activeResellerPackage', 'name')
|
||||
->searchable()
|
||||
->preload()
|
||||
->nullable(),
|
||||
TextInput::make('remaining_events')
|
||||
->label(__('admin.tenants.fields.remaining_events'))
|
||||
->readOnly()
|
||||
->dehydrated(false)
|
||||
->getStateUsing(fn (Tenant $record) => $record->activeResellerPackage?->remaining_events ?? 0),
|
||||
Toggle::make('is_active')
|
||||
->label(__('admin.tenants.fields.is_active'))
|
||||
->default(true),
|
||||
@@ -95,19 +91,21 @@ class TenantResource extends Resource
|
||||
Tables\Columns\TextColumn::make('name')->searchable()->sortable(),
|
||||
Tables\Columns\TextColumn::make('slug')->searchable(),
|
||||
Tables\Columns\TextColumn::make('contact_email'),
|
||||
Tables\Columns\TextColumn::make('event_credits_balance')
|
||||
->label(__('admin.common.credits'))
|
||||
Tables\Columns\TextColumn::make('activeResellerPackage.name')
|
||||
->label(__('admin.tenants.fields.active_package'))
|
||||
->badge()
|
||||
->color(fn ($state) => $state < 5 ? 'warning' : 'success'),
|
||||
Tables\Columns\TextColumn::make('subscription_tier')
|
||||
->color('success'),
|
||||
Tables\Columns\TextColumn::make('remaining_events')
|
||||
->label(__('admin.tenants.fields.remaining_events'))
|
||||
->badge()
|
||||
->color(fn (string $state): string => match($state) {
|
||||
'free' => 'gray',
|
||||
'starter' => 'info',
|
||||
'pro' => 'success',
|
||||
'agency' => 'warning',
|
||||
'lifetime' => 'danger',
|
||||
}),
|
||||
->color(fn ($state) => $state < 1 ? 'danger' : 'success')
|
||||
->getStateUsing(fn (Tenant $record) => $record->activeResellerPackage?->remaining_events ?? 0),
|
||||
Tables\Columns\TextColumn::make('activeResellerPackage.expires_at')
|
||||
->dateTime()
|
||||
->label(__('admin.tenants.fields.package_expires_at'))
|
||||
->badge()
|
||||
->color(fn ($state) => $state && $state->isPast() ? 'danger' : 'success')
|
||||
->getStateUsing(fn (Tenant $record) => $record->activeResellerPackage?->expires_at),
|
||||
Tables\Columns\TextColumn::make('total_revenue')
|
||||
->money('EUR')
|
||||
->sortable(),
|
||||
@@ -120,23 +118,36 @@ class TenantResource extends Resource
|
||||
->filters([])
|
||||
->actions([
|
||||
Actions\EditAction::make(),
|
||||
Actions\Action::make('add_credits')
|
||||
->label('Credits hinzufügen')
|
||||
Actions\Action::make('add_package')
|
||||
->label('Package hinzufügen')
|
||||
->icon('heroicon-o-plus')
|
||||
->form([
|
||||
Forms\Components\TextInput::make('credits')->numeric()->required()->minValue(1),
|
||||
Select::make('package_id')
|
||||
->label('Package')
|
||||
->options(\App\Models\Package::where('type', 'reseller')->pluck('name', 'id'))
|
||||
->searchable()
|
||||
->preload()
|
||||
->required(),
|
||||
Forms\Components\DateTimePicker::make('expires_at')
|
||||
->label('Ablaufdatum')
|
||||
->default(now()->addYear()),
|
||||
Forms\Components\Textarea::make('reason')->label('Grund')->rows(3),
|
||||
])
|
||||
->action(function (Tenant $record, array $data) {
|
||||
$record->increment('event_credits_balance', $data['credits']);
|
||||
\App\Models\EventPurchase::create([
|
||||
\App\Models\TenantPackage::create([
|
||||
'tenant_id' => $record->id,
|
||||
'package_id' => 'manual_adjustment',
|
||||
'credits_added' => $data['credits'],
|
||||
'price' => 0,
|
||||
'platform' => 'manual',
|
||||
'transaction_id' => null,
|
||||
'reason' => $data['reason'],
|
||||
'package_id' => $data['package_id'],
|
||||
'expires_at' => $data['expires_at'],
|
||||
'active' => true,
|
||||
'reason' => $data['reason'] ?? null,
|
||||
]);
|
||||
\App\Models\PackagePurchase::create([
|
||||
'tenant_id' => $record->id,
|
||||
'package_id' => $data['package_id'],
|
||||
'provider_id' => 'manual',
|
||||
'type' => 'reseller_subscription',
|
||||
'purchased_price' => 0,
|
||||
'metadata' => ['reason' => $data['reason'] ?? 'manual assignment'],
|
||||
]);
|
||||
}),
|
||||
Actions\Action::make('suspend')
|
||||
@@ -164,4 +175,12 @@ class TenantResource extends Resource
|
||||
'edit' => Pages\EditTenant::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
TenantPackagesRelationManager::class,
|
||||
PackagePurchasesRelationManager::class,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\TenantResource\RelationManagers;
|
||||
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Toggle;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Resources\RelationManagers\RelationManager;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Actions\BulkActionGroup;
|
||||
use Filament\Tables\Actions\DeleteBulkAction;
|
||||
use Filament\Tables\Actions\ViewAction;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Filters\SelectFilter;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
|
||||
class PackagePurchasesRelationManager extends RelationManager
|
||||
{
|
||||
protected static string $relationship = 'packagePurchases';
|
||||
|
||||
protected static ?string $title = 'Package-Käufe';
|
||||
|
||||
public function form(Schema $form): Schema
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Select::make('package_id')
|
||||
->label('Paket')
|
||||
->relationship('package', 'name')
|
||||
->searchable()
|
||||
->preload()
|
||||
->required(),
|
||||
Select::make('type')
|
||||
->label('Typ')
|
||||
->options([
|
||||
'endcustomer_event' => 'Endkunden-Event',
|
||||
'reseller_subscription' => 'Reseller-Abo',
|
||||
])
|
||||
->required(),
|
||||
TextInput::make('purchased_price')
|
||||
->label('Gekaufter Preis')
|
||||
->numeric()
|
||||
->step(0.01)
|
||||
->prefix('€')
|
||||
->required(),
|
||||
Select::make('provider_id')
|
||||
->label('Anbieter')
|
||||
->options([
|
||||
'stripe' => 'Stripe',
|
||||
'paypal' => 'PayPal',
|
||||
'manual' => 'Manuell',
|
||||
'free' => 'Kostenlos',
|
||||
])
|
||||
->required(),
|
||||
TextInput::make('transaction_id')
|
||||
->label('Transaktions-ID')
|
||||
->maxLength(255),
|
||||
Toggle::make('refunded')
|
||||
->label('Rückerstattet'),
|
||||
Textarea::make('metadata')
|
||||
->label('Metadaten')
|
||||
->json()
|
||||
->columnSpanFull(),
|
||||
])
|
||||
->columns(2);
|
||||
}
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->recordTitleAttribute('package.name')
|
||||
->columns([
|
||||
TextColumn::make('package.name')
|
||||
->label('Paket')
|
||||
->badge()
|
||||
->color('success'),
|
||||
TextColumn::make('type')
|
||||
->badge()
|
||||
->color(fn (string $state): string => match($state) {
|
||||
'endcustomer_event' => 'info',
|
||||
'reseller_subscription' => 'success',
|
||||
default => 'gray',
|
||||
}),
|
||||
TextColumn::make('purchased_price')
|
||||
->money('EUR')
|
||||
->sortable(),
|
||||
TextColumn::make('provider_id')
|
||||
->badge()
|
||||
->color(fn (string $state): string => match($state) {
|
||||
'stripe' => 'info',
|
||||
'paypal' => 'warning',
|
||||
'manual' => 'gray',
|
||||
'free' => 'success',
|
||||
}),
|
||||
TextColumn::make('transaction_id')
|
||||
->copyable()
|
||||
->toggleable(),
|
||||
TextColumn::make('metadata')
|
||||
->label('Metadaten')
|
||||
->toggleable(),
|
||||
IconColumn::make('refunded')
|
||||
->boolean()
|
||||
->color(fn (bool $state): string => $state ? 'danger' : 'success'),
|
||||
TextColumn::make('created_at')
|
||||
->dateTime()
|
||||
->sortable(),
|
||||
])
|
||||
->filters([
|
||||
SelectFilter::make('type')
|
||||
->options([
|
||||
'endcustomer_event' => 'Endkunden-Event',
|
||||
'reseller_subscription' => 'Reseller-Abo',
|
||||
]),
|
||||
SelectFilter::make('provider_id')
|
||||
->options([
|
||||
'stripe' => 'Stripe',
|
||||
'paypal' => 'PayPal',
|
||||
'manual' => 'Manuell',
|
||||
'free' => 'Kostenlos',
|
||||
]),
|
||||
SelectFilter::make('refunded')
|
||||
->options([
|
||||
'1' => 'Rückerstattet',
|
||||
'0' => 'Nicht rückerstattet',
|
||||
]),
|
||||
])
|
||||
->headerActions([])
|
||||
->actions([
|
||||
ViewAction::make(),
|
||||
])
|
||||
->bulkActions([
|
||||
BulkActionGroup::make([
|
||||
DeleteBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\TenantResource\RelationManagers;
|
||||
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\DateTimePicker;
|
||||
use Filament\Forms\Components\Toggle;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Resources\RelationManagers\RelationManager;
|
||||
use Filament\Tables;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\BulkActionGroup;
|
||||
use Filament\Actions\DeleteBulkAction;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Filters\SelectFilter;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
|
||||
class TenantPackagesRelationManager extends RelationManager
|
||||
{
|
||||
protected static string $relationship = 'tenantPackages';
|
||||
|
||||
protected static ?string $title = 'Reseller-Pakete';
|
||||
|
||||
public function form(Schema $form): Schema
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Select::make('package_id')
|
||||
->label('Paket')
|
||||
->relationship('package', 'name')
|
||||
->searchable()
|
||||
->preload()
|
||||
->required(),
|
||||
DateTimePicker::make('expires_at')
|
||||
->label('Ablaufdatum')
|
||||
->required(),
|
||||
Toggle::make('active')
|
||||
->label('Aktiv'),
|
||||
Textarea::make('reason')
|
||||
->label('Grund')
|
||||
->maxLength(65535)
|
||||
->columnSpanFull(),
|
||||
])
|
||||
->columns(2);
|
||||
}
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->recordTitleAttribute('package.name')
|
||||
->columns([
|
||||
TextColumn::make('package.name')
|
||||
->label('Paket')
|
||||
->badge()
|
||||
->color('success'),
|
||||
TextColumn::make('used_events')
|
||||
->label('Genutzte Events')
|
||||
->badge(),
|
||||
TextColumn::make('remaining_events')
|
||||
->label('Verbleibende Events')
|
||||
->badge()
|
||||
->color(fn ($state) => $state < 1 ? 'danger' : 'success')
|
||||
->getStateUsing(fn ($record) => $record->remaining_events),
|
||||
TextColumn::make('expires_at')
|
||||
->dateTime()
|
||||
->sortable(),
|
||||
IconColumn::make('active')
|
||||
->boolean()
|
||||
->color(fn (bool $state): string => $state ? 'success' : 'danger'),
|
||||
TextColumn::make('created_at')
|
||||
->dateTime()
|
||||
->sortable(),
|
||||
])
|
||||
->filters([
|
||||
SelectFilter::make('active')
|
||||
->options([
|
||||
'1' => 'Aktiv',
|
||||
'0' => 'Inaktiv',
|
||||
]),
|
||||
])
|
||||
->headerActions([])
|
||||
->actions([
|
||||
EditAction::make(),
|
||||
Action::make('activate')
|
||||
->label('Aktivieren')
|
||||
->icon('heroicon-o-check-circle')
|
||||
->color('success')
|
||||
->action(fn ($record) => $record->update(['active' => true])),
|
||||
Action::make('deactivate')
|
||||
->label('Deaktivieren')
|
||||
->icon('heroicon-o-x-circle')
|
||||
->color('danger')
|
||||
->requiresConfirmation()
|
||||
->action(fn ($record) => $record->update(['active' => false])),
|
||||
])
|
||||
->bulkActions([
|
||||
BulkActionGroup::make([
|
||||
DeleteBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user