Move SealShare onto Livewire Material
Replaces maryUI and daisyUI with nonameweb/livewire-material: the Vibrant indigo scheme, a system/light/dark theme under sealshare-theme, one top app bar with the account menu, the upload drop zone and link-ready moments, M3 fields, dialogs instead of wire:confirm, snackbars instead of flashed messages, a sortable admin table, and the starter-kit cleanup. Docker builds assets after Composer; CI drops the Flux step. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
This commit is contained in:
co-authored by
Claude Opus 5
parent
6d7120e8e2
commit
c4a17b65c8
@@ -184,142 +184,81 @@ new class extends Component {
|
||||
:heading="__('Two Factor Authentication')"
|
||||
:subheading="__('Manage your two-factor authentication settings')"
|
||||
>
|
||||
<div class="flex flex-col w-full mx-auto space-y-6 text-sm" wire:cloak>
|
||||
<div class="grid w-full gap-6" wire:cloak>
|
||||
@if ($twoFactorEnabled)
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="badge badge-success">{{ __('Enabled') }}</span>
|
||||
</div>
|
||||
<div class="grid justify-items-start gap-4">
|
||||
<x-badge :value="__('Enabled')" tonal color="success" />
|
||||
|
||||
<p class="opacity-70">
|
||||
<p class="type-body-md text-on-surface-variant">
|
||||
{{ __('With two-factor authentication enabled, you will be prompted for a secure, random pin during login, which you can retrieve from the TOTP-supported application on your phone.') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<livewire:pages::settings.two-factor.recovery-codes :$requiresConfirmation />
|
||||
<livewire:pages::settings.two-factor.recovery-codes :$requiresConfirmation />
|
||||
|
||||
<div class="flex justify-start">
|
||||
<x-button
|
||||
label="{{ __('Disable 2FA') }}"
|
||||
icon="o-shield-exclamation"
|
||||
class="btn-error"
|
||||
wire:click="disable"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<x-button :label="__('Disable 2FA')" icon="remove_moderator" danger wire:click="disable" />
|
||||
</div>
|
||||
@else
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="badge badge-error">{{ __('Disabled') }}</span>
|
||||
</div>
|
||||
<div class="grid justify-items-start gap-4">
|
||||
<x-badge :value="__('Disabled')" tonal color="error" />
|
||||
|
||||
<p class="opacity-60">
|
||||
<p class="type-body-md text-on-surface-variant">
|
||||
{{ __('When you enable two-factor authentication, you will be prompted for a secure pin during login. This pin can be retrieved from a TOTP-supported application on your phone.') }}
|
||||
</p>
|
||||
|
||||
<x-button
|
||||
label="{{ __('Enable 2FA') }}"
|
||||
icon="o-shield-check"
|
||||
class="btn-primary"
|
||||
wire:click="enable"
|
||||
/>
|
||||
<x-button :label="__('Enable 2FA')" icon="shield_lock" variant="filled" wire:click="enable" />
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-pages::settings.layout>
|
||||
|
||||
<x-modal wire:model="showModal" :title="$this->modalConfig['title']" class="max-w-md">
|
||||
<p class="text-sm opacity-70">{{ $this->modalConfig['description'] }}</p>
|
||||
|
||||
<x-modal wire:model="showModal" :title="$this->modalConfig['title']" :subtitle="$this->modalConfig['description']" fullscreen>
|
||||
@if ($showVerificationStep)
|
||||
<div class="space-y-6 mt-4">
|
||||
<div class="flex flex-col items-center space-y-3 justify-center">
|
||||
<x-input
|
||||
name="code"
|
||||
wire:model="code"
|
||||
placeholder="000000"
|
||||
class="text-center text-2xl tracking-widest"
|
||||
maxlength="6"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<x-slot:actions>
|
||||
<x-button
|
||||
label="{{ __('Back') }}"
|
||||
wire:click="resetVerification"
|
||||
/>
|
||||
<x-button
|
||||
label="{{ __('Confirm') }}"
|
||||
class="btn-primary"
|
||||
wire:click="confirmTwoFactor"
|
||||
x-bind:disabled="$wire.code.length < 6"
|
||||
/>
|
||||
</x-slot:actions>
|
||||
<div class="mt-2">
|
||||
<x-input
|
||||
name="code"
|
||||
wire:model="code"
|
||||
:label="__('Code')"
|
||||
inputmode="numeric"
|
||||
autocomplete="one-time-code"
|
||||
maxlength="6"
|
||||
mono
|
||||
autofocus
|
||||
/>
|
||||
</div>
|
||||
|
||||
<x-slot:actions>
|
||||
<x-button :label="__('Back')" wire:click="resetVerification" />
|
||||
<x-button :label="__('Confirm')" variant="filled" wire:click="confirmTwoFactor" x-bind:disabled="$wire.code.length < 6" />
|
||||
</x-slot:actions>
|
||||
@else
|
||||
@error('setupData')
|
||||
<div class="alert alert-error mt-4">{{ $message }}</div>
|
||||
<x-alert color="error" class="mt-2">{{ $message }}</x-alert>
|
||||
@enderror
|
||||
|
||||
<div class="flex justify-center mt-4">
|
||||
<div class="relative w-64 overflow-hidden border rounded-lg border-base-300 aspect-square">
|
||||
<div class="mt-2 flex justify-center">
|
||||
{{-- The QR code keeps a white ground in both themes: scanners read dark on light. --}}
|
||||
<div class="grid aspect-square w-64 place-items-center overflow-hidden rounded-corner-lg bg-white p-4">
|
||||
@empty($qrCodeSvg)
|
||||
<div class="absolute inset-0 flex items-center justify-center bg-base-200 animate-pulse">
|
||||
<span class="loading loading-spinner"></span>
|
||||
</div>
|
||||
<x-loading :label="__('Loading')" />
|
||||
@else
|
||||
<div class="flex items-center justify-center h-full p-4 bg-white">
|
||||
{!! $qrCodeSvg !!}
|
||||
</div>
|
||||
{!! $qrCodeSvg !!}
|
||||
@endempty
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4 mt-4">
|
||||
<div class="relative flex items-center justify-center w-full">
|
||||
<div class="absolute inset-0 w-full h-px top-1/2 bg-base-300"></div>
|
||||
<span class="relative px-2 text-sm bg-base-100 opacity-60">
|
||||
{{ __('or, enter the code manually') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="mt-6 grid gap-3">
|
||||
<p class="text-center type-label-lg text-on-surface-variant">{{ __('or, enter the code manually') }}</p>
|
||||
|
||||
<div
|
||||
class="flex items-center space-x-2"
|
||||
x-data="{
|
||||
copied: false,
|
||||
async copy() {
|
||||
try {
|
||||
await navigator.clipboard.writeText('{{ $manualSetupKey }}');
|
||||
this.copied = true;
|
||||
setTimeout(() => this.copied = false, 1500);
|
||||
} catch (e) {
|
||||
console.warn('Could not copy to clipboard');
|
||||
}
|
||||
}
|
||||
}"
|
||||
>
|
||||
<div class="join w-full">
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
value="{{ $manualSetupKey }}"
|
||||
class="input input-bordered join-item w-full"
|
||||
/>
|
||||
<button
|
||||
@click="copy()"
|
||||
class="btn join-item"
|
||||
>
|
||||
<x-icon x-show="!copied" name="o-document-duplicate" class="w-4 h-4" />
|
||||
<x-icon x-show="copied" name="o-check" class="w-4 h-4 text-success" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<x-input :label="__('Setup key')" :value="$manualSetupKey" readonly copyable mono />
|
||||
</div>
|
||||
|
||||
<x-slot:actions>
|
||||
<x-button
|
||||
:disabled="$errors->has('setupData')"
|
||||
label="{{ $this->modalConfig['buttonText'] }}"
|
||||
class="btn-primary"
|
||||
:label="$this->modalConfig['buttonText']"
|
||||
variant="filled"
|
||||
wire:click="showVerificationIfNecessary"
|
||||
/>
|
||||
</x-slot:actions>
|
||||
|
||||
Reference in New Issue
Block a user