all feature

This commit is contained in:
antv
2026-06-29 11:55:33 +07:00
parent 08d13336cd
commit 8a77324f89
59 changed files with 5587 additions and 145 deletions
@@ -0,0 +1,56 @@
@extends('layouts.app')
@section('title', 'Đổi Mật Khẩu')
@section('content')
<div class="form-container mt-10">
<div class="form-header">
<h3 class="form-header-title">Đổi Mật Khẩu</h3>
</div>
<form action="{{ route('user.update_password') }}" method="POST">
@csrf
<div class="form-body">
@if(Auth::user()->first_login == \App\Models\User::FIRST_LOGIN_TRUE)
<div class="mb-6 p-4 bg-orange-50 border border-orange-200 text-orange-800 rounded-lg text-sm font-medium leading-relaxed flex items-start gap-2 shadow-sm">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 shrink-0 text-orange-500 mt-0.5">
<path stroke-linecap="round" stroke-linejoin="round" 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>
<span>Đây lần đầu tiên bạn đăng nhập vào hệ thống. Để đảm bảo an toàn, hệ thống yêu cầu bạn đổi mật khẩu trước khi sử dụng các chức năng.</span>
</div>
@endif
<div class="form-group">
<label class="form-label" for="password">Mật khẩu mới <span class="text-red-500">*</span></label>
<div class="form-input-group">
<span class="form-input-icon">
<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">
<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" required placeholder="Nhập mật khẩu mới">
</div>
@error('password')<span class="error-text">{{ $message }}</span>@enderror
</div>
<div class="form-group">
<label class="form-label" for="password_confirmation">Xác nhận lại mật khẩu mới <span class="text-red-500">*</span></label>
<div class="form-input-group">
<span class="form-input-icon">
<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">
<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>
</span>
<input type="password" name="password_confirmation" id="password_confirmation" class="form-input form-input-with-icon" required placeholder="Nhập lại mật khẩu mới">
</div>
</div>
</div>
<div class="form-footer">
@if(Auth::user()->first_login != \App\Models\User::FIRST_LOGIN_TRUE)
<a href="{{ route('user.dashboard') }}" class="btn-secondary">Hủy bỏ</a>
@endif
<button type="submit" class="btn-primary">Xác Nhận Đổi Mật Khẩu</button>
</div>
</form>
</div>
@endsection
+77
View File
@@ -0,0 +1,77 @@
@extends('layouts.app')
@section('title', 'My Page')
@section('content')
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-6">
<div class="flex items-center gap-4">
<h2 class="text-xl font-bold text-text-dark flex items-center gap-2">
My Page
</h2>
<div class="px-4 py-1.5 bg-orange-100 border border-orange-200 rounded-full text-orange-800 font-bold text-sm flex items-center gap-2 shadow-sm">
<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 text-orange-500">
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 8.25h19.5M2.25 9h19.5m-16.5 5.25h6m-6 2.25h3m-3.75 3h15a2.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.5z" />
</svg>
Thẻ hiện : <span class="text-lg">{{ $user->card }}</span>
</div>
</div>
<form action="{{ route('user.dashboard') }}" method="GET" class="flex items-center gap-2">
<input type="month" name="month" value="{{ $selectedMonth }}" class="form-input !py-1.5 !text-sm w-auto border-border-medium rounded">
<button type="submit" class="btn-primary !py-1.5 !px-4 text-sm rounded shadow-sm hover:translate-y-[-1px]">Lọc</button>
</form>
</div>
<div class="bg-white shadow-sm rounded-xl overflow-hidden border border-border-light">
<div class="px-6 py-4 border-b border-border-light bg-gray-50/50">
<h3 class="text-lg font-bold text-text-dark">Lịch sử nhận / gửi thẻ (Tháng {{ Carbon\Carbon::parse($selectedMonth)->format('m/Y') }})</h3>
</div>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-border-light text-sm">
<thead class="bg-gray-50/80">
<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">Loại giao dịch</th>
<th class="px-6 py-4 text-left text-xs font-bold text-text-light uppercase tracking-wider">Đối tác</th>
<th class="px-6 py-4 text-left text-xs font-bold text-text-light uppercase tracking-wider">Số lượng</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-border-light">
@forelse($administrations as $admin_record)
@php
$isReceiver = $admin_record->msnv == $user->msnv && $admin_record->received > 0;
@endphp
<tr class="hover:bg-slate-50 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 whitespace-nowrap">
@if($isReceiver)
<span class="px-3 py-1 inline-flex text-xs leading-5 font-bold rounded-full bg-green-100 text-green-800">Bạn đã nhận thẻ</span>
@else
<span class="px-3 py-1 inline-flex text-xs leading-5 font-bold rounded-full bg-blue-100 text-blue-800">Bạn gửi tặng thẻ</span>
@endif
</td>
<td class="px-6 py-3 whitespace-nowrap font-bold text-text-dark">
@if($isReceiver)
Nhận từ: <span class="text-primary">{{ $admin_record->sender }}</span>
@else
Gửi cho: <span class="text-primary">{{ $admin_record->receiver }}</span>
@endif
</td>
<td class="px-6 py-3 whitespace-nowrap font-extrabold text-lg">
@if($isReceiver)
<span class="text-green-600">+{{ $admin_record->received }}</span>
@else
<span class="text-blue-600">-{{ $admin_record->sent }}</span>
@endif
</td>
</tr>
@empty
<tr>
<td colspan="4" class="px-6 py-12 text-center text-text-light font-medium text-base">Bạn chưa giao dịch gửi/nhận thẻ nào trong tháng này.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
@endsection
+146
View File
@@ -0,0 +1,146 @@
@extends('layouts.app')
@section('title', 'Gửi Thank Card')
@section('content')
<div class="form-container">
<div class="form-header">
<h3 class="form-header-title">Gửi Thank Card Tới Đồng Nghiệp</h3>
</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>
<!-- Custom Popup Modal -->
<x-modal id="customModal" title="Thông báo" maxWidth="md">
<p id="modalMessage" class="text-sm text-gray-600 leading-relaxed"></p>
<x-slot name="footer">
<div id="modalActions" class="w-full flex flex-col sm:flex-row-reverse gap-3">
<!-- Buttons injected by JS -->
</div>
</x-slot>
</x-modal>
@endsection
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function () {
const btnSubmit = document.getElementById('btnSubmitSend');
const form = document.getElementById('sendCardForm');
const modalTitle = document.getElementById('modal-title-customModal');
const modalMessage = document.getElementById('modalMessage');
const modalActions = document.getElementById('modalActions');
const showModal = (title, message, isConfirm, confirmCallback) => {
modalTitle.textContent = title;
modalMessage.innerHTML = message;
if(isConfirm) {
modalTitle.className = "text-lg font-bold text-primary";
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="btnCancel" class="w-full sm:w-auto btn-secondary !px-6 shadow-sm">Hủy</button>
`;
document.getElementById('btnCancel').addEventListener('click', () => window.closeModal('customModal'));
document.getElementById('btnConfirmOk').addEventListener('click', function() {
this.disabled = true;
this.innerHTML = '<span class="flex items-center gap-2"><svg class="animate-spin h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg> Đang xử lý...</span>';
confirmCallback();
});
} else {
modalTitle.className = "text-lg font-bold text-red-600";
modalActions.innerHTML = `
<button id="btnOkOnly" class="w-full sm:w-auto btn-secondary !px-6 shadow-sm">Đóng</button>
`;
document.getElementById('btnOkOnly').addEventListener('click', () => window.closeModal('customModal'));
}
window.openModal('customModal');
};
btnSubmit.addEventListener('click', function() {
if(!form.checkValidity()) {
form.reportValidity();
return;
}
const receiver = document.getElementById('receiver').value;
const amount = document.getElementById('amount').value;
showModal(
'Xác nhận gửi Thank Card',
`Bạn có chắc chắn muốn gửi <b>${amount} thẻ</b> cho nhân viên <span class="font-bold text-primary">${receiver}</span> không? <br><br><span class="text-xs text-text-light">Hành động này không thể hoàn tác, tin nhắn CO sẽ được gửi đi!</span>`,
true,
() => {
fetch('{{ route('user.store_send') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
},
body: JSON.stringify({
receiver: receiver,
amount: amount
})
})
.then(response => response.json().then(data => ({status: response.status, body: data})))
.then(res => {
if (res.status === 200 && res.body.success) {
alert(res.body.message);
window.location.href = '{{ route('user.dashboard') }}';
} else {
window.closeModal('customModal');
setTimeout(() => {
showModal('Lỗi Giao Dịch', res.body.message || 'Có lỗi xảy ra, vui lòng thử lại.', false, null);
}, 350);
}
})
.catch(err => {
window.closeModal('customModal');
setTimeout(() => {
showModal('Lỗi Hệ Thống', 'Không thể kết nối đến máy chủ.', false, null);
}, 350);
});
}
);
});
});
</script>
@endpush