Add all files
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'title' => 'Privacy & Security',
|
||||
'navigation_privacy' => 'Privacy',
|
||||
'deletion_heading' => 'Account Deletion (GDPR)',
|
||||
'deletion_description' => 'Request deletion or anonymisation of your personal data. Invoices and legally-required billing records stay intact.',
|
||||
'deletion_reason_label' => 'Reason (optional)',
|
||||
'deletion_submit' => 'Request deletion',
|
||||
'deletion_status_pending' => 'Your deletion request is pending review.',
|
||||
'deletion_status_completed' => 'Your account was anonymised on :date.',
|
||||
'deletion_status_denied' => 'Your deletion request was denied.',
|
||||
'deletion_status_none' => 'You have not requested account deletion yet.',
|
||||
'deletion_request_exists' => 'You already have a pending deletion request.',
|
||||
'deletion_requested' => 'Deletion request submitted. We will email you once it is processed.',
|
||||
'deletion_confirm' => 'Are you sure you want to request account deletion? This action cannot be undone.',
|
||||
'pin_heading' => 'Support PIN',
|
||||
'pin_description' => 'Generate a temporary PIN to verify your identity when speaking to support.',
|
||||
'pin_generate' => 'Generate new PIN',
|
||||
'pin_revoke' => 'Revoke PIN',
|
||||
'pin_active_until' => 'Active until :date',
|
||||
'pin_revoked' => 'Support PIN revoked.',
|
||||
'pin_generated' => 'Support PIN generated.',
|
||||
'pin_plaintext_notice' => 'Copy this PIN now. For security reasons it will not be shown again.',
|
||||
'pin_none' => 'No active PIN. Generate a new one below.',
|
||||
'pin_show' => 'Show PIN',
|
||||
'pin_hide' => 'Hide PIN',
|
||||
'pin_copy' => 'Copy PIN',
|
||||
'pin_copied' => 'PIN copied to clipboard.',
|
||||
];
|
||||
@@ -0,0 +1,63 @@
|
||||
<x-filament::page>
|
||||
<form wire:submit.prevent="validatePin" class="space-y-6">
|
||||
|
||||
{{-- Render the form --}}
|
||||
<div>
|
||||
{{ $this->form }}
|
||||
</div>
|
||||
|
||||
{{-- Submit Button --}}
|
||||
<div class="flex justify-end">
|
||||
<x-filament::button type="submit" color="primary">
|
||||
Validate PIN
|
||||
</x-filament::button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
{{-- Error Message --}}
|
||||
@if ($errorMessage)
|
||||
<div class="rounded-lg border border-red-200 bg-red-50 p-4 text-sm text-red-700 mt-4">
|
||||
{{ $errorMessage }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Validated PIN Info --}}
|
||||
@if ($validated)
|
||||
<div class="rounded-lg border border-green-200 bg-green-50 p-4 space-y-2 mt-4">
|
||||
<h3 class="text-sm font-semibold text-green-900">Validated PIN details</h3>
|
||||
|
||||
<dl class="grid grid-cols-1 gap-3 sm:grid-cols-2 text-sm text-green-800">
|
||||
<div>
|
||||
<dt class="font-medium">Client</dt>
|
||||
<dd>{{ $validated['user_name'] ?? 'Unknown' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="font-medium">Email</dt>
|
||||
<dd>{{ $validated['user_email'] ?? 'Unknown' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="font-medium">Expires</dt>
|
||||
<dd>{{ $validated['expires_at'] ?? 'n/a' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="font-medium">Last validated</dt>
|
||||
<dd>{{ $validated['last_validated_at'] ?? 'n/a' }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
|
||||
@if (!empty($validated['user_url']))
|
||||
<div class="flex justify-end pt-2">
|
||||
<x-filament::button
|
||||
tag="a"
|
||||
:href="$validated['user_url']"
|
||||
icon="heroicon-o-arrow-top-right-on-square"
|
||||
target="_blank"
|
||||
>
|
||||
View client in admin
|
||||
</x-filament::button>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</x-filament::page>
|
||||
@@ -0,0 +1,141 @@
|
||||
<div>
|
||||
<x-navigation.breadcrumb />
|
||||
|
||||
<div class="px-2 space-y-6">
|
||||
<!-- Account deletion -->
|
||||
<section class="bg-background-secondary border border-neutral rounded-lg p-6 space-y-6">
|
||||
<div>
|
||||
<h2 class="text-lg font-semibold text-base">{{ __('compliance-tools::client.deletion_heading') }}</h2>
|
||||
<p class="mt-1 text-sm text-base/60 leading-relaxed">
|
||||
{{ __('compliance-tools::client.deletion_description') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@php
|
||||
$deletionStatus = __('compliance-tools::client.deletion_status_none');
|
||||
if ($deletionRequest) {
|
||||
$deletionStatus = match ($deletionRequest->status) {
|
||||
\Paymenter\Extensions\Others\ComplianceTools\Models\AccountDeletionRequest::STATUS_PENDING => __('compliance-tools::client.deletion_status_pending'),
|
||||
\Paymenter\Extensions\Others\ComplianceTools\Models\AccountDeletionRequest::STATUS_COMPLETED => __('compliance-tools::client.deletion_status_completed', ['date' => optional($deletionRequest->processed_at)->toDayDateTimeString()]),
|
||||
\Paymenter\Extensions\Others\ComplianceTools\Models\AccountDeletionRequest::STATUS_DENIED => __('compliance-tools::client.deletion_status_denied'),
|
||||
default => __('compliance-tools::client.deletion_status_none'),
|
||||
};
|
||||
}
|
||||
|
||||
$disableDeletion = $deletionRequest && in_array($deletionRequest->status, [
|
||||
\Paymenter\Extensions\Others\ComplianceTools\Models\AccountDeletionRequest::STATUS_PENDING,
|
||||
\Paymenter\Extensions\Others\ComplianceTools\Models\AccountDeletionRequest::STATUS_COMPLETED,
|
||||
]);
|
||||
@endphp
|
||||
|
||||
<div class="bg-background border border-neutral rounded-md px-4 py-3 text-sm text-base font-medium">
|
||||
{{ $deletionStatus }}
|
||||
</div>
|
||||
|
||||
<form wire:submit.prevent="submitDeletionRequest" onsubmit="return confirm('{{ addslashes(__('compliance-tools::client.deletion_confirm')) }}');" class="space-y-5">
|
||||
<h2 class="text-lg font-semibold text-base">Reason for Request</h2>
|
||||
|
||||
<textarea
|
||||
name="reason"
|
||||
wire:model.defer="reason"
|
||||
placeholder="{{ __('compliance-tools::client.deletion_reason_label') }}"
|
||||
rows="3"
|
||||
class="min-h-[7rem] mt-4 w-full border border-gray-300 rounded-md p-2 resize-none"
|
||||
></textarea>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="bg-red-600 text-white px-4 py-2 rounded-md hover:bg-red-700 disabled:opacity-50 disabled:cursor-not-allowed w-fit"
|
||||
@if($disableDeletion) disabled @endif
|
||||
>
|
||||
{{ __('compliance-tools::client.deletion_submit') }}
|
||||
</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<!-- Support PIN -->
|
||||
<section class="bg-background-secondary border border-neutral rounded-lg p-6 space-y-6">
|
||||
<div>
|
||||
<h2 class="text-lg font-semibold text-base">{{ __('compliance-tools::client.pin_heading') }}</h2>
|
||||
<p class="mt-1 text-sm text-base/60 leading-relaxed">
|
||||
{{ __('compliance-tools::client.pin_description') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@php
|
||||
$hasGeneratedPin = filled($generatedPin);
|
||||
$maskedPin = $hasGeneratedPin ? str_repeat('•', strlen($generatedPin)) : '';
|
||||
$plainPin = $generatedPin ?? '';
|
||||
$disableRevoke = !$activePin;
|
||||
@endphp
|
||||
|
||||
@if($hasGeneratedPin)
|
||||
<div class="bg-primary/20 border border-primary/40 rounded-md px-4 py-4 space-y-4 text-center"
|
||||
x-data="{
|
||||
pin: @js($plainPin),
|
||||
async copy() {
|
||||
if (!this.pin) return;
|
||||
try {
|
||||
await navigator.clipboard.writeText(this.pin);
|
||||
} catch (e) {
|
||||
const helper = document.createElement('textarea');
|
||||
helper.value = this.pin;
|
||||
helper.style.position = 'fixed';
|
||||
helper.style.opacity = '0';
|
||||
document.body.appendChild(helper);
|
||||
helper.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(helper);
|
||||
}
|
||||
$wire.pinCopied();
|
||||
}
|
||||
}">
|
||||
<p class="text-sm font-semibold text-primary flex items-center justify-center gap-2">
|
||||
<x-ri-key-line class="size-5" />
|
||||
{{ __('compliance-tools::client.pin_plaintext_notice') }}
|
||||
</p>
|
||||
<p class="font-mono text-2xl tracking-[0.6em] text-primary select-all">
|
||||
{{ $showGeneratedPin ? $generatedPin : $maskedPin }}
|
||||
</p>
|
||||
<div class="flex flex-wrap justify-center gap-3">
|
||||
<button type="button" wire:click="toggleGeneratedPinVisibility" class="bg-gray-200 text-gray-800 px-3 py-2 rounded-md hover:bg-gray-300 md:w-fit !w-auto">
|
||||
@if($showGeneratedPin)
|
||||
<x-ri-eye-off-line class="size-4" />
|
||||
{{ __('compliance-tools::client.pin_hide') }}
|
||||
@else
|
||||
<x-ri-eye-line class="size-4" />
|
||||
{{ __('compliance-tools::client.pin_show') }}
|
||||
@endif
|
||||
</button>
|
||||
|
||||
<button type="button" class="bg-gray-200 text-gray-800 px-3 py-2 rounded-md hover:bg-gray-300 md:w-fit !w-auto" x-on:click="copy()">
|
||||
<x-ri-file-copy-line class="size-4" />
|
||||
{{ __('compliance-tools::client.pin_copy') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@elseif($activePin)
|
||||
<div class="bg-success/20 border border-success/40 rounded-md px-4 py-3 text-sm text-success flex items-center gap-2">
|
||||
<x-ri-shield-check-line class="size-5" />
|
||||
{{ __('compliance-tools::client.pin_active_until', ['date' => optional($activePin->expires_at)->toDayDateTimeString()]) }}
|
||||
</div>
|
||||
@else
|
||||
<div class="bg-background border border-neutral rounded-md px-4 py-3 text-sm text-base font-medium">
|
||||
{{ __('compliance-tools::client.pin_none') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="flex flex-col md:flex-row gap-3 md:items-center">
|
||||
<button type="button" wire:click="generatePin" class="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 md:w-fit flex items-center gap-2">
|
||||
<x-ri-refresh-line class="size-4" />
|
||||
{{ __('compliance-tools::client.pin_generate') }}
|
||||
</button>
|
||||
|
||||
<button type="button" wire:click="revokePin" class="bg-gray-200 text-gray-800 px-4 py-2 rounded-md hover:bg-gray-300 md:w-fit flex items-center gap-2" @if($disableRevoke) disabled @endif>
|
||||
<x-ri-close-circle-line class="size-4" />
|
||||
{{ __('compliance-tools::client.pin_revoke') }}
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user