diff --git a/app/Http/Controllers/AdminController.php b/app/Http/Controllers/Admin/AdminController.php
similarity index 97%
rename from app/Http/Controllers/AdminController.php
rename to app/Http/Controllers/Admin/AdminController.php
index d48343f..d1fb405 100644
--- a/app/Http/Controllers/AdminController.php
+++ b/app/Http/Controllers/Admin/AdminController.php
@@ -1,7 +1,8 @@
role == User::ROLE_MEMBER && Auth::user()->status == User::STATUS_ACTIVE) {
+ if (Auth::check() && in_array(Auth::user()->role, [User::ROLE_MEMBER, User::ROLE_ADMIN]) && Auth::user()->status == User::STATUS_ACTIVE) {
return $next($request);
}
return redirect()->route('login');
diff --git a/app/Services/Auth/AuthService.php b/app/Services/Auth/AuthService.php
index 47fe660..83a331a 100644
--- a/app/Services/Auth/AuthService.php
+++ b/app/Services/Auth/AuthService.php
@@ -40,10 +40,6 @@ class AuthService implements AuthServiceInterface
return route('user.change_password');
}
- if ($user->role == User::ROLE_ADMIN) {
- return route('admin.dashboard');
- }
-
return route('user.dashboard');
}
}
\ No newline at end of file
diff --git a/bootstrap/app.php b/bootstrap/app.php
index 1d919d8..d664775 100644
--- a/bootstrap/app.php
+++ b/bootstrap/app.php
@@ -3,19 +3,24 @@
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
+use Illuminate\Support\Facades\Route;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
+ then: function () {
+ Route::middleware('web')
+ ->group(base_path('routes/admin.php'));
+ },
)
->withMiddleware(function (Middleware $middleware): void {
$middleware->trustProxies(at: '*');
$middleware->alias([
- 'admin' => \App\Http\Middleware\AdminMiddleware::class,
- 'member' => \App\Http\Middleware\MemberMiddleware::class,
+ 'admin' => \App\Http\Middleware\Admin\AdminMiddleware::class,
+ 'member' => \App\Http\Middleware\User\MemberMiddleware::class,
'force_change_password' => \App\Http\Middleware\ForceChangePasswordMiddleware::class,
]);
})
diff --git a/public/images/hero_banner_illustration.png b/public/images/hero_banner_illustration.png
new file mode 100644
index 0000000..c8fa38e
Binary files /dev/null and b/public/images/hero_banner_illustration.png differ
diff --git a/public/images/pic-woman-gmo.png b/public/images/pic-woman-gmo.png
new file mode 100644
index 0000000..7caaf84
Binary files /dev/null and b/public/images/pic-woman-gmo.png differ
diff --git a/public/images/sidebar_illustration.png b/public/images/sidebar_illustration.png
new file mode 100644
index 0000000..3a99163
Binary files /dev/null and b/public/images/sidebar_illustration.png differ
diff --git a/public/images/thanks_card_illustration.png b/public/images/thanks_card_illustration.png
new file mode 100644
index 0000000..9620805
Binary files /dev/null and b/public/images/thanks_card_illustration.png differ
diff --git a/resources/assets/icons/eye-slash.svg b/resources/assets/icons/eye-slash.svg
new file mode 100644
index 0000000..f1d56a5
--- /dev/null
+++ b/resources/assets/icons/eye-slash.svg
@@ -0,0 +1,3 @@
+
diff --git a/resources/assets/icons/eye.svg b/resources/assets/icons/eye.svg
new file mode 100644
index 0000000..a69fcdb
--- /dev/null
+++ b/resources/assets/icons/eye.svg
@@ -0,0 +1,4 @@
+
diff --git a/resources/assets/icons/lock.svg b/resources/assets/icons/lock.svg
new file mode 100644
index 0000000..6a41a3d
--- /dev/null
+++ b/resources/assets/icons/lock.svg
@@ -0,0 +1,3 @@
+
diff --git a/resources/assets/icons/mail.svg b/resources/assets/icons/mail.svg
new file mode 100644
index 0000000..28871f6
--- /dev/null
+++ b/resources/assets/icons/mail.svg
@@ -0,0 +1,3 @@
+
diff --git a/resources/css/app.css b/resources/css/app.css
index 2b93ce1..7339476 100644
--- a/resources/css/app.css
+++ b/resources/css/app.css
@@ -1,3 +1,5 @@
+@import url('https://fonts.googleapis.com/css2?family=Caveat:wght@700&display=swap');
+
@font-face {
font-family: 'SVN-Poppins-Regular';
src: url('/fonts/FZ Poppins-Regular.ttf') format('truetype');
@@ -85,6 +87,10 @@
--color-calendar-header: #4b729f;
+ --color-terminal-bg: #263238;
+ --color-terminal-base: #b0bec5;
+ --color-terminal-accent: #90a4ae;
+
--shadow-card: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03);
--shadow-sidebar: 0 4px 20px 0 rgba(0, 0, 0, 0.05);
--shadow-header: 0 2px 4px 0 rgba(0, 0, 0, 0.02);
@@ -261,3 +267,6 @@ select.form-input {
.table-action-link {
@apply hover:text-primary-hover font-semibold underline underline-offset-2;
}
+
+@import "./login.css";
+
diff --git a/resources/css/login.css b/resources/css/login.css
new file mode 100644
index 0000000..6dccd5c
--- /dev/null
+++ b/resources/css/login.css
@@ -0,0 +1,47 @@
+/* resources/css/login.css */
+
+@keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+
+.animate-fade-in {
+ animation: fadeIn 0.3s ease-in-out forwards;
+}
+
+.floating-group {
+ @apply relative w-full;
+}
+
+.floating-input {
+ @apply w-full h-[56px] pl-11 pr-12 text-sm text-text-dark bg-transparent border border-[#c4c4c4] rounded outline-none transition-all duration-200 ease-in-out;
+}
+
+.floating-input:hover {
+ @apply border-text-medium;
+}
+
+.floating-input:focus {
+ @apply border-2 border-primary pl-[43px];
+}
+
+.floating-label {
+ @apply absolute left-11 top-1/2 -translate-y-1/2 text-sm text-text-muted pointer-events-none transition-all duration-200 ease-in-out bg-transparent p-0;
+}
+
+.floating-input:focus ~ .floating-label,
+.floating-input:not(:placeholder-shown) ~ .floating-label {
+ @apply top-0 -translate-y-1/2 text-xs text-primary bg-white px-1.5 left-10;
+}
+
+.floating-input:not(:focus):not(:placeholder-shown) ~ .floating-label {
+ @apply text-text-light;
+}
+
+.floating-icon {
+ @apply absolute left-4 top-1/2 -translate-y-1/2 text-text-muted pointer-events-none transition-colors duration-200 ease-in-out flex items-center justify-center;
+}
+
+.floating-input:focus ~ .floating-icon {
+ @apply text-primary;
+}
diff --git a/resources/js/app.js b/resources/js/app.js
index 75f1282..edcbd0c 100644
--- a/resources/js/app.js
+++ b/resources/js/app.js
@@ -25,3 +25,51 @@ window.openDeleteModal = function(msnv, url) {
window.openModal('deleteModal');
}
};
+
+window.togglePasswordVisibility = function(inputId, button) {
+ const input = document.getElementById(inputId);
+ if (!input) return;
+
+ const eyeOpen = button.querySelector('.eye-icon-open');
+ const eyeClosed = button.querySelector('.eye-icon-closed');
+
+ if (input.type === 'password') {
+ input.type = 'text';
+ button.setAttribute('aria-label', 'Hide password');
+ if (eyeOpen) eyeOpen.classList.remove('hidden');
+ if (eyeClosed) eyeClosed.classList.add('hidden');
+ } else {
+ input.type = 'password';
+ button.setAttribute('aria-label', 'Show password');
+ if (eyeOpen) eyeOpen.classList.add('hidden');
+ if (eyeClosed) eyeClosed.classList.remove('hidden');
+ }
+};
+
+document.addEventListener('DOMContentLoaded', () => {
+ const loginForm = document.querySelector('form[action$="/login"]');
+ if (loginForm) {
+ loginForm.addEventListener('submit', (e) => {
+ const submitBtn = loginForm.querySelector('button[type="submit"]');
+ if (submitBtn) {
+ if (submitBtn.disabled) {
+ e.preventDefault();
+ return;
+ }
+ submitBtn.disabled = true;
+
+ const originalText = submitBtn.textContent.trim();
+ submitBtn.dataset.originalText = originalText;
+
+ submitBtn.innerHTML = `
+
+ ${originalText}
+ `;
+ }
+ });
+ }
+});
+
diff --git a/resources/views/coming-soon.blade.php b/resources/views/coming-soon.blade.php
new file mode 100644
index 0000000..e4105ae
--- /dev/null
+++ b/resources/views/coming-soon.blade.php
@@ -0,0 +1,9 @@
+@extends('layouts.app')
+
+@section('title', 'Tính năng đang phát triển')
+
+@section('content')
+
+
+
+@endsection
diff --git a/resources/views/components/coming-soon.blade.php b/resources/views/components/coming-soon.blade.php
new file mode 100644
index 0000000..dae0c93
--- /dev/null
+++ b/resources/views/components/coming-soon.blade.php
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Chúng tôi đang nỗ lực để mang đến trải nghiệm tốt nhất cho bạn. Vui lòng quay lại sau nhé!
+
+
+
+
+
+ Quay về trang chủ
+
+
+
+
diff --git a/resources/views/components/floating-input.blade.php b/resources/views/components/floating-input.blade.php
new file mode 100644
index 0000000..e65c92f
--- /dev/null
+++ b/resources/views/components/floating-input.blade.php
@@ -0,0 +1,56 @@
+@props([
+ 'type' => 'text',
+ 'name',
+ 'id' => null,
+ 'label' => '',
+ 'required' => false,
+ 'value' => '',
+ 'autocomplete' => null,
+ 'icon' => null,
+ 'hasError' => false,
+])
+
+@php
+ $id = $id ?? $name;
+ $isPassword = $type === 'password';
+@endphp
+
+
+ merge(['class' => 'floating-input' . ($hasError ? ' border-red-500 focus:border-red-500 focus:ring-red-500/20' : '')]) }}
+ @if($hasError)
+ aria-invalid="true"
+ aria-describedby="login-errors"
+ @endif
+ />
+
+ @if($icon)
+
+
+
+ @endif
+
+ @if($isPassword)
+
+ @endif
+
diff --git a/resources/views/components/header.blade.php b/resources/views/components/header.blade.php
new file mode 100644
index 0000000..b8a4b58
--- /dev/null
+++ b/resources/views/components/header.blade.php
@@ -0,0 +1,19 @@
+
diff --git a/resources/views/components/hero-banner.blade.php b/resources/views/components/hero-banner.blade.php
new file mode 100644
index 0000000..ee56fef
--- /dev/null
+++ b/resources/views/components/hero-banner.blade.php
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
 }})
+
+
+
+
+
diff --git a/resources/views/components/icon-arrow-left.blade.php b/resources/views/components/icon-arrow-left.blade.php
new file mode 100644
index 0000000..5875f3a
--- /dev/null
+++ b/resources/views/components/icon-arrow-left.blade.php
@@ -0,0 +1,3 @@
+
diff --git a/resources/views/components/icon-rocket.blade.php b/resources/views/components/icon-rocket.blade.php
new file mode 100644
index 0000000..f4a90c0
--- /dev/null
+++ b/resources/views/components/icon-rocket.blade.php
@@ -0,0 +1,27 @@
+
diff --git a/resources/views/components/icon-sparkle.blade.php b/resources/views/components/icon-sparkle.blade.php
new file mode 100644
index 0000000..53c7b0f
--- /dev/null
+++ b/resources/views/components/icon-sparkle.blade.php
@@ -0,0 +1,3 @@
+
diff --git a/resources/views/components/icon-star.blade.php b/resources/views/components/icon-star.blade.php
new file mode 100644
index 0000000..bc052d5
--- /dev/null
+++ b/resources/views/components/icon-star.blade.php
@@ -0,0 +1,3 @@
+
diff --git a/resources/views/components/info-card.blade.php b/resources/views/components/info-card.blade.php
new file mode 100644
index 0000000..c47c2fd
--- /dev/null
+++ b/resources/views/components/info-card.blade.php
@@ -0,0 +1,11 @@
+
+
+
Bạn có biết?
+
+ Một lời cảm ơn chân thành có thể nhân đôi động lực làm việc của đồng nghiệp!
+
+
+
+ 💡
+
+
diff --git a/resources/views/components/login-bg-decorations.blade.php b/resources/views/components/login-bg-decorations.blade.php
new file mode 100644
index 0000000..aeea3fa
--- /dev/null
+++ b/resources/views/components/login-bg-decorations.blade.php
@@ -0,0 +1,13 @@
+
diff --git a/resources/views/components/login-illustration.blade.php b/resources/views/components/login-illustration.blade.php
new file mode 100644
index 0000000..088eff4
--- /dev/null
+++ b/resources/views/components/login-illustration.blade.php
@@ -0,0 +1,68 @@
+
+
+
+
+
diff --git a/resources/views/components/quick-send-card.blade.php b/resources/views/components/quick-send-card.blade.php
new file mode 100644
index 0000000..bce6cb1
--- /dev/null
+++ b/resources/views/components/quick-send-card.blade.php
@@ -0,0 +1,45 @@
+
diff --git a/resources/views/components/ranking-item.blade.php b/resources/views/components/ranking-item.blade.php
new file mode 100644
index 0000000..a54565c
--- /dev/null
+++ b/resources/views/components/ranking-item.blade.php
@@ -0,0 +1,34 @@
+@props(['rank', 'name', 'team', 'avatar', 'score', 'isCurrent' => false, 'unit' => 'điểm'])
+
+
+
+
+
+ @if($rank == 1)
+ 👑
+ 1
+ @elseif($rank == 2)
+ 2
+ @elseif($rank == 3)
+ 3
+ @else
+ {{ $rank }}
+ @endif
+
+
+
+
+

+
+ {{ $name }}
+ {{ $team }}
+
+
+
+
+
+
+
+ {{ $unit }}
+
+
diff --git a/resources/views/components/sidebar-item.blade.php b/resources/views/components/sidebar-item.blade.php
new file mode 100644
index 0000000..ee2f9bf
--- /dev/null
+++ b/resources/views/components/sidebar-item.blade.php
@@ -0,0 +1,49 @@
+@props(['active' => false, 'icon' => 'home', 'href' => '#'])
+
+@php
+$classes = $active
+ ? 'flex items-center gap-4 px-4 py-3 rounded-xl bg-[#eff4ff] text-[#3462f7] font-semibold transition-all duration-200'
+ : 'flex items-center gap-4 px-4 py-3 rounded-xl text-gray-500 hover:bg-gray-50 hover:text-gray-900 font-medium transition-all duration-200';
+@endphp
+
+merge(['class' => $classes]) }}>
+ @if($icon == 'home')
+
+ @elseif($icon == 'paper-plane-send')
+
+ @elseif($icon == 'heart')
+
+ @elseif($icon == 'paper-plane-outline')
+
+ @elseif($icon == 'trophy')
+
+ @elseif($icon == 'folder')
+
+ @elseif($icon == 'chart')
+
+ @elseif($icon == 'users')
+
+ @elseif($icon == 'gear')
+
+ @endif
+ {{ $slot }}
+
diff --git a/resources/views/components/sidebar.blade.php b/resources/views/components/sidebar.blade.php
new file mode 100644
index 0000000..8c17a8a
--- /dev/null
+++ b/resources/views/components/sidebar.blade.php
@@ -0,0 +1,68 @@
+@props(['active' => false])
+
+
diff --git a/resources/views/components/stat-card.blade.php b/resources/views/components/stat-card.blade.php
index 6d2d976..be17a11 100644
--- a/resources/views/components/stat-card.blade.php
+++ b/resources/views/components/stat-card.blade.php
@@ -1,14 +1,34 @@
-@props(['title', 'user', 'value', 'bgClass' => 'bg-[#0f4c81]', 'valueLabel' => 'Tổng thẻ đã nhận'])
+@props(['title' => '', 'value' => '', 'growth' => '', 'growthType' => 'up', 'iconBgClass' => '', 'iconColorClass' => '', 'subtitle' => ''])
-
-
-
-
{{ $title }}
-
{{ $user && $value > 0 ? $user : 'Chưa có' }}
+
+
+
+
-
-
{{ $valueLabel }}
-
{{ $value }} thẻ
+
+
+
+
{{ $title }}
+
+ {{ $value }}
+
+
+ @if($growth)
+
+
+ @if($growthType === 'up')
+
+ @else
+
+ @endif
+ {{ $growth }}
+
+
{{ $subtitle }}
+
+ @endif
diff --git a/resources/views/components/thank-card.blade.php b/resources/views/components/thank-card.blade.php
new file mode 100644
index 0000000..b882304
--- /dev/null
+++ b/resources/views/components/thank-card.blade.php
@@ -0,0 +1,16 @@
+@props(['title', 'message', 'isLiked' => false, 'bgClass', 'titleColorClass', 'textColorClass'])
+
+
diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php
deleted file mode 100644
index 3eca797..0000000
--- a/resources/views/dashboard.blade.php
+++ /dev/null
@@ -1,419 +0,0 @@
-
-
-
-
-
-
-
GMO-Z.com RUNSYSTEM - Dashboard
- @vite(['resources/css/app.css', 'resources/js/app.js'])
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Ngày công chuẩn
- 22
-
-
- Ngày công lễ
- 0
-
-
-
-
-
-
-
- Ngày công thực tế
- 20
-
-
- Nghỉ phép
- 0
-
-
-
-
-
-
-
- Số lần đi muộn, về sớm
- 0
-
-
- Số giờ đi muộn, về sớm
- 0
-
-
-
-
-
-
-
- Số giờ OT 150%
- 0
-
-
- Số giờ OT 200%
- 0
-
-
- Số giờ OT 300%
- 0
-
-
-
-
-
-
-
-
-
-
Tháng 06/2026
-
-
-
-
-
-
-
-
-
-
-
-
-
- 1
-
-
-
07:45 - 17:13
-
+ 0:28
-
-
-
-
-
- 2
-
-
-
07:53 - 17:08
-
+ 0:15
-
-
-
-
-
- 3
-
-
-
07:54 - 17:16
-
+ 0:22
-
-
-
-
-
- 4
-
-
-
07:52 - 17:04
-
+ 0:12
-
-
-
-
-
- 5
-
-
-
07:50 - 17:20
-
+ 0:30
-
-
-
-
-
-
-
-
- 8
-
-
-
07:54 - 17:05
-
+ 0:11
-
-
-
-
-
- 9
-
-
-
07:48 - 17:05
-
+ 0:17
-
-
-
-
-
- 10
-
-
-
07:51 - 17:05
-
+ 0:14
-
-
-
-
-
- 11
-
-
-
07:45 - 17:13
-
+ 0:28
-
-
-
-
-
- 12
-
-
-
07:45 - 17:37
-
+ 0:52
-
-
-
-
-
-
-
-
- 15
-
-
-
07:47 - 17:04
-
+ 0:17
-
-
-
-
-
- 16
-
-
-
07:45 - 17:02
-
+ 0:17
-
-
-
-
-
- 17
-
-
-
07:54 - 17:02
-
+ 0:08
-
-
-
-
-
- 18
-
-
-
07:45 - 17:03
-
+ 0:18
-
-
-
-
-
- 19
-
-
-
07:45 - 17:04
-
+ 0:19
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php
index 23924d4..66d6129 100644
--- a/resources/views/layouts/app.blade.php
+++ b/resources/views/layouts/app.blade.php
@@ -12,93 +12,12 @@
-
-
-
-
+
+
+
@if(session('success'))
@@ -175,6 +94,32 @@
document.body.style.overflow = '';
}, 300); // 300ms matches Tailwind duration-300
};
+
+ // Mobile Sidebar Toggle Logic
+ document.addEventListener('DOMContentLoaded', function () {
+ const hamburger = document.getElementById('sidebar-hamburger');
+ const closeBtn = document.getElementById('sidebar-close');
+ const overlay = document.getElementById('sidebar-overlay');
+ const sidebar = document.querySelector('.sidebar');
+
+ if (hamburger && sidebar && overlay && closeBtn) {
+ function openSidebar() {
+ sidebar.classList.remove('-translate-x-full');
+ overlay.classList.remove('hidden');
+ document.body.style.overflow = 'hidden';
+ }
+
+ function closeSidebar() {
+ sidebar.classList.add('-translate-x-full');
+ overlay.classList.add('hidden');
+ document.body.style.overflow = '';
+ }
+
+ hamburger.addEventListener('click', openSidebar);
+ closeBtn.addEventListener('click', closeSidebar);
+ overlay.addEventListener('click', closeSidebar);
+ }
+ });
@stack('scripts')
+
-
-
-
+
+
-
-
-
-
-
-
 }})
-
-
-
-
{{ __('auth.login') }}
-
-
-
-
-
- © {{ __('auth.developed_by') }}
-
-
-
-
-
-
-
-
-
-
+