feat: subscriptions
CI / 🧪 Tests Laravel (push) Successful in 2m48s
CI / 🐳 Build & Push Image (push) Successful in 3m0s

This commit is contained in:
2026-08-07 11:53:46 +02:00
parent 99cb0e63e6
commit 2ece4b6e3e
51 changed files with 1489 additions and 254 deletions
@@ -0,0 +1,61 @@
<?php
namespace App\Filament\Resources\IdentityVerificationRequests;
use App\Filament\Resources\IdentityVerificationRequests\Pages\ListIdentityVerificationRequests;
use App\Filament\Resources\IdentityVerificationRequests\Pages\ViewIdentityVerificationRequest;
use App\Filament\Resources\IdentityVerificationRequests\Schemas\IdentityVerificationRequestInfolist;
use App\Filament\Resources\IdentityVerificationRequests\Tables\IdentityVerificationRequestsTable;
use App\Models\IdentityVerificationRequest;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Table;
class IdentityVerificationRequestResource extends Resource
{
protected static ?string $model = IdentityVerificationRequest::class;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedIdentification;
protected static ?string $recordTitleAttribute = 'id';
public static function getNavigationLabel(): string
{
return __('admin.identity_verification_requests.navigation.label');
}
public static function getModelLabel(): string
{
return __('admin.identity_verification_requests.navigation.singular');
}
public static function getPluralModelLabel(): string
{
return __('admin.identity_verification_requests.navigation.plural');
}
public static function canCreate(): bool
{
return false;
}
public static function infolist(Schema $schema): Schema
{
return IdentityVerificationRequestInfolist::configure($schema);
}
public static function table(Table $table): Table
{
return IdentityVerificationRequestsTable::configure($table);
}
public static function getPages(): array
{
return [
'index' => ListIdentityVerificationRequests::route('/'),
'view' => ViewIdentityVerificationRequest::route('/{record}'),
];
}
}
@@ -0,0 +1,16 @@
<?php
namespace App\Filament\Resources\IdentityVerificationRequests\Pages;
use App\Filament\Resources\IdentityVerificationRequests\IdentityVerificationRequestResource;
use Filament\Resources\Pages\ListRecords;
class ListIdentityVerificationRequests extends ListRecords
{
protected static string $resource = IdentityVerificationRequestResource::class;
protected function getHeaderActions(): array
{
return [];
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Filament\Resources\IdentityVerificationRequests\Pages;
use App\Filament\Resources\IdentityVerificationRequests\IdentityVerificationRequestResource;
use App\Filament\Resources\IdentityVerificationRequests\Tables\IdentityVerificationRequestsTable;
use Filament\Resources\Pages\ViewRecord;
class ViewIdentityVerificationRequest extends ViewRecord
{
protected static string $resource = IdentityVerificationRequestResource::class;
protected function getHeaderActions(): array
{
return [
IdentityVerificationRequestsTable::approveAction(),
IdentityVerificationRequestsTable::rejectAction(),
];
}
}
@@ -0,0 +1,55 @@
<?php
namespace App\Filament\Resources\IdentityVerificationRequests\Schemas;
use App\Models\IdentityVerificationRequest;
use Filament\Infolists\Components\TextEntry;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
class IdentityVerificationRequestInfolist
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
Section::make(__('admin.identity_verification_requests.sections.request'))
->columns(3)
->schema([
TextEntry::make('id')
->label(__('admin.identity_verification_requests.fields.id'))
->copyable(),
TextEntry::make('status')
->label(__('admin.identity_verification_requests.fields.status'))
->badge(),
TextEntry::make('user.email')
->label(__('admin.identity_verification_requests.fields.user'))
->copyable(),
TextEntry::make('submitted_at')
->label(__('admin.identity_verification_requests.fields.submitted_at'))
->dateTime(),
TextEntry::make('reviewed_at')
->label(__('admin.identity_verification_requests.fields.reviewed_at'))
->dateTime()
->placeholder(__('admin.identity_verification_requests.placeholders.empty')),
TextEntry::make('reviewer.email')
->label(__('admin.identity_verification_requests.fields.reviewed_by'))
->placeholder(__('admin.identity_verification_requests.placeholders.empty')),
TextEntry::make('document')
->label(__('admin.identity_verification_requests.fields.document'))
->state(__('admin.identity_verification_requests.actions.open_document'))
->url(fn (IdentityVerificationRequest $record): ?string => $record->temporaryDocumentUrl())
->openUrlInNewTab()
->visible(fn (IdentityVerificationRequest $record): bool => $record->hasDocument()),
TextEntry::make('document_deleted_at')
->label(__('admin.identity_verification_requests.fields.document_deleted_at'))
->dateTime()
->placeholder(__('admin.identity_verification_requests.placeholders.empty')),
TextEntry::make('rejection_reason')
->label(__('admin.identity_verification_requests.fields.rejection_reason'))
->placeholder(__('admin.identity_verification_requests.placeholders.empty'))
->columnSpanFull(),
]),
]);
}
}
@@ -0,0 +1,98 @@
<?php
namespace App\Filament\Resources\IdentityVerificationRequests\Tables;
use App\Actions\ReviewIdentityVerification;
use App\Enums\IdentityVerificationStatus;
use App\Models\IdentityVerificationRequest;
use App\Models\User;
use Filament\Actions\Action;
use Filament\Actions\ViewAction;
use Filament\Forms\Components\Textarea;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Table;
class IdentityVerificationRequestsTable
{
public static function configure(Table $table): Table
{
return $table
->defaultSort('submitted_at', 'desc')
->columns([
TextColumn::make('submitted_at')
->label(__('admin.identity_verification_requests.fields.submitted_at'))
->dateTime()
->sortable(),
TextColumn::make('status')
->label(__('admin.identity_verification_requests.fields.status'))
->badge()
->sortable(),
TextColumn::make('user.email')
->label(__('admin.identity_verification_requests.fields.user'))
->searchable(),
TextColumn::make('reviewed_at')
->label(__('admin.identity_verification_requests.fields.reviewed_at'))
->dateTime()
->sortable()
->placeholder(__('admin.identity_verification_requests.placeholders.empty')),
])
->filters([
SelectFilter::make('status')
->label(__('admin.identity_verification_requests.fields.status'))
->options(IdentityVerificationStatus::class),
])
->recordActions([
ViewAction::make(),
self::approveAction(),
self::rejectAction(),
]);
}
public static function approveAction(): Action
{
return Action::make('approve')
->label(__('admin.identity_verification_requests.actions.approve'))
->color('success')
->requiresConfirmation()
->visible(fn (IdentityVerificationRequest $record): bool => $record->status === IdentityVerificationStatus::PENDING)
->action(function (IdentityVerificationRequest $record): void {
app(ReviewIdentityVerification::class)->execute(
$record,
IdentityVerificationStatus::APPROVED,
self::reviewer(),
);
});
}
public static function rejectAction(): Action
{
return Action::make('reject')
->label(__('admin.identity_verification_requests.actions.reject'))
->color('danger')
->schema([
Textarea::make('reason')
->label(__('admin.identity_verification_requests.fields.rejection_reason'))
->required()
->maxLength(2000),
])
->visible(fn (IdentityVerificationRequest $record): bool => $record->status === IdentityVerificationStatus::PENDING)
->action(function (array $data, IdentityVerificationRequest $record): void {
app(ReviewIdentityVerification::class)->execute(
$record,
IdentityVerificationStatus::REJECTED,
self::reviewer(),
$data['reason'],
);
});
}
private static function reviewer(): User
{
$reviewer = auth()->user();
abort_unless($reviewer instanceof User, 403);
return $reviewer;
}
}