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:
Andreas Reinhold / reini
2026-09-13 10:49:38 +02:00
co-authored by Claude Opus 5
parent 6d7120e8e2
commit c4a17b65c8
70 changed files with 3043 additions and 1680 deletions
@@ -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>