feature: make style like main page

This commit is contained in:
antv
2026-07-07 11:21:21 +07:00
parent af529a4e81
commit 63764d0718
15 changed files with 385 additions and 185 deletions
+48 -1
View File
@@ -72,7 +72,7 @@
--color-warning: #ed6c02; --color-warning: #ed6c02;
--color-info: #0288d1; --color-info: #0288d1;
--color-bg-main: #f0f4f9; --color-bg-main: #FAFBFF;
--color-bg-sidebar: #ffffff; --color-bg-sidebar: #ffffff;
--color-bg-card: #ffffff; --color-bg-card: #ffffff;
--color-bg-card-soft: #eef2f6; --color-bg-card-soft: #eef2f6;
@@ -270,3 +270,50 @@ select.form-input {
@import "./login.css"; @import "./login.css";
@media (min-width: 1024px) {
body.sidebar-collapsed .sidebar {
width: 80px !important;
min-width: 80px !important;
}
body.sidebar-collapsed .sidebar span {
display: none !important;
}
body.sidebar-collapsed .sidebar nav {
padding-left: 0.5rem !important;
padding-right: 0.5rem !important;
}
body.sidebar-collapsed .sidebar a {
justify-content: center !important;
padding-left: 0 !important;
padding-right: 0 !important;
gap: 0 !important;
}
body.sidebar-collapsed #sidebar-profile div.flex-col,
body.sidebar-collapsed #sidebar-profile svg {
display: none !important;
}
body.sidebar-collapsed #sidebar-profile {
justify-content: center !important;
padding-left: 0.75rem !important;
padding-right: 0.75rem !important;
}
body.sidebar-collapsed .sidebar div.xl\:flex {
display: none !important;
}
body.sidebar-collapsed .sidebar img[alt="GMO-Z.com RUNSYSTEM"] {
display: none !important;
}
body.sidebar-collapsed .sidebar div.shrink-0 {
padding-left: 0.5rem !important;
padding-right: 0.5rem !important;
justify-content: center !important;
}
}
+12 -1
View File
@@ -2,5 +2,16 @@
@section('title', 'Thêm User Mới') @section('title', 'Thêm User Mới')
@section('content') @section('content')
<x-user-page mode="create" /> <div class="mb-6 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 pl-1">
<div>
<h1 class="text-[28px] font-extrabold text-[#1a2b49] flex items-center gap-2 mb-1.5 tracking-tight">
Thêm User Mới
</h1>
<p class="text-gray-500 font-medium text-[14px]">
Tạo tài khoản nhân viên mới thiết lập các thông tin bản.
</p>
</div>
</div>
<x-user-page mode="create" />
@endsection @endsection
+12 -1
View File
@@ -2,5 +2,16 @@
@section('title', 'Chi tiết User') @section('title', 'Chi tiết User')
@section('content') @section('content')
<x-user-page mode="edit" :user="$user" :administrations="$administrations" :selectedMonth="$selectedMonth" /> <div class="mb-6 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 pl-1">
<div>
<h1 class="text-[28px] font-extrabold text-[#1a2b49] flex items-center gap-2 mb-1.5 tracking-tight">
Chi tiết User
</h1>
<p class="text-gray-500 font-medium text-[14px]">
Quản thông tin thẻ theo dõi lịch sử nhận gửi của nhân viên.
</p>
</div>
</div>
<x-user-page mode="edit" :user="$user" :administrations="$administrations" :selectedMonth="$selectedMonth" />
@endsection @endsection
+47 -10
View File
@@ -2,25 +2,62 @@
@section('title', 'Quản lý Users') @section('title', 'Quản lý Users')
@section('content') @section('content')
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4 border-b border-border-light pb-4 mb-4"> <div class="mb-6 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 pl-1">
<h2 id="pageTitle" class="text-xl font-bold text-text-dark flex items-center gap-2"> <div>
@include('admin.users.partials.title') <h1 class="text-[28px] font-extrabold text-[#1a2b49] flex items-center gap-2 mb-1.5 tracking-tight">
</h2> <span id="pageTitle" class="inline-flex items-center gap-2">
<form action="{{ route('admin.reset_cards') }}" method="POST" class="w-full sm:w-auto" onsubmit="return confirm('Bạn có chắc chắn muốn reset toàn bộ số card hiện tại của các user về 0? Hành động này thường chỉ thực hiện vào cuối tháng.');"> @include('admin.users.partials.title')
@csrf </span>
<button type="submit" class="border border-red-200 hover:bg-red-50 text-red-600 font-semibold py-1.5 px-4 rounded text-sm transition-colors shadow-sm w-full sm:w-auto">Reset Card Tháng</button> </h1>
</form> <p class="text-gray-500 font-medium text-[14px]">
Xem danh sách nhân viên, cấp phát thẻ cập nhật trạng thái làm việc.
</p>
</div>
<div class="flex flex-col sm:flex-row items-center gap-3 shrink-0 w-full sm:w-auto">
<a href="{{ route('admin.users.create') }}" class="inline-flex items-center justify-center bg-[#3462f7] hover:bg-blue-700 text-white font-bold py-2.5 px-5 rounded-xl text-sm transition-colors shadow-md shadow-blue-500/10 w-full sm:w-auto">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2.5" stroke="currentColor" class="w-4 h-4 mr-2">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
Thêm user mới
</a>
<button type="button" onclick="openModal('resetModal')" class="cursor-pointer border border-red-200 bg-red-50/50 hover:bg-red-50 text-red-600 font-bold py-2.5 px-5 rounded-xl text-sm transition-colors shadow-sm w-full sm:w-auto">Reset Card Tháng</button>
</div>
</div> </div>
<div id="statsGrid" class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6"> <div id="statsGrid" class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
@include('admin.users.partials.stats') @include('admin.users.partials.stats')
</div> </div>
<x-user-filter-form :action="route('admin.users.index')" :search="$search" :flag-send="$flagSend" :selected-month="$selectedMonth" /> <x-user-filter-form :action="route('admin.users.index')" :search="$search" :flag-send="$flagSend" :selected-month="$selectedMonth" />
<div id="userTableContainer" class="bg-white shadow-sm rounded-xl overflow-hidden border border-border-light mt-2"> <div id="userTableContainer" class="bg-white rounded-[24px] shadow-[0_10px_30px_rgba(15,23,42,0.05)] border border-white overflow-hidden mt-4">
@include('admin.users.partials.table') @include('admin.users.partials.table')
</div> </div>
<x-confirm-modal id="deleteModal" title="Xác nhận nghỉ việc" confirmText="Xác nhận Nghỉ việc" /> <x-confirm-modal id="deleteModal" title="Xác nhận nghỉ việc" confirmText="Xác nhận Nghỉ việc" />
<x-modal id="resetModal" title="Xác nhận reset card" maxWidth="md" hideHeader="true">
<div class="flex items-start gap-4">
<div class="flex-shrink-0 flex items-center justify-center w-12 h-12 bg-red-100 rounded-full">
<svg class="w-6 h-6 text-red-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
</div>
<div class="flex-1 mt-1">
<h3 class="text-lg font-bold text-gray-900">Xác nhận reset card tháng</h3>
<p class="text-sm text-gray-600 mt-2 leading-relaxed">
Bạn chắc chắn muốn reset toàn bộ số card hiện tại của các user về 0?<br><br>Hành động này thường chỉ thực hiện vào cuối tháng không thể hoàn tác.
</p>
</div>
</div>
<x-slot name="footer">
<form action="{{ route('admin.reset_cards') }}" method="POST" class="w-full sm:w-auto">
@csrf
<button type="submit" class="w-full btn-primary !px-6 shadow-sm bg-red-600 hover:bg-red-700">Đồng ý Reset</button>
</form>
<button type="button" onclick="closeModal('resetModal')" class="w-full sm:w-auto btn-secondary !px-6 shadow-sm">Hủy bỏ</button>
</x-slot>
</x-modal>
@endsection @endsection
@@ -1,2 +1,45 @@
<x-stat-card title="User nhận nhiều nhất" :user="$topReceivedUser?->msnv" :value="$topReceivedUser?->total_received ?? 0" bgClass="!bg-[#0f4c81]" valueLabel="Tổng thẻ đã nhận" /> <div class="bg-white rounded-[24px] shadow-[0_10px_30px_rgba(15,23,42,0.05)] p-6 border border-white hover:shadow-md transition-all flex items-center justify-between">
<x-stat-card title="User gửi nhiều nhất" :user="$topSentUser?->msnv" :value="$topSentUser?->total_sent ?? 0" bgClass="!bg-[#ef222e]" valueLabel="Tổng thẻ đã gửi" /> <div class="flex items-center gap-4">
<div class="w-14 h-14 rounded-2xl bg-purple-50 flex items-center justify-center text-purple-500 shrink-0">
<svg class="w-7 h-7" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
<path stroke-linecap="round" stroke-linejoin="round" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
</svg>
</div>
<div class="flex flex-col">
<span class="text-[13px] font-bold text-gray-400 uppercase tracking-wider mb-1">User nhận nhiều nhất</span>
@if($topReceivedUser)
<span class="text-[16px] font-extrabold text-[#1a2b49] leading-tight">{{ $topReceivedUser->name }}</span>
<span class="text-[12px] text-gray-500 font-semibold mt-0.5">MSNV: {{ $topReceivedUser->msnv }}</span>
@else
<span class="text-[15px] font-semibold text-gray-400">Không dữ liệu</span>
@endif
</div>
</div>
<div class="text-right">
<span class="text-[36px] font-extrabold text-purple-600 leading-none">{{ $topReceivedUser?->total_received ?? 0 }}</span>
<span class="block text-[11px] text-gray-400 font-bold uppercase mt-1">Thẻ Nhận</span>
</div>
</div>
<div class="bg-white rounded-[24px] shadow-[0_10px_30px_rgba(15,23,42,0.05)] p-6 border border-white hover:shadow-md transition-all flex items-center justify-between">
<div class="flex items-center gap-4">
<div class="w-14 h-14 rounded-2xl bg-blue-50 flex items-center justify-center text-blue-500 shrink-0">
<svg class="w-7 h-7 transform -rotate-45" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" />
</svg>
</div>
<div class="flex flex-col">
<span class="text-[13px] font-bold text-gray-400 uppercase tracking-wider mb-1">User gửi nhiều nhất</span>
@if($topSentUser)
<span class="text-[16px] font-extrabold text-[#1a2b49] leading-tight">{{ $topSentUser->name }}</span>
<span class="text-[12px] text-gray-500 font-semibold mt-0.5">MSNV: {{ $topSentUser->msnv }}</span>
@else
<span class="text-[15px] font-semibold text-gray-400">Không dữ liệu</span>
@endif
</div>
</div>
<div class="text-right">
<span class="text-[36px] font-extrabold text-blue-600 leading-none">{{ $topSentUser?->total_sent ?? 0 }}</span>
<span class="block text-[11px] text-gray-400 font-bold uppercase mt-1">Thẻ Gửi</span>
</div>
</div>
@@ -1,18 +1,18 @@
<div class="px-6 py-4 border-b border-border-light flex justify-between items-center bg-gray-50/50"> <div class="px-6 py-4 border-b border-gray-100 flex justify-between items-center bg-white">
<span class="text-sm font-medium text-text-medium" id="paginationStats"> <span class="text-sm font-semibold text-gray-500" id="paginationStats">
@if($users->total() > 0) @if($users->total() > 0)
Showing {{ $users->firstItem() }}-{{ $users->lastItem() }} of {{ $users->total() }} users Hiển thị {{ $users->firstItem() }}-{{ $users->lastItem() }} trên {{ $users->total() }} nhân viên
@else @else
Showing 0 users Hiển thị 0 nhân viên
@endif @endif
</span> </span>
</div> </div>
<div class="overflow-x-auto"> <div class="overflow-x-auto">
<table class="min-w-full divide-y divide-border-light text-sm"> <table class="min-w-full divide-y divide-gray-100 text-sm">
<thead class="bg-gray-50/80"> <thead class="bg-gray-50/50">
<tr> <tr>
<th class="table-header-cell">MSNV</th> <th class="table-header-cell">MSNV</th>
<th class="table-header-cell">Email</th> <th class="table-header-cell">Nhân viên</th>
<th class="table-header-cell">Đã Nhận</th> <th class="table-header-cell">Đã Nhận</th>
<th class="table-header-cell">Đã Gửi</th> <th class="table-header-cell">Đã Gửi</th>
<th class="table-header-cell">Số thẻ</th> <th class="table-header-cell">Số thẻ</th>
@@ -20,24 +20,29 @@
<th class="table-header-cell text-right">Thao tác</th> <th class="table-header-cell text-right">Thao tác</th>
</tr> </tr>
</thead> </thead>
<tbody class="bg-white divide-y divide-border-light"> <tbody class="bg-white divide-y divide-gray-100">
@forelse($users as $u) @forelse($users as $u)
<tr class="hover:bg-slate-50 transition-colors"> <tr class="hover:bg-slate-50/60 transition-colors">
<td class="table-body-cell font-bold text-text-dark">{{ $u->msnv }}</td> <td class="table-body-cell font-bold text-text-dark">{{ $u->msnv }}</td>
<td class="table-body-cell text-text-medium">{{ $u->mail }}</td> <td class="table-body-cell">
<div class="flex flex-col">
<span class="font-bold text-text-dark">{{ $u->name }}</span>
<span class="text-xs text-gray-400 mt-0.5">{{ $u->mail }}</span>
</div>
</td>
<td class="table-body-cell font-extrabold text-green-600">+{{ $u->total_received }}</td> <td class="table-body-cell font-extrabold text-green-600">+{{ $u->total_received }}</td>
<td class="table-body-cell font-extrabold text-blue-600">{{ $u->total_sent }}</td> <td class="table-body-cell font-extrabold text-blue-600">{{ $u->total_sent }}</td>
<td class="table-body-cell font-extrabold text-orange-500">{{ $u->card }}</td> <td class="table-body-cell font-extrabold text-orange-500">{{ $u->card }}</td>
<td class="table-body-cell"> <td class="table-body-cell">
@if($u->flag_send) @if($u->flag_send)
<span class="px-2 py-1 inline-flex text-[10px] leading-5 font-semibold rounded-full bg-green-100 text-green-800">Được gửi</span> <span class="px-2.5 py-1 inline-flex text-[10px] leading-5 font-bold rounded-full bg-green-50 text-green-700 border border-green-100">Được gửi</span>
@else @else
<span class="px-2 py-1 inline-flex text-[10px] leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">Không</span> <span class="px-2.5 py-1 inline-flex text-[10px] leading-5 font-bold rounded-full bg-gray-50 text-gray-500 border border-gray-100">Không</span>
@endif @endif
</td> </td>
<td class="table-body-cell text-right font-medium space-x-3"> <td class="table-body-cell text-right font-medium space-x-3">
<a href="{{ route('admin.users.edit', $u->msnv) }}" class="table-action-link text-primary">Quản </a> <a href="{{ route('admin.users.edit', $u->msnv) }}" class="table-action-link !text-[#3462f7] hover:!text-blue-800">Quản </a>
<button type="button" onclick="openDeleteModal('{{ $u->msnv }}', '{{ route('admin.users.destroy', $u->msnv) }}')" class="table-action-link text-red-500">Nghỉ việc</button> <button type="button" onclick="openDeleteModal('{{ $u->msnv }}', '{{ route('admin.users.destroy', $u->msnv) }}')" class="table-action-link !text-red-500 hover:!text-red-700">Nghỉ việc</button>
</td> </td>
</tr> </tr>
@empty @empty
@@ -49,7 +54,7 @@
</table> </table>
</div> </div>
@if($users->hasPages()) @if($users->hasPages())
<div class="px-6 py-4 border-t border-border-light bg-gray-50/30"> <div class="px-6 py-4 border-t border-gray-100 bg-white">
{{ $users->links() }} {{ $users->links() }}
</div> </div>
@endif @endif
@@ -1,4 +1,4 @@
Danh sách User Danh sách User
<div id="loadingSpinner" class="hidden animate-spin rounded-full h-5 w-5 border-2 border-indigo-600 border-t-transparent" role="status"> <div id="loadingSpinner" class="hidden animate-spin rounded-full h-5 w-5 border-2 border-[#3462f7] border-t-transparent" role="status">
<span class="sr-only">Loading...</span> <span class="sr-only">Loading...</span>
</div> </div>
@@ -1,17 +1,16 @@
@props(['mode', 'user' => null]) @props(['mode', 'user' => null])
<div class="form-footer flex space-x-2"> <div class="px-6 py-5 bg-gray-50/50 border-t border-gray-100 flex flex-col sm:flex-row justify-end gap-3 rounded-b-[24px]">
@if($mode === 'create') @if($mode === 'create')
<a href="{{ route('admin.users.index') }}" class="btn-secondary min-w-[120px] h-[45px]">Hủy bỏ</a> <a href="{{ route('admin.users.index') }}" class="inline-flex items-center justify-center bg-white border border-[#d9dfe7] hover:bg-gray-50 text-[#1a2b49] font-bold py-2.5 px-6 rounded-xl text-sm transition-colors shadow-sm min-w-[120px] h-[45px]">Hủy bỏ</a>
<button type="submit" class="btn-primary min-w-[120px] h-[45px]">Tạo Nhân Viên</button> <button type="submit" class="inline-flex items-center justify-center bg-[#3462f7] hover:bg-blue-700 text-white font-bold py-2.5 px-6 rounded-xl text-sm transition-colors shadow-md shadow-blue-500/10 min-w-[120px] h-[45px]">Tạo Nhân Viên</button>
@elseif($mode === 'edit') @elseif($mode === 'edit')
<a href="{{ route('admin.users.index') }}" class="btn-secondary min-w-[120px] h-[45px]">Quay lại</a> <a href="{{ route('admin.users.index') }}" class="inline-flex items-center justify-center bg-white border border-[#d9dfe7] hover:bg-gray-50 text-[#1a2b49] font-bold py-2.5 px-6 rounded-xl text-sm transition-colors shadow-sm min-w-[120px] h-[45px]">Quay lại</a>
<button type="submit" class="btn-primary min-w-[120px] h-[45px]">Lưu Thay Đổi</button> <button type="submit" class="inline-flex items-center justify-center bg-[#3462f7] hover:bg-blue-700 text-white font-bold py-2.5 px-6 rounded-xl text-sm transition-colors shadow-md shadow-blue-500/10 min-w-[120px] h-[45px]">Lưu Thay Đổi</button>
@elseif($mode === 'delete') @elseif($mode === 'delete')
<a href="{{ route('admin.users.index') }}" class="btn-secondary min-w-[120px] h-[45px]">Quay lại</a> <a href="{{ route('admin.users.index') }}" class="inline-flex items-center justify-center bg-white border border-[#d9dfe7] hover:bg-gray-50 text-[#1a2b49] font-bold py-2.5 px-6 rounded-xl text-sm transition-colors shadow-sm min-w-[120px] h-[45px]">Quay lại</a>
<button type="submit" class="btn-primary bg-red-600 hover:bg-red-700 min-w-[120px] h-[45px]">Nghỉ việc</button> <button type="submit" class="inline-flex items-center justify-center bg-red-600 hover:bg-red-700 text-white font-bold py-2.5 px-6 rounded-xl text-sm transition-colors shadow-md shadow-red-500/10 min-w-[120px] h-[45px]">Nghỉ việc</button>
@elseif($mode === 'self') @elseif($mode === 'self')
<a href="{{ route('user.dashboard') }}" class="btn-secondary min-w-[120px] h-[45px]">Quay lại</a> <a href="{{ route('user.dashboard') }}" class="inline-flex items-center justify-center bg-white border border-[#d9dfe7] hover:bg-gray-50 text-[#1a2b49] font-bold py-2.5 px-6 rounded-xl text-sm transition-colors shadow-sm min-w-[120px] h-[45px]">Quay lại</a>
@endif @endif
</div> </div>
{{-- duplicated block removed --}}
@@ -7,6 +7,6 @@
@endphp @endphp
<form action="{{ $actionUrl }}" method="GET" class="flex items-center gap-2 flex-shrink-0"> <form action="{{ $actionUrl }}" method="GET" class="flex items-center gap-2 flex-shrink-0">
<input type="month" name="month" value="{{ $selectedMonth }}" onchange="this.form.submit()" class="form-input !min-h-[45px] !py-0 !text-sm w-auto border-border-medium rounded bg-white"> <input type="month" name="month" value="{{ $selectedMonth }}" onchange="this.form.submit()" class="w-full sm:w-auto h-[40px] px-4 bg-white border border-[#d9dfe7] rounded-xl outline-none text-text-dark text-sm placeholder-gray-400 focus:border-[#3462f7] focus:ring-2 focus:ring-blue-500/20 hover:border-gray-400 transition-all duration-200 shadow-sm">
<button type="submit" class="btn-primary !h-[45px] !px-4 text-sm rounded shadow-sm hover:translate-y-[-1px] sm:hidden">Lọc</button> <button type="submit" class="inline-flex items-center justify-center bg-[#3462f7] hover:bg-blue-700 text-white font-bold h-[40px] px-4 rounded-xl text-sm transition-colors shadow-md shadow-blue-500/10 sm:hidden">Lọc</button>
</form> </form>
@@ -1,8 +1,8 @@
@props(['mode', 'user', 'administrations', 'selectedMonth']) @props(['mode', 'user', 'administrations', 'selectedMonth'])
<div class="bg-white rounded-xl shadow-sm border border-border-light overflow-hidden flex flex-col h-full"> <div class="bg-white rounded-[24px] shadow-[0_10px_30px_rgba(15,23,42,0.05)] border border-white overflow-hidden flex flex-col h-full">
<div class="px-6 py-4 sm:py-5 border-b border-border-light bg-gray-50/50 flex flex-col sm:flex-row sm:items-center justify-between gap-3"> <div class="px-6 py-5 border-b border-gray-100 bg-white flex flex-col sm:flex-row sm:items-center justify-between gap-3">
<h3 class="text-lg font-bold text-text-dark"> <h3 class="text-[17px] font-extrabold text-[#1a2b49] leading-tight">
@if($mode === 'self') @if($mode === 'self')
Lịch sử nhận / gửi thẻ (Tháng {{ Carbon\Carbon::parse($selectedMonth)->format('m/Y') }}) Lịch sử nhận / gửi thẻ (Tháng {{ Carbon\Carbon::parse($selectedMonth)->format('m/Y') }})
@else @else
@@ -14,8 +14,8 @@
</div> </div>
<div class="p-0 overflow-y-auto max-h-[600px]"> <div class="p-0 overflow-y-auto max-h-[600px]">
<table class="min-w-full divide-y divide-border-light text-sm"> <table class="min-w-full divide-y divide-gray-100 text-sm">
<thead class="bg-gray-50/80 sticky top-0"> <thead class="bg-gray-50/50 sticky top-0">
<tr> <tr>
<th class="px-6 py-4 text-left text-xs font-bold text-text-light uppercase tracking-wider">Ngày</th> <th class="px-6 py-4 text-left text-xs font-bold text-text-light uppercase tracking-wider">Ngày</th>
<th class="px-6 py-4 text-left text-xs font-bold text-text-light uppercase tracking-wider"> <th class="px-6 py-4 text-left text-xs font-bold text-text-light uppercase tracking-wider">
@@ -27,32 +27,32 @@
<th class="px-6 py-4 text-left text-xs font-bold text-text-light uppercase tracking-wider">Số lượng</th> <th class="px-6 py-4 text-left text-xs font-bold text-text-light uppercase tracking-wider">Số lượng</th>
</tr> </tr>
</thead> </thead>
<tbody class="bg-white divide-y divide-border-light"> <tbody class="bg-white divide-y divide-gray-100">
@forelse($administrations as $admin_record) @forelse($administrations as $admin_record)
@php @php
$isReceiver = $admin_record->msnv == $user->msnv && $admin_record->received > 0; $isReceiver = $admin_record->msnv == $user->msnv && $admin_record->received > 0;
@endphp @endphp
<tr class="hover:bg-slate-50 transition-colors"> <tr class="hover:bg-slate-50/60 transition-colors">
<td class="px-6 py-3 whitespace-nowrap text-text-medium font-medium">{{ Carbon\Carbon::parse($admin_record->date)->format('d/m/Y') }}</td> <td class="px-6 py-3.5 whitespace-nowrap text-text-medium font-medium">{{ Carbon\Carbon::parse($admin_record->date)->format('d/m/Y') }}</td>
<td class="px-6 py-3 whitespace-nowrap"> <td class="px-6 py-3.5 whitespace-nowrap">
@if($isReceiver) @if($isReceiver)
<span class="px-2 py-1 inline-flex text-xs leading-5 font-bold rounded-full bg-green-100 text-green-800"> <span class="px-2.5 py-1 inline-flex text-[10px] leading-5 font-bold rounded-full bg-green-50 text-green-700 border border-green-100">
{{ $mode === 'self' ? 'Bạn đã nhận thẻ' : 'Nhận thẻ' }} {{ $mode === 'self' ? 'Bạn đã nhận thẻ' : 'Nhận thẻ' }}
</span> </span>
@else @else
<span class="px-2 py-1 inline-flex text-xs leading-5 font-bold rounded-full bg-blue-100 text-blue-800"> <span class="px-2.5 py-1 inline-flex text-[10px] leading-5 font-bold rounded-full bg-blue-50 text-blue-700 border border-blue-100">
{{ $mode === 'self' ? 'Bạn gửi tặng thẻ' : 'Gửi thẻ' }} {{ $mode === 'self' ? 'Bạn gửi tặng thẻ' : 'Gửi thẻ' }}
</span> </span>
@endif @endif
</td> </td>
<td class="px-6 py-3 whitespace-nowrap font-bold text-text-dark"> <td class="px-6 py-3.5 whitespace-nowrap font-bold text-[#1a2b49]">
@if($isReceiver) @if($isReceiver)
{{ $mode === 'self' ? 'Nhận từ: ' : 'Từ: ' }}<span class="text-primary">{{ $admin_record->sender }}</span> {{ $mode === 'self' ? 'Nhận từ: ' : 'Từ: ' }}<span class="text-[#3462f7]">{{ $admin_record->sender }}</span>
@else @else
{{ $mode === 'self' ? 'Gửi cho: ' : 'Tới: ' }}<span class="text-primary">{{ $admin_record->receiver }}</span> {{ $mode === 'self' ? 'Gửi cho: ' : 'Tới: ' }}<span class="text-[#3462f7]">{{ $admin_record->receiver }}</span>
@endif @endif
</td> </td>
<td class="px-6 py-3 whitespace-nowrap font-extrabold text-lg"> <td class="px-6 py-3.5 whitespace-nowrap font-extrabold text-base">
@if($isReceiver) @if($isReceiver)
<span class="text-green-600">+{{ $admin_record->received }}</span> <span class="text-green-600">+{{ $admin_record->received }}</span>
@else @else
@@ -71,7 +71,7 @@
</table> </table>
</div> </div>
@if($administrations->hasPages()) @if($administrations->hasPages())
<div class="px-6 py-4 border-t border-border-light bg-gray-50/30"> <div class="px-6 py-4 border-t border-gray-100 bg-white">
{{ $administrations->links() }} {{ $administrations->links() }}
</div> </div>
@endif @endif
@@ -1,28 +1,28 @@
@props(['action', 'search' => '', 'flagSend' => '', 'selectedMonth' => '']) @props(['action', 'search' => '', 'flagSend' => '', 'selectedMonth' => ''])
<form id="filterForm" action="{{ $action }}" method="GET" class="flex flex-col md:flex-row items-stretch md:items-center gap-3 w-full mb-6 bg-gray-50/50 p-4 rounded-xl border border-border-light"> <form id="filterForm" action="{{ $action }}" method="GET" class="flex flex-col md:flex-row items-stretch md:items-center gap-3.5 w-full mb-6 bg-white p-5 rounded-[24px] border border-white shadow-[0_10px_30px_rgba(15,23,42,0.05)]">
<div class="relative flex items-center flex-1 min-w-[200px]"> <div class="relative flex items-center flex-1 min-w-[200px]">
<input type="text" id="searchInput" name="search" value="{{ $search }}" placeholder="Tìm MSNV hoặc Email..." class="form-input !py-1.5 !text-sm !pl-10 !pr-10 w-full border-border-medium rounded bg-white"> <input type="text" id="searchInput" name="search" value="{{ $search }}" placeholder="Tìm MSNV hoặc Email..." class="form-input !py-1.5 !text-sm !pl-10 !pr-10 w-full border-[#d9dfe7] rounded-xl bg-white">
<svg class="w-4 h-4 text-text-light absolute left-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" style="pointer-events: none;"> <svg class="w-4 h-4 text-text-light absolute left-3.5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" style="pointer-events: none;">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg> </svg>
<button type="button" id="btnClearSearch" class="{{ empty($search) ? 'hidden' : '' }} absolute right-3 text-text-light hover:text-text-dark focus:outline-none"> <button type="button" id="btnClearSearch" class="{{ empty($search) ? 'hidden' : '' }} absolute right-3.5 text-text-light hover:text-text-dark focus:outline-none">
<svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg> </svg>
</button> </button>
</div> </div>
<select name="flag_send" id="flagSendFilter" class="form-input !py-1.5 !text-sm w-full md:w-auto border-border-medium rounded bg-white min-w-[150px]"> <select name="flag_send" id="flagSendFilter" class="form-input !py-1.5 !text-sm w-full md:w-auto border-[#d9dfe7] rounded-xl bg-white min-w-[180px]">
<option value="" {{ is_null($flagSend) || $flagSend === '' ? 'selected' : '' }}>Quyền gửi: Tất cả</option> <option value="" {{ is_null($flagSend) || $flagSend === '' ? 'selected' : '' }}>Quyền gửi: Tất cả</option>
<option value="1" {{ $flagSend === '1' ? 'selected' : '' }}>Được gửi</option> <option value="1" {{ $flagSend === '1' ? 'selected' : '' }}>Được gửi</option>
<option value="0" {{ $flagSend === '0' ? 'selected' : '' }}>Không được gửi</option> <option value="0" {{ $flagSend === '0' ? 'selected' : '' }}>Không được gửi</option>
</select> </select>
<input type="month" name="month" id="monthFilter" value="{{ $selectedMonth }}" class="form-input !py-1.5 !text-sm w-full md:w-auto border-border-medium rounded bg-white"> <input type="month" name="month" id="monthFilter" value="{{ $selectedMonth }}" class="form-input !py-1.5 !text-sm w-full md:w-auto border-[#d9dfe7] rounded-xl bg-white">
<div class="flex items-center gap-2 w-full md:w-auto"> <div class="flex items-center gap-2 w-full md:w-auto shrink-0">
<button type="submit" id="btnFilter" class="btn-primary !py-1.5 !px-4 text-sm rounded shadow-sm hover:translate-y-[-1px] flex items-center justify-center min-w-[70px] flex-1 md:flex-none">Tìm</button> <button type="submit" id="btnFilter" class="btn-primary !py-2.5 !px-5 text-sm rounded-xl font-bold shadow-md shadow-blue-500/10 !bg-[#3462f7] hover:!bg-blue-700 flex items-center justify-center min-w-[80px] flex-1 md:flex-none">Tìm</button>
<button type="button" id="btnClearFilters" class="btn-secondary !py-1.5 !px-4 text-sm rounded border border-border-medium hover:bg-gray-100 flex items-center justify-center min-w-[100px] flex-1 md:flex-none">Xóa bộ lọc</button> <button type="button" id="btnClearFilters" class="btn-secondary !py-2.5 !px-5 text-sm rounded-xl font-bold border border-[#d9dfe7] hover:bg-gray-100 flex items-center justify-center min-w-[100px] flex-1 md:flex-none">Xóa bộ lọc</button>
</div> </div>
</form> </form>
+69 -67
View File
@@ -20,32 +20,34 @@
}; };
@endphp @endphp
<div class="form-container flex flex-col h-full justify-between"> <div class="bg-white rounded-[24px] shadow-[0_10px_30px_rgba(15,23,42,0.05)] border border-white overflow-hidden flex flex-col justify-between">
<div class="form-header"> <div class="bg-gradient-to-r from-blue-50/70 via-indigo-50/50 to-purple-50/30 px-6 py-6 sm:px-8 border-b border-gray-100">
<h3 class="form-header-title"> <h3 class="text-[17px] font-extrabold text-[#1a2b49] leading-tight flex items-center gap-2">
{{ match($mode) { <span>{{ match($mode) {
'create' => 'Thêm User Mới', 'create' => '👥 Thêm User Mới',
'edit' => 'Quản Lý Thẻ: ' . ($user?->msnv), 'edit' => '⚙️ Quản Lý Thẻ: ' . ($user?->msnv),
'delete' => 'Xác nhận nghỉ việc: ' . ($user?->msnv), 'delete' => '⚠️ Xác nhận nghỉ việc: ' . ($user?->msnv),
'self' => 'Thông tin cá nhân: ' . ($user?->msnv), 'self' => '👤 Thông tin cá nhân: ' . ($user?->msnv),
} }} } }}</span>
</h3> </h3>
</div> </div>
@if(!$isCreate && $user) @if(!$isCreate && $user)
<div class="form-body pb-0"> <div class="p-6 sm:p-8 pb-0">
<div class="mb-6 pb-6 border-b border-border-light space-y-3"> <div class="mb-2 p-5 rounded-2xl bg-gradient-to-br from-slate-50 to-gray-50 border border-gray-100 flex items-center gap-4 shadow-sm">
<div class="flex justify-between items-center text-sm"> <div class="w-12 h-12 rounded-full bg-blue-50 text-[#3462f7] flex items-center justify-center font-extrabold text-lg shadow-inner flex-shrink-0">
<span class="text-text-medium font-bold">Email:</span> {{ strtoupper(substr($user->name ?? $user->msnv, 0, 2)) }}
<span class="font-semibold text-text-dark">{{ $user->mail }}</span>
</div> </div>
<div class="flex justify-between items-center text-sm"> <div class="flex-1 min-w-0">
<span class="text-text-medium font-bold">Role:</span> <h4 class="font-extrabold text-[#1a2b49] text-base truncate">{{ $user->name }}</h4>
<span class="font-semibold text-text-dark">{{ $user->role == \App\Models\User::ROLE_ADMIN ? 'Admin' : 'Member' }}</span> <p class="text-xs text-gray-500 font-medium truncate mb-1">{{ $user->mail }}</p>
</div> <div class="flex items-center gap-2">
<div class="flex justify-between items-center text-sm"> <span class="px-2 py-0.5 text-[10px] font-bold rounded-full bg-blue-50 text-blue-700 border border-blue-100">
<span class="text-text-medium font-bold">Số thẻ:</span> {{ $user->role == \App\Models\User::ROLE_ADMIN ? 'Admin' : 'Member' }}
<span class="font-extrabold text-orange-500 text-xl">{{ $user->card }}</span> </span>
<span class="text-xs text-gray-300 font-semibold"></span>
<span class="text-xs font-bold text-gray-600">Thẻ: <span class="text-orange-500 font-extrabold text-sm">{{ $user->card }}</span></span>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -59,94 +61,94 @@
@endif @endif
@endif @endif
<div class="form-body {{ !$isCreate ? 'pt-0' : '' }}"> <div class="p-6 sm:p-8 {{ !$isCreate ? 'pt-0' : '' }} space-y-5">
@if($isCreate) @if($isCreate)
<div class="form-group"> <div class="relative">
<label class="form-label" for="msnv"> nhân viên (MSNV) <span class="text-red-500">*</span></label> <label class="block text-[14px] font-bold text-[#1a2b49] mb-2" for="msnv"> nhân viên (MSNV) <span class="text-red-500">*</span></label>
<div class="form-input-group"> <div class="relative flex items-center w-full">
<span class="form-input-icon"> <span class="absolute left-4 text-gray-400 pointer-events-none w-5 h-5 flex items-center justify-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.8" stroke="currentColor" class="w-5 h-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M15 9h3.75M15 12h3.75M15 15h3.75M4.5 19.5h15a2.25 2.25 0 002.25-2.25V6.75A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25v10.5A2.25 2.25 0 004.5 19.5zm6-10.125a1.875 1.875 0 11-3.75 0 1.875 1.875 0 013.75 0zm1.294 6.336a6.721 6.721 0 01-3.17.789 6.721 6.721 0 01-3.168-.789 3.376 3.376 0 016.338 0z" /> <path stroke-linecap="round" stroke-linejoin="round" d="M15 9h3.75M15 12h3.75M15 15h3.75M4.5 19.5h15a2.25 2.25 0 002.25-2.25V6.75A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25v10.5A2.25 2.25 0 004.5 19.5zm6-10.125a1.875 1.875 0 11-3.75 0 1.875 1.875 0 013.75 0zm1.294 6.336a6.721 6.721 0 01-3.17.789 6.721 6.721 0 01-3.168-.789 3.376 3.376 0 016.338 0z" />
</svg> </svg>
</span> </span>
<input type="number" name="msnv" id="msnv" class="form-input form-input-with-icon bg-white" required value="{{ old('msnv') }}" placeholder="VD: 1001"> <input type="number" name="msnv" id="msnv" class="form-input form-input-with-icon !rounded-xl !focus:border-[#3462f7] !focus:ring-[#3462f7]/20" style="padding-left: 48px;" required value="{{ old('msnv') }}" placeholder="VD: 1001">
</div> </div>
@error('msnv')<span class="error-text">{{ $message }}</span>@enderror @error('msnv')<span class="text-red-500 text-[13px] font-semibold mt-1.5 block">{{ $message }}</span>@enderror
</div> </div>
<div class="form-group"> <div class="relative">
<label class="form-label" for="name">Họ tên <span class="text-red-500">*</span></label> <label class="block text-[14px] font-bold text-[#1a2b49] mb-2" for="name">Họ tên <span class="text-red-500">*</span></label>
<div class="form-input-group"> <div class="relative flex items-center w-full">
<span class="form-input-icon"> <span class="absolute left-4 text-gray-400 pointer-events-none w-5 h-5 flex items-center justify-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.8" stroke="currentColor" class="w-5 h-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" /> <path stroke-linecap="round" stroke-linejoin="round" d="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" />
</svg> </svg>
</span> </span>
<input type="text" name="name" id="name" class="form-input form-input-with-icon bg-white" required value="{{ old('name') }}" placeholder="VD: Nguyễn Văn A"> <input type="text" name="name" id="name" class="form-input form-input-with-icon !rounded-xl !focus:border-[#3462f7] !focus:ring-[#3462f7]/20" style="padding-left: 48px;" required value="{{ old('name') }}" placeholder="VD: Nguyễn Văn A">
</div> </div>
@error('name')<span class="error-text">{{ $message }}</span>@enderror @error('name')<span class="text-red-500 text-[13px] font-semibold mt-1.5 block">{{ $message }}</span>@enderror
</div> </div>
<div class="form-group"> <div class="relative">
<label class="form-label" for="mail">Email <span class="text-red-500">*</span></label> <label class="block text-[14px] font-bold text-[#1a2b49] mb-2" for="mail">Email <span class="text-red-500">*</span></label>
<div class="form-input-group"> <div class="relative flex items-center w-full">
<span class="form-input-icon"> <span class="absolute left-4 text-gray-400 pointer-events-none w-5 h-5 flex items-center justify-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.8" stroke="currentColor" class="w-5 h-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75" /> <path stroke-linecap="round" stroke-linejoin="round" d="M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75" />
</svg> </svg>
</span> </span>
<input type="email" name="mail" id="mail" class="form-input form-input-with-icon bg-white" required value="{{ old('mail') }}" placeholder="VD: nguyenvana@runsystem.net"> <input type="email" name="mail" id="mail" class="form-input form-input-with-icon !rounded-xl !focus:border-[#3462f7] !focus:ring-[#3462f7]/20" style="padding-left: 48px;" required value="{{ old('mail') }}" placeholder="VD: nguyenvana@runsystem.net">
</div> </div>
@error('mail')<span class="error-text">{{ $message }}</span>@enderror @error('mail')<span class="text-red-500 text-[13px] font-semibold mt-1.5 block">{{ $message }}</span>@enderror
</div> </div>
<div class="form-group"> <div class="relative">
<label class="form-label" for="departments">Phòng ban <span class="text-red-500">*</span></label> <label class="block text-[14px] font-bold text-[#1a2b49] mb-2" for="departments">Phòng ban <span class="text-red-500">*</span></label>
<select name="departments" id="departments" class="form-input bg-white" required> <select name="departments" id="departments" class="form-input !rounded-xl !focus:border-[#3462f7] !focus:ring-[#3462f7]/20" required>
<option value="1" {{ old('departments') == '1' ? 'selected' : '' }}>Phòng Phát Triển (Dev)</option> <option value="1" {{ old('departments') == '1' ? 'selected' : '' }}>Phòng Phát Triển (Dev)</option>
<option value="2" {{ old('departments') == '2' ? 'selected' : '' }}>Phòng Nhân Sự (HR)</option> <option value="2" {{ old('departments') == '2' ? 'selected' : '' }}>Phòng Nhân Sự (HR)</option>
<option value="3" {{ old('departments') == '3' ? 'selected' : '' }}>Phòng Kinh Doanh (Sale)</option> <option value="3" {{ old('departments') == '3' ? 'selected' : '' }}>Phòng Kinh Doanh (Sale)</option>
<option value="4" {{ old('departments') == '4' ? 'selected' : '' }}>Ban Giám Đốc (Board)</option> <option value="4" {{ old('departments') == '4' ? 'selected' : '' }}>Ban Giám Đốc (Board)</option>
<option value="5" {{ old('departments') == '5' ? 'selected' : '' }}>Phòng Marketing</option> <option value="5" {{ old('departments') == '5' ? 'selected' : '' }}>Phòng Marketing</option>
</select> </select>
@error('departments')<span class="error-text">{{ $message }}</span>@enderror @error('departments')<span class="text-red-500 text-[13px] font-semibold mt-1.5 block">{{ $message }}</span>@enderror
</div> </div>
<div class="form-group"> <div class="relative">
<label class="form-label" for="password">Mật khẩu khởi tạo <span class="text-red-500">*</span></label> <label class="block text-[14px] font-bold text-[#1a2b49] mb-2" for="password">Mật khẩu khởi tạo <span class="text-red-500">*</span></label>
<div class="form-input-group"> <div class="relative flex items-center w-full">
<span class="form-input-icon"> <span class="absolute left-4 text-gray-400 pointer-events-none w-5 h-5 flex items-center justify-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.8" stroke="currentColor" class="w-5 h-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 10-9 0V10.5m-2.812 10.5h14.625c.621 0 1.125-.504 1.125-1.125V11.25c0-.621-.504-1.125-1.125-1.125H3.75c-.621 0-1.125.504-1.125 1.125v7.875c0 .621.504 1.125 1.125 1.125z" /> <path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 10-9 0V10.5m-2.812 10.5h14.625c.621 0 1.125-.504 1.125-1.125V11.25c0-.621-.504-1.125-1.125-1.125H3.75c-.621 0-1.125.504-1.125 1.125v7.875c0 .621.504 1.125 1.125 1.125z" />
</svg> </svg>
</span> </span>
<input type="text" name="password" id="password" class="form-input form-input-with-icon bg-gray-50" readonly required value="1234@Dcba"> <input type="text" name="password" id="password" class="form-input form-input-with-icon !rounded-xl !bg-gray-50 !text-gray-500 cursor-not-allowed" style="padding-left: 48px;" readonly required value="1234@Dcba">
</div> </div>
<span class="helper-text">Mật khẩu mặc định cho tất cả user mới tạo.</span> <span class="text-[13px] text-gray-400 mt-2 block">Mật khẩu mặc định cho tất cả user mới tạo.</span>
@error('password')<span class="error-text">{{ $message }}</span>@enderror @error('password')<span class="text-red-500 text-[13px] font-semibold mt-1.5 block">{{ $message }}</span>@enderror
</div> </div>
<div class="form-group mb-0"> <div class="relative">
<label class="form-label" for="role">Quyền hạn <span class="text-red-500">*</span></label> <label class="block text-[14px] font-bold text-[#1a2b49] mb-2" for="role">Quyền hạn <span class="text-red-500">*</span></label>
<select name="role" id="role" class="form-input bg-white" required> <select name="role" id="role" class="form-input !rounded-xl !focus:border-[#3462f7] !focus:ring-[#3462f7]/20" required>
<option value="{{ \App\Models\User::ROLE_MEMBER }}" {{ old('role') == \App\Models\User::ROLE_MEMBER ? 'selected' : '' }}>Nhân viên (Member)</option> <option value="{{ \App\Models\User::ROLE_MEMBER }}" {{ old('role') == \App\Models\User::ROLE_MEMBER ? 'selected' : '' }}>Nhân viên (Member)</option>
<option value="{{ \App\Models\User::ROLE_ADMIN }}" {{ old('role') == \App\Models\User::ROLE_ADMIN ? 'selected' : '' }}>Quản trị viên (Admin)</option> <option value="{{ \App\Models\User::ROLE_ADMIN }}" {{ old('role') == \App\Models\User::ROLE_ADMIN ? 'selected' : '' }}>Quản trị viên (Admin)</option>
</select> </select>
@error('role')<span class="error-text">{{ $message }}</span>@enderror @error('role')<span class="text-red-500 text-[13px] font-semibold mt-1.5 block">{{ $message }}</span>@enderror
</div> </div>
@endif @endif
@if($isEdit && $user) @if($isEdit && $user)
<div class="form-group"> <div class="relative">
<label class="form-label" for="num_card">Nạp thêm thẻ</label> <label class="block text-[14px] font-bold text-[#1a2b49] mb-2" for="num_card">Nạp thêm thẻ</label>
<input type="number" name="num_card" id="num_card" min="1" class="form-input bg-white" placeholder="Nhập số lượng thẻ muốn nạp..."> <input type="number" name="num_card" id="num_card" min="1" class="form-input !rounded-xl !focus:border-[#3462f7] !focus:ring-[#3462f7]/20" placeholder="Nhập số lượng thẻ muốn nạp...">
@error('num_card')<span class="error-text">{{ $message }}</span>@enderror @error('num_card')<span class="text-red-500 text-[13px] font-semibold mt-1.5 block">{{ $message }}</span>@enderror
</div> </div>
<div class="form-group mb-0"> <div class="relative pt-2">
<label class="flex items-center gap-3 cursor-pointer p-4 border border-[#d9dfe7] rounded-lg hover:bg-gray-50 hover:border-gray-400 transition-colors shadow-sm bg-white"> <label class="flex items-center gap-3.5 cursor-pointer p-4 border border-[#e2e8f0] rounded-xl hover:bg-slate-50 hover:border-gray-300 transition-all shadow-sm bg-white">
<input type="checkbox" name="flag_send" id="flag_send" value="1" class="form-input !w-5 !h-5 !min-h-0" {{ $user->flag_send ? 'checked' : '' }}> <input type="checkbox" name="flag_send" id="flag_send" value="1" class="w-5 h-5 text-[#3462f7] border-[#d9dfe7] rounded-lg focus:ring-[#3462f7] focus:ring-2 cursor-pointer transition-colors" {{ $user->flag_send ? 'checked' : '' }}>
<span class="text-[14px] font-[600] text-text-dark">Bật quyền cho phép User gửi thẻ</span> <span class="text-[14px] font-bold text-[#1a2b49]">Cho phép nhân viên gửi thẻ</span>
</label> </label>
</div> </div>
@endif @endif
+14 -2
View File
@@ -9,13 +9,18 @@
@stack('styles') @stack('styles')
</head> </head>
<body class="bg-bg-main text-text-dark font-sans antialiased" data-sidebar="expanded"> <body class="bg-bg-main text-text-dark font-sans antialiased" data-sidebar="expanded">
<script>
if (localStorage.getItem('sidebar-collapsed') === 'true' && window.innerWidth >= 1024) {
document.body.classList.add('sidebar-collapsed');
}
</script>
<div class="app-layout"> <div class="app-layout">
<!-- Mobile overlay (click outside to close) --> <!-- Mobile overlay (click outside to close) -->
<div id="sidebar-overlay" class="fixed inset-0 bg-black/30 hidden" aria-hidden="true"></div> <div id="sidebar-overlay" class="fixed inset-0 bg-black/30 hidden" aria-hidden="true"></div>
<!-- Sidebar Component --> <!-- Sidebar Component -->
<x-sidebar /> <x-sidebar />
<main class="main-content flex-1 flex flex-col h-full bg-[#FAFBFF] overflow-hidden relative w-full lg:w-auto"> <main class="main-content flex-1 flex flex-col min-h-screen bg-[#FAFBFF] overflow-hidden relative w-full lg:w-auto">
<!-- Header Component --> <!-- Header Component -->
<x-header /> <x-header />
@@ -115,7 +120,14 @@
document.body.style.overflow = ''; document.body.style.overflow = '';
} }
hamburger.addEventListener('click', openSidebar); hamburger.addEventListener('click', function () {
if (window.innerWidth >= 1024) {
const isCollapsed = document.body.classList.toggle('sidebar-collapsed');
localStorage.setItem('sidebar-collapsed', isCollapsed ? 'true' : 'false');
} else {
openSidebar();
}
});
closeBtn.addEventListener('click', closeSidebar); closeBtn.addEventListener('click', closeSidebar);
overlay.addEventListener('click', closeSidebar); overlay.addEventListener('click', closeSidebar);
} }
+18 -6
View File
@@ -27,26 +27,38 @@
<div class="flex flex-col"> <div class="flex flex-col">
<label class="text-[13px] font-bold text-gray-700 mb-1.5" for="password">Mật khẩu mới <span class="text-red-500">*</span></label> <label class="text-[13px] font-bold text-gray-700 mb-1.5" for="password">Mật khẩu mới <span class="text-red-500">*</span></label>
<div class="relative flex items-center"> <div class="relative">
<span class="absolute left-4 text-gray-400"> <span class="absolute left-3.5 top-1/2 -translate-y-1/2 text-gray-400">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 10-9 0V10.5m-2.812 10.5h14.625c.621 0 1.125-.504 1.125-1.125V11.25c0-.621-.504-1.125-1.125-1.125H3.75c-.621 0-1.125.504-1.125 1.125v7.875c0 .621.504 1.125 1.125 1.125z" /> <path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 10-9 0V10.5m-2.812 10.5h14.625c.621 0 1.125-.504 1.125-1.125V11.25c0-.621-.504-1.125-1.125-1.125H3.75c-.621 0-1.125.504-1.125 1.125v7.875c0 .621.504 1.125 1.125 1.125z" />
</svg> </svg>
</span> </span>
<input type="password" name="password" id="password" class="w-full h-[48px] pl-12 pr-4 bg-[#F8FAFC] border border-gray-200 rounded-xl outline-none text-[#1a2b49] text-[14px] placeholder-gray-400 focus:border-[#3462f7] focus:bg-white focus:ring-4 focus:ring-[#3462f7]/10 hover:border-gray-300 transition-all duration-200" required placeholder="Nhập mật khẩu mới"> <input type="password" name="password" id="password" class="w-full h-[46px] pl-[42px] pr-12 bg-white border border-gray-200 rounded-lg text-sm text-gray-800 focus:border-[#2563eb] focus:ring-1 focus:ring-[#2563eb] outline-none transition-all shadow-sm placeholder:text-gray-400" required placeholder="Nhập mật khẩu mới">
<button type="button" onclick="togglePasswordVisibility('password', this)" class="absolute right-3.5 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 focus:outline-none cursor-pointer" aria-label="Show password">
<!-- Eye Closed (Default) -->
<svg class="w-5 h-5 eye-icon-closed" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M3.98 8.223A10.477 10.477 0 001.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.45 10.45 0 0112 4.5c4.756 0 8.773 3.162 10.065 7.498a10.523 10.523 0 01-4.293 5.774M6.228 6.228L3 3m3.228 3.228l3.65 3.65m7.894 7.894L21 21m-3.228-3.228l-3.65-3.65m0 0a3 3 0 10-4.243-4.243m4.242 4.242L9.88 9.88" /></svg>
<!-- Eye Open -->
<svg class="w-5 h-5 eye-icon-open hidden" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z" /><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /></svg>
</button>
</div> </div>
@error('password')<span class="text-red-500 text-[12px] font-semibold mt-1.5 block">{{ $message }}</span>@enderror @error('password')<span class="text-red-500 text-[12px] font-semibold mt-1.5 block">{{ $message }}</span>@enderror
</div> </div>
<div class="flex flex-col"> <div class="flex flex-col">
<label class="text-[13px] font-bold text-gray-700 mb-1.5" for="password_confirmation">Xác nhận lại mật khẩu mới <span class="text-red-500">*</span></label> <label class="text-[13px] font-bold text-gray-700 mb-1.5" for="password_confirmation">Xác nhận lại mật khẩu mới <span class="text-red-500">*</span></label>
<div class="relative flex items-center"> <div class="relative">
<span class="absolute left-4 text-gray-400"> <span class="absolute left-3.5 top-1/2 -translate-y-1/2 text-gray-400">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> <path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg> </svg>
</span> </span>
<input type="password" name="password_confirmation" id="password_confirmation" class="w-full h-[48px] pl-12 pr-4 bg-[#F8FAFC] border border-gray-200 rounded-xl outline-none text-[#1a2b49] text-[14px] placeholder-gray-400 focus:border-[#3462f7] focus:bg-white focus:ring-4 focus:ring-[#3462f7]/10 hover:border-gray-300 transition-all duration-200" required placeholder="Nhập lại mật khẩu mới"> <input type="password" name="password_confirmation" id="password_confirmation" class="w-full h-[46px] pl-[42px] pr-12 bg-white border border-gray-200 rounded-lg text-sm text-gray-800 focus:border-[#2563eb] focus:ring-1 focus:ring-[#2563eb] outline-none transition-all shadow-sm placeholder:text-gray-400" required placeholder="Nhập lại mật khẩu mới">
<button type="button" onclick="togglePasswordVisibility('password_confirmation', this)" class="absolute right-3.5 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 focus:outline-none cursor-pointer" aria-label="Show password">
<!-- Eye Closed (Default) -->
<svg class="w-5 h-5 eye-icon-closed" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M3.98 8.223A10.477 10.477 0 001.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.45 10.45 0 0112 4.5c4.756 0 8.773 3.162 10.065 7.498a10.523 10.523 0 01-4.293 5.774M6.228 6.228L3 3m3.228 3.228l3.65 3.65m7.894 7.894L21 21m-3.228-3.228l-3.65-3.65m0 0a3 3 0 10-4.243-4.243m4.242 4.242L9.88 9.88" /></svg>
<!-- Eye Open -->
<svg class="w-5 h-5 eye-icon-open hidden" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z" /><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /></svg>
</button>
</div> </div>
</div> </div>
+67 -46
View File
@@ -2,57 +2,78 @@
@section('title', 'Gửi Thank Card') @section('title', 'Gửi Thank Card')
@section('content') @section('content')
<div class="form-container"> <div class="max-w-[640px] mx-auto w-full pb-10">
<div class="form-header"> <div class="bg-white rounded-[24px] shadow-[0_10px_30px_rgba(15,23,42,0.05)] border border-white overflow-hidden">
<h3 class="form-header-title">Gửi Thank Card Tới Đồng Nghiệp</h3>
<div class="bg-gradient-to-r from-[#eff4ff] to-[#faeffa] px-8 py-8 flex flex-col relative overflow-hidden">
<div class="absolute -top-10 -right-10 w-32 h-32 bg-white/30 rounded-full blur-xl"></div>
<div class="absolute -bottom-10 -left-10 w-24 h-24 bg-white/20 rounded-full blur-lg"></div>
<div class="relative z-10 flex items-center gap-3">
<h3 class="text-[22px] font-extrabold text-[#1a2b49] tracking-tight">Gửi Thank Card</h3>
<span class="text-xl animate-pulse">💌</span>
</div>
<p class="text-[13px] text-gray-500 font-medium mt-1.5 relative z-10">
Gửi gắm lời cảm ơn chân thành đến người đồng nghiệp tuyệt vời của bạn!
</p>
</div>
<form id="sendCardForm">
@csrf
<div class="p-8 space-y-6">
<div class="flex flex-col gap-2">
<label class="text-[14px] font-bold text-text-dark" for="receiver">
Chọn người nhận <span class="text-red-500">*</span>
</label>
<select name="receiver" id="receiver" class="form-input rounded-xl border-[#d9dfe7]" required>
<option value="" disabled selected>-- Chọn đồng nghiệp --</option>
@foreach($users as $u)
<option value="{{ $u->msnv }}">{{ $u->name }} - {{ $u->msnv }} ({{ $u->mail }})</option>
@endforeach
</select>
</div>
<div class="flex flex-col gap-2">
<label class="text-[14px] font-bold text-text-dark" for="amount">
Số lượng thẻ <span class="text-red-500">*</span>
</label>
<select name="amount" id="amount" class="form-input rounded-xl border-[#d9dfe7]" required>
<option value="1">1 Thẻ</option>
<option value="2">2 Thẻ</option>
<option value="3">3 Thẻ</option>
<option value="4">4 Thẻ</option>
<option value="{{ \App\Models\Administration::MAX_SEND_CARD_PER_MONTH }}">{{ \App\Models\Administration::MAX_SEND_CARD_PER_MONTH }} Thẻ (Tối đa)</option>
</select>
<div class="mt-1 flex items-start gap-2 bg-[#eff4ff] p-3.5 rounded-xl border border-blue-50">
<svg class="w-5 h-5 text-[#3462f7] shrink-0 mt-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<p class="text-[12px] text-gray-600 font-medium leading-relaxed">
Lưu ý: Chỉ được gửi tối đa {{ \App\Models\Administration::MAX_SEND_CARD_PER_MONTH }} thẻ cho cùng 1 người trong 1 tháng để đảm bảo tính công bằng.
</p>
</div>
</div>
</div>
<div class="px-8 py-5 border-t border-[#d9dfe7]/50 bg-gray-50/50 flex flex-col sm:flex-row justify-end gap-3">
<a href="{{ route('user.dashboard') }}" class="btn-secondary rounded-xl font-bold">Hủy bỏ</a>
<button type="button" id="btnSubmitSend" class="btn-primary rounded-xl font-bold !bg-[#3462f7] hover:!bg-blue-700 shadow-md shadow-blue-500/10">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2.5" stroke="currentColor" class="w-4 h-4 mr-2">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 12L3.269 3.126A59.768 59.768 0 0121.485 12 59.77 59.77 0 013.27 20.876L5.999 12zm0 0h7.5" />
</svg>
Gửi Card
</button>
</div>
</form>
</div> </div>
<form id="sendCardForm">
@csrf
<div class="form-body">
<div class="form-group">
<label class="form-label" for="receiver">Chọn người nhận <span class="text-red-500">*</span></label>
<select name="receiver" id="receiver" class="form-input" required>
<option value="" disabled selected>-- Chọn đồng nghiệp --</option>
@foreach($users as $u)
<option value="{{ $u->msnv }}">{{ $u->msnv }} ({{ $u->mail }})</option>
@endforeach
</select>
</div>
<div class="form-group">
<label class="form-label" for="amount">Số lượng thẻ <span class="text-red-500">*</span></label>
<select name="amount" id="amount" class="form-input" required>
<option value="1">1 Thẻ</option>
<option value="2">2 Thẻ</option>
<option value="3">3 Thẻ</option>
<option value="4">4 Thẻ</option>
<option value="{{ \App\Models\Administration::MAX_SEND_CARD_PER_MONTH }}">{{ \App\Models\Administration::MAX_SEND_CARD_PER_MONTH }} Thẻ (Tối đa)</option>
</select>
<p class="helper-text">Lưu ý: Chỉ được gửi tối đa {{ \App\Models\Administration::MAX_SEND_CARD_PER_MONTH }} thẻ cho cùng 1 người trong 1 tháng.</p>
</div>
</div>
<div class="form-footer">
<a href="{{ route('user.dashboard') }}" class="btn-secondary">Hủy bỏ</a>
<button type="button" id="btnSubmitSend" class="btn-primary">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5 mr-2">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 12L3.269 3.126A59.768 59.768 0 0121.485 12 59.77 59.77 0 013.27 20.876L5.999 12zm0 0h7.5" />
</svg>
Gửi Card
</button>
</div>
</form>
</div> </div>
<!-- Custom Popup Modal -->
<x-modal id="customModal" title="Thông báo" maxWidth="md"> <x-modal id="customModal" title="Thông báo" maxWidth="md">
<p id="modalMessage" class="text-sm text-gray-600 leading-relaxed"></p> <p id="modalMessage" class="text-sm text-gray-600 leading-relaxed"></p>
<x-slot name="footer"> <x-slot name="footer">
<div id="modalActions" class="w-full flex flex-col sm:flex-row-reverse gap-3"> <div id="modalActions" class="w-full flex flex-col sm:flex-row-reverse gap-3">
<!-- Buttons injected by JS -->
</div> </div>
</x-slot> </x-slot>
</x-modal> </x-modal>
@@ -75,8 +96,8 @@
if(isConfirm) { if(isConfirm) {
modalTitle.className = "text-lg font-bold text-primary"; modalTitle.className = "text-lg font-bold text-primary";
modalActions.innerHTML = ` modalActions.innerHTML = `
<button id="btnConfirmOk" class="w-full sm:w-auto btn-primary !px-6 shadow-sm">Xác nhận gửi</button> <button id="btnConfirmOk" class="w-full sm:w-auto btn-primary !px-6 shadow-sm rounded-xl font-bold">Xác nhận gửi</button>
<button id="btnCancel" class="w-full sm:w-auto btn-secondary !px-6 shadow-sm">Hủy</button> <button id="btnCancel" class="w-full sm:w-auto btn-secondary !px-6 shadow-sm rounded-xl font-bold">Hủy</button>
`; `;
document.getElementById('btnCancel').addEventListener('click', () => window.closeModal('customModal')); document.getElementById('btnCancel').addEventListener('click', () => window.closeModal('customModal'));
document.getElementById('btnConfirmOk').addEventListener('click', function() { document.getElementById('btnConfirmOk').addEventListener('click', function() {
@@ -87,7 +108,7 @@
} else { } else {
modalTitle.className = "text-lg font-bold text-red-600"; modalTitle.className = "text-lg font-bold text-red-600";
modalActions.innerHTML = ` modalActions.innerHTML = `
<button id="btnOkOnly" class="w-full sm:w-auto btn-secondary !px-6 shadow-sm">Đóng</button> <button id="btnOkOnly" class="w-full sm:w-auto btn-secondary !px-6 shadow-sm rounded-xl font-bold">Đóng</button>
`; `;
document.getElementById('btnOkOnly').addEventListener('click', () => window.closeModal('customModal')); document.getElementById('btnOkOnly').addEventListener('click', () => window.closeModal('customModal'));
} }