clusev/resources/views/livewire/settings/index.blade.php

124 lines
8.1 KiB
PHP

@php
$u = auth()->user();
$initials = strtoupper(mb_substr($u->name, 0, 2));
$field = 'h-9 w-full rounded-md border border-line bg-inset px-3 font-sans text-sm text-ink placeholder:text-ink-4 focus:border-accent/40 focus:outline-none';
$label = 'mb-1 block font-mono text-[11px] uppercase tracking-wider text-ink-3';
$err = 'mt-1 flex items-center gap-1.5 font-mono text-[11px] text-offline';
$tabs = [
['key' => 'profile', 'label' => __('settings.tab_profile'), 'icon' => 'settings'],
['key' => 'security', 'label' => __('settings.tab_security'), 'icon' => 'shield'],
];
@endphp
<div class="space-y-5">
{{-- Identity header --}}
<div class="flex flex-wrap items-center gap-4 rounded-xl border border-line bg-surface p-5 shadow-panel">
<span class="grid h-14 w-14 shrink-0 place-items-center rounded-xl border border-accent/25 bg-accent/10 font-display text-xl font-semibold text-accent">{{ $initials }}</span>
<div class="min-w-0 flex-1">
<p class="font-mono text-[11px] uppercase tracking-[0.2em] text-accent-text">{{ __('settings.account') }}</p>
<h2 class="mt-0.5 truncate font-display text-xl font-semibold text-ink">{{ $u->name }}</h2>
<p class="truncate font-mono text-[11px] text-ink-3">{{ $u->email }}</p>
</div>
<div class="flex items-center gap-2">
<x-badge tone="neutral">{{ __('settings.role_admin') }}</x-badge>
<span @class([
'inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 font-mono text-[11px]',
'border-online/30 text-online' => $twoFactorEnabled,
'border-line text-ink-3' => ! $twoFactorEnabled,
])>
<x-status-dot :status="$twoFactorEnabled ? 'online' : 'offline'" />{{ $twoFactorEnabled ? __('settings.two_factor_on') : __('settings.two_factor_off') }}
</span>
</div>
</div>
<div class="grid grid-cols-1 gap-5 lg:grid-cols-[200px_minmax(0,1fr)]">
{{-- Section nav --}}
<nav class="flex gap-1 lg:flex-col">
@foreach ($tabs as $t)
<button type="button" wire:click="$set('tab', '{{ $t['key'] }}')" @class([
'inline-flex min-h-10 flex-1 items-center gap-2 rounded-md px-3 text-sm transition-colors lg:flex-none',
'bg-accent/10 text-accent-text shadow-[inset_2px_0_0_var(--color-accent)]' => $tab === $t['key'],
'text-ink-2 hover:bg-raised hover:text-ink' => $tab !== $t['key'],
])>
<x-icon :name="$t['icon']" class="h-4 w-4" />{{ $t['label'] }}
</button>
@endforeach
</nav>
{{-- Content --}}
<div class="space-y-5">
@if ($tab === 'profile')
<x-panel :title="__('settings.profile_title')" :subtitle="__('settings.profile_subtitle')">
<form wire:submit="updateProfile" class="space-y-4">
<div>
<label class="{{ $label }}">{{ __('settings.name') }}</label>
<input wire:model="name" type="text" class="{{ $field }}" />
@error('name') <p class="{{ $err }}"><x-icon name="alert" class="h-3.5 w-3.5 shrink-0" />{{ $message }}</p> @enderror
</div>
<div>
<label class="{{ $label }}">{{ __('settings.email') }}</label>
<input wire:model="email" type="email" class="{{ $field }} font-mono" />
@error('email') <p class="{{ $err }}"><x-icon name="alert" class="h-3.5 w-3.5 shrink-0" />{{ $message }}</p> @enderror
</div>
<div class="flex justify-end">
<x-btn variant="primary" type="submit" wire:target="updateProfile" wire:loading.attr="disabled">
<svg wire:loading wire:target="updateProfile" class="h-3.5 w-3.5 animate-spin" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><path d="M21 12a9 9 0 1 1-6.219-8.56" stroke-linecap="round" /></svg>
{{ __('common.save') }}
</x-btn>
</div>
</form>
</x-panel>
@else
<x-panel :title="__('settings.password_title')" :subtitle="__('settings.password_subtitle')">
<form wire:submit="updatePassword" class="space-y-4">
<div>
<label class="{{ $label }}">{{ __('settings.current_password') }}</label>
<input wire:model="current_password" type="password" autocomplete="current-password" class="{{ $field }} font-mono" />
@error('current_password') <p class="{{ $err }}"><x-icon name="alert" class="h-3.5 w-3.5 shrink-0" />{{ $message }}</p> @enderror
</div>
<div class="grid gap-4 sm:grid-cols-2">
<div>
<label class="{{ $label }}">{{ __('settings.new_password') }}</label>
<input wire:model="password" type="password" autocomplete="new-password" class="{{ $field }} font-mono" />
@error('password') <p class="{{ $err }}"><x-icon name="alert" class="h-3.5 w-3.5 shrink-0" />{{ $message }}</p> @enderror
</div>
<div>
<label class="{{ $label }}">{{ __('settings.repeat_password') }}</label>
<input wire:model="password_confirmation" type="password" autocomplete="new-password" class="{{ $field }} font-mono" />
</div>
</div>
<div class="flex justify-end">
<x-btn variant="primary" type="submit" wire:target="updatePassword" wire:loading.attr="disabled">
<svg wire:loading wire:target="updatePassword" class="h-3.5 w-3.5 animate-spin" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><path d="M21 12a9 9 0 1 1-6.219-8.56" stroke-linecap="round" /></svg>
{{ __('settings.change_password') }}
</x-btn>
</div>
</form>
</x-panel>
<x-panel :title="__('settings.twofa_title')" :subtitle="__('settings.twofa_subtitle')">
<div class="flex flex-wrap items-center justify-between gap-3">
<div class="flex items-center gap-3">
<x-status-dot :status="$twoFactorEnabled ? 'online' : 'offline'" />
<div>
<p class="text-sm text-ink">{{ $twoFactorEnabled ? __('settings.twofa_status_on') : __('settings.twofa_status_off') }}</p>
<p class="font-mono text-[11px] text-ink-3">{{ $twoFactorEnabled ? __('settings.twofa_hint_on') : __('settings.twofa_hint_off') }}</p>
</div>
</div>
@if ($twoFactorEnabled)
<div class="flex items-center gap-2">
<x-btn variant="secondary" :href="route('two-factor.recovery')" wire:navigate>{{ __('auth.recovery_manage') }}</x-btn>
<x-btn variant="danger-soft" wire:click="confirmDisableTwoFactor">{{ __('common.disable') }}</x-btn>
</div>
@else
<x-btn variant="accent" :href="route('two-factor.setup')" wire:navigate>
<x-icon name="shield" class="h-3.5 w-3.5" /> {{ __('settings.twofa_setup') }}
</x-btn>
@endif
</div>
</x-panel>
@endif
</div>
</div>
</div>