Add all files
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace Paymenter\Extensions\Others\ComplianceTools\Admin\Pages;
|
||||
|
||||
use Filament\Forms\Concerns\InteractsWithForms;
|
||||
use Filament\Forms\Contracts\HasForms;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Group;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Pages\Page;
|
||||
use Paymenter\Extensions\Others\ComplianceTools\Services\SupportPinService;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use App\Admin\Resources\UserResource;
|
||||
|
||||
class ValidateSupportPin extends Page implements HasForms
|
||||
{
|
||||
use InteractsWithForms;
|
||||
|
||||
public ?string $pin = null; // <-- add this
|
||||
|
||||
public ?array $validated = null;
|
||||
public ?string $errorMessage = null;
|
||||
|
||||
protected static ?string $title = 'Validate Support PIN';
|
||||
protected static ?string $navigationIcon = 'ri-customer-service-2-line';
|
||||
protected static ?string $activeNavigationIcon = 'ri-customer-service-2-fill';
|
||||
protected static ?string $navigationGroup = 'Compliance';
|
||||
protected static ?string $slug = 'compliance/validate-pin';
|
||||
protected static ?string $navigationLabel = 'Validate Support PIN';
|
||||
protected static string $view = 'compliance-tools::admin.validate-support-pin';
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->form->fill([
|
||||
'pin' => '',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getFormSchema(): array
|
||||
{
|
||||
return [
|
||||
TextInput::make('pin')
|
||||
->label('Support PIN')
|
||||
->required()
|
||||
->minLength(4)
|
||||
->maxLength(16)
|
||||
->placeholder('Enter PIN shared by client')
|
||||
->autocomplete('off'),
|
||||
];
|
||||
}
|
||||
|
||||
public function validatePin(SupportPinService $service): void
|
||||
{
|
||||
$pin = $this->pin; // <-- access the bound property
|
||||
$this->validated = null;
|
||||
$this->errorMessage = null;
|
||||
|
||||
$record = $service->validate($pin);
|
||||
|
||||
if (!$record) {
|
||||
$this->errorMessage = 'No active PIN matched the value provided.';
|
||||
Notification::make()
|
||||
->title('Support PIN not found or expired')
|
||||
->danger()
|
||||
->send();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->validated = [
|
||||
'user_name' => $record->user?->name,
|
||||
'user_email' => $record->user?->email,
|
||||
'expires_at' => $record->expires_at?->toDayDateTimeString(),
|
||||
'last_validated_at' => $record->last_validated_at?->toDayDateTimeString(),
|
||||
'user_id' => $record->user?->getKey(),
|
||||
'user_url' => $record->user ? UserResource::getUrl('edit', ['record' => $record->user]) : null,
|
||||
];
|
||||
|
||||
Notification::make()
|
||||
->title('Support PIN validated')
|
||||
->success()
|
||||
->send();
|
||||
}
|
||||
|
||||
|
||||
public static function canAccess(): bool
|
||||
{
|
||||
$user = Auth::user();
|
||||
return $user && $user->hasPermission('admin.compliance.validate_pin');
|
||||
}
|
||||
|
||||
// Required to avoid Filament 2 breadcrumb error
|
||||
public function getBreadcrumbs(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace Paymenter\Extensions\Others\ComplianceTools\Admin\Resources;
|
||||
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Filters\SelectFilter;
|
||||
use Paymenter\Extensions\Others\ComplianceTools\Admin\Resources\AuditLogResource\Pages\ListAuditLogs;
|
||||
use Paymenter\Extensions\Others\ComplianceTools\Models\AuditLog;
|
||||
|
||||
class AuditLogResource extends Resource
|
||||
{
|
||||
protected static ?string $model = AuditLog::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'ri-clipboard-line';
|
||||
|
||||
protected static ?string $activeNavigationIcon = 'ri-clipboard-fill';
|
||||
|
||||
protected static ?string $navigationGroup = 'Compliance';
|
||||
|
||||
protected static ?string $navigationLabel = 'Audit Logs';
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->defaultSort('created_at', 'desc')
|
||||
->columns([
|
||||
TextColumn::make('category')
|
||||
->label('Category')
|
||||
->sortable()
|
||||
->searchable()
|
||||
->badge(),
|
||||
TextColumn::make('event')
|
||||
->label('Event')
|
||||
->sortable()
|
||||
->searchable()
|
||||
->badge(),
|
||||
TextColumn::make('subject.name')
|
||||
->label('Subject')
|
||||
->sortable()
|
||||
->searchable(['subject.name', 'subject.email'])
|
||||
->formatStateUsing(fn (?string $state, AuditLog $record) => $state ?? $record->subject?->email),
|
||||
TextColumn::make('actor_type')
|
||||
->label('Actor type')
|
||||
->sortable()
|
||||
->formatStateUsing(fn (?string $state) => $state ? class_basename($state) : 'System'),
|
||||
TextColumn::make('actor_id')
|
||||
->label('Actor ID')
|
||||
->sortable(),
|
||||
TextColumn::make('ip_address')
|
||||
->label('IP')
|
||||
->sortable(),
|
||||
TextColumn::make('created_at')
|
||||
->label('Logged at')
|
||||
->dateTime()
|
||||
->sortable(),
|
||||
])
|
||||
->filters([
|
||||
SelectFilter::make('category')
|
||||
->options(fn () => AuditLog::query()->distinct()->pluck('category', 'category')->toArray()),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => ListAuditLogs::route('/'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Paymenter\Extensions\Others\ComplianceTools\Admin\Resources\AuditLogResource\Pages;
|
||||
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
use Paymenter\Extensions\Others\ComplianceTools\Admin\Resources\AuditLogResource;
|
||||
|
||||
class ListAuditLogs extends ListRecords
|
||||
{
|
||||
protected static string $resource = AuditLogResource::class;
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
|
||||
namespace Paymenter\Extensions\Others\ComplianceTools\Admin\Resources;
|
||||
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Infolists\Components\TextEntry;
|
||||
use Filament\Infolists\Components\ViewEntry;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Filters\SelectFilter;
|
||||
use Filament\Tables\Actions\Action as TableAction;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Paymenter\Extensions\Others\ComplianceTools\Admin\Resources\DeletionRequestResource\Pages\ListDeletionRequests;
|
||||
use Paymenter\Extensions\Others\ComplianceTools\Admin\Resources\DeletionRequestResource\Pages\ViewDeletionRequest;
|
||||
use Paymenter\Extensions\Others\ComplianceTools\Models\AccountDeletionRequest;
|
||||
|
||||
class DeletionRequestResource extends Resource
|
||||
{
|
||||
protected static ?string $model = AccountDeletionRequest::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'ri-user-unfollow-line';
|
||||
protected static ?string $activeNavigationIcon = 'ri-user-unfollow-fill';
|
||||
protected static ?string $navigationGroup = 'Compliance';
|
||||
protected static ?string $navigationLabel = 'Deletion Requests';
|
||||
|
||||
protected static bool $shouldRegisterNavigation = true; // Ensure it shows in navigation
|
||||
|
||||
// Ensure Filament can show this resource in the navigation
|
||||
public static function canView(?Model $record = null): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Forms
|
||||
public static function getFormSchema(): array
|
||||
{
|
||||
return [
|
||||
Textarea::make('admin_notes')->label('Admin Notes'),
|
||||
];
|
||||
}
|
||||
|
||||
// Infolists
|
||||
public static function getInfolistSchema(): array
|
||||
{
|
||||
return [
|
||||
TextEntry::make('client')
|
||||
->label('Client')
|
||||
->state(fn (AccountDeletionRequest $record) => $record?->user?->name ?? '—'),
|
||||
|
||||
TextEntry::make('email')
|
||||
->label('Email')
|
||||
->state(fn (AccountDeletionRequest $record) => $record?->user?->email ?? '—')
|
||||
->copyable(),
|
||||
|
||||
TextEntry::make('status')
|
||||
->label('Status')
|
||||
->badge()
|
||||
->color(fn (AccountDeletionRequest $record) => match ($record->status) {
|
||||
AccountDeletionRequest::STATUS_COMPLETED => 'success',
|
||||
AccountDeletionRequest::STATUS_DENIED => 'danger',
|
||||
AccountDeletionRequest::STATUS_APPROVED => 'warning',
|
||||
default => 'info',
|
||||
}),
|
||||
|
||||
TextEntry::make('reason')
|
||||
->label('Reason')
|
||||
->state(fn (AccountDeletionRequest $record) => $record->reason ?? '—')
|
||||
->columnSpanFull()
|
||||
->extraAttributes(['class' => 'whitespace-pre-wrap']),
|
||||
|
||||
TextEntry::make('requested_at')
|
||||
->label('Requested at')
|
||||
->state(fn (AccountDeletionRequest $record) => optional($record->created_at)?->toDayDateTimeString() ?? '—'),
|
||||
|
||||
TextEntry::make('processed_at')
|
||||
->label('Processed at')
|
||||
->state(fn (AccountDeletionRequest $record) => optional($record->processed_at)?->toDayDateTimeString() ?? '—'),
|
||||
|
||||
TextEntry::make('processed_by')
|
||||
->label('Processed by')
|
||||
->state(fn (AccountDeletionRequest $record) => $record?->processor?->name ?? '—'),
|
||||
|
||||
TextEntry::make('admin_notes')
|
||||
->label('Admin notes')
|
||||
->state(fn (AccountDeletionRequest $record) => $record->admin_notes ?? '—')
|
||||
->columnSpanFull()
|
||||
->extraAttributes(['class' => 'whitespace-pre-wrap']),
|
||||
|
||||
ViewEntry::make('metadata')
|
||||
->label('Request metadata')
|
||||
->view('admin.infolists.components.json')
|
||||
->state(fn (AccountDeletionRequest $record) => $record->metadata)
|
||||
->columnSpanFull(),
|
||||
|
||||
ViewEntry::make('snapshot')
|
||||
->label('Snapshot')
|
||||
->view('admin.infolists.components.json')
|
||||
->state(fn (AccountDeletionRequest $record) => $record->snapshot)
|
||||
->columnSpanFull(),
|
||||
];
|
||||
}
|
||||
|
||||
// Table
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
TextColumn::make('user.name')
|
||||
->label('Client')
|
||||
->searchable()
|
||||
->sortable(),
|
||||
|
||||
TextColumn::make('user.email')
|
||||
->label('Email')
|
||||
->searchable()
|
||||
->sortable(),
|
||||
|
||||
TextColumn::make('status')
|
||||
->label('Status')
|
||||
->badge()
|
||||
->color(fn (string $state) => match ($state) {
|
||||
AccountDeletionRequest::STATUS_COMPLETED => 'success',
|
||||
AccountDeletionRequest::STATUS_DENIED => 'danger',
|
||||
AccountDeletionRequest::STATUS_APPROVED => 'warning',
|
||||
default => 'info',
|
||||
})
|
||||
->icon(fn (string $state) => match ($state) {
|
||||
AccountDeletionRequest::STATUS_COMPLETED => 'heroicon-o-check-circle',
|
||||
AccountDeletionRequest::STATUS_DENIED => 'heroicon-o-x-circle',
|
||||
AccountDeletionRequest::STATUS_APPROVED => 'heroicon-o-hand-raised',
|
||||
default => 'heroicon-o-clock',
|
||||
})
|
||||
->sortable(),
|
||||
|
||||
TextColumn::make('created_at')
|
||||
->label('Requested')
|
||||
->dateTime()
|
||||
->sortable(),
|
||||
|
||||
TextColumn::make('processed_at')
|
||||
->label('Processed')
|
||||
->dateTime()
|
||||
->sortable(),
|
||||
])
|
||||
->filters([
|
||||
SelectFilter::make('status')
|
||||
->options([
|
||||
AccountDeletionRequest::STATUS_PENDING => 'Pending',
|
||||
AccountDeletionRequest::STATUS_APPROVED => 'Approved',
|
||||
AccountDeletionRequest::STATUS_COMPLETED => 'Completed',
|
||||
AccountDeletionRequest::STATUS_DENIED => 'Denied',
|
||||
]),
|
||||
])
|
||||
->actions([
|
||||
TableAction::make('view')
|
||||
->label('View')
|
||||
->icon('heroicon-o-eye')
|
||||
->url(fn (AccountDeletionRequest $record): string => static::getUrl('view', ['record' => $record]))
|
||||
->visible(fn () => static::getPages()['view'] ?? false),
|
||||
]);
|
||||
}
|
||||
|
||||
// Pages
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => ListDeletionRequests::route('/'),
|
||||
'view' => ViewDeletionRequest::route('/{record}'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Paymenter\Extensions\Others\ComplianceTools\Admin\Resources\DeletionRequestResource\Pages;
|
||||
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
use Paymenter\Extensions\Others\ComplianceTools\Admin\Resources\DeletionRequestResource;
|
||||
|
||||
class ListDeletionRequests extends ListRecords
|
||||
{
|
||||
protected static string $resource = DeletionRequestResource::class;
|
||||
|
||||
protected function getActions(): array
|
||||
{
|
||||
return [
|
||||
Action::make('refresh')
|
||||
->label('Refresh')
|
||||
->icon('heroicon-o-arrow-path')
|
||||
->action(fn () => null),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Paymenter\Extensions\Others\ComplianceTools\Admin\Resources\DeletionRequestResource\Pages;
|
||||
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Paymenter\Extensions\Others\ComplianceTools\Admin\Resources\DeletionRequestResource;
|
||||
use Paymenter\Extensions\Others\ComplianceTools\Models\AccountDeletionRequest;
|
||||
use Paymenter\Extensions\Others\ComplianceTools\Services\AccountDeletionService;
|
||||
|
||||
class ViewDeletionRequest extends ViewRecord
|
||||
{
|
||||
protected static string $resource = DeletionRequestResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Action::make('approve')
|
||||
->label('Approve & anonymise')
|
||||
->icon('heroicon-o-check-circle')
|
||||
->color('success')
|
||||
->visible(fn () => $this->record->status !== AccountDeletionRequest::STATUS_COMPLETED)
|
||||
->form([
|
||||
Textarea::make('notes')->label('Internal notes')->rows(3),
|
||||
])
|
||||
->requiresConfirmation()
|
||||
->action(function (array $data, AccountDeletionService $service) {
|
||||
$service->approve($this->record, Auth::user(), $data['notes'] ?? null);
|
||||
$this->record->refresh();
|
||||
Notification::make()
|
||||
->title('Account anonymised successfully')
|
||||
->success()
|
||||
->send();
|
||||
})
|
||||
->authorize(fn () => Auth::user()?->can('update', $this->record) ?? false),
|
||||
Action::make('deny')
|
||||
->label('Deny request')
|
||||
->icon('heroicon-o-x-circle')
|
||||
->color('danger')
|
||||
->visible(fn () => $this->record->status === AccountDeletionRequest::STATUS_PENDING)
|
||||
->form([
|
||||
Textarea::make('notes')->label('Internal notes')->rows(3)->required(),
|
||||
])
|
||||
->requiresConfirmation()
|
||||
->action(function (array $data, AccountDeletionService $service) {
|
||||
$service->deny($this->record, Auth::user(), $data['notes'] ?? null);
|
||||
$this->record->refresh();
|
||||
Notification::make()
|
||||
->title('Deletion request marked as denied')
|
||||
->success()
|
||||
->send();
|
||||
})
|
||||
->authorize(fn () => Auth::user()?->can('update', $this->record) ?? false),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace Paymenter\Extensions\Others\ComplianceTools\Admin\Resources;
|
||||
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Columns\BadgeColumn;
|
||||
use Filament\Tables\Filters\Filter;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Paymenter\Extensions\Others\ComplianceTools\Admin\Resources\SupportPinResource\Pages\ListSupportPins;
|
||||
use Paymenter\Extensions\Others\ComplianceTools\Models\SupportPin;
|
||||
|
||||
class SupportPinResource extends Resource
|
||||
{
|
||||
protected static ?string $model = SupportPin::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'ri-key-2-line';
|
||||
protected static ?string $activeNavigationIcon = 'ri-key-2-fill';
|
||||
protected static ?string $navigationGroup = 'Compliance';
|
||||
protected static ?string $navigationLabel = 'Support PINs';
|
||||
|
||||
public static function table(Tables\Table $table): Tables\Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
TextColumn::make('user.name')
|
||||
->label('Client')
|
||||
->searchable()
|
||||
->sortable(),
|
||||
TextColumn::make('user.email')
|
||||
->label('Email')
|
||||
->searchable()
|
||||
->sortable(),
|
||||
TextColumn::make('created_at')
|
||||
->label('Generated')
|
||||
->dateTime()
|
||||
->sortable(),
|
||||
TextColumn::make('expires_at')
|
||||
->label('Expires')
|
||||
->dateTime()
|
||||
->sortable(),
|
||||
TextColumn::make('last_validated_at')
|
||||
->label('Last validated')
|
||||
->dateTime()
|
||||
->sortable(),
|
||||
TextColumn::make('revoked_at')
|
||||
->label('Revoked at')
|
||||
->dateTime()
|
||||
->sortable(),
|
||||
BadgeColumn::make('status')
|
||||
->label('Status')
|
||||
->getStateUsing(fn (SupportPin $record) =>
|
||||
$record->revoked_at ? 'Revoked' : ($record->expires_at->isPast() ? 'Expired' : 'Active')
|
||||
)
|
||||
->colors([
|
||||
'success' => fn ($state) => $state === 'Active',
|
||||
'warning' => fn ($state) => $state === 'Expired',
|
||||
'danger' => fn ($state) => $state === 'Revoked',
|
||||
]),
|
||||
])
|
||||
->filters([
|
||||
Filter::make('active')
|
||||
->label('Active only')
|
||||
->query(fn ($query) => $query->whereNull('revoked_at')->where('expires_at', '>', now())),
|
||||
])
|
||||
->actions([
|
||||
Action::make('revoke')
|
||||
->label('Revoke')
|
||||
->icon('heroicon-o-no-symbol')
|
||||
->color('danger')
|
||||
->requiresConfirmation()
|
||||
->visible(fn (SupportPin $record) => is_null($record->revoked_at) && !$record->expires_at->isPast())
|
||||
->action(fn (SupportPin $record) =>
|
||||
app(\Paymenter\Extensions\Others\ComplianceTools\Services\SupportPinService::class)
|
||||
->revoke($record, auth()->user())
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => ListSupportPins::route('/'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Paymenter\Extensions\Others\ComplianceTools\Admin\Resources\SupportPinResource\Pages;
|
||||
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
use Paymenter\Extensions\Others\ComplianceTools\Admin\Resources\SupportPinResource;
|
||||
|
||||
class ListSupportPins extends ListRecords
|
||||
{
|
||||
protected static string $resource = SupportPinResource::class;
|
||||
|
||||
// Optional: disable breadcrumbs if you are not using them in Filament 2
|
||||
protected static bool $hasBreadcrumbs = false;
|
||||
|
||||
// Optional: set a custom title for the page
|
||||
protected static ?string $title = 'Support PINs';
|
||||
|
||||
// Optional: override the breadcrumbs function if needed
|
||||
public function getBreadcrumbs(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user