113 lines
5.2 KiB
PHP
113 lines
5.2 KiB
PHP
<?php
|
|
|
|
namespace Modules\Core\Privacy\RelationManagers;
|
|
|
|
use Filament\Actions\Action;
|
|
use Filament\Forms\Components\Checkbox;
|
|
use Filament\Infolists\Components\RepeatableEntry;
|
|
use Filament\Infolists\Components\TextEntry;
|
|
use Filament\Notifications\Notification;
|
|
use Filament\Tables\Table;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Modules\Core\Customer\RelationManagers\UserRelationManager as CoreUserRelationManager;
|
|
use Modules\Core\Privacy\Models\DataErasureRequest;
|
|
use Modules\Core\Privacy\Services\PrivacyService;
|
|
|
|
/**
|
|
* Extends core's own Customer -> User relation manager to add:
|
|
* - a "Privacy Requests" row action, since a User's erasure/export requests
|
|
* can't be shown as a nested relation manager two levels deep (Customer ->
|
|
* User -> Requests isn't a shape Filament relation managers support) — a
|
|
* modal listing that specific User's requests is the practical alternative.
|
|
* - a "Request Erasure" row action — the User-scoped panel entry point,
|
|
* mirroring Modules\Core\Privacy\Filament\Extensions\
|
|
* CustomerErasureActionsExtension on the Customer side, including the same
|
|
* "erase immediately" checkbox for a staff-triggered urgent request.
|
|
* See docs/privacy.md "User-scope vs Customer-scope".
|
|
*/
|
|
class UserRelationManager extends CoreUserRelationManager
|
|
{
|
|
public function getDefaultTable(Table $table): Table
|
|
{
|
|
$table = parent::getDefaultTable($table);
|
|
|
|
return $table->recordActions([
|
|
...$table->getActions(),
|
|
Action::make('privacyRequests')
|
|
->label('Privacy Requests')
|
|
->icon('heroicon-o-shield-exclamation')
|
|
->modalHeading(fn (Model $record) => "Privacy requests for {$record->name}")
|
|
->modalSubmitAction(false)
|
|
->modalCancelActionLabel('Close')
|
|
->schema(fn (Model $record) => $this->requestsInfolist($record)),
|
|
Action::make('requestErasure')
|
|
->label('Request Erasure')
|
|
->icon('heroicon-o-shield-exclamation')
|
|
->color('danger')
|
|
->requiresConfirmation()
|
|
->modalDescription('Opens a cancellable grace-period erasure request for this individual — deactivates their login and detaches them from every linked Customer account once it completes. No Customer account\'s own data is affected.')
|
|
->schema([
|
|
Checkbox::make('immediate')
|
|
->label('Erase immediately (skip the 30-day grace period)')
|
|
->helperText('Staff-only, for a formal legal request or regulator inquiry that genuinely requires urgency — not a routine deletion. Runs synchronously, cannot be cancelled once submitted.')
|
|
->default(false),
|
|
])
|
|
->action(function (Model $record, array $data) {
|
|
$privacyService = app(PrivacyService::class);
|
|
$staff = auth('staff')->user();
|
|
|
|
if ($data['immediate']) {
|
|
$privacyService->requestImmediateErasureForUser($record, $staff);
|
|
|
|
Notification::make()
|
|
->title('User erased')
|
|
->body('Erasure ran immediately — see the Erasure Requests list for the outcome.')
|
|
->success()
|
|
->send();
|
|
|
|
return;
|
|
}
|
|
|
|
$privacyService->requestErasureForUser($record, $staff);
|
|
|
|
Notification::make()
|
|
->title('Erasure requested')
|
|
->body('The grace period starts now, and this user\'s login is deactivated immediately — see the Erasure Requests list.')
|
|
->success()
|
|
->send();
|
|
}),
|
|
]);
|
|
}
|
|
|
|
private function requestsInfolist(Model $record): array
|
|
{
|
|
return [
|
|
TextEntry::make('erasure_heading')
|
|
->label('')
|
|
->state('Erasure Requests'),
|
|
RepeatableEntry::make('erasureRequests')
|
|
->label('')
|
|
->state(fn () => $record->erasureRequests()->latest()->get())
|
|
->schema([
|
|
TextEntry::make('status')->formatStateUsing(fn ($state) => ucfirst($state->value)),
|
|
TextEntry::make('scheduled_for')->dateTime(),
|
|
TextEntry::make('requestedBy')->label('Requested by')->state(
|
|
fn (DataErasureRequest $record) => DataErasureRequest::displayNameFor($record->requestedBy)
|
|
),
|
|
])
|
|
->columns(3),
|
|
TextEntry::make('export_heading')
|
|
->label('')
|
|
->state('Export Requests'),
|
|
RepeatableEntry::make('exportRequests')
|
|
->label('')
|
|
->state(fn () => $record->exportRequests()->latest()->get())
|
|
->schema([
|
|
TextEntry::make('status')->formatStateUsing(fn ($state) => ucfirst($state->value)),
|
|
TextEntry::make('created_at')->label('Requested at')->dateTime(),
|
|
])
|
|
->columns(2),
|
|
];
|
|
}
|
|
}
|