Done Profile

master
sundayenglish 4 weeks ago
parent 6381bb444e
commit b387c315b6
  1. 53
      app/Components/Profile/Manager.php
  2. 114
      resources/views/components/profile/manager.blade.php
  3. 2
      resources/views/components/skeleton-form.blade.php
  4. 31
      resources/views/layouts/partials/sidebar.blade.php

@ -4,13 +4,26 @@ namespace App\Components\Profile;
use Livewire\Component;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Spatie\Permission\Models\Role;
class Manager extends Component
{
public object $user;
public array $roles = [];
public array $allRoles = [];
// Profile & roles
public object $user;
public array $roles = [];
public array $allRoles = [];
// Đổi mật khẩu
public string $mode = 'view'; // 'view' | 'password'
public string $currentPassword = '';
public string $password = '';
public string $password_confirmation = '';
protected array $rules = [
'currentPassword' => ['required', 'string'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
];
public function mount(): void
{
@ -19,6 +32,40 @@ class Manager extends Component
$this->roles = $this->user->roles->pluck('name')->toArray();
}
public function showPasswordForm(): void
{
$this->mode = 'password';
$this->resetErrorBag();
$this->resetValidation();
}
public function showView(): void
{
$this->mode = 'view';
$this->reset(['currentPassword', 'password', 'password_confirmation']);
$this->resetErrorBag();
}
public function updatePassword(): void
{
// 1. Validate
$this->validate();
// 2. Kiểm tra mật khẩu hiện tại
if (! Hash::check($this->currentPassword, $this->user->password)) {
$this->addError('currentPassword', 'Mật khẩu hiện tại không đúng.');
return;
}
// 3. Cập nhật mật khẩu mới
$this->user->password = Hash::make($this->password);
$this->user->save();
// 4. Thông báo & quay về view
session()->flash('message', 'Đổi mật khẩu thành công.');
$this->showView();
}
public function render()
{
return view('components.profile.manager', [

@ -1,13 +1,22 @@
<div class="row">
<div class="col-12">
<div class="card mb-4">
{{-- Header --}}
<div class="card-header d-flex justify-content-between align-items-center pb-0">
<h6 class="mb-0">{{ $title }}</h6>
<button type="button" onclick="window.history.back()" class="btn btn-sm btn-secondary">
← Back
</button>
<div class="d-flex align-items-center">
@if ($mode === 'view')
<button wire:click="showPasswordForm" class="btn btn-sm btn-info">
Đổi mật khẩu
</button>
@else
<button wire:click="showView" class="btn btn-sm btn-secondary">
← Quay lại
</button>
@endif
</div>
</div>
{{-- Body --}}
@ -16,26 +25,85 @@
<div class="alert alert-success">{{ session('message') }}</div>
@endif
<dl class="row mb-0" style="line-height:2.5rem">
<dt class="col-sm-3">Họ và tên</dt>
<dd class="col-sm-9">{{ $user->fullname }}</dd>
<dt class="col-sm-3">Email</dt>
<dd class="col-sm-9">{{ $user->email }}</dd>
<dt class="col-sm-3">Vai trò</dt>
<dd class="col-sm-9">
@if ($roles)
{{ implode(', ', $roles) }}
@else
<em>Chưa có vai trò</em>
@endif
</dd>
<dt class="col-sm-3">Ngày tạo tài khoản</dt>
<dd class="col-sm-9">{{ $user->created_at?->format('d/m/Y H:i') ?? '—' }}</dd>
</dl>
@if ($mode === 'view')
{{-- Skeleton khi loading dữ liệu profile --}}
<div wire:loading class="w-100">
{{-- 2 columns (label + value), 4 rows --}}
<x-skeleton-form :columns="2" :rows="4" height="2.5rem" />
</div>
{{-- Actual profile --}}
<div wire:loading.remove>
<dl class="row mb-0" style="line-height:2.5rem">
<dt class="col-sm-3">Họ và tên</dt>
<dd class="col-sm-9">{{ $user->fullname }}</dd>
<dt class="col-sm-3">Email</dt>
<dd class="col-sm-9">{{ $user->email }}</dd>
<dt class="col-sm-3">Vai trò</dt>
<dd class="col-sm-9">
@if ($roles)
{{ implode(', ', $roles) }}
@else
<em>Chưa có vai trò</em>
@endif
</dd>
<dt class="col-sm-3">Ngày tạo tài khoản</dt>
<dd class="col-sm-9">
{{ $user->created_at?->format('d/m/Y H:i') ?? '—' }}
</dd>
</dl>
</div>
@else
{{-- Skeleton khi loading form đổi mật khẩu --}}
<div wire:loading class="w-100">
{{-- 3 fields + 2 buttons --}}
<x-skeleton-form :fields="3" :button-count="2" />
</div>
{{-- Actual đổi mật khẩu --}}
<div wire:loading.remove>
<form wire:submit.prevent="updatePassword">
<div class="mb-3">
<label class="form-label">Mật khẩu hiện tại</label>
<input type="password" wire:model.defer="currentPassword" class="form-control"
placeholder="Nhập mật khẩu hiện tại">
@error('currentPassword')
<div class="text-danger text-xs mt-1">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label class="form-label">Mật khẩu mới</label>
<input type="password" wire:model.defer="password" class="form-control"
placeholder="Tối thiểu 8 ký tự">
@error('password')
<div class="text-danger text-xs mt-1">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label class="form-label">Xác nhận mật khẩu mới</label>
<input type="password" wire:model.defer="password_confirmation" class="form-control"
placeholder="Nhập lại mật khẩu mới">
</div>
<div class="d-flex justify-content-end">
<button wire:click="showView" type="button" class="btn btn-secondary btn-sm me-2">
Hủy
</button>
<button type="submit" class="btn btn-info btn-sm"
onclick="return confirm('Bạn có chắc muốn đổi mật khẩu?')">
Xác nhận
</button>
</div>
</form>
</div>
@endif
</div>
</div>
</div>

@ -1,7 +1,7 @@
@props(['fields', 'buttonCount'])
<div wire:loading class="w-100">
<div class="p-4 border rounded-lg shadow-sm max-w-md mx-auto space-y-4">
<div class="p-4 rounded-lg max-w-md mx-auto space-y-4">
{{-- Title --}}
<div class="skeleton rounded" style="width:10rem; height:2rem;"></div>

@ -141,7 +141,7 @@
<!-- Profile -->
<li class="nav-item">
<a class="nav-link" href="{{ asset('soft-ui/pages/profile.html') }}">
<a class="nav-link {{ request()->routeIs('profile.*') ? 'active' : '' }}" href="{{ route('profile.index') }}">
<div
class="icon icon-shape icon-sm shadow border-radius-md bg-white text-center me-2 d-flex align-items-center justify-content-center">
<svg width="12px" height="12px" viewBox="0 0 46 42" version="1.1"
@ -170,35 +170,6 @@
<span class="nav-link-text ms-1">Profile</span>
</a>
</li>
<!-- Change Password -->
<li class="nav-item">
<a class="nav-link" href="{{ asset('soft-ui/pages/sign-in.html') }}">
<div
class="icon icon-shape icon-sm shadow border-radius-md bg-white text-center me-2 d-flex align-items-center justify-content-center">
<svg width="12px" height="12px" viewBox="0 0 40 44" version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>document</title>
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g transform="translate(-1870.000000, -591.000000)" fill="#FFFFFF"
fill-rule="nonzero">
<g transform="translate(1716.000000, 291.000000)">
<g transform="translate(154.000000, 300.000000)">
<path class="color-background opacity-6"
d="M40,40 L36.3636364,40 L36.3636364,3.63636364 L5.45454545,3.63636364 L5.45454545,0 L38.1818182,0 C39.1854545,0 40,0.814545455 40,1.81818182 L40,40 Z">
</path>
<path class="color-background"
d="M30.9090909,7.27272727 L1.81818182,7.27272727 C0.814545455,7.27272727 0,8.08727273 0,9.09090909 L0,41.8181818 C0,42.8218182 0.814545455,43.6363636 1.81818182,43.6363636 L30.9090909,43.6363636 C31.9127273,43.6363636 32.7272727,42.8218182 32.7272727,41.8181818 L32.7272727,9.09090909 C32.7272727,8.08727273 31.9127273,7.27272727 30.9090909,7.27272727 Z M18.1818182,34.5454545 L7.27272727,34.5454545 L7.27272727,30.9090909 L18.1818182,30.9090909 L18.1818182,34.5454545 Z M25.4545455,27.2727273 L7.27272727,27.2727273 L7.27272727,23.6363636 L25.4545455,23.6363636 L25.4545455,27.2727273 Z M25.4545455,20 L7.27272727,20 L7.27272727,16.3636364 L25.4545455,16.3636364 L25.4545455,20 Z">
</path>
</g>
</g>
</g>
</g>
</svg>
</div>
<span class="nav-link-text ms-1">Change Password</span>
</a>
</li>
</ul>
</div>
</aside>

Loading…
Cancel
Save