Add join token expiry action in event modal
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled

This commit is contained in:
Codex Agent
2026-02-04 16:03:51 +01:00
parent beaff1c4e9
commit e911c2bd16
5 changed files with 195 additions and 1 deletions

View File

@@ -6,19 +6,23 @@ use App\Filament\Clusters\DailyOps\DailyOpsCluster;
use App\Filament\Resources\EventResource\Pages;
use App\Filament\Resources\EventResource\RelationManagers\EventPackagesRelationManager;
use App\Models\Event;
use App\Models\EventJoinToken;
use App\Models\EventJoinTokenEvent;
use App\Models\EventType;
use App\Models\Tenant;
use App\Services\Audit\SuperAdminAuditLogger;
use App\Services\EventJoinTokenService;
use App\Support\JoinTokenLayoutRegistry;
use BackedEnum;
use Carbon\Carbon;
use Filament\Actions;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\DateTimePicker;
use Filament\Forms\Components\KeyValue;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Notifications\Notification;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Tables;
@@ -164,7 +168,100 @@ class EventResource extends Resource
->modalHeading(__('admin.events.modal.join_link_heading'))
->modalSubmitActionLabel(__('admin.common.close'))
->modalWidth('xl')
->modalContent(function ($record) {
->registerModalActions([
Actions\Action::make('extend_join_token_expiry')
->label(__('admin.events.join_link.extend_expiry'))
->icon('heroicon-o-clock')
->color('warning')
->size('xs')
->modalHeading(function (Actions\Action $action, Event $record): string {
$token = static::resolveJoinTokenFromAction($record, $action);
return $token
? __('admin.events.join_link.extend_expiry_heading', [
'label' => $token->label ?: __('admin.events.join_link.token_default', ['id' => $token->id]),
])
: __('admin.events.join_link.extend_expiry_heading_fallback');
})
->schema(function (Event $record): array {
$minimumExpiry = app(EventJoinTokenService::class)->minimumExpiryForEvent($record);
$rules = [
'date',
'after:now',
];
if ($minimumExpiry) {
$rules[] = 'after_or_equal:'.$minimumExpiry->toDateTimeString();
}
return [
DateTimePicker::make('expires_at')
->label(__('admin.events.join_link.extend_expiry_label'))
->required()
->seconds(false)
->rules($rules)
->helperText($minimumExpiry
? __('admin.events.join_link.extend_expiry_min', [
'date' => $minimumExpiry->isoFormat('LLL'),
])
: null),
];
})
->fillForm(function (Actions\Action $action, Event $record): array {
$token = static::resolveJoinTokenFromAction($record, $action);
if (! $token) {
return [];
}
return [
'expires_at' => $token->expires_at,
];
})
->action(function (array $data, Actions\Action $action, Event $record): void {
$token = static::resolveJoinTokenFromAction($record, $action);
if (! $token) {
Notification::make()
->title(__('admin.events.join_link.extend_expiry_missing'))
->danger()
->send();
return;
}
$expiresAt = $data['expires_at'] ?? null;
if (! $expiresAt) {
Notification::make()
->title(__('admin.events.join_link.extend_expiry_missing_date'))
->danger()
->send();
return;
}
$resolvedExpiry = $expiresAt instanceof Carbon
? $expiresAt
: Carbon::parse($expiresAt);
$token->forceFill([
'expires_at' => $resolvedExpiry,
])->save();
app(SuperAdminAuditLogger::class)->recordModelMutation(
'updated',
$token,
source: static::class
);
Notification::make()
->title(__('admin.events.join_link.extend_expiry_success'))
->success()
->send();
}),
])
->modalContent(function (Actions\Action $action, $record) {
$tokens = $record->joinTokens()
->orderByDesc('created_at')
->get();
@@ -253,6 +350,7 @@ class EventResource extends Resource
return view('filament.events.join-link', [
'event' => $record,
'tokens' => $tokens,
'action' => $action,
]);
}),
])
@@ -303,6 +401,19 @@ class EventResource extends Resource
return is_string($name) ? $name : '';
}
private static function resolveJoinTokenFromAction(Event $record, Actions\Action $action): ?EventJoinToken
{
$tokenId = $action->getArguments()['token_id'] ?? null;
if (! $tokenId) {
return null;
}
return $record->joinTokens()
->whereKey($tokenId)
->first();
}
public static function getPages(): array
{
return [

View File

@@ -407,6 +407,14 @@ return [
'layouts_heading' => 'Drucklayouts',
'layouts_fallback' => 'Layout-Übersicht öffnen',
'token_expiry' => 'Läuft ab am :date',
'extend_expiry' => 'Ablauf verlängern',
'extend_expiry_label' => 'Neues Ablaufdatum',
'extend_expiry_heading' => 'Ablauf für :label verlängern',
'extend_expiry_heading_fallback' => 'Ablauf der Einladung verlängern',
'extend_expiry_min' => 'Mindestlaufzeit: :date',
'extend_expiry_missing' => 'Einladung nicht gefunden.',
'extend_expiry_missing_date' => 'Bitte ein neues Ablaufdatum wählen.',
'extend_expiry_success' => 'Ablauf der Einladung aktualisiert.',
],
'analytics' => [
'success_total' => 'Erfolgreiche Zugriffe',

View File

@@ -402,6 +402,14 @@ return [
'layouts_heading' => 'Printable layouts',
'layouts_fallback' => 'Open layout overview',
'token_expiry' => 'Expires at :date',
'extend_expiry' => 'Extend expiry',
'extend_expiry_label' => 'New expiry',
'extend_expiry_heading' => 'Extend expiry for :label',
'extend_expiry_heading_fallback' => 'Extend invitation expiry',
'extend_expiry_min' => 'Minimum expiry: :date',
'extend_expiry_missing' => 'Invitation not found.',
'extend_expiry_missing_date' => 'Please select a new expiry.',
'extend_expiry_success' => 'Invitation expiry updated.',
'deprecated_notice' => 'Direct access via slug :slug has been retired. Share the invitations below or manage QR layouts in the admin app.',
'open_admin' => 'Open admin app',
],

View File

@@ -76,6 +76,9 @@
>
{{ __('admin.events.join_link.copy_link') }}
</x-filament::button>
@if (isset($action))
{{ $action->getModalAction('extend_join_token_expiry')(['token_id' => $token['id']]) }}
@endif
</div>
</div>

View File

@@ -0,0 +1,64 @@
<?php
namespace Tests\Feature;
use App\Filament\Resources\EventResource\Pages\ListEvents;
use App\Models\Event;
use App\Models\User;
use App\Services\EventJoinTokenService;
use Filament\Actions\Testing\TestAction;
use Filament\Facades\Filament;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Tests\TestCase;
class EventJoinTokenExpiryActionTest extends TestCase
{
use RefreshDatabase;
public function test_superadmin_can_extend_join_token_expiry(): void
{
$user = User::factory()->create(['role' => 'super_admin']);
$event = Event::factory()->create([
'date' => now()->addDays(10),
]);
$token = $event->joinTokens()->latest('id')->first();
$minimumExpiry = app(EventJoinTokenService::class)->minimumExpiryForEvent($event);
$newExpiry = ($minimumExpiry ?? now()->addDay())->copy()->addDays(2)->seconds(0);
$this->bootSuperAdminPanel($user);
Livewire::test(ListEvents::class)
->callAction(
[
TestAction::make('join_tokens')->table($event),
TestAction::make('extend_join_token_expiry')
->arguments(['token_id' => $token->id]),
],
[
'expires_at' => $newExpiry->toDateTimeString(),
]
)
->assertHasNoErrors();
$token->refresh();
$this->assertSame(
$newExpiry->toDateTimeString(),
$token->expires_at?->toDateTimeString()
);
}
private function bootSuperAdminPanel(User $user): void
{
$panel = Filament::getPanel('superadmin');
$this->assertNotNull($panel);
Filament::setCurrentPanel($panel);
Filament::bootCurrentPanel();
Filament::auth()->login($user);
}
}