feature: form edit
This commit is contained in:
@@ -64,7 +64,7 @@ class AdminController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function create(): View
|
public function create(): View
|
||||||
{
|
{
|
||||||
return view('admin.users.create');
|
return view('admin.users.form', ['mode' => 'create']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -85,21 +85,29 @@ class AdminController extends Controller
|
|||||||
$user = $this->adminService->getUserByMsnv($msnv);
|
$user = $this->adminService->getUserByMsnv($msnv);
|
||||||
$selectedMonth = (string) $request->input('month', Carbon::now()->format('Y-m'));
|
$selectedMonth = (string) $request->input('month', Carbon::now()->format('Y-m'));
|
||||||
$administrations = $this->adminService->getUserTransactions($msnv, $selectedMonth);
|
$administrations = $this->adminService->getUserTransactions($msnv, $selectedMonth);
|
||||||
$addCards = $this->adminService->getAddCardsHistory($user->msnv);
|
$addCards = $this->adminService->getAddCardsHistory($user->msnv, $selectedMonth);
|
||||||
$admins = $this->adminService->getActiveAdmins();
|
$admins = $this->adminService->getActiveAdmins();
|
||||||
|
|
||||||
return view('admin.users.edit', compact('user', 'administrations', 'selectedMonth', 'addCards', 'admins'));
|
return view('admin.users.form', compact('user', 'administrations', 'selectedMonth', 'addCards', 'admins'))->with('mode', 'edit');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the specified user in storage.
|
* Update the specified user in storage.
|
||||||
*/
|
*/
|
||||||
public function update(UpdateUserRequest $request, string $msnv): RedirectResponse
|
public function update(UpdateUserRequest $request, string $msnv)
|
||||||
{
|
{
|
||||||
$user = $this->adminService->getUserByMsnv($msnv);
|
$user = $this->adminService->getUserByMsnv($msnv);
|
||||||
|
|
||||||
$this->adminService->updateUser($user, $request->validated());
|
$this->adminService->updateUser($user, $request->validated());
|
||||||
|
|
||||||
|
if ($request->expectsJson()) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => __('messages.user_update_success'),
|
||||||
|
'card_balance' => $user->fresh()->card,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
return redirect()->back()->with('success', __('messages.user_update_success'));
|
return redirect()->back()->with('success', __('messages.user_update_success'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,12 +124,24 @@ class AdminController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Update card allocation history record.
|
* Update card allocation history record.
|
||||||
*/
|
*/
|
||||||
public function updateAddCard(UpdateAddCardRequest $request, int $id): RedirectResponse
|
public function updateAddCard(UpdateAddCardRequest $request, int $id)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->adminService->updateAddCard($id, $request->validated());
|
$this->adminService->updateAddCard($id, $request->validated());
|
||||||
|
if ($request->expectsJson()) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Cập nhật lịch sử cấp phát thẻ thành công.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
return redirect()->back()->with('success', 'Cập nhật lịch sử cấp phát thẻ thành công.');
|
return redirect()->back()->with('success', 'Cập nhật lịch sử cấp phát thẻ thành công.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
if ($request->expectsJson()) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
], 422);
|
||||||
|
}
|
||||||
return redirect()->back()->withErrors(['error' => $e->getMessage()]);
|
return redirect()->back()->withErrors(['error' => $e->getMessage()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -219,11 +219,18 @@ class AdminService implements AdminServiceInterface
|
|||||||
return User::where('msnv', $msnv)->firstOrFail();
|
return User::where('msnv', $msnv)->firstOrFail();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAddCardsHistory(string $buyerMsnv): \Illuminate\Database\Eloquent\Collection
|
public function getAddCardsHistory(string $buyerMsnv, ?string $selectedMonth = null): \Illuminate\Database\Eloquent\Collection
|
||||||
{
|
{
|
||||||
return AddCard::with('sellerUser')
|
$query = AddCard::with('sellerUser')
|
||||||
->where('buyer', $buyerMsnv)
|
->where('buyer', $buyerMsnv);
|
||||||
->orderBy('date', 'desc')
|
|
||||||
|
if ($selectedMonth) {
|
||||||
|
$startOfMonth = Carbon::parse($selectedMonth)->startOfMonth()->format('Y-m-d');
|
||||||
|
$endOfMonth = Carbon::parse($selectedMonth)->endOfMonth()->format('Y-m-d');
|
||||||
|
$query->whereBetween('date', [$startOfMonth, $endOfMonth]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query->orderBy('date', 'desc')
|
||||||
->orderBy('id', 'desc')
|
->orderBy('id', 'desc')
|
||||||
->get();
|
->get();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ interface AdminServiceInterface
|
|||||||
|
|
||||||
public function getUserByMsnv(string $msnv): \App\Models\User;
|
public function getUserByMsnv(string $msnv): \App\Models\User;
|
||||||
|
|
||||||
public function getAddCardsHistory(string $buyerMsnv): \Illuminate\Database\Eloquent\Collection;
|
public function getAddCardsHistory(string $buyerMsnv, ?string $selectedMonth = null): \Illuminate\Database\Eloquent\Collection;
|
||||||
|
|
||||||
public function getActiveAdmins(): \Illuminate\Database\Eloquent\Collection;
|
public function getActiveAdmins(): \Illuminate\Database\Eloquent\Collection;
|
||||||
}
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
@extends('layouts.app')
|
|
||||||
@section('title', 'Thêm User Mới')
|
|
||||||
|
|
||||||
@section('content')
|
|
||||||
<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 và thiết lập các thông tin cơ bản.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<x-user-page mode="create" />
|
|
||||||
@endsection
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
@extends('layouts.app')
|
|
||||||
@section('title', 'Chi tiết User')
|
|
||||||
|
|
||||||
@section('content')
|
|
||||||
<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 lý thông tin thẻ và 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" :addCards="$addCards" :admins="$admins" />
|
|
||||||
@endsection
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
@extends('layouts.app')
|
||||||
|
@section('title', $mode === 'create' ? 'Thêm User Mới' : 'Chi tiết User')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<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">
|
||||||
|
{{ $mode === 'create' ? 'Thêm User Mới' : 'Chi tiết User' }}
|
||||||
|
</h1>
|
||||||
|
<p class="text-gray-500 font-medium text-[14px]">
|
||||||
|
{{ $mode === 'create' ? 'Tạo tài khoản nhân viên mới và thiết lập các thông tin cơ bản.' : 'Quản lý thông tin thẻ và theo dõi lịch sử nhận gửi của nhân viên.' }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<x-user-page :mode="$mode" :user="$user ?? null" :administrations="$administrations ?? null" :selectedMonth="$selectedMonth ?? null" :addCards="$addCards ?? null" :admins="$admins ?? null" />
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@push('modals')
|
||||||
|
@if($mode === 'edit' && $user)
|
||||||
|
<x-confirm-modal id="deleteModal" title="Xác nhận nghỉ việc" confirmText="Xác nhận Nghỉ việc" />
|
||||||
|
@endif
|
||||||
|
@endpush
|
||||||
@@ -63,9 +63,9 @@
|
|||||||
<x-slot name="footer">
|
<x-slot name="footer">
|
||||||
<form action="{{ route('admin.reset_cards') }}" method="POST" class="w-full sm:w-auto">
|
<form action="{{ route('admin.reset_cards') }}" method="POST" class="w-full sm:w-auto">
|
||||||
@csrf
|
@csrf
|
||||||
<button type="submit" class="w-full btn-primary !px-6 shadow-sm bg-red-600 hover:bg-red-700">Đồng ý Reset</button>
|
<button type="submit" class="w-full sm:w-auto 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-[42px]">Đồng ý Reset</button>
|
||||||
</form>
|
</form>
|
||||||
<button type="button" onclick="closeModal('resetModal')" class="w-full sm:w-auto btn-secondary !px-6 shadow-sm">Hủy bỏ</button>
|
<button type="button" onclick="closeModal('resetModal')" class="w-full sm:w-auto 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-[42px]">Hủy bỏ</button>
|
||||||
</x-slot>
|
</x-slot>
|
||||||
</x-modal>
|
</x-modal>
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
<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>
|
<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="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>
|
<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>
|
||||||
|
@if($user && $user->status == config('constants.STATUS_ACTIVE'))
|
||||||
|
<button type="button" onclick="openDeleteModal('{{ $user->name }} (MSNV: {{ $user->msnv }})', '{{ route('admin.users.destroy', $user->msnv) }}')" 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] sm:mr-auto">Cho Nghỉ Việc</button>
|
||||||
|
@endif
|
||||||
<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>
|
<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="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>
|
<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>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
@props(['id', 'title' => 'Xác nhận', 'message' => '', 'confirmText' => 'Đồng ý', 'cancelText' => 'Hủy', 'type' => 'danger'])
|
@props(['id', 'title' => 'Xác nhận', 'message' => '', 'confirmText' => 'Đồng ý', 'cancelText' => 'Hủy', 'type' => 'danger'])
|
||||||
|
|
||||||
<div id="{{ $id }}" class="fixed inset-0 z-[100] hidden flex-col items-center justify-center outline-none opacity-0 transition-opacity duration-300" aria-labelledby="modal-title-{{ $id }}" role="dialog" aria-modal="true">
|
<div id="{{ $id }}" class="fixed inset-0 z-[100] hidden flex-col items-center justify-center outline-none opacity-0 transition-opacity duration-300" aria-labelledby="modal-title-{{ $id }}" role="dialog" aria-modal="true">
|
||||||
<div class="absolute inset-0 bg-slate-900/60 backdrop-blur-sm cursor-pointer" onclick="closeModal('{{ $id }}')"></div>
|
<div class="absolute inset-0 bg-gray-900/60 backdrop-blur-sm cursor-pointer" onclick="closeModal('{{ $id }}')"></div>
|
||||||
<div class="modal-panel bg-white rounded-2xl shadow-2xl w-full sm:max-w-md overflow-hidden z-50 relative transform scale-95 translate-y-4 transition-all duration-300 m-4 flex flex-col">
|
<div class="modal-panel bg-white rounded-xl shadow-2xl w-full sm:max-w-md overflow-hidden z-50 relative transform scale-95 translate-y-4 transition-all duration-300 m-4 flex flex-col">
|
||||||
<div class="p-6 flex flex-col items-center text-center">
|
<div class="p-6 flex flex-col items-center text-center">
|
||||||
<!-- Icon based on type -->
|
<!-- Icon based on type -->
|
||||||
<div class="w-14 h-14 rounded-full flex items-center justify-center mb-4 {{ $type === 'danger' ? 'bg-red-50 text-red-500' : 'bg-blue-50 text-blue-500' }}">
|
<div class="w-14 h-14 rounded-full flex items-center justify-center mb-4 {{ $type === 'danger' ? 'bg-red-50 text-red-500' : 'bg-blue-50 text-blue-500' }}">
|
||||||
@@ -17,14 +17,14 @@
|
|||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 id="modal-title-{{ $id }}" class="text-xl font-bold text-slate-800 mb-2">{{ $title }}</h3>
|
<h3 id="modal-title-{{ $id }}" class="text-[18px] font-extrabold text-[#1a2b49] mb-2">{{ $title }}</h3>
|
||||||
<p class="text-slate-500 text-sm leading-relaxed">{{ $message }}</p>
|
<p class="text-gray-500 text-sm leading-relaxed">{{ $message }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="px-6 py-4 bg-slate-50 flex gap-3 border-t border-slate-100 justify-end">
|
<div class="px-6 py-4 bg-gray-50 flex gap-3 border-t border-gray-100 justify-end">
|
||||||
<button type="button" onclick="closeModal('{{ $id }}')" class="px-4 py-2 bg-white hover:bg-slate-100 border border-slate-200 text-slate-700 text-sm font-semibold rounded-lg transition-all duration-200 cursor-pointer">
|
<button type="button" onclick="closeModal('{{ $id }}')" 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-[42px] cursor-pointer">
|
||||||
{{ $cancelText }}
|
{{ $cancelText }}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" onclick="closeModal('{{ $id }}')" class="px-4 py-2 text-white text-sm font-semibold rounded-lg transition-all duration-200 cursor-pointer {{ $type === 'danger' ? 'bg-red-600 hover:bg-red-700' : 'bg-primary hover:bg-primary-hover' }}">
|
<button type="button" onclick="closeModal('{{ $id }}')" class="inline-flex items-center justify-center font-bold py-2.5 px-6 rounded-xl text-sm transition-colors text-white shadow-md min-w-[120px] h-[42px] cursor-pointer {{ $type === 'danger' ? 'bg-red-600 hover:bg-red-700 shadow-red-500/10' : 'bg-[#3462f7] hover:bg-blue-700 shadow-blue-500/10' }}">
|
||||||
{{ $confirmText }}
|
{{ $confirmText }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -19,8 +19,8 @@
|
|||||||
<form id="{{ $id }}-form" method="POST" class="w-full sm:w-auto">
|
<form id="{{ $id }}-form" method="POST" class="w-full sm:w-auto">
|
||||||
@csrf
|
@csrf
|
||||||
@method('DELETE')
|
@method('DELETE')
|
||||||
<button type="submit" id="{{ $id }}-confirm-btn" class="w-full btn-primary !px-6 shadow-sm {{ $confirmBg }}">{{ $confirmText }}</button>
|
<button type="submit" id="{{ $id }}-confirm-btn" class="w-full sm:w-auto inline-flex items-center justify-center 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-[42px] {{ $confirmBg }}">{{ $confirmText }}</button>
|
||||||
</form>
|
</form>
|
||||||
<button type="button" onclick="closeModal('{{ $id }}')" class="w-full sm:w-auto btn-secondary !px-6 shadow-sm">{{ $cancelText }}</button>
|
<button type="button" onclick="closeModal('{{ $id }}')" class="w-full sm:w-auto 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-[42px]">{{ $cancelText }}</button>
|
||||||
</x-slot>
|
</x-slot>
|
||||||
</x-modal>
|
</x-modal>
|
||||||
|
|||||||
@@ -50,17 +50,7 @@
|
|||||||
@if($admin_record->senderUser)
|
@if($admin_record->senderUser)
|
||||||
{{ $mode === 'self' ? 'Nhận từ: ' : 'Từ: ' }}<span class="text-[#3462f7]">{{ $admin_record->senderUser->name }}</span>
|
{{ $mode === 'self' ? 'Nhận từ: ' : 'Từ: ' }}<span class="text-[#3462f7]">{{ $admin_record->senderUser->name }}</span>
|
||||||
<div class="text-[11px] text-gray-400 font-medium mt-1.5 leading-none">
|
<div class="text-[11px] text-gray-400 font-medium mt-1.5 leading-none">
|
||||||
{{ $admin_record->senderUser->mail }} •
|
MSNV: {{ $admin_record->senderUser->msnv }} • {{ $admin_record->senderUser->mail }}
|
||||||
@php
|
|
||||||
$deptVal = $admin_record->senderUser->departments;
|
|
||||||
if (is_numeric($deptVal)) {
|
|
||||||
$idx = ((int)$deptVal) - 1;
|
|
||||||
$deptName = config('constants.DEPARTMENTS')[$idx] ?? 'Team';
|
|
||||||
} else {
|
|
||||||
$deptName = $deptVal ?? 'Team';
|
|
||||||
}
|
|
||||||
@endphp
|
|
||||||
{{ $deptName }}
|
|
||||||
</div>
|
</div>
|
||||||
@else
|
@else
|
||||||
{{ $mode === 'self' ? 'Nhận từ: ' : 'Từ: ' }}<span class="text-[#3462f7]">{{ $admin_record->sender }}</span>
|
{{ $mode === 'self' ? 'Nhận từ: ' : 'Từ: ' }}<span class="text-[#3462f7]">{{ $admin_record->sender }}</span>
|
||||||
@@ -69,17 +59,7 @@
|
|||||||
@if($admin_record->receiverUser)
|
@if($admin_record->receiverUser)
|
||||||
{{ $mode === 'self' ? 'Gửi cho: ' : 'Tới: ' }}<span class="text-[#3462f7]">{{ $admin_record->receiverUser->name }}</span>
|
{{ $mode === 'self' ? 'Gửi cho: ' : 'Tới: ' }}<span class="text-[#3462f7]">{{ $admin_record->receiverUser->name }}</span>
|
||||||
<div class="text-[11px] text-gray-400 font-medium mt-1.5 leading-none">
|
<div class="text-[11px] text-gray-400 font-medium mt-1.5 leading-none">
|
||||||
{{ $admin_record->receiverUser->mail }} •
|
MSNV: {{ $admin_record->receiverUser->msnv }} • {{ $admin_record->receiverUser->mail }}
|
||||||
@php
|
|
||||||
$deptVal = $admin_record->receiverUser->departments;
|
|
||||||
if (is_numeric($deptVal)) {
|
|
||||||
$idx = ((int)$deptVal) - 1;
|
|
||||||
$deptName = config('constants.DEPARTMENTS')[$idx] ?? 'Team';
|
|
||||||
} else {
|
|
||||||
$deptName = $deptVal ?? 'Team';
|
|
||||||
}
|
|
||||||
@endphp
|
|
||||||
{{ $deptName }}
|
|
||||||
</div>
|
</div>
|
||||||
@else
|
@else
|
||||||
{{ $mode === 'self' ? 'Gửi cho: ' : 'Tới: ' }}<span class="text-[#3462f7]">{{ $admin_record->receiver }}</span>
|
{{ $mode === 'self' ? 'Gửi cho: ' : 'Tới: ' }}<span class="text-[#3462f7]">{{ $admin_record->receiver }}</span>
|
||||||
@@ -97,7 +77,7 @@
|
|||||||
@empty
|
@empty
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="4" class="px-6 py-12 text-center text-text-light font-medium text-base">
|
<td colspan="4" class="px-6 py-12 text-center text-text-light font-medium text-base">
|
||||||
{{ $mode === 'self' ? 'Bạn chưa có giao dịch gửi/nhận thẻ nào trong tháng này.' : 'Không có giao dịch nào trong tháng này.' }}
|
Chưa có dữ liệu
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@endforelse
|
@endforelse
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if(!$isCreate && $user)
|
@if(!$isCreate && $user && !$isEdit)
|
||||||
<div class="p-6 sm:p-8 pb-0">
|
<div class="p-6 sm:p-8 pb-0">
|
||||||
<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="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="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">
|
<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">
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
@endif
|
@endif
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<div class="p-6 sm:p-8 {{ !$isCreate ? 'pt-0' : '' }} space-y-5">
|
<div class="p-6 sm:p-8 {{ !$isCreate && !$isEdit ? 'pt-0' : '' }} space-y-5">
|
||||||
@if($isCreate)
|
@if($isCreate)
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<label class="block text-[14px] font-bold text-[#1a2b49] mb-2" for="msnv">Mã 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">Mã nhân viên (MSNV) <span class="text-red-500">*</span></label>
|
||||||
@@ -131,25 +131,85 @@
|
|||||||
<div class="relative">
|
<div class="relative">
|
||||||
<label class="block text-[14px] font-bold text-[#1a2b49] mb-2" 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 !rounded-xl !focus:border-[#3462f7] !focus:ring-[#3462f7]/20" required>
|
<select name="role" id="role" class="form-input !rounded-xl !focus:border-[#3462f7] !focus:ring-[#3462f7]/20" required>
|
||||||
<option value="{{ config('constants.ROLE_MEMBER') }}" {{ old('role') == config('constants.ROLE_MEMBER') ? 'selected' : '' }}>Nhân viên (Member)</option>
|
<option value="{{ config('constants.ROLE_MEMBER') }}" {{ old('role') == config('constants.ROLE_MEMBER') ? 'selected' : '' }}>Member</option>
|
||||||
<option value="{{ config('constants.ROLE_ADMIN') }}" {{ old('role') == config('constants.ROLE_ADMIN') ? 'selected' : '' }}>Quản trị viên (Admin)</option>
|
<option value="{{ config('constants.ROLE_ADMIN') }}" {{ old('role') == config('constants.ROLE_ADMIN') ? 'selected' : '' }}>Admin</option>
|
||||||
</select>
|
</select>
|
||||||
@error('role')<span class="text-red-500 text-[13px] font-semibold mt-1.5 block">{{ $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="relative">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<label class="block text-[14px] font-bold text-[#1a2b49] mb-2" for="num_card">Nạp thêm thẻ</label>
|
<div class="relative">
|
||||||
<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...">
|
<label class="block text-[14px] font-bold text-[#1a2b49] mb-2" for="mail">Email (Chỉ xem)</label>
|
||||||
@error('num_card')<span class="text-red-500 text-[13px] font-semibold mt-1.5 block">{{ $message }}</span>@enderror
|
<div class="relative flex items-center w-full">
|
||||||
</div>
|
<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.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" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<input type="email" name="mail" id="mail" class="form-input form-input-with-icon !rounded-xl !bg-gray-50 !text-gray-500 cursor-not-allowed w-full" style="padding-left: 48px;" readonly value="{{ $user->mail }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="relative pt-2">
|
<div class="relative">
|
||||||
<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">
|
<label class="block text-[14px] font-bold text-[#1a2b49] mb-2" for="password">Mật khẩu (Chỉ xem)</label>
|
||||||
<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' : '' }}>
|
<div class="relative flex items-center w-full">
|
||||||
<span class="text-[14px] font-bold text-[#1a2b49]">Cho phép nhân viên gửi thẻ</span>
|
<span class="absolute left-4 text-gray-400 pointer-events-none w-5 h-5 flex items-center justify-center">
|
||||||
</label>
|
<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" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<input type="password" name="password" id="password" class="form-input form-input-with-icon !rounded-xl !bg-gray-50 !text-gray-500 cursor-not-allowed w-full" style="padding-left: 48px;" readonly value="********">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="relative">
|
||||||
|
<label class="block text-[14px] font-bold text-[#1a2b49] mb-2">Quyền hạn</label>
|
||||||
|
<div class="grid grid-cols-2 gap-2 mt-2 bg-gray-100/80 p-1 rounded-xl">
|
||||||
|
<label class="flex items-center justify-center gap-2 cursor-pointer py-2 rounded-lg transition-all text-center has-[:checked]:bg-white has-[:checked]:text-[#3462f7] has-[:checked]:shadow-sm text-gray-500 font-bold text-sm hover:bg-white/50">
|
||||||
|
<input type="radio" name="role" value="{{ config('constants.ROLE_MEMBER') }}" class="sr-only" {{ old('role', $user->role) == config('constants.ROLE_MEMBER') ? 'checked' : '' }}>
|
||||||
|
<span>Member</span>
|
||||||
|
</label>
|
||||||
|
<label class="flex items-center justify-center gap-2 cursor-pointer py-2 rounded-lg transition-all text-center has-[:checked]:bg-white has-[:checked]:text-[#3462f7] has-[:checked]:shadow-sm text-gray-500 font-bold text-sm hover:bg-white/50">
|
||||||
|
<input type="radio" name="role" value="{{ config('constants.ROLE_ADMIN') }}" class="sr-only" {{ old('role', $user->role) == config('constants.ROLE_ADMIN') ? 'checked' : '' }}>
|
||||||
|
<span>Admin</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
@error('role')<span class="text-red-500 text-[13px] font-semibold mt-1.5 block">{{ $message }}</span>@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="relative">
|
||||||
|
<label class="block text-[14px] font-bold text-[#1a2b49] mb-2">Trạng thái</label>
|
||||||
|
<div class="grid grid-cols-2 gap-2 mt-2 bg-gray-100/80 p-1 rounded-xl">
|
||||||
|
<label class="flex items-center justify-center gap-2 cursor-pointer py-2 rounded-lg transition-all text-center has-[:checked]:bg-white has-[:checked]:text-[#3462f7] has-[:checked]:shadow-sm text-gray-500 font-bold text-sm hover:bg-white/50">
|
||||||
|
<input type="radio" name="status" value="{{ config('constants.STATUS_ACTIVE') }}" class="sr-only" {{ old('status', $user->status) == config('constants.STATUS_ACTIVE') ? 'checked' : '' }}>
|
||||||
|
<span>Đang làm việc</span>
|
||||||
|
</label>
|
||||||
|
<label class="flex items-center justify-center gap-2 cursor-pointer py-2 rounded-lg transition-all text-center has-[:checked]:bg-white has-[:checked]:text-[#3462f7] has-[:checked]:shadow-sm text-gray-500 font-bold text-sm hover:bg-white/50">
|
||||||
|
<input type="radio" name="status" value="{{ config('constants.STATUS_INACTIVE') }}" class="sr-only" {{ old('status', $user->status) == config('constants.STATUS_INACTIVE') ? 'checked' : '' }}>
|
||||||
|
<span>Đã nghỉ việc</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
@error('status')<span class="text-red-500 text-[13px] font-semibold mt-1.5 block">{{ $message }}</span>@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="relative">
|
||||||
|
<label class="block text-[14px] font-bold text-[#1a2b49] mb-2">Quyền gửi thẻ</label>
|
||||||
|
<div class="grid grid-cols-2 gap-2 mt-2 bg-gray-100/80 p-1 rounded-xl">
|
||||||
|
<label class="flex items-center justify-center gap-2 cursor-pointer py-2 rounded-lg transition-all text-center has-[:checked]:bg-white has-[:checked]:text-[#3462f7] has-[:checked]:shadow-sm text-gray-500 font-bold text-sm hover:bg-white/50">
|
||||||
|
<input type="radio" name="flag_send" value="{{ config('constants.FLAG_SEND_ENABLED') }}" class="sr-only" {{ old('flag_send', $user->flag_send) == config('constants.FLAG_SEND_ENABLED') ? 'checked' : '' }}>
|
||||||
|
<span>Được phép gửi</span>
|
||||||
|
</label>
|
||||||
|
<label class="flex items-center justify-center gap-2 cursor-pointer py-2 rounded-lg transition-all text-center has-[:checked]:bg-white has-[:checked]:text-[#3462f7] has-[:checked]:shadow-sm text-gray-500 font-bold text-sm hover:bg-white/50">
|
||||||
|
<input type="radio" name="flag_send" value="{{ config('constants.FLAG_SEND_DISABLED') }}" class="sr-only" {{ old('flag_send', $user->flag_send) == config('constants.FLAG_SEND_DISABLED') ? 'checked' : '' }}>
|
||||||
|
<span>Không cho phép</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
@error('flag_send')<span class="text-red-500 text-[13px] font-semibold mt-1.5 block">{{ $message }}</span>@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hidden md:block"></div>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
|||||||
@@ -16,81 +16,215 @@
|
|||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<div class="grid grid-cols-1 {{ in_array($mode, ['edit', 'delete']) ? 'lg:grid-cols-3' : '' }} gap-6 items-start">
|
@if($mode === 'edit' && $user)
|
||||||
|
<!-- 1. USER SUMMARY CARD -->
|
||||||
|
<div class="mb-6 bg-white rounded-2xl border border-gray-100 shadow-[0_8px_30px_rgb(0,0,0,0.03)] p-6 flex flex-col sm:flex-row sm:items-center justify-between gap-6 relative overflow-hidden">
|
||||||
|
<div class="absolute right-0 top-0 w-36 h-36 bg-blue-50/30 rounded-bl-full pointer-events-none -mr-6 -mt-6"></div>
|
||||||
|
|
||||||
<!-- LEFT PANEL: User Profile & Actions / Form -->
|
<div class="flex items-center gap-5 relative z-10">
|
||||||
<div class="{{ in_array($mode, ['edit', 'delete']) ? 'col-span-1' : 'col-span-full' }}">
|
<div class="w-16 h-16 rounded-2xl bg-gradient-to-tr from-blue-500 to-indigo-600 text-white flex items-center justify-center text-2xl font-black shadow-md shadow-blue-500/20">
|
||||||
@if($mode === 'create')
|
{{ strtoupper(substr($user->name ?? $user->msnv, 0, 1)) }}
|
||||||
<div class="max-w-2xl mx-auto w-full">
|
|
||||||
<x-user-form :mode="$mode" :user="$user" />
|
|
||||||
</div>
|
</div>
|
||||||
@else
|
<div>
|
||||||
<x-user-form :mode="$mode" :user="$user" />
|
<h2 class="text-xl font-black text-[#1a2b49] tracking-tight">{{ $user->name }}</h2>
|
||||||
@endif
|
<p class="text-gray-500 text-sm font-semibold mt-0.5">{{ $user->mail }}</p>
|
||||||
|
<div class="flex flex-wrap gap-2 mt-3">
|
||||||
|
@if($user->role == config('constants.ROLE_ADMIN'))
|
||||||
|
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-bold bg-blue-50 text-blue-700 border border-blue-100">Admin</span>
|
||||||
|
@else
|
||||||
|
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-bold bg-slate-50 text-slate-700 border border-slate-100">Member</span>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if($user->status == config('constants.STATUS_ACTIVE'))
|
||||||
|
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-bold bg-green-50 text-green-700 border border-green-100">Đang làm việc</span>
|
||||||
|
@else
|
||||||
|
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-bold bg-red-50 text-red-700 border border-red-100">Đã nghỉ việc</span>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-4 bg-slate-50 border border-slate-100 rounded-2xl p-4 relative z-10 min-w-[200px] shadow-sm">
|
||||||
|
<div class="w-11 h-11 bg-white rounded-xl shadow-sm flex items-center justify-center text-xl">
|
||||||
|
🎫
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="text-[10px] font-bold text-gray-500 uppercase tracking-wider block">Số thẻ hiện có</span>
|
||||||
|
<span id="user-card-balance" class="text-xl font-black text-[#1a2b49]">{{ $user->card }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- RIGHT PANEL: Transaction History & Add Card History -->
|
<!-- 2. USER FORM (FULL WIDTH) -->
|
||||||
@if(in_array($mode, ['edit', 'delete', 'self']) && $user && $administrations)
|
<div class="mb-8">
|
||||||
<div class="col-span-1 lg:col-span-2 flex flex-col gap-6">
|
<x-user-form :mode="$mode" :user="$user" />
|
||||||
<x-transaction-history :mode="$mode" :user="$user" :administrations="$administrations" :selectedMonth="$selectedMonth" />
|
</div>
|
||||||
|
|
||||||
@if($mode === 'edit' && isset($addCards))
|
<!-- 3. HISTORY SECTION -->
|
||||||
<div class="bg-white rounded-[24px] shadow-[0_10px_30px_rgba(15,23,42,0.05)] border border-white overflow-hidden flex flex-col">
|
@if($administrations)
|
||||||
<div class="px-6 py-5 border-b border-gray-100 bg-white">
|
<div class="mb-6">
|
||||||
<h3 class="text-[17px] font-extrabold text-[#1a2b49] leading-tight">
|
<h4 class="text-lg font-black text-[#1a2b49] mb-4 flex items-center gap-2">
|
||||||
Lịch sử cấp phát thẻ
|
<span class="w-1.5 h-6 bg-[#3462f7] rounded-full inline-block"></span>
|
||||||
</h3>
|
Lịch sử hoạt động
|
||||||
</div>
|
</h4>
|
||||||
|
<div id="history-lists-container" class="grid grid-cols-1 lg:grid-cols-2 gap-6 items-stretch">
|
||||||
|
<!-- Card Allocation History: Lịch sử cấp phát thẻ -->
|
||||||
|
@if(isset($addCards))
|
||||||
|
<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-5 border-b border-gray-100 bg-white flex flex-col sm:flex-row sm:items-center justify-between gap-3">
|
||||||
|
<h3 class="text-[17px] font-extrabold text-[#1a2b49] leading-tight">
|
||||||
|
Lịch sử cấp phát thẻ
|
||||||
|
</h3>
|
||||||
|
<x-month-filter :mode="$mode" :user="$user" :selectedMonth="$selectedMonth" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="p-0 overflow-y-auto max-h-[600px]">
|
<div class="flex-1 overflow-y-auto max-h-[600px]">
|
||||||
<table class="min-w-full divide-y divide-gray-100 text-sm">
|
<table class="min-w-full divide-y divide-gray-100 text-sm">
|
||||||
<thead class="bg-gray-50/50 sticky top-0">
|
<thead class="bg-gray-50/50 sticky top-0 z-10">
|
||||||
<tr>
|
|
||||||
<th class="px-6 py-4 text-left text-xs font-bold text-text-light uppercase tracking-wider">Ngày cấp</th>
|
|
||||||
<th class="px-6 py-4 text-left text-xs font-bold text-text-light uppercase tracking-wider">Người cấp (Admin)</th>
|
|
||||||
<th class="px-6 py-4 text-left text-xs font-bold text-text-light uppercase tracking-wider">Số lượng thẻ</th>
|
|
||||||
<th class="px-6 py-4 text-center text-xs font-bold text-text-light uppercase tracking-wider w-24">Hành động</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody class="bg-white divide-y divide-gray-100">
|
|
||||||
@forelse($addCards as $addCard)
|
|
||||||
@php
|
|
||||||
$isCurrentMonth = Carbon\Carbon::parse($addCard->date)->format('Y-m') === Carbon\Carbon::now()->format('Y-m');
|
|
||||||
@endphp
|
|
||||||
<tr class="hover:bg-slate-50/60 transition-colors">
|
|
||||||
<td class="px-6 py-3.5 whitespace-nowrap text-text-medium font-medium">{{ Carbon\Carbon::parse($addCard->date)->format('d/m/Y') }}</td>
|
|
||||||
<td class="px-6 py-3.5 whitespace-nowrap font-bold text-[#1a2b49]">
|
|
||||||
@if($addCard->sellerUser)
|
|
||||||
{{ $addCard->sellerUser->name }} <span class="text-xs font-medium text-gray-500">(MSNV: {{ $addCard->seller }})</span>
|
|
||||||
@else
|
|
||||||
MSNV: {{ $addCard->seller }}
|
|
||||||
@endif
|
|
||||||
</td>
|
|
||||||
<td class="px-6 py-3.5 whitespace-nowrap font-extrabold text-base text-green-600">+{{ $addCard->num_card }}</td>
|
|
||||||
<td class="px-6 py-3.5 whitespace-nowrap text-center">
|
|
||||||
@if($isCurrentMonth)
|
|
||||||
<button type="button" onclick="openEditAddCardModal({{ json_encode($addCard) }})" class="text-blue-600 hover:text-blue-900 font-bold transition-colors">Sửa</button>
|
|
||||||
@else
|
|
||||||
<span class="text-gray-400 font-semibold text-xs cursor-not-allowed" title="Không thể chỉnh sửa giao dịch tháng trước">Tháng trước</span>
|
|
||||||
@endif
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
@empty
|
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="4" class="px-6 py-12 text-center text-text-light font-medium text-base">
|
<th class="px-6 py-4 text-left text-xs font-bold text-text-light uppercase tracking-wider">Ngày cấp</th>
|
||||||
Chưa có lịch sử cấp phát thẻ nào.
|
<th class="px-6 py-4 text-left text-xs font-bold text-text-light uppercase tracking-wider">Người cấp (Admin)</th>
|
||||||
</td>
|
<th class="px-6 py-4 text-left text-xs font-bold text-text-light uppercase tracking-wider">Số lượng thẻ</th>
|
||||||
|
<th class="px-6 py-4 text-center text-xs font-bold text-text-light uppercase tracking-wider w-24">Hành động</th>
|
||||||
</tr>
|
</tr>
|
||||||
@endforelse
|
</thead>
|
||||||
</tbody>
|
<tbody class="bg-white divide-y divide-gray-100">
|
||||||
</table>
|
@forelse($addCards as $addCard)
|
||||||
|
@php
|
||||||
|
$isCurrentMonth = Carbon\Carbon::parse($addCard->date)->format('Y-m') === Carbon\Carbon::now()->format('Y-m');
|
||||||
|
@endphp
|
||||||
|
<tr class="hover:bg-slate-50/60 transition-colors">
|
||||||
|
<td class="px-6 py-3.5 whitespace-nowrap text-text-medium font-medium">{{ Carbon\Carbon::parse($addCard->date)->format('d/m/Y') }}</td>
|
||||||
|
<td class="px-6 py-3.5 whitespace-nowrap font-bold text-[#1a2b49]">
|
||||||
|
@if($addCard->sellerUser)
|
||||||
|
<span class="text-[#3462f7]">{{ $addCard->sellerUser->name }}</span>
|
||||||
|
<div class="text-[11px] text-gray-400 font-medium mt-1.5 leading-none">
|
||||||
|
MSNV: {{ $addCard->sellerUser->msnv }} • {{ $addCard->sellerUser->mail }}
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
MSNV: {{ $addCard->seller }}
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
<td class="px-6 py-3.5 whitespace-nowrap font-extrabold text-base text-green-600">+{{ $addCard->num_card }}</td>
|
||||||
|
<td class="px-6 py-3.5 whitespace-nowrap text-center">
|
||||||
|
@if($isCurrentMonth)
|
||||||
|
<button type="button" onclick="openEditAddCardModal({{ json_encode($addCard) }})" class="text-blue-600 hover:text-blue-900 font-bold transition-colors">Sửa</button>
|
||||||
|
@else
|
||||||
|
<span class="text-gray-400 font-semibold text-xs cursor-not-allowed" title="Không thể chỉnh sửa giao dịch tháng trước">Tháng trước</span>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@empty
|
||||||
|
<tr>
|
||||||
|
<td colspan="4" class="px-6 py-12 text-center text-text-light font-medium text-base">
|
||||||
|
Chưa có dữ liệu
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforelse
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="px-6 py-4 bg-slate-50 border-t border-gray-100 flex justify-end">
|
||||||
|
<button type="button" onclick="openAddCardModal()" class="inline-flex items-center gap-2 bg-[#3462f7] hover:bg-blue-700 text-white font-bold text-sm px-5 py-2.5 rounded-xl shadow-md shadow-blue-500/10 transition-all">
|
||||||
|
➕ Thêm thẻ
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<!-- Transaction History: Lịch sử nhận/gửi -->
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<x-transaction-history :mode="$mode" :user="$user" :administrations="$administrations" :selectedMonth="$selectedMonth" />
|
||||||
</div>
|
</div>
|
||||||
@endif
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
@else
|
||||||
|
<div class="grid grid-cols-1 {{ in_array($mode, ['edit', 'delete']) ? 'lg:grid-cols-3' : '' }} gap-6 items-start">
|
||||||
|
|
||||||
</div>
|
<!-- LEFT PANEL: User Profile & Actions / Form -->
|
||||||
|
<div class="{{ in_array($mode, ['edit', 'delete']) ? 'col-span-1' : 'col-span-full' }}">
|
||||||
|
@if($mode === 'create')
|
||||||
|
<div class="max-w-2xl mx-auto w-full">
|
||||||
|
<x-user-form :mode="$mode" :user="$user" />
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
<x-user-form :mode="$mode" :user="$user" />
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- RIGHT PANEL: Transaction History & Add Card History -->
|
||||||
|
@if(in_array($mode, ['edit', 'delete', 'self']) && $user && $administrations)
|
||||||
|
<div id="history-lists-container" class="col-span-1 lg:col-span-2 flex flex-col gap-6">
|
||||||
|
<x-transaction-history :mode="$mode" :user="$user" :administrations="$administrations" :selectedMonth="$selectedMonth" />
|
||||||
|
|
||||||
|
@if($mode === 'edit' && isset($addCards))
|
||||||
|
<div class="bg-white rounded-[24px] shadow-[0_10px_30px_rgba(15,23,42,0.05)] border border-white overflow-hidden flex flex-col">
|
||||||
|
<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-[17px] font-extrabold text-[#1a2b49] leading-tight">
|
||||||
|
Lịch sử cấp phát thẻ
|
||||||
|
</h3>
|
||||||
|
<x-month-filter :mode="$mode" :user="$user" :selectedMonth="$selectedMonth" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-0 overflow-y-auto max-h-[600px]">
|
||||||
|
<table class="min-w-full divide-y divide-gray-100 text-sm">
|
||||||
|
<thead class="bg-gray-50/50 sticky top-0">
|
||||||
|
<tr>
|
||||||
|
<th class="px-6 py-4 text-left text-xs font-bold text-text-light uppercase tracking-wider">Ngày cấp</th>
|
||||||
|
<th class="px-6 py-4 text-left text-xs font-bold text-text-light uppercase tracking-wider">Người cấp (Admin)</th>
|
||||||
|
<th class="px-6 py-4 text-left text-xs font-bold text-text-light uppercase tracking-wider">Số lượng thẻ</th>
|
||||||
|
<th class="px-6 py-4 text-center text-xs font-bold text-text-light uppercase tracking-wider w-24">Hành động</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="bg-white divide-y divide-gray-100">
|
||||||
|
@forelse($addCards as $addCard)
|
||||||
|
@php
|
||||||
|
$isCurrentMonth = Carbon\Carbon::parse($addCard->date)->format('Y-m') === Carbon\Carbon::now()->format('Y-m');
|
||||||
|
@endphp
|
||||||
|
<tr class="hover:bg-slate-50/60 transition-colors">
|
||||||
|
<td class="px-6 py-3.5 whitespace-nowrap text-text-medium font-medium">{{ Carbon\Carbon::parse($addCard->date)->format('d/m/Y') }}</td>
|
||||||
|
<td class="px-6 py-3.5 whitespace-nowrap font-bold text-[#1a2b49]">
|
||||||
|
@if($addCard->sellerUser)
|
||||||
|
<span class="text-[#3462f7]">{{ $addCard->sellerUser->name }}</span>
|
||||||
|
<div class="text-[11px] text-gray-400 font-medium mt-1.5 leading-none">
|
||||||
|
MSNV: {{ $addCard->sellerUser->msnv }} • {{ $addCard->sellerUser->mail }}
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
MSNV: {{ $addCard->seller }}
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
<td class="px-6 py-3.5 whitespace-nowrap font-extrabold text-base text-green-600">+{{ $addCard->num_card }}</td>
|
||||||
|
<td class="px-6 py-3.5 whitespace-nowrap text-center">
|
||||||
|
@if($isCurrentMonth)
|
||||||
|
<button type="button" onclick="openEditAddCardModal({{ json_encode($addCard) }})" class="text-blue-600 hover:text-blue-900 font-bold transition-colors">Sửa</button>
|
||||||
|
@else
|
||||||
|
<span class="text-gray-400 font-semibold text-xs cursor-not-allowed" title="Không thể chỉnh sửa giao dịch tháng trước">Tháng trước</span>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@empty
|
||||||
|
<tr>
|
||||||
|
<td colspan="4" class="px-6 py-12 text-center text-text-light font-medium text-base">
|
||||||
|
Chưa có lịch sử cấp phát thẻ nào.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforelse
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="px-6 py-4 bg-slate-50 border-t border-gray-100 flex justify-end">
|
||||||
|
<button type="button" onclick="openAddCardModal()" class="inline-flex items-center gap-2 bg-[#3462f7] hover:bg-blue-700 text-white font-bold text-sm px-5 py-2.5 rounded-xl shadow-md shadow-blue-500/10 transition-all">
|
||||||
|
➕ Thêm thẻ
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
@if($mode === 'edit' && isset($addCards) && isset($admins))
|
@if($mode === 'edit' && isset($addCards) && isset($admins))
|
||||||
<x-modal id="editAddCardModal" title="Chỉnh sửa giao dịch cấp phát thẻ">
|
<x-modal id="editAddCardModal" title="Chỉnh sửa giao dịch cấp phát thẻ">
|
||||||
@@ -99,13 +233,13 @@
|
|||||||
@method('PUT')
|
@method('PUT')
|
||||||
|
|
||||||
<div class="mb-5">
|
<div class="mb-5">
|
||||||
<label for="modal_num_card" class="block text-sm font-bold text-gray-700 mb-2">Số lượng card</label>
|
<label for="modal_num_card" class="block text-sm font-bold text-[#1a2b49] mb-2">Số lượng card</label>
|
||||||
<input type="number" id="modal_num_card" name="num_card" min="1" required class="w-full border border-gray-300 rounded-xl px-4 py-2.5 text-sm focus:border-blue-500 focus:ring focus:ring-blue-200 outline-none transition-all">
|
<input type="number" id="modal_num_card" name="num_card" min="1" required class="w-full border border-[#d9dfe7] rounded-xl px-4 py-2.5 text-sm focus:border-[#3462f7] focus:ring focus:ring-[#3462f7]/20 outline-none transition-all">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-5">
|
<div class="mb-5">
|
||||||
<label for="modal_seller" class="block text-sm font-bold text-gray-700 mb-2">Người cấp (Admin)</label>
|
<label for="modal_seller" class="block text-sm font-bold text-[#1a2b49] mb-2">Người cấp (Admin)</label>
|
||||||
<select id="modal_seller" name="seller" required class="w-full border border-gray-300 rounded-xl px-4 py-2.5 text-sm focus:border-blue-500 focus:ring focus:ring-blue-200 outline-none transition-all">
|
<select id="modal_seller" name="seller" required class="w-full border border-[#d9dfe7] bg-white rounded-xl px-4 py-2.5 text-sm focus:border-[#3462f7] focus:ring focus:ring-[#3462f7]/20 outline-none transition-all">
|
||||||
@foreach($admins as $admin)
|
@foreach($admins as $admin)
|
||||||
<option value="{{ $admin->msnv }}">{{ $admin->name }} (MSNV: {{ $admin->msnv }})</option>
|
<option value="{{ $admin->msnv }}">{{ $admin->name }} (MSNV: {{ $admin->msnv }})</option>
|
||||||
@endforeach
|
@endforeach
|
||||||
@@ -113,13 +247,30 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
<label for="modal_date" class="block text-sm font-bold text-gray-700 mb-2">Ngày cấp</label>
|
<label for="modal_date" class="block text-sm font-bold text-[#1a2b49] mb-2">Ngày cấp</label>
|
||||||
<input type="date" id="modal_date" name="date" required min="{{ Carbon\Carbon::now()->startOfMonth()->format('Y-m-d') }}" max="{{ Carbon\Carbon::now()->endOfMonth()->format('Y-m-d') }}" class="w-full border border-gray-300 rounded-xl px-4 py-2.5 text-sm focus:border-blue-500 focus:ring focus:ring-blue-200 outline-none transition-all">
|
<input type="date" id="modal_date" name="date" required min="{{ Carbon\Carbon::now()->startOfMonth()->format('Y-m-d') }}" max="{{ Carbon\Carbon::now()->endOfMonth()->format('Y-m-d') }}" class="w-full border border-[#d9dfe7] rounded-xl px-4 py-2.5 text-sm focus:border-[#3462f7] focus:ring focus:ring-[#3462f7]/20 outline-none transition-all">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-end gap-3 pt-4 border-t border-gray-100">
|
<div class="flex justify-end gap-3 pt-4 border-t border-gray-100">
|
||||||
<button type="button" onclick="closeModal('editAddCardModal')" class="btn-secondary !px-5">Hủy bỏ</button>
|
<button type="button" onclick="closeModal('editAddCardModal')" 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-[42px]">Hủy bỏ</button>
|
||||||
<button type="submit" class="btn-primary !px-5 bg-[#3462f7] hover:bg-blue-700">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-[42px]">Lưu thay đổi</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</x-modal>
|
||||||
|
|
||||||
|
<x-modal id="addCardModal" title="Cấp phát thêm thẻ cho user">
|
||||||
|
<form id="addCardForm" method="POST" action="{{ route('admin.users.update', $user->msnv) }}">
|
||||||
|
@csrf
|
||||||
|
@method('PUT')
|
||||||
|
|
||||||
|
<div class="mb-6">
|
||||||
|
<label for="add_modal_num_card" class="block text-sm font-bold text-[#1a2b49] mb-2">Số lượng card cấp phát thêm</label>
|
||||||
|
<input type="number" id="add_modal_num_card" name="num_card" min="1" required class="w-full border border-[#d9dfe7] rounded-xl px-4 py-2.5 text-sm focus:border-[#3462f7] focus:ring focus:ring-[#3462f7]/20 outline-none transition-all" placeholder="VD: 5">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-end gap-3 pt-4 border-t border-gray-100">
|
||||||
|
<button type="button" onclick="closeModal('addCardModal')" 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-[42px]">Hủy bỏ</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-[42px]">Xác nhận</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</x-modal>
|
</x-modal>
|
||||||
@@ -138,5 +289,102 @@
|
|||||||
|
|
||||||
openModal('editAddCardModal');
|
openModal('editAddCardModal');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openAddCardModal() {
|
||||||
|
document.getElementById('add_modal_num_card').value = '';
|
||||||
|
openModal('addCardModal');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitModalForm(e, formId, modalId) {
|
||||||
|
e.preventDefault();
|
||||||
|
const form = document.getElementById(formId);
|
||||||
|
const submitBtn = form.querySelector('button[type="submit"]');
|
||||||
|
if (submitBtn) submitBtn.disabled = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const formData = new FormData(form);
|
||||||
|
const response = await fetch(form.action, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'X-Requested-With': 'XMLHttpRequest',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
||||||
|
},
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
if (response.ok && result.success) {
|
||||||
|
closeModal(modalId);
|
||||||
|
|
||||||
|
if (window.showToast) {
|
||||||
|
window.showToast(result.message || 'Thành công!');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.card_balance !== undefined) {
|
||||||
|
const balanceEl = document.getElementById('user-card-balance');
|
||||||
|
if (balanceEl) {
|
||||||
|
balanceEl.textContent = result.card_balance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await reloadListsSection();
|
||||||
|
} else {
|
||||||
|
alert(result.message || 'Đã xảy ra lỗi. Vui lòng thử lại.');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
alert('Đã xảy ra lỗi kết nối.');
|
||||||
|
} finally {
|
||||||
|
if (submitBtn) submitBtn.disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function reloadListsSection() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(window.location.href, {
|
||||||
|
headers: {
|
||||||
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (response.ok) {
|
||||||
|
const html = await response.text();
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const doc = parser.parseFromString(html, 'text/html');
|
||||||
|
|
||||||
|
const newLists = doc.getElementById('history-lists-container');
|
||||||
|
const currentLists = document.getElementById('history-lists-container');
|
||||||
|
if (newLists && currentLists) {
|
||||||
|
currentLists.innerHTML = newLists.innerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newBalance = doc.getElementById('user-card-balance');
|
||||||
|
const currentBalance = document.getElementById('user-card-balance');
|
||||||
|
if (newBalance && currentBalance) {
|
||||||
|
currentBalance.textContent = newBalance.textContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to reload list section:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindFormEvents() {
|
||||||
|
const addForm = document.getElementById('addCardForm');
|
||||||
|
if (addForm) {
|
||||||
|
addForm.addEventListener('submit', (e) => submitModalForm(e, 'addCardForm', 'addCardModal'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const editForm = document.getElementById('editAddCardForm');
|
||||||
|
if (editForm) {
|
||||||
|
editForm.addEventListener('submit', (e) => submitModalForm(e, 'editAddCardForm', 'editAddCardModal'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState !== 'loading') {
|
||||||
|
bindFormEvents();
|
||||||
|
} else {
|
||||||
|
document.addEventListener('DOMContentLoaded', bindFormEvents);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
@endif
|
@endif
|
||||||
|
|||||||
@@ -142,7 +142,44 @@
|
|||||||
overlay.addEventListener('click', closeSidebar);
|
overlay.addEventListener('click', closeSidebar);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
window.showToast = function(message) {
|
||||||
|
const toast = document.getElementById('global-toast');
|
||||||
|
if (!toast) return;
|
||||||
|
|
||||||
|
const msgEl = toast.querySelector('.text-sm');
|
||||||
|
if (msgEl) msgEl.textContent = message;
|
||||||
|
|
||||||
|
toast.classList.remove('hidden');
|
||||||
|
toast.classList.add('flex');
|
||||||
|
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
toast.classList.remove('opacity-0', 'translate-y-[-10px]');
|
||||||
|
toast.classList.add('opacity-100', 'translate-y-0');
|
||||||
|
});
|
||||||
|
|
||||||
|
if (toast.dataset.timeoutId) {
|
||||||
|
clearTimeout(toast.dataset.timeoutId);
|
||||||
|
}
|
||||||
|
toast.dataset.timeoutId = setTimeout(() => {
|
||||||
|
window.closeToast('global-toast');
|
||||||
|
}, 3000);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.closeToast = function(id) {
|
||||||
|
const toast = document.getElementById(id);
|
||||||
|
if (!toast) return;
|
||||||
|
|
||||||
|
toast.classList.remove('opacity-100', 'translate-y-0');
|
||||||
|
toast.classList.add('opacity-0', 'translate-y-[-10px]');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
toast.classList.add('hidden');
|
||||||
|
toast.classList.remove('flex');
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
<x-toast id="global-toast" type="success" message="" />
|
||||||
@stack('scripts')
|
@stack('scripts')
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user