# Kế hoạch plugin WordPress — Đặt vé tàu cao tốc (datvetaucaotoc)

Tài liệu tổng hợp: đánh giá khả thi, kiến trúc, chuyển đổi từ mirror Next.js, checklist triển khai.  
Cập nhật theo xác nhận của khách hàng (admin + DB, import lịch tháng, lưu đơn không gửi mail MVP).

---

## 1. Mục tiêu

| Mục tiêu | Mô tả |
|----------|--------|
| **Sản phẩm** | Plugin WordPress cài qua **Plugins → Add new**, không phụ thuộc Next.js trên production. |
| **Frontend** | Wizard 4 bước + màn xác nhận (UI đã hoàn thiện trên mirror `FerryBookingWizard`). |
| **Backend** | Admin quản trị tuyến/chuyến; **import lịch tháng**; CRUD lẻ sau import; **lưu đơn + hành khách vào DB**. |
| **MVP vận hành** | Sau submit **chỉ lưu DB** cho admin xử lý — **không gửi email** (giai đoạn sau). |
| **Thay thế dần** | Trang WP **Lịch tàu** (`/lich-tau/`): tab tuyến + `[wpdatatable]` → shortcode/block plugin. |

---

## 2. Hiện trạng (mirror Next.js)

### 2.1 Code đã có (tái sử dụng UI/logic)

| Thành phần | Vị trí | Ghi chú |
|------------|--------|---------|
| Wizard 4 bước + success | `src/components/booking/FerryBookingWizard.tsx` | ~700+ dòng |
| Thanh toán / xác nhận | `src/components/booking/BookingPaymentPanel.tsx` | CK, QR, link `/cs-huy-doi/` |
| Controls (ngày, tuyến, quốc tịch, …) | `src/components/booking/*` | react-select |
| Pricing / validation / search | `src/lib/booking-*.ts` | Port sang PHP (validate server) + giữ TS client preview |
| Copy thanh toán | `src/config/booking-payment.ts` | Chuyển sang Settings plugin |
| Mock dữ liệu | `src/config/booking-mock.ts` | **Bỏ trên WP** → REST + DB |
| CSS | `src/styles/ferry-booking.css` | ~1.6k dòng, enqueue scoped |

### 2.2 Hạn chế hiện tại

- Dữ liệu chuyến **fix cứng / mock**; không persistence.
- Submit = state client (`submitted`), không API.
- Production WP: **Lịch tàu** dùng block/tab + **wpDataTables** — chưa gom wizard vào plugin.

---

## 3. Quyết định đã chốt với khách hàng

1. **Nguồn dữ liệu:** Admin quản trị + lưu DB; FE **chỉ đọc/ghi qua API**.
2. **Lịch chuyến:** **Import lịch tháng** + **thêm / sửa / xóa** từng chuyến sau import.
3. **Số vé:** Mỗi chuyến khi import có **số lượng vé cố định** (`capacity`).
4. **Tồn kho (đề xuất kỹ thuật):** Dùng **`capacity` + `seats_remaining`** (count), không chỉ cờ hết vé — cập nhật khi tạo đơn (xem §5.4).
5. **Sau submit:** MVP **không email** — chỉ lưu DB; admin xử lý thủ công.

---

## 4. Kiến trúc đích

```text
┌─────────────────────────────────────────────────────────┐
│  WordPress Admin                                         │
│  • Tuyến • Chuyến (CRUD) • Import CSV tháng              │
│  • Đơn đặt vé • Cài đặt (STK, QR, hotline, link CS)     │
└──────────────────────────┬──────────────────────────────┘
                           │ wpdb / custom tables
┌──────────────────────────▼──────────────────────────────┐
│  MySQL — datvetau_* tables                               │
└──────────────────────────┬──────────────────────────────┘
                           │ REST API (datvetau/v1)
┌──────────────────────────▼──────────────────────────────┐
│  React bundle (shortcode / block) — ferry-booking UI     │
└─────────────────────────────────────────────────────────┘
```

**Nguyên tắc:** Pricing/validation hiển thị nhanh trên client; **mọi đơn POST phải validate + tính tiền lại trên server** trước khi ghi DB.

---

## 5. Mô hình dữ liệu

### 5.1 Bảng master

#### `wp_datvetau_routes`

| Cột | Kiểu | Mô tả |
|-----|------|--------|
| `id` | BIGINT PK | |
| `slug` | VARCHAR | VD: `rach-gia-phu-quoc` |
| `label` | VARCHAR | Hiển thị: RẠCH GIÁ - PHÚ QUỐC |
| `reverse_route_id` | BIGINT NULL | Cặp chiều về (khứ hồi) |
| `sort_order` | INT | Thứ tự tab Lịch tàu |
| `is_active` | TINYINT | |

#### `wp_datvetau_trips`

| Cột | Kiểu | Mô tả |
|-----|------|--------|
| `id` | BIGINT PK | |
| `route_id` | BIGINT FK | |
| `trip_date` | DATE | Ngày chạy |
| `departure_time` | TIME | Xuất cảng |
| `arrival_time` | TIME | Cập bến |
| `operator_name` | VARCHAR | Hãng tàu |
| `operator_logo_url` | VARCHAR NULL | URL media hoặc path |
| `seat_class` | ENUM eco,vip | |
| `price_adult_eco_base` | INT | VND, giá người lớn Eco (VIP tính hệ số) |
| `capacity` | INT | Số vé tối đa (import) |
| `seats_remaining` | INT | Ghế còn lại |
| `status` | ENUM active,cancelled | Hủy chuyến |
| `created_at`, `updated_at` | DATETIME | |

**Hiển thị “Hết vé”:** `seats_remaining <= 0` hoặc `status != active`.

#### `wp_datvetau_settings`

Key-value (JSON): STK BIDV/VCB, URL QR, hotline, địa chỉ TT trực tiếp, `policy_link` (`/cs-huy-doi/`), copy thanh toán.

(Fare rules trẻ em / cao tuổi / VIP: MVP **hardcode trong plugin** giống `booking-pricing.ts`; phase 2 có thể đưa vào settings.)

### 5.2 Bảng giao dịch

#### `wp_datvetau_bookings`

| Cột | Kiểu | Mô tả |
|-----|------|--------|
| `id` | BIGINT PK | |
| `booking_code` | VARCHAR UNIQUE | VD: `DV202610060001` |
| `status` | ENUM pending_payment,confirmed,cancelled | MVP: `pending_payment` |
| `trip_type` | ENUM one_way,round_trip | |
| `route_id` | BIGINT | Tuyến chính (search) |
| `outbound_trip_id` | BIGINT FK | Chiều đi |
| `inbound_trip_id` | BIGINT NULL | Chiều về |
| `seat_class` | ENUM eco,vip | |
| `ticket_count` | INT | = số hành khách |
| `total_vnd` | INT | Server tính |
| `booker_name`, `booker_phone`, `booker_email`, `booker_note` | TEXT/VARCHAR | |
| `created_at` | DATETIME | |

#### `wp_datvetau_passengers`

| Cột | Kiểu | Mô tả |
|-----|------|--------|
| `id` | BIGINT PK | |
| `booking_id` | BIGINT FK | |
| `full_name`, `birth_date`, `nationality`, `id_number`, `hometown` | | |
| `fare_category` | ENUM adult,child,senior | Lúc submit |
| `unit_price_vnd`, `legs_count` | INT | Audit giá |

### 5.3 Import lịch tháng (CSV)

**Cột gợi ý (UTF-8, dấu phẩy):**

```text
route_slug,trip_date,departure_time,arrival_time,operator_name,seat_class,price_adult_vnd,capacity
rach-gia-phu-quoc,2026-10-06,12:00,14:30,Phú Quốc Express,eco,315000,120
```

**Quy tắc import:**

- `route_slug` phải tồn tại trong `routes`.
- `seats_remaining` = `capacity` khi tạo mới.
- Trùng khóa `(route_id, trip_date, departure_time, seat_class)` → **update** hoặc **skip** (admin chọn trong UI import).
- Báo cáo: N dòng OK / lỗi kèm dòng CSV.

### 5.4 Trừ ghế khi đặt vé (MVP)

1. `POST /bookings` trong **transaction**.
2. Khóa hàng trip (`SELECT … FOR UPDATE` hoặc atomic `UPDATE … WHERE seats_remaining >= ?`).
3. Trừ `seats_remaining` += `- passenger_count` (mỗi HK = 1 vé; khứ hồi vẫn 1 vé/HK nhưng **tính tiền 2 chân**).
4. Chiều đi + chiều về: trừ **cả hai** trip nếu round-trip.
5. Nếu không đủ ghế → 409 + message.

**Hoàn ghế** khi admin hủy đơn (phase admin): cộng lại `seats_remaining` (cap tại `capacity`).

---

## 6. REST API

Namespace: `datvetau/v1`.

| Method | Route | Quyền | Mục đích |
|--------|-------|------|----------|
| GET | `/routes` | Public | Danh sách tuyến active |
| GET | `/trips` | Public | Query: `route_slug`, `date`, `seat_class`, `leg` (outbound/inbound helper) |
| GET | `/settings/public` | Public | QR, STK, copy CK, policy link |
| POST | `/bookings` | Public + **nonce** | Tạo đơn + passengers |
| GET | `/bookings/{code}` | Admin | Chi tiết (optional MVP) |

**Body POST `/bookings` (tóm tắt):** `trip_type`, `seat_class`, `outbound_trip_id`, `inbound_trip_id?`, `booker`, `passengers[]`.

**Response:** `booking_code`, `total_vnd`, payload hiển thị màn success (mirror `BookingPaymentPanel`).

---

## 7. WordPress Admin — checklist chức năng

### 7.1 Tuyến (`Routes`)

- [ ] Danh sách, thêm, sửa, ẩn/hiện, sắp xếp
- [ ] Gán cặp chiều về (khứ hồi)

### 7.2 Chuyến (`Trips`)

- [ ] Lọc: tuyến, tháng, hạng ghế
- [ ] Thêm / sửa / xóa từng chuyến
- [ ] Sửa `capacity`, `seats_remaining`, `status`
- [ ] **Import CSV lịch tháng** (upload + preview + commit)
- [ ] Log import (file name, user, timestamp)

### 7.3 Đơn đặt vé (`Bookings`)

- [ ] Danh sách + lọc trạng thái / ngày
- [ ] Chi tiết: booker, HK, chuyến, tổng tiền
- [ ] Đổi trạng thái (confirmed / cancelled) + hoàn ghế nếu cancel

### 7.4 Cài đặt (`Settings`)

- [ ] Thông tin CK (BIDV, VCB, QR URL)
- [ ] Hotline, địa chỉ TT trực tiếp
- [ ] Link chính sách hủy đổi (`/cs-huy-doi/`)

### 7.5 Plugin shell

- [ ] Activate → `dbDelta` migrations
- [ ] Deactivate: không xóa data (uninstall hook optional)
- [ ] Capability: `manage_options` hoặc custom `manage_datvetau_bookings`

---

## 8. Frontend plugin (chuyển đổi từ Next)

### 8.1 Nhúng WP

- [ ] Shortcode: `[datvetau_booking route="rach-gia-phu-quoc"]` (route optional)
- [ ] (Tuỳ chọn) Block Gutenberg
- [ ] Enqueue JS/CSS chỉ khi shortcode có trên page
- [ ] `wp_localize_script`: `restUrl`, `nonce`, `assetsBase`

### 8.2 Build React

- [ ] Tách wizard khỏi Next (`Link` → `<a>`, bỏ `next/*`)
- [ ] `@wordpress/scripts` hoặc Vite → `build/index.js`, `build/style.css`
- [ ] Thay mock bằng `fetch(restUrl + '/trips?…')`
- [ ] Submit → `POST /bookings` → màn success

### 8.3 CSS / theme

- [ ] Giữ namespace `.ferry-booking` + override `.entry-content` (Astra)
- [ ] Mobile: table scroll, bottom action bar, input 16px iOS

### 8.4 Mapping file Next → Plugin

| Next | Plugin |
|------|--------|
| `booking-mock.ts` | DB + REST |
| `FerryBookingWizard.tsx` | `assets/js/wizard/` |
| `booking-pricing.ts` | `includes/class-pricing.php` + TS preview |
| `booking-form-validation.ts` | `includes/class-validator.php` |
| `ferry-booking.css` | `assets/css/ferry-booking.css` |

---

## 9. Tích hợp trang Lịch tàu (WordPress)

**Hiện tại:** Tab tuyến (Gutena/block) + `[wpdatatable id=…]` + nút ĐẶT VÉ.

**Mục tiêu:**

- [ ] Tab **ĐẶT VÉ** (hoặc block tương đương): thay shortcode table bằng `[datvetau_booking route="…"]`
- [ ] Preselect `route` theo tab đang chọn
- [ ] Tab **lịch tra cứu** (nếu giữ): có thể vẫn wpDataTables đến khi admin quen import CSV vào plugin
- [ ] QA từng tuyến trên menu tab (10+ tuyến)

---

## 10. Lộ trình triển khai (phases)

| Phase | Nội dung | Deliverable |
|-------|----------|-------------|
| **0** | Spec CSV mẫu + wireframe admin | File mẫu + sign-off |
| **1** | Plugin skeleton, migrations, settings cơ bản | ZIP cài được, bảng rỗng |
| **2** | Admin Routes + Trips CRUD + Import CSV | Nhập được lịch tháng |
| **3** | REST GET routes/trips/settings | Postman collection |
| **4** | Port React wizard + nối REST | Shortcode chạy staging |
| **5** | POST bookings + trừ ghế + admin Orders | Đơn lưu DB, không mail |
| **6** | Seed từ mock Next (script one-time) | Demo data |
| **7** | Gắn `/lich-tau/`, UAT mobile/desktop | Go-live plugin |

**Ước lượng:** 1 dev full-stack **~6–8 tuần** tới MVP (DB + admin + wizard + lich-tau), chưa gồm email/API hãng tàu.

---

## 11. Definition of Done — MVP

### Plugin

- [ ] Cài/activate trên WP 6.x+, PHP 8.1+, không conflict Astra
- [ ] Shortcode hiển thị wizard giống bản Next đã duyệt

### Dữ liệu

- [ ] Import CSV tháng thành công; sửa/xóa chuyến lẻ
- [ ] `capacity` / `seats_remaining` đúng sau import và sau đặt vé

### Đặt vé

- [ ] 4 bước + xác nhận; CK + link chính sách
- [ ] Một chiều / khứ hồi; pricing rules như mirror
- [ ] Đơn + hành khách trong DB; **không gửi email**

### Admin

- [ ] Xem danh sách đơn, chi tiết, đổi trạng thái (tối thiểu)

### Frontend

- [ ] Mobile iOS không zoom input; bảng chuyến scroll ngang khi cần

---

## 12. Backlog (sau MVP)

- [ ] Email admin / khách (WP Mail / SMTP)
- [ ] Webhook / API đồng bộ hãng tàu
- [ ] Fare rules cấu hình trong admin
- [ ] Báo cáo doanh thu, export Excel
- [ ] Đa ngôn ngữ
- [ ] Giữ ghế tạm (hold) + timeout thanh toán

---

## 13. Cấu trúc thư mục plugin (đề xuất)

```text
datvetaucaotoc-booking/
├── datvetaucaotoc-booking.php    # Bootstrap
├── includes/
│   ├── class-activator.php       # dbDelta
│   ├── class-rest.php
│   ├── class-pricing.php
│   ├── class-validator.php
│   ├── class-booking-service.php # POST + trừ ghế
│   └── admin/
│       ├── class-routes.php
│       ├── class-trips.php
│       ├── class-import.php
│       ├── class-bookings.php
│       └── class-settings.php
├── assets/
│   ├── js/build/wizard.js
│   └── css/ferry-booking.css
└── readme.txt
```

*(Repo Next mirror có thể thêm `wordpress-plugin/` copy skeleton khi bắt Phase 1.)*

---

## 14. Rủi ro & giảm thiểu

| Rủi ro | Giảm thiểu |
|--------|------------|
| Theme CSS đè wizard | Namespace + specificity đã test trên mirror |
| Race condition trừ ghế | Transaction + UPDATE có điều kiện |
| Import CSV sai format | Preview + validate từng dòng |
| wpDataTables song song | Roadmap: chỉ lịch xem; đặt vé 100% plugin |

---

## 15. Tham chiếu code mirror (repo hiện tại)

- Wizard: `src/components/booking/FerryBookingWizard.tsx`
- Payment / success: `src/components/booking/BookingPaymentPanel.tsx`
- Mock (thay thế): `src/config/booking-mock.ts`
- Pricing: `src/lib/booking-pricing.ts`
- Validation: `src/lib/booking-form-validation.ts`
- Trang Lịch tàu Next: `src/components/pages/LichTauPage.tsx`
- WP slug: `lich-tau` trong `content/routes.json`

---

*Tài liệu này là single source of truth cho dự án plugin WP; cập nhật khi scope thay đổi.*
