2026-08-25 09:33:03 +03:00
<? php
namespace Modules\Core\Privacy\RelationManagers ;
2026-09-16 00:24:18 +03:00
use Filament\Actions\Action ;
2026-08-25 09:33:03 +03:00
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 ;
2026-09-16 01:21:22 +03:00
use Modules\Core\Privacy\Services\PrivacyService ;
2026-08-25 09:33:03 +03:00
/**
* 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 );
2026-09-16 00:24:18 +03:00
return $table -> recordActions ([
2026-08-25 09:33:03 +03:00
... $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' )
2026-09-16 00:24:18 +03:00
-> schema ( fn ( Model $record ) => $this -> requestsInfolist ( $record )),
2026-08-25 09:33:03 +03:00
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.' )
2026-09-16 00:24:18 +03:00
-> schema ([
2026-08-25 09:33:03 +03:00
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 ),
];
}
}