This commit is contained in:
phaichayon
2026-04-26 00:15:22 +07:00
parent a330abf9b6
commit 043edff93a
64 changed files with 25076 additions and 461 deletions

View File

@@ -7,7 +7,7 @@ This project uses ElysiaJS integrated with Next.js App Router to create high-per
## Base URL
```
http://localhost:3001
http://localhost:3000
```
## Endpoints
@@ -32,19 +32,19 @@ GET /api/customers/:branch
1. Get all customers from branch-01:
```bash
curl http://localhost:3001/api/customers/branch-01
curl http://localhost:3000/api/customers/branch-01
```
2. Get active customers from branch-02:
```bash
curl "http://localhost:3001/api/customers/branch-02?status=active"
curl "http://localhost:3000/api/customers/branch-02?status=active"
```
3. Get pending customers from head-office:
```bash
curl "http://localhost:3001/api/customers/head-office?status=pending"
curl "http://localhost:3000/api/customers/head-office?status=pending"
```
**Response Format:**
@@ -117,13 +117,13 @@ GET /api/quotations/:branch
1. Get all quotations from branch-01:
```bash
curl http://localhost:3001/api/quotations/branch-01
curl http://localhost:3000/api/quotations/branch-01
```
2. Get sent quotations from head-office:
```bash
curl "http://localhost:3001/api/quotations/head-office?status=sent"
curl "http://localhost:3000/api/quotations/head-office?status=sent"
```
**Response Format:**
@@ -181,6 +181,422 @@ DELETE /api/quotations/:branch/:id
---
### Master Options API
#### Get All Master Options
```
GET /api/master-options
```
**Query Parameters:**
- `category` (optional): Filter by category (e.g., `customer_type`, `payment_method`, `industry`)
- `isActive` (optional): Filter by active status (`true` or `false`)
- `search` (optional): Search in code, nameTh, or nameEn
**Example:**
```bash
curl "http://localhost:3000/api/master-options?category=customer_type&isActive=true"
```
**Response Format:**
```json
{
"success": true,
"data": [
{
"id": "opt-001",
"branchId": "branch-01",
"category": "customer_type",
"code": "CORPORATE",
"nameTh": "องค์กร/บริษัท",
"nameEn": "Corporate",
"descriptionTh": "ลูกค้าประเภทองค์กร",
"descriptionEn": "Corporate customers",
"isActive": true,
"createdAt": "2024-01-15T09:00:00Z",
"updatedAt": "2024-01-15T09:00:00Z"
}
],
"count": 1,
"message": "Found 1 master option(s)"
}
```
#### Get Single Master Option
```
GET /api/master-options/:id
```
#### Create Master Option
```
POST /api/master-options
```
**Request Body:**
```json
{
"category": "customer_type",
"code": "INDIVIDUAL",
"nameTh": "บุคคลธรรมดา",
"nameEn": "Individual",
"descriptionTh": "ลูกค้ารายบุคคล",
"descriptionEn": "Individual customers"
}
```
#### Update Master Option
```
PUT /api/master-options/:id
```
#### Delete Master Option
```
DELETE /api/master-options/:id
```
#### Toggle Active Status
```
PATCH /api/master-options/:id/toggle
```
---
### Locations API
#### Get All Locations
```
GET /api/locations
```
**Query Parameters:**
- `type` (optional): Filter by location type (`country`, `province`, `district`, `subdistrict`)
- `parentId` (optional): Filter by parent location ID
- `search` (optional): Search in code, nameTh, or nameEn
- `isActive` (optional): Filter by active status
**Example:**
```bash
curl "http://localhost:3000/api/locations?type=province&search=กรุงเทพ"
```
**Response Format:**
```json
{
"success": true,
"data": [
{
"id": "loc-001",
"branchId": "head-office",
"code": "TH-10",
"nameTh": "กรุงเทพมหานคร",
"nameEn": "Bangkok",
"type": "province",
"parentId": "country-th-id",
"isActive": true,
"createdAt": "2024-01-15T09:00:00Z",
"updatedAt": "2024-01-15T09:00:00Z"
}
],
"count": 1,
"message": "Found 1 location(s)"
}
```
#### Get Locations by Type
```
GET /api/locations/type/:type
```
**Parameters:**
- `type` (path parameter): `country`, `province`, `district`, or `subdistrict`
**Example:**
```bash
curl http://localhost:3000/api/locations/type/province
```
#### Get Single Location
```
GET /api/locations/:id
```
#### Create Location
```
POST /api/locations
```
**Request Body:**
```json
{
"code": "TH-10",
"nameTh": "กรุงเทพมหานคร",
"nameEn": "Bangkok",
"type": "province",
"parentId": "country-th-id"
}
```
#### Update Location
```
PUT /api/locations/:id
```
#### Delete Location
```
DELETE /api/locations/:id
```
#### Toggle Active Status
```
PATCH /api/locations/:id/toggle
```
---
### Industrial Estates API
#### Get All Industrial Estates
```
GET /api/industrial-estates
```
**Query Parameters:**
- `locationId` (optional): Filter by location ID
- `isActive` (optional): Filter by active status
- `search` (optional): Search in code, nameTh, or nameEn
**Example:**
```bash
curl "http://localhost:3000/api/industrial-estates?locationId=th-10&isActive=true"
```
**Response Format:**
```json
{
"success": true,
"data": [
{
"id": "ie-001",
"branchId": "head-office",
"code": "BPL",
"nameTh": "นิคมอุตสาหกรรมบางพลี",
"nameEn": "Bangpoo Industrial Estate",
"locationId": "th-10",
"latitude": 13.5991,
"longitude": 100.7015,
"isActive": true,
"createdAt": "2024-01-15T09:00:00Z",
"updatedAt": "2024-01-15T09:00:00Z"
}
],
"count": 1,
"message": "Found 1 industrial estate(s)"
}
```
#### Get Industrial Estates by Location
```
GET /api/industrial-estates/location/:locationId
```
**Example:**
```bash
curl http://localhost:3000/api/industrial-estates/location/th-10
```
#### Get Single Industrial Estate
```
GET /api/industrial-estates/:id
```
#### Create Industrial Estate
```
POST /api/industrial-estates
```
**Request Body:**
```json
{
"code": "BPL",
"nameTh": "นิคมอุตสาหกรรมบางพลี",
"nameEn": "Bangpoo Industrial Estate",
"locationId": "th-10",
"latitude": 13.5991,
"longitude": 100.7015
}
```
#### Update Industrial Estate
```
PUT /api/industrial-estates/:id
```
#### Delete Industrial Estate
```
DELETE /api/industrial-estates/:id
```
#### Toggle Active Status
```
PATCH /api/industrial-estates/:id/toggle
```
---
### Audit Logs API
**Note:** This API requires Admin/Superadmin/Auditor access level.
#### Get All Audit Logs
```
GET /api/audit-logs
```
**Query Parameters:**
- `startDate` (optional): Filter logs from this date (ISO 8601 format)
- `endDate` (optional): Filter logs until this date (ISO 8601 format)
- `action` (optional): Filter by action type (`CREATE`, `UPDATE`, `DELETE`, `READ`)
- `entityType` (optional): Filter by entity type (`customer`, `quotation`, `location`, etc.)
- `limit` (optional): Number of results to return (default: 50)
- `offset` (optional): Number of results to skip (for pagination)
**Example:**
```bash
curl "http://localhost:3000/api/audit-logs?action=CREATE&limit=10"
```
**Response Format:**
```json
{
"success": true,
"data": [
{
"id": "audit-001",
"branchId": "branch-01",
"userId": "user-123",
"actorId": "user-123",
"entityType": "customer",
"entityId": "cust-001",
"action": "CREATE",
"actionTh": "สร้าง",
"oldValues": null,
"newValues": {
"name": "สมชาย ใจดี",
"email": "somchai@example.com"
},
"ipAddress": "192.168.1.100",
"userAgent": "Mozilla/5.0...",
"createdAt": "2024-01-15T09:00:00Z"
}
],
"count": 1,
"message": "Found 1 audit log(s)"
}
```
#### Get Audit Log Statistics
```
GET /api/audit-logs/stats
```
**Response Format:**
```json
{
"success": true,
"data": {
"totalLogs": 1250,
"byAction": {
"CREATE": 350,
"UPDATE": 500,
"DELETE": 150,
"READ": 250
},
"byEntityType": {
"customer": 400,
"quotation": 300,
"location": 200,
"industrial_estate": 100,
"master_option": 250
},
"todayCount": 45,
"thisWeekCount": 320
}
}
```
#### Get Logs by Entity
```
GET /api/audit-logs/entity/:entityType/:entityId
```
**Example:**
```bash
curl http://localhost:3000/api/audit-logs/entity/customer/cust-001
```
#### Get Logs by User
```
GET /api/audit-logs/user/:userId
```
**Example:**
```bash
curl http://localhost:3000/api/audit-logs/user/user-123
```
#### Get Single Audit Log
```
GET /api/audit-logs/:id
```
---
## Available Data
### Customers
@@ -195,20 +611,62 @@ DELETE /api/quotations/:branch/:id
- `branch-02`: 1 quotation (draft)
- `head-office`: 1 quotation (sent)
### Master Options
- Categories: `customer_type`, `payment_method`, `industry`, `lead_source`
- Each category has multiple options with Thai/English names
### Locations
- Countries: Thailand, etc.
- Provinces: All Thai provinces
- Districts/Subdistricts: Hierarchical data structure
### Industrial Estates
- Multiple industrial estates across Thailand
- Linked to locations with GPS coordinates
### Audit Logs
- Complete audit trail for all operations
- Admin-only access
## Testing with Browser
Simply open these URLs in your browser:
### Customers
- http://localhost:3001/api/customers/branch-01
- http://localhost:3001/api/customers/branch-02?status=active
- http://localhost:3001/api/customers/head-office
- http://localhost:3000/api/customers/branch-01
- http://localhost:3000/api/customers/branch-02?status=active
- http://localhost:3000/api/customers/head-office
### Quotations
- http://localhost:3001/api/quotations/branch-01
- http://localhost:3001/api/quotations/head-office?status=sent
- http://localhost:3000/api/quotations/branch-01
- http://localhost:3000/api/quotations/head-office?status=sent
### Master Options
- http://localhost:3000/api/master-options
- http://localhost:3000/api/master-options?category=customer_type
### Locations
- http://localhost:3000/api/locations
- http://localhost:3000/api/locations/type/province
- http://localhost:3000/api/locations?search=กรุงเทพ
### Industrial Estates
- http://localhost:3000/api/industrial-estates
- http://localhost:3000/api/industrial-estates?isActive=true
### Audit Logs (Admin only)
- http://localhost:3000/api/audit-logs
- http://localhost:3000/api/audit-logs/stats
## Project Structure
@@ -225,7 +683,23 @@ src/
│ │ ├── controller.ts # HTTP handlers & routing
│ │ ├── service.ts # Business logic
│ │ └── model.ts # Schemas & validation
── quotations/
── quotations/
│ │ ├── controller.ts # HTTP handlers & routing
│ │ ├── service.ts # Business logic
│ │ └── model.ts # Schemas & validation
│ ├── master-options/
│ │ ├── controller.ts # HTTP handlers & routing
│ │ ├── service.ts # Business logic
│ │ └── model.ts # Schemas & validation
│ ├── locations/
│ │ ├── controller.ts # HTTP handlers & routing
│ │ ├── service.ts # Business logic
│ │ └── model.ts # Schemas & validation
│ ├── industrial-estates/
│ │ ├── controller.ts # HTTP handlers & routing
│ │ ├── service.ts # Business logic
│ │ └── model.ts # Schemas & validation
│ └── audit-logs/
│ ├── controller.ts # HTTP handlers & routing
│ ├── service.ts # Business logic
│ └── model.ts # Schemas & validation
@@ -233,6 +707,8 @@ src/
│ └── customer.ts # Shared types
├── lib/
│ └── mock-data.ts # Mock data
└── database/
└── schema.ts # Drizzle ORM schema
```
### File Responsibilities
@@ -275,6 +751,10 @@ This project follows the **correct ElysiaJS + Next.js integration pattern**:
- ✅ WinterCG compliant - works as normal Next.js API route
- ✅ Feature-based MVC pattern for maintainability
- ✅ Clear separation of concerns between Model, View, and Controller
- ✅ Branch-level data scoping for multi-tenant architecture
- ✅ Audit logging for all operations
- ✅ Soft delete with `deletedAt` field
- ✅ Multi-language support (Thai/English)
## Technologies Used
@@ -282,19 +762,28 @@ This project follows the **correct ElysiaJS + Next.js integration pattern**:
- **Next.js 16**: React framework with App Router
- **TypeScript**: Type safety throughout
- **TypeBox**: Schema validation (via `@elysiajs/schema`)
- **Drizzle ORM**: Type-safe SQL ORM
- **PostgreSQL**: Primary database
## Features
✅ Feature-based MVC architecture
✅ Dynamic branch parameter support
✅ Type-safe request/response validation
✅ Optional query parameter filtering
✅ Mock data for customers and quotations
✅ Full TypeScript support
✅ Auto-generated API documentation (Swagger/OpenAPI ready)
✅ Correct ElysiaJS + Next.js integration pattern
✅ Scalable and maintainable code structure
✅ Feature-based MVC architecture
✅ Dynamic branch parameter support
✅ Type-safe request/response validation
✅ Optional query parameter filtering
✅ Mock data for customers and quotations
✅ Full TypeScript support
✅ Auto-generated API documentation (Swagger/OpenAPI ready)
✅ Correct ElysiaJS + Next.js integration pattern
✅ Scalable and maintainable code structure
✅ Clear separation of concerns
✅ Multi-tenant architecture with branch scoping
✅ Complete audit logging system
✅ Soft delete for data integrity
✅ Multi-language support (Thai/English)
✅ Hierarchical data structures (locations)
✅ GPS coordinate support (industrial estates)
✅ Admin-only access control (audit logs)
## Adding New Modules
@@ -304,7 +793,8 @@ To add a new module (e.g., `products`):
2. Create `model.ts` - Define schemas
3. Create `service.ts` - Business logic
4. Create `controller.ts` - Routes and handlers
5. Update `src/app/api/[[...slugs]]/route.ts`:
5. Create `index.ts` - Module exports
6. Update `src/app/api/[[...slugs]]/route.ts`:
```typescript
import { products } from "@/modules/products/controller";
@@ -312,5 +802,32 @@ To add a new module (e.g., `products`):
const app = new Elysia({ prefix: "/api" })
.use(customers)
.use(quotations)
.use(masterOptions)
.use(locations)
.use(industrialEstates)
.use(auditLogs)
.use(products); // Add new module
```
## Security & Access Control
### Branch Middleware
All routes use `branchMiddleware` which injects:
- `currentBranchId` - Current user's branch
- `userId` - Current user ID
- `userGroups` - User groups/roles
- `accessibleBranches` - Branches user can access
### Permission Levels
- **Standard Users**: Access to branch-scoped data
- **Admin/Superadmin**: Full access + audit logs
- **Auditor**: Read-only access to audit logs
### Data Isolation
- All queries are automatically filtered by `branchId`
- Cross-branch access is prevented
- Soft delete ensures data integrity