Hệ thống CRM quản lý Lead, báo giá, đơn hàng, hóa đơn và học viên cho trung tâm ngoại ngữ ODIN Language Center. Đây là đồ án tốt nghiệp.
Mỗi nhân viên đăng nhập sẽ thấy giao diện và menu theo vai trò của mình:
| Vai trò | role_name |
Đường dẫn |
|---|---|---|
| Giám đốc | director |
/director |
| Sale Admin | sale_admin |
/sale-admin |
| Sale Leader | sale_leader |
/sale-leader |
| Salesperson | salesperson |
/salesperson |
| Kế toán | accountant |
/accountant |
Trạng thái hiện tại: đã có đăng nhập, phân quyền, khung giao diện (menu dọc, menu ngang, đổi màu, đổi ngôn ngữ) và trang chủ của từng vai trò. Các màn hình nghiệp vụ (Lead, báo giá, đơn hàng...) đang được phát triển.
- Công nghệ sử dụng
- Cài đặt và chạy web
- Tài khoản demo
- Cấu trúc thư mục
- Code giao diện / frontend nằm ở đâu
- Thêm một trang mới
- Phân quyền và route
- REST API
- Cơ sở dữ liệu
- Đa ngôn ngữ
Hệ thống chia làm 3 phần: Frontend (giao diện), Backend (xử lý nghiệp vụ, cung cấp API) và Xác thực - phân quyền. Frontend không truy cập CSDL trực tiếp mà chỉ gọi API của Backend.
┌──────────────────────┐ HTTP + JSON (RESTful API) ┌──────────────────────────┐ ┌─────────┐
│ Frontend │ ───────────────────────────▶ │ Backend │ ─────▶ │ MySQL │
│ ReactJS, HTML5/CSS3 │ Authorization: Bearer token │ Laravel + Eloquent ORM │ ◀───── │ (odin) │
│ thư viện giao diện │ ◀─────────────────────────── │ Sanctum + middleware │ └─────────┘
└──────────────────────┘ └──────────────────────────┘
| Phần | Công nghệ | Vai trò trong hệ thống |
|---|---|---|
| Lập trình giao diện (Frontend) | ReactJS, HTML5/CSS3 và thư viện giao diện | Xây dựng giao diện từ các thành phần (component) dùng lại được; hiển thị dữ liệu, nhận thao tác của người dùng và gửi yêu cầu tới Backend qua API. Thư viện giao diện giúp thống nhất cách trình bày biểu mẫu, bảng dữ liệu và các thành phần tương tác. |
| Lập trình xử lý nghiệp vụ (Backend) | Laravel (PHP), Eloquent ORM | Xây dựng và cung cấp RESTful API; xử lý các nghiệp vụ như tạo và phân bổ Lead, cập nhật quá trình tư vấn, quản lý học viên và giao dịch (báo giá, đơn hàng, hóa đơn). Eloquent ORM hỗ trợ truy vấn, cập nhật dữ liệu và quản lý quan hệ giữa các bảng. |
| Xác thực và phân quyền | Laravel Sanctum; cơ chế phân quyền của ứng dụng | Sanctum cấp và kiểm tra token cho mỗi yêu cầu gọi API. Quyền dùng chức năng và phạm vi dữ liệu được kiểm soát tại Backend theo 5 vai trò: Giám đốc, Sale Admin, Sale Leader, Salesperson và Kế toán. |
Tình trạng hiện tại của từng phần:
| Phần | Đã có trong code | Vị trí |
|---|---|---|
| Frontend ReactJS | Chưa bắt đầu. Trong lúc chờ, giao diện web đang được làm bằng Livewire + Blade + Bootstrap (chạy song song, dùng chung Backend) | resources/views, resources/css, resources/js (xem mục 5) |
| Backend Laravel + Eloquent | Model cho toàn bộ bảng trong odin.sql (kèm quan hệ giữa các bảng); API đăng nhập |
app/Models, app/Http/Controllers/Api, routes/api.php |
| Xác thực - phân quyền | Đăng nhập bằng token Sanctum (API) và session (web); middleware role: chặn truy cập sai vai trò |
app/Http/Middleware/CheckRole.php, config/sanctum.php |
Vì sao chọn Sanctum thay vì Passport? Passport cài đặt đầy đủ chuẩn OAuth2, phù hợp khi cho ứng dụng bên thứ ba đăng nhập. Hệ thống chỉ có frontend của chính mình gọi API, nên Sanctum (token đơn giản, nhẹ hơn) là đủ.
| Thành phần | Công nghệ |
|---|---|
| Ngôn ngữ backend | PHP 8.2+ |
| Framework | Laravel 11 |
| ORM | Eloquent ORM |
| Frontend (dự kiến) | ReactJS + HTML5/CSS3 + thư viện giao diện, gọi RESTful API |
| Giao diện web (hiện tại) | Livewire 3 + Blade |
| CSS / UI | Bootstrap 5.3 + CSS riêng của dự án, font Be Vietnam Pro |
| JavaScript | JS thuần (ES module) + Bootstrap JS (dropdown, modal, offcanvas) |
| Build CSS/JS | Vite 6 (laravel-vite-plugin) |
| Xác thực API | Laravel Sanctum (Bearer token) — dùng cho frontend React |
| Cơ sở dữ liệu | MySQL / MariaDB |
| Đa ngôn ngữ | Laravel Localization + laravel-lang/common (Tiếng Việt, English) |
- PHP 8.2 trở lên (có extension
pdo_mysql,mbstring,openssl,fileinfo) - Composer 2
- Node.js 18+ và npm
- MySQL 8 hoặc MariaDB 10.4+ (XAMPP / Laragon đều được)
# 1. Lấy code về
git clone <đường-dẫn-repo>
cd odincode
# 2. Cài thư viện PHP và JS
composer install
npm install
# 3. Tạo file cấu hình và khoá ứng dụng
cp .env.example .env # Windows CMD: copy .env.example .env
php artisan key:generate4. Tạo cơ sở dữ liệu: import file dataset demo SQL/odin.sql vào MySQL. File này tự tạo database odin, các bảng nghiệp vụ và dữ liệu demo.
mysql -u root -p < "dataset demo SQL/odin.sql"Hoặc mở phpMyAdmin → Import → chọn odin.sql.
Sau đó sửa DB_USERNAME / DB_PASSWORD trong .env cho đúng với máy của bạn.
# 5. Tạo các bảng hệ thống của Laravel (sessions, cache, jobs, token API)
php artisan migrate
# 6. Build CSS/JS
npm run build
# 7. Chạy web
php artisan serveMở trình duyệt: http://127.0.0.1:8000 → tự chuyển tới trang đăng nhập.
Chạy song song 2 terminal để CSS/JS tự cập nhật khi lưu file, không cần build lại:
php artisan serve # terminal 1
npm run dev # terminal 2 (Vite dev server)Khi tắt
npm run dev, nhớ chạy lạinpm run build(hoặc xoá filepublic/hot) thì web mới dùng bản CSS/JS đã build.
Có trong odin.sql. Mật khẩu của tất cả tài khoản: password
| Tên đăng nhập | Họ tên | Vai trò |
|---|---|---|
director |
Nguyen Minh Anh | Giám đốc |
saleadmin |
Tran Thu Ha | Sale Admin |
leader1 |
Le Quang Huy | Sale Leader |
leader2 |
Vo Thanh Tam | Sale Leader |
sales01 |
Pham Ngoc Lan | Salesperson |
sales02 |
Do Tuan Kiet | Salesperson |
sales03 |
Bui Gia Han | Salesperson |
accountant |
Nguyen Duc Long | Kế toán |
Chỉ liệt kê những phần chính cần biết khi làm việc với dự án.
odincode/
├── app/ # Code PHP (backend)
│ ├── Http/
│ │ ├── Controllers/Api/ # Controller cho REST API (AuthController...)
│ │ └── Middleware/
│ │ ├── CheckRole.php # Kiểm tra vai trò: middleware 'role:director,...'
│ │ └── SetLocale.php # Đặt ngôn ngữ theo session (vi / en)
│ ├── Livewire/ # Class xử lý của từng trang (mỗi vai trò 1 thư mục)
│ │ ├── Auth/Login.php # Trang đăng nhập
│ │ ├── Director/ # Giám đốc
│ │ ├── SaleAdmin/ # Sale Admin
│ │ ├── SaleLeader/ # Sale Leader
│ │ ├── Salesperson/ # Salesperson
│ │ └── Accountant/ # Kế toán
│ ├── Models/ # Model Eloquent, ánh xạ 1-1 với bảng trong odin.sql
│ │ ├── Concerns/ # Trait dùng chung: khoá chuỗi (LEAD001...), khoá ghép
│ │ └── User.php # Bảng Accounts (tài khoản đăng nhập)
│ └── Providers/
│
├── bootstrap/app.php # Đăng ký middleware, route, xử lý lỗi
│
├── config/
│ ├── sidebar.php # ★ Menu dọc của từng vai trò (thứ tự, menu con)
│ └── ... # Cấu hình chuẩn của Laravel
│
├── database/migrations/ # CHỈ chứa bảng hệ thống Laravel (sessions, cache, jobs, tokens)
├── dataset demo SQL/odin.sql # ★ Schema + dữ liệu demo — nguồn chuẩn của CSDL
│
├── lang/ # File dịch
│ ├── vi/ # Tiếng Việt (menu.php, topbar.php, theme.php, auth.php...)
│ └── en/ # English
│
├── public/ # Thư mục web public
│ ├── images/ # Logo, favicon
│ └── build/ # CSS/JS đã build bởi Vite (tự sinh, không sửa tay)
│
├── resources/ # ★ Code giao diện (frontend)
│ ├── css/ # CSS (mỗi phần 1 file)
│ ├── js/ # JavaScript
│ └── views/ # Blade template (HTML)
│ ├── layouts/ # Khung trang
│ ├── components/ # Thành phần dùng lại (menu, popup...)
│ └── livewire/ # Nội dung từng trang, theo vai trò
│
├── routes/
│ ├── web.php # Route web chung: đăng nhập, đăng xuất, đổi ngôn ngữ, gom route vai trò
│ ├── web/ # Route web của từng vai trò (director.php, sale-admin.php...)
│ └── api.php # Route REST API (/api/...)
│
├── tests/ # Test PHPUnit
├── vite.config.js # Khai báo các file CSS/JS cho Vite build
├── composer.json # Thư viện PHP
└── package.json # Thư viện JS
Giao diện web dùng Blade + Livewire: HTML nằm trong file .blade.php, phần xử lý dữ liệu nằm trong class PHP ở app/Livewire. CSS/JS nằm trong resources/css và resources/js, được Vite build ra public/build.
| File | Nội dung |
|---|---|
resources/views/layouts/app.blade.php |
Khung trang: menu dọc trái + menu ngang trên + nội dung bên phải |
resources/views/layouts/auth.blade.php |
Khung trang đăng nhập |
Các trang chỉ cần dùng layout layouts.app là tự có menu, không cần code lại menu.
| File | Nội dung |
|---|---|
sidebar.blade.php |
Menu dọc bên trái (đọc danh sách mục từ config/sidebar.php), có menu con, thu gọn chỉ còn icon |
topbar.blade.php |
Menu ngang phía trên: nút thu gọn menu, tiêu đề trang, các nút bên phải |
notification-bell.blade.php |
Chuông thông báo |
account-menu.blade.php |
Tài khoản đang đăng nhập (avatar, tên, vai trò, đăng xuất) |
theme-picker.blade.php |
Chọn màu giao diện |
language-switcher.blade.php |
Đổi ngôn ngữ Việt / Anh |
logout-modal.blade.php |
Popup xác nhận đăng xuất |
| Phần | Vị trí |
|---|---|
| Class xử lý (PHP) | app/Livewire/{VaiTro}/TenTrang.php |
| Giao diện (Blade) | resources/views/livewire/{vai-tro}/ten-trang.blade.php |
Ví dụ trang chủ Giám đốc: app/Livewire/Director/Home.php + resources/views/livewire/director/home.blade.php.
| File | Dùng cho |
|---|---|
app.css |
Style chung, nạp Bootstrap, biến màu (--odin-primary, --odin-accent...) |
sidebar.css |
Menu dọc + khung trang |
topbar.css |
Menu ngang phía trên |
theme.css |
Bộ chọn màu, popup đăng xuất |
auth.css |
Trang đăng nhập |
Khi thêm file CSS mới: khai báo trong vite.config.js (mục input) và trong @vite([...]) của layout.
| File | Dùng cho |
|---|---|
app.js |
File chính: nạp Bootstrap JS và các file bên dưới |
sidebar.js |
Thu gọn / mở rộng menu dọc, mở menu con (nhớ trạng thái bằng localStorage) |
theme.js |
Đổi màu giao diện (nhớ màu bằng localStorage) |
logoodin-sidebar.png (logo nền trong suốt cho menu), logoodin-mark.png (biểu tượng khi menu thu gọn), favicon.png...
Ví dụ: thêm trang Quản lý Lead cho Giám đốc.
Bước 1 — Tạo class Livewire app/Livewire/Director/Leads.php:
<?php
namespace App\Livewire\Director;
use Livewire\Attributes\Layout;
use Livewire\Component;
#[Layout('layouts.app')]
class Leads extends Component
{
public function render()
{
return view('livewire.director.leads')->title(__('menu.leads'));
}
}Bước 2 — Tạo giao diện resources/views/livewire/director/leads.blade.php:
<div>
{{-- Nội dung trang, menu đã có sẵn từ layout --}}
</div>Bước 3 — Khai báo route trong routes/web/director.php:
Route::get('/leads', \App\Livewire\Director\Leads::class)->name('leads');Tên route đầy đủ là director.leads. Menu dọc tự nhận link này và tô sáng mục Quản lý Lead khi đang mở trang, không cần sửa menu. Quy tắc: tên route = {vai trò}.{route}, trong đó route lấy từ config/sidebar.php.
| Mục menu | Tên route |
|---|---|
| Dashboard & Báo cáo | {vai trò}.home |
| Quản lý Lead / Lead | {vai trò}.leads |
| Lịch hẹn | {vai trò}.appointments |
| Quản lý nhân viên / Nhân viên | {vai trò}.employees |
| Đội kinh doanh | {vai trò}.sales-teams |
| Báo giá, đơn hàng, hóa đơn | {vai trò}.quotations, .orders, .invoices |
| Học viên, khóa học, voucher | {vai trò}.students, .courses, .vouchers |
Mục menu chưa có route sẽ tạm trỏ tới #.
Sửa menu (thêm/bớt mục, đổi thứ tự, thêm menu con cho 1 vai trò): chỉ sửa config/sidebar.php, tên hiển thị nằm ở lang/vi/menu.php và lang/en/menu.php.
- Vai trò của người dùng:
Accounts→Employees.account_id→Roles.role_name. - Sau khi đăng nhập,
/tự chuyển về trang chủ của đúng vai trò. - Mỗi nhóm route được bảo vệ bởi middleware
auth+role:{vai trò}(khai báo trongroutes/web.php). Vào sai vai trò sẽ báo lỗi 403. - Chỉ đăng nhập được khi tài khoản
status = 'active'và nhân viênstatus = 'Working'.
Dùng Laravel Sanctum (Bearer token), chạy song song với web Livewire, chuẩn bị cho frontend React sau này. Tất cả route có tiền tố /api, khai báo trong routes/api.php.
| Phương thức | Đường dẫn | Mô tả |
|---|---|---|
POST |
/api/login |
Đăng nhập (username, password, device_name tuỳ chọn) → trả về token |
POST |
/api/logout |
Thu hồi token hiện tại |
GET |
/api/me |
Thông tin người dùng đang đăng nhập |
Các route có token gửi kèm header: Authorization: Bearer {token}. Nhóm route theo vai trò (/api/director, /api/sale-admin...) đã có sẵn khung, sẽ bổ sung sau.
dataset demo SQL/odin.sqllà nguồn chuẩn của toàn bộ bảng nghiệp vụ (tên bảng dạng PascalCase nhưLeads,Employees; khoá chính dạng chuỗi nhưLEAD001,EMP001).- Không tạo migration cho bảng nghiệp vụ. Thư mục
database/migrationschỉ chứa bảng hệ thống của Laravel:sessions,cache,jobs,personal_access_tokens. - Model trong
app/Modelsđược viết khớp vớiodin.sql. Khi sửa cấu trúc bảng thì sửaodin.sqltrước, sau đó cập nhật model tương ứng.
- Hỗ trợ Tiếng Việt (mặc định) và English, đổi bằng nút ngôn ngữ trên menu ngang (route
/lang/{vi|en}). - Chuỗi hiển thị nằm trong
lang/vi/*.phpvàlang/en/*.php, gọi trong Blade bằng{{ __('menu.leads') }}. - Khi thêm chuỗi mới, nhớ thêm vào cả 2 thư mục
vivàen.