# PHẦN 1: API DOCUMENTATION ĐẦY ĐỦ

### **StoryForge API v1.0**

Base URL: `https://api.storyforge.ai/v1` Authentication: Bearer Token (JWT)

***

#### **1. AUTHENTICATION**

**POST /auth/register**

Đăng ký tài khoản mới.**Request:**&#x4A;SON Copy

```json
{
  "email": "user@example.com",
  "password": "SecurePass123!",
  "name": "Nguyễn Văn A"
}
```

**Response (201):**&#x4A;SON Copy

```json
{
  "success": true,
  "data": {
    "user_id": "usr_8f7d2a9e1b3c",
    "email": "user@example.com",
    "name": "Nguyễn Văn A",
    "subscription_tier": "free",
    "tokens_remaining": 10000,
    "created_at": "2024-01-15T08:30:00Z"
  },
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```

**Error Codes:**

* `400`: Email đã tồn tại
* `422`: Password không đủ mạnh
* `429`: Quá nhiều request đăng ký

***

**POST /auth/login**

Đăng nhập.**Request:**&#x4A;SON Copy

```json
{
  "email": "user@example.com",
  "password": "SecurePass123!"
}
```

**Response (200):**&#x4A;SON Copy

```json
{
  "success": true,
  "data": {
    "user_id": "usr_8f7d2a9e1b3c",
    "email": "user@example.com",
    "name": "Nguyễn Văn A",
    "subscription_tier": "pro",
    "tokens_remaining": 45000,
    "projects_count": 12,
    "last_login": "2024-01-15T08:30:00Z"
  },
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "rt_9a8b7c6d5e4f..."
}
```

***

**POST /auth/refresh**

Làm mới token.**Request:**&#x4A;SON Copy

```json
{
  "refresh_token": "rt_9a8b7c6d5e4f..."
}
```

**Response (200):**&#x4A;SON Copy

```json
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_in": 3600
}
```

***

**POST /auth/logout**

Đăng xuất (vô hiệu hóa token).**Headers:** `Authorization: Bearer {token}`**Response (200):**&#x4A;SON Copy

```json
{
  "success": true,
  "message": "Logged out successfully"
}
```

***

#### **2. USER MANAGEMENT**

**GET /users/me**

Lấy thông tin user hiện tại.**Headers:** `Authorization: Bearer {token}`**Response (200):**&#x4A;SON Copy

```json
{
  "user_id": "usr_8f7d2a9e1b3c",
  "email": "user@example.com",
  "name": "Nguyễn Văn A",
  "avatar_url": "https://cdn.storyforge.ai/avatars/usr_8f7d.jpg",
  "subscription": {
    "tier": "pro",
    "started_at": "2024-01-01T00:00:00Z",
    "expires_at": "2024-12-31T23:59:59Z",
    "features": {
      "max_projects": -1,
      "max_words_per_project": 50000,
      "available_modes": ["romance-pure", "drama", "rom-com", "thriller", "slice-of-life"],
      "export_formats": ["epub", "pdf", "docx", "audio"],
      "priority_processing": true
    }
  },
  "usage": {
    "projects_this_month": 8,
    "words_generated_this_month": 125000,
    "tokens_remaining": 45000
  },
  "preferences": {
    "default_writing_mode": "romance-pure",
    "language": "vi",
    "email_notifications": true
  }
}
```

***

**PATCH /users/me**

Cập nhật thông tin.**Headers:** `Authorization: Bearer {token}`**Request:**&#x4A;SON Copy

```json
{
  "name": "Nguyễn Văn B",
  "preferences": {
    "default_writing_mode": "drama",
    "email_notifications": false
  }
}
```

**Response (200):** Updated user object

***

**POST /users/me/avatar**

Upload avatar.**Headers:**

* `Authorization: Bearer {token}`
* `Content-Type: multipart/form-data`

**Body:** `file` (image, max 5MB)**Response (200):**&#x4A;SON Copy

```json
{
  "avatar_url": "https://cdn.storyforge.ai/avatars/usr_8f7d_new.jpg"
}
```

***

#### **3. PROJECT MANAGEMENT**

**GET /projects**

Liệt kê tất cả projects.**Headers:** `Authorization: Bearer {token}`**Query Parameters:**&#x54;able Copy

| Param     | Type   | Description                     | Default     |
| --------- | ------ | ------------------------------- | ----------- |
| `page`    | int    | Trang hiện tại                  | 1           |
| `limit`   | int    | Số items/trang                  | 20          |
| `status`  | string | Lọc theo status                 | all         |
| `sort_by` | string | created\_at, updated\_at, title | updated\_at |
| `order`   | string | asc, desc                       | desc        |

**Response (200):**&#x4A;SON Copy

```json
{
  "success": true,
  "data": {
    "projects": [
      {
        "project_id": "prj_9a8b7c6d5e4f",
        "title": "Y tá nhỏ của tôi",
        "status": "completed",
        "content_rating": "R-18",
        "writing_mode": "romance-pure",
        "progress": {
          "chapters_total": 20,
          "chapters_completed": 20,
          "words_total": 45000
        },
        "created_at": "2024-01-10T08:00:00Z",
        "updated_at": "2024-01-15T14:30:00Z",
        "thumbnail_url": "https://cdn.storyforge.ai/covers/prj_9a8b.jpg"
      }
    ],
    "pagination": {
      "current_page": 1,
      "total_pages": 3,
      "total_items": 45,
      "has_next": true,
      "has_prev": false
    }
  }
}
```

***

**POST /projects**

Tạo project mới.**Headers:** `Authorization: Bearer {token}`**Request:**&#x4A;SON Copy

```json
{
  "title": "Y tá nhỏ của tôi",
  "outline_file": "[FILE_UPLOAD]", // multipart
  "writing_mode": "romance-pure",
  "options": {
    "target_word_count": 50000,
    "chapter_count": 20,
    "language": "vi",
    "tone": "warm,emotional",
    "pov": "third_person_limited",
    "target_audience": "young_adult"
  }
}
```

**Response (201):**&#x4A;SON Copy

```json
{
  "success": true,
  "data": {
    "project_id": "prj_9a8b7c6d5e4f",
    "title": "Y tá nhỏ của tôi",
    "status": "uploading",
    "upload_url": "https://upload.storyforge.ai/temp/upl_12345",
    "estimated_processing_time": 30,
    "tokens_deducted": 500
  }
}
```

***

**GET /projects/:id**

Lấy chi tiết project.**Headers:** `Authorization: Bearer {token}`**Response (200):**&#x4A;SON Copy

```json
{
  "success": true,
  "data": {
    "project_id": "prj_9a8b7c6d5e4f",
    "title": "Y tá nhỏ của tôi",
    "tagline": "Em 20, lần đầu biết yêu trong phòng trực đêm mưa...",
    "status": "completed",
    "content_rating": "R-18",
    "content_flags": ["age_gap_relationship", "workplace_romance"],
    "writing_mode": "romance-pure",
    "original_outline": {
      "file_url": "https://cdn.storyforge.ai/outlines/prj_9a8b_original.pdf",
      "text_excerpt": "CHƯƠNG 1: CÔ GÁI NHỎ VÀO KHOA NGOẠI..."
    },
    "parsed_structure": {
      "metadata": { "...": "..." },
      "characters": [ "..." ],
      "chapters": [ "..." ],
      "sensory_elements": { "..." }
    },
    "progress": {
      "chapters_total": 20,
      "chapters_completed": 20,
      "chapters_in_review": 0,
      "words_total": 45230,
      "estimated_completion": null
    },
    "chapters": [
      {
        "chapter_id": "chap_001",
        "chapter_number": 1,
        "title": "Cô gái nhỏ vào khoa Ngoại",
        "status": "completed",
        "word_count": 2150,
        "preview": "Bối cảnh: Sáng sớm, bệnh viện tỉnh..."
      }
    ],
    "safety_logs": [
      {
        "log_id": "sfl_001",
        "scene_id": "1.1",
        "flag": "suggestive_attire_description",
        "severity": "low",
        "action_taken": "rewritten",
        "user_approved": true
      }
    ],
    "created_at": "2024-01-10T08:00:00Z",
    "updated_at": "2024-01-15T14:30:00Z",
    "completed_at": "2024-01-15T14:30:00Z"
  }
}
```

***

**PATCH /projects/:id**

Cập nhật project.**Headers:** `Authorization: Bearer {token}`**Request:**&#x4A;SON Copy

```json
{
  "title": "Y tá nhỏ của tôi - Bản chỉnh sửa",
  "writing_mode": "drama",
  "options": {
    "tone": "melancholic,intense"
  }
}
```

**Lưu ý:** Không thể đổi writing\_mode nếu đã bắt đầu viết.

***

**DELETE /projects/:id**

Xóa project.**Headers:** `Authorization: Bearer {token}`**Response (200):**&#x4A;SON Copy

```json
{
  "success": true,
  "message": "Project deleted successfully",
  "refunded_tokens": 200
}
```

***

**POST /projects/:id/duplicate**

Nhân bản project.**Headers:** `Authorization: Bearer {token}`**Request:**&#x4A;SON Copy

```json
{
  "new_title": "Y tá nhỏ của tôi - Phiên bản Drama",
  "writing_mode": "drama"
}
```

**Response (201):** New project object

***

#### **4. OUTLINE PARSING**

**GET /projects/:id/parse**

Lấy kết quả parse outline.**Headers:** `Authorization: Bearer {token}`**Response (200):**&#x4A;SON Copy

```json
{
  "success": true,
  "data": {
    "parse_status": "completed",
    "parsed_at": "2024-01-10T08:05:00Z",
    "structure": {
      "metadata": {
        "title": "Y tá nhỏ của tôi",
        "tagline": "Em 20, lần đầu biết yêu...",
        "detected_genres": ["romance", "medical", "drama"],
        "content_rating": "R-18",
        "confidence_score": 0.94
      },
      "characters": [
        {
          "character_id": "char_001",
          "name": "Trinh",
          "age": 20,
          "role": "protagonist",
          "occupation": "y tá",
          "personality_traits": ["ngây thơ", "chăm chỉ", "nhạy cảm"],
          "physical_description": "Dáng thanh mảnh, da trắng hồng, mắt bồ câu trong veo",
          "character_arc": "Từ ngây thơ đến trưởng thành qua tình yêu",
          "relationships": [
            {
              "with_character": "An",
              "relationship_type": "love_interest",
              "dynamic": "mentor-mentee, age_gap"
            }
          ]
        },
        {
          "character_id": "char_002",
          "name": "An",
          "age": 35,
          "role": "deuteragonist",
          "occupation": "bác sĩ phẫu thuật",
          "personality_traits": ["lạnh lùng", "tài giỏi", "cô đơn"],
          "backstory": "Từng thất bại trong tình yêu, đóng kín trái tim",
          "character_arc": "Mở lòng và yêu lại"
        }
      ],
      "chapters": [
        {
          "chapter_id": "chap_001",
          "chapter_number": 1,
          "title": "Cô gái nhỏ vào khoa Ngoại",
          "setting": {
            "location": "Bệnh viện tỉnh, khoa Ngoại",
            "time": "Sáng sớm",
            "atmosphere": "hối hả, mới mẻ"
          },
          "scenes": [
            {
              "scene_id": "1.1",
              "sequence": 1,
              "location": "Phòng thay đồ nữ",
              "characters_present": ["Trinh"],
              "action_summary": "Trinh chuẩn bị đi làm ngày đầu tiên, lo lắng nhưng tự tin",
              "key_moments": [
                "Trinh ngắm mình trong gương với đồng phục mới",
                "Mẹ gọi video động viên",
                "Trinh tự nhủ phải cố gắng"
              ],
              "sensory_details": {
                "visual": ["màu xanh nhạt của đồng phục", "ánh sáng vàng của đèn phòng thay đồ"],
                "tactile": ["vải đồng phục hơi chật", "da mịn sau khi tắm"],
                "auditory": ["tiếng điện thoại rung", "tiếng cười của mẹ qua video"]
              },
              "emotional_tone": "hồi hộp, hy vọng",
              "estimated_word_count": 800,
              "content_flags": ["suggestive_attire"]
            }
          ],
          "chapter_arc": "Introduction - Trinh bước vào thế giới mới"
        }
      ],
      "themes": ["tình yêu chênh lệch tuổi", "trưởng thành", "chữa lành"],
      "motifs": ["mưa", "ánh sáng và bóng tối", "màu trắng của y tế"],
      "sensory_palette": {
        "dominant_colors": ["trắng", "xanh nhạt", "xám"],
        "dominant_sounds": ["tiếng bước chân", "tiếng máy móc", "tiếng mưa"],
        "dominant_scents": ["mùi bạc hà", "mùi thuốc sát trùng", "mùi mưa"]
      }
    },
    "suggestions": [
      {
        "type": "character_development",
        "message": "Có thể thêm backstory về gia đình Trinh để tăng chiều sâu"
      },
      {
        "type": "pacing",
        "message": "Chương 5-6 có nhiều cảnh tình cảm liên tiếp, nên xen kẽ cảnh y tế"
      }
    ]
  }
}
```

***

**POST /projects/:id/parse/retry**

Yêu cầu parse lại nếu lỗi.**Headers:** `Authorization: Bearer {token}`**Response (202):** Accepted, processing

***

#### **5. WRITING ENGINE**

**POST /projects/:id/write**

Bắt đầu quá trình viết.**Headers:** `Authorization: Bearer {token}`**Request:**&#x4A;SON Copy

```json
{
  "chapters": ["all"], // hoặc ["chap_001", "chap_002"]
  "writing_mode": "romance-pure",
  "options": {
    "style_preset": "contemporary_vietnamese",
    "sentence_complexity": "medium",
    "dialogue_ratio": 0.3,
    "description_detail": "rich",
    "emotional_depth": "deep",
    "pacing": "moderate"
  },
  "priority": "normal" // hoặc "high" (Pro+)
}
```

**Response (202):**&#x4A;SON Copy

```json
{
  "success": true,
  "data": {
    "job_id": "job_9a8b7c6d5e4f",
    "status": "queued",
    "estimated_duration": 1800,
    "chapters_queued": 20,
    "websocket_channel": "ws://ws.storyforge.ai/projects/prj_9a8b"
  }
}
```

***

**GET /jobs/:id**

Kiểm tra tiến độ writing job.**Headers:** `Authorization: Bearer {token}`**Response (200):**&#x4A;SON Copy

```json
{
  "success": true,
  "data": {
    "job_id": "job_9a8b7c6d5e4f",
    "status": "processing", // queued, processing, completed, failed, paused
    "progress": {
      "total_chapters": 20,
      "completed_chapters": 8,
      "current_chapter": {
        "chapter_id": "chap_009",
        "chapter_number": 9,
        "title": "Thang máy và hầm xe",
        "status": "writing",
        "words_written": 1200,
        "estimated_remaining": 300
      },
      "overall_percentage": 45,
      "elapsed_time": 420,
      "estimated_remaining_time": 510
    },
    "logs": [
      {
        "timestamp": "2024-01-10T08:15:00Z",
        "level": "info",
        "message": "Started chapter 9: Thang máy và hầm xe"
      },
      {
        "timestamp": "2024-01-10T08:18:00Z",
        "level": "warning",
        "message": "Content flag detected in scene 9.4, auto-rewriting",
        "flag": {
          "type": "suggestive_content",
          "scene_id": "9.4",
          "action": "auto_rewritten"
        }
      }
    ]
  }
}
```

***

**POST /jobs/:id/pause**

Tạm dừng job.**Headers:** `Authorization: Bearer {token}`**Response (200):** Updated job status

***

**POST /jobs/:id/resume**

Tiếp tục job.**Headers:** `Authorization: Bearer {token}`

***

**POST /jobs/:id/cancel**

Hủy job.**Headers:** `Authorization: Bearer {token}`**Response (200):**&#x4A;SON Copy

```json
{
  "success": true,
  "refunded_tokens": 1500,
  "completed_chapters_saved": 8
}
```

***

#### **6. CHAPTER MANAGEMENT**

**GET /chapters/:id**

Lấy nội dung chapter.**Headers:** `Authorization: Bearer {token}`**Query Parameters:**&#x54;able Copy

| Param               | Type    | Default                            |
| ------------------- | ------- | ---------------------------------- |
| `format`            | string  | json (json, html, markdown, plain) |
| `include_outline`   | boolean | false                              |
| `include_revisions` | boolean | false                              |

**Response (200):**&#x4A;SON Copy

```json
{
  "success": true,
  "data": {
    "chapter_id": "chap_009",
    "project_id": "prj_9a8b7c6d5e4f",
    "chapter_number": 9,
    "title": "Thang máy và hầm xe",
    "status": "completed",
    "content": {
      "full_text": "Chiều muộn, bệnh viện bắt đầu vắng bóng người...",
      "word_count": 2450,
      "reading_time": 12,
      "paragraphs": [
        {
          "index": 1,
          "text": "Chiều muộn, bệnh viện bắt đầu vắng bóng người...",
          "type": "narration",
          "sentiment": "neutral"
        },
        {
          "index": 2,
          "text": "\"Em đi sinh nhật bạn à?\" An hỏi, giọng khàn hơn thường lệ.",
          "type": "dialogue",
          "speaker": "An",
          "sentiment": "curious"
        }
      ]
    },
    "outline": {
      "scene_id": "9.1",
      "original_summary": "Trinh chuẩn bị đi sinh nhật, gặp An ở hành lang"
    },
    "revisions": [
      {
        "revision_id": "rev_001",
        "created_at": "2024-01-10T09:00:00Z",
        "change_type": "auto_rewrite",
        "reason": "Content safety: suggestive_description",
        "original_snippet": "Chiếc váy ôm sát lấy đường cong cơ thể...",
        "rewritten_snippet": "Chiếc váy màu pastel khiến Trinh trông thật khác, như một nàng thơ bước ra từ tranh vẽ..."
      }
    ],
    "metadata": {
      "ai_model": "gpt-4",
      "temperature": 0.7,
      "generation_time": 45,
      "tokens_used": 3500
    },
    "created_at": "2024-01-10T08:15:00Z",
    "updated_at": "2024-01-10T09:00:00Z"
  }
}
```

***

**PATCH /chapters/:id**

Cập nhật chapter (manual edit).**Headers:** `Authorization: Bearer {token}`**Request:**&#x4A;SON Copy

```json
{
  "title": "Thang máy và hầm xe - Bản chỉnh sửa",
  "content": {
    "full_text": "Nội dung mới..."
  }
}
```

**Response (200):** Updated chapter

***

**POST /chapters/:id/regenerate**

Viết lại chapter/scene.**Headers:** `Authorization: Bearer {token}`**Request:**&#x4A;SON Copy

```json
{
  "scope": "full", // full, scene, paragraph
  "target": {
    "scene_id": "9.4", // nếu scope = scene
    // hoặc "paragraph_index": 5
  },
  "prompt": "Viết lại cảnh trong thang máy, tập trung vào xung đột nội tâm của An thay vì mô tả bề ngoài Trinh",
  "writing_mode": "drama", // có thể đổi mode
  "preserve_word_count": true
}
```

**Response (202):**&#x4A;SON Copy

```json
{
  "revision_job_id": "job_rev_001",
  "estimated_time": 120
}
```

***

**POST /chapters/:id/feedback**

Gửi feedback cho AI.**Headers:** `Authorization: Bearer {token}`**Request:**&#x4A;SON Copy

```json
{
  "rating": 4,
  "feedback_text": "Cảnh hội thoại hay nhưng miêu tả cảm xúc hơi sến súa",
  "specific_issues": [
    {
      "type": "too_melodramatic",
      "location": "paragraph 12-15"
    }
  ],
  "suggested_improvement": "Giảm bớt tính từ cảm xúc, tăng hành động cụ thể"
}
```

***

#### **7. CONTENT SAFETY**

**GET /projects/:id/safety-review**

Xem các cảnh cần review.**Headers:** `Authorization: Bearer {token}`**Response (200):**&#x4A;SON Copy

```json
{
  "success": true,
  "data": {
    "pending_reviews": 3,
    "items": [
      {
        "review_id": "rev_safety_001",
        "chapter_id": "chap_009",
        "scene_id": "9.4",
        "location": {
          "paragraph_start": 15,
          "paragraph_end": 18
        },
        "flag_type": "suggestive_content",
        "severity": "medium",
        "ai_analysis": {
          "original_text": "An nhìn thấy đường cong lấp ló sau lớp vải mỏng...",
          "concern": "Mô tả nhắm đến cơ thể với tính chất kích thích",
          "suggested_rewrite": "An nhìn thấy Trinh thật khác lạ trong bộ váy mới, và anh buộc phải nhận ra mình đã chú ý đến cô nhiều hơn mức bác sĩ nên có..."
        },
        "options": [
          {
            "action": "accept_rewrite",
            "description": "Chấp nhận đề xuất viết lại",
            "preview": "An nhìn thấy Trinh thật khác lạ..."
          },
          {
            "action": "custom_rewrite",
            "description": "Tự viết lại",
            "custom_prompt": ""
          },
          {
            "action": "skip_scene",
            "description": "Bỏ qua cảnh này",
            "note": "Chapter sẽ ngắn hơn ~200 từ"
          },
          {
            "action": "accept_original",
            "description": "Giữ nguyên (yêu cầu xác nhận)",
            "requires_confirmation": true,
            "confirmation_reason": "Nội dung có thể vi phạm chính sách nền tảng"
          }
        ],
        "created_at": "2024-01-10T08:20:00Z",
        "expires_at": "2024-01-17T08:20:00Z"
      }
    ]
  }
}
```

***

**POST /safety-review/:id/resolve**

Giải quyết content flag.**Headers:** `Authorization: Bearer {token}`**Request:**&#x4A;SON Copy

```json
{
  "action": "accept_rewrite",
  "custom_text": null, // nếu action = custom_rewrite
  "confirmation_password": null // nếu action = accept_original
}
```

**Response (200):** Updated chapter

***

#### **8. EXPORT & DOWNLOAD**

**POST /projects/:id/export**

Xuất project.**Headers:** `Authorization: Bearer {token}`**Request:**&#x4A;SON Copy

```json
{
  "format": "epub", // epub, pdf, docx, txt, html, audio
  "options": {
    "include_cover": true,
    "cover_image": "[FILE_UPLOAD]", // hoặc auto-generate
    "include_toc": true,
    "include_metadata": true,
    "font_family": "Noto Serif", // PDF
    "font_size": 12,
    "line_spacing": 1.5,
    "page_size": "A5", // PDF
    "chapter_separator": "page_break",
    "header_template": "{title} - {author}",
    "footer_template": "{page_number}",
    "watermark": null, // hoặc text
    "drm": false // EPUB
  },
  "audio_options": { // nếu format = audio
    "voice": "vi-female-1",
    "speed": 1.0,
    "emotion": "warm",
    "background_music": "soft_piano",
    "split_by": "chapter"
  }
}
```

**Response (202):**&#x4A;SON Copy

```json
{
  "success": true,
  "data": {
    "export_job_id": "exp_001",
    "status": "processing",
    "estimated_time": 60,
    "download_url": null // sẽ có khi completed
  }
}
```

***

**GET /exports/:id/status**

Kiểm tra tiến độ export.**Headers:** `Authorization: Bearer {token}`**Response (200):**&#x4A;SON Copy

```json
{
  "export_job_id": "exp_001",
  "status": "completed",
  "download_url": "https://cdn.storyforge.ai/exports/prj_9a8b.epub?token=xyz",
  "expires_at": "2024-01-17T10:00:00Z",
  "file_size": 2450000,
  "checksum": "sha256:abc123..."
}
```

***

**GET /download/:token**

Download file (public URL với token tạm thời).**Query:** `token` từ download\_url**Response:** File binary với headers:plain Copy

```
Content-Type: application/epub+zip
Content-Disposition: attachment; filename="y-ta-nho-cua-toi.epub"
X-Content-Checksum: sha256:abc123...
```

***

#### **9. WEBSOCKET EVENTS**

**Connection:** `wss://ws.storyforge.ai/v1`**Authentication:** Query param `?token={jwt}`

**Client → Server Events**

Table Copy

| Event                 | Payload          | Description      |
| --------------------- | ---------------- | ---------------- |
| `subscribe_project`   | `{ project_id }` | Theo dõi project |
| `unsubscribe_project` | `{ project_id }` | Hủy theo dõi     |
| `ping`                | `{}`             | Keep-alive       |

**Server → Client Events**

Table Copy

| Event              | Payload                                         | Description        |
| ------------------ | ----------------------------------------------- | ------------------ |
| `connected`        | `{ session_id }`                                | Kết nối thành công |
| `project_update`   | `{ project_id, status, progress }`              | Cập nhật project   |
| `chapter_complete` | `{ chapter_id, chapter_number, word_count }`    | Chapter xong       |
| `content_warning`  | `{ review_id, chapter_id, scene_id, severity }` | Cần review         |
| `job_complete`     | `{ job_id, type, download_url? }`               | Job hoàn thành     |
| `error`            | `{ code, message, details }`                    | Lỗi                |

**Example WebSocket Flow:**&#x4A;avaScript Copy

```javascript
// Client
const ws = new WebSocket('wss://ws.storyforge.ai/v1?token=eyJhb...');

ws.onopen = () => {
  ws.send(JSON.stringify({
    event: 'subscribe_project',
    data: { project_id: 'prj_9a8b' }
  }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  
  switch(msg.event) {
    case 'chapter_complete':
      console.log(`Chapter ${msg.data.chapter_number} done!`);
      break;
    case 'content_warning':
      showReviewModal(msg.data);
      break;
  }
};
```

***

#### **10. ERROR HANDLING**

**Error Response Format**

JSON Copy

```json
{
  "success": false,
  "error": {
    "code": "INSUFFICIENT_TOKENS",
    "message": "Not enough tokens to complete this request",
    "details": {
      "required": 5000,
      "available": 2300,
      "suggested_action": "upgrade_subscription"
    },
    "request_id": "req_9a8b7c6d5e4f",
    "timestamp": "2024-01-10T08:30:00Z"
  }
}
```

**Error Codes**

Table Copy

| Code                       | HTTP Status | Description                | Resolution              |
| -------------------------- | ----------- | -------------------------- | ----------------------- |
| `UNAUTHORIZED`             | 401         | Token hết hạn/không hợp lệ | Refresh hoặc login lại  |
| `FORBIDDEN`                | 403         | Không đủ quyền             | Upgrade subscription    |
| `NOT_FOUND`                | 404         | Resource không tồn tại     | Kiểm tra ID             |
| `VALIDATION_ERROR`         | 422         | Dữ liệu không hợp lệ       | Kiểm tra schema         |
| `RATE_LIMITED`             | 429         | Quá nhiều request          | Chờ hoặc upgrade        |
| `INSUFFICIENT_TOKENS`      | 402         | Hết tokens                 | Mua thêm hoặc chờ reset |
| `AI_SERVICE_UNAVAILABLE`   | 503         | AI service đang bảo trì    | Thử lại sau             |
| `CONTENT_POLICY_VIOLATION` | 400         | Outline vi phạm chính sách | Sửa outline             |
| `FILE_TOO_LARGE`           | 413         | File > 50MB                | Nén hoặc chia nhỏ       |
| `UNSUPPORTED_FORMAT`       | 415         | Định dạng không hỗ trợ     | Chuyển đổi format       |

***

#### **11. RATE LIMITING**

Table Copy

| Tier       | Requests/minute | Concurrent jobs | Max file size |
| ---------- | --------------- | --------------- | ------------- |
| Free       | 30              | 1               | 10MB          |
| Pro        | 120             | 3               | 50MB          |
| Enterprise | 600             | 10              | 200MB         |

**Headers trong mọi response:**&#x70;lain Copy

```
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 115
X-RateLimit-Reset: 1704892200
```

***

## **PHẦN 2: DATABASE SCHEMA CHI TIẾT**

### **PostgreSQL Schema**

sql Copy

```sql
-- Enable required extensions
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -- For text search

-- Enums
CREATE TYPE user_tier AS ENUM ('free', 'pro', 'enterprise');
CREATE TYPE project_status AS ENUM ('draft', 'uploading', 'parsing', 'parsed', 'writing', 'reviewing', 'completed', 'archived', 'deleted');
CREATE TYPE chapter_status AS ENUM ('pending', 'writing', 'completed', 'revision_pending', 'approved');
CREATE TYPE writing_mode AS ENUM ('romance_pure', 'romance_steamy', 'drama', 'rom_com', 'thriller', 'slice_of_life', 'custom');
CREATE TYPE content_flag_severity AS ENUM ('info', 'low', 'medium', 'high', 'critical');
CREATE TYPE job_status AS ENUM ('queued', 'processing', 'completed', 'failed', 'paused', 'cancelled');
CREATE TYPE export_format AS ENUM ('epub', 'pdf', 'docx', 'txt', 'html', 'audio_mp3', 'audio_m4b');

-- ============================================
-- USERS & AUTHENTICATION
-- ============================================

CREATE TABLE users (
    user_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    email VARCHAR(255) UNIQUE NOT NULL,
    password_hash VARCHAR(255) NOT NULL, -- bcrypt
    name VARCHAR(100) NOT NULL,
    avatar_url VARCHAR(500),
    
    -- Subscription
    subscription_tier user_tier DEFAULT 'free',
    subscription_started_at TIMESTAMP,
    subscription_expires_at TIMESTAMP,
    
    -- Usage tracking
    tokens_balance INTEGER DEFAULT 10000,
    tokens_used_total BIGINT DEFAULT 0,
    tokens_used_this_month INTEGER DEFAULT 0,
    tokens_reset_at TIMESTAMP, -- Monthly reset
    
    -- Limits
    max_projects INTEGER DEFAULT 3,
    max_words_per_project INTEGER DEFAULT 5000,
    
    -- Preferences
    preferences JSONB DEFAULT '{
        "default_writing_mode": "romance_pure",
        "language": "vi",
        "email_notifications": true,
        "theme": "light"
    }'::jsonb,
    
    -- Security
    email_verified BOOLEAN DEFAULT false,
    email_verified_at TIMESTAMP,
    last_login_at TIMESTAMP,
    last_login_ip INET,
    failed_login_attempts INTEGER DEFAULT 0,
    locked_until TIMESTAMP,
    
    -- Timestamps
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    deleted_at TIMESTAMP -- Soft delete
);

CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_subscription ON users(subscription_tier, subscription_expires_at);

-- Refresh tokens
CREATE TABLE refresh_tokens (
    token_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id UUID NOT NULL REFERENCES users(user_id) ON DELETE CASCADE,
    token_hash VARCHAR(255) NOT NULL,
    expires_at TIMESTAMP NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    revoked_at TIMESTAMP,
    replaced_by UUID REFERENCES refresh_tokens(token_id),
    ip_address INET,
    user_agent TEXT
);

CREATE INDEX idx_refresh_tokens_user ON refresh_tokens(user_id);
CREATE INDEX idx_refresh_tokens_token ON refresh_tokens(token_hash);

-- API keys (for enterprise)
CREATE TABLE api_keys (
    key_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id UUID NOT NULL REFERENCES users(user_id) ON DELETE CASCADE,
    key_hash VARCHAR(255) NOT NULL,
    name VARCHAR(100),
    permissions JSONB DEFAULT '["read", "write"]'::jsonb,
    rate_limit INTEGER DEFAULT 600, -- per minute
    last_used_at TIMESTAMP,
    expires_at TIMESTAMP,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    revoked_at TIMESTAMP
);

-- ============================================
-- PROJECTS
-- ============================================

CREATE TABLE projects (
    project_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id UUID NOT NULL REFERENCES users(user_id) ON DELETE CASCADE,
    
    -- Basic info
    title VARCHAR(255) NOT NULL,
    tagline VARCHAR(500),
    slug VARCHAR(300) UNIQUE, -- URL-friendly
    
    -- Status & workflow
    status project_status DEFAULT 'draft',
    writing_mode writing_mode DEFAULT 'romance_pure',
    
    -- Content analysis
    content_rating VARCHAR(10), -- G, PG, PG-13, R, R-18
    content_flags JSONB DEFAULT '[]'::jsonb, -- ["age_gap", "workplace_romance"]
    detected_genres JSONB DEFAULT '[]'::jsonb,
    
    -- Source files
    original_outline_url VARCHAR(500),
    original_outline_text TEXT, -- Extracted text
    original_filename VARCHAR(255),
    original_file_size INTEGER,
    original_file_hash VARCHAR(64), -- SHA-256
    
    -- Parsed structure
    parsed_structure JSONB, -- Full parsed outline
    parsed_at TIMESTAMP,
    parse_version INTEGER DEFAULT 1, -- Increment on re-parse
    
    -- Writing config
    writing_config JSONB DEFAULT '{
        "target_word_count": 50000,
        "chapter_count": 20,
        "language": "vi",
        "pov": "third_person_limited",
        "tone": "warm",
        "style_preset": "contemporary_vietnamese"
    }'::jsonb,
    
    -- Progress
    chapters_total INTEGER DEFAULT 0,
    chapters_completed INTEGER DEFAULT 0,
    words_total INTEGER DEFAULT 0,
    
    -- Current job
    current_job_id UUID,
    
    -- Metadata
    cover_image_url VARCHAR(500),
    thumbnail_url VARCHAR(500),
    
    -- Timestamps
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    started_writing_at TIMESTAMP,
    completed_at TIMESTAMP,
    archived_at TIMESTAMP,
    deleted_at TIMESTAMP
);

CREATE INDEX idx_projects_user ON projects(user_id, deleted_at);
CREATE INDEX idx_projects_status ON projects(status);
CREATE INDEX idx_projects_created ON projects(created_at DESC);

-- Project versions (for undo/backup)
CREATE TABLE project_versions (
    version_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    project_id UUID NOT NULL REFERENCES projects(project_id) ON DELETE CASCADE,
    version_number INTEGER NOT NULL,
    snapshot JSONB NOT NULL, -- Full project snapshot
    created_by UUID REFERENCES users(user_id),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    reason VARCHAR(255) -- "manual_save", "auto_save", "pre_regenerate"
);

CREATE UNIQUE INDEX idx_project_versions_number ON project_versions(project_id, version_number);

-- ============================================
-- CHARACTERS (Extracted from outline)
-- ============================================

CREATE TABLE characters (
    character_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    project_id UUID NOT NULL REFERENCES projects(project_id) ON DELETE CASCADE,
    
    name VARCHAR(100) NOT NULL,
    aliases JSONB DEFAULT '[]'::jsonb, -- Other names they're called
    
    -- Basic attributes
    age INTEGER,
    age_approximate BOOLEAN DEFAULT false, -- "khoảng 20"
    gender VARCHAR(20),
    occupation VARCHAR(100),
    
    -- Role in story
    role_type VARCHAR(50), -- protagonist, deuteragonist, antagonist, supporting
    importance INTEGER DEFAULT 3, -- 1-5 scale
    
    -- Descriptions
    physical_description TEXT,
    personality_traits JSONB DEFAULT '[]'::jsonb,
    backstory TEXT,
    motivations JSONB DEFAULT '[]'::jsonb,
    fears JSONB DEFAULT '[]'::jsonb,
    
    -- Arc
    character_arc TEXT,
    arc_status VARCHAR(50), -- incomplete, developing, completed
    
    -- AI-generated enrichments
    ai_suggested_traits JSONB,
    ai_suggested_backstory TEXT,
    user_approved_enrichments BOOLEAN DEFAULT false,
    
    -- Tracking
    first_appearance_chapter INTEGER,
    appearance_count INTEGER DEFAULT 0,
    
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_characters_project ON characters(project_id);
CREATE INDEX idx_characters_role ON characters(project_id, role_type);

-- Character relationships
CREATE TABLE character_relationships (
    relationship_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    project_id UUID NOT NULL REFERENCES projects(project_id) ON DELETE CASCADE,
    character_a_id UUID NOT NULL REFERENCES characters(character_id) ON DELETE CASCADE,
    character_b_id UUID NOT NULL REFERENCES characters(character_id) ON DELETE CASCADE,
    
    relationship_type VARCHAR(50), -- love_interest, family, colleague, enemy
    dynamic_description TEXT, -- "mentor-mentee with romantic tension"
    tension_level INTEGER, -- 1-10
    evolution JSONB, -- How relationship changes over chapters
    
    UNIQUE(character_a_id, character_b_id, relationship_type)
);

-- ============================================
-- CHAPTERS
-- ============================================

CREATE TABLE chapters (
    chapter_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    project_id UUID NOT NULL REFERENCES projects(project_id) ON DELETE CASCADE,
    
    chapter_number INTEGER NOT NULL,
    title VARCHAR(255) NOT NULL,
    
    status chapter_status DEFAULT 'pending',
    
    -- Structure from outline
    outline_summary TEXT,
    setting_location VARCHAR(255),
    setting_time VARCHAR(100),
    atmosphere VARCHAR(100),
    chapter_arc VARCHAR(100), -- setup, rising_action, climax, falling_action, resolution
    
    -- Content
    content_full_text TEXT,
    content_html TEXT, -- Formatted version
    content_markdown TEXT,
    
    -- Breakdown
    paragraphs JSONB DEFAULT '[]'::jsonb, -- Array of paragraph objects
    scenes JSONB DEFAULT '[]'::jsonb, -- Scene breakdown
    
    -- Metrics
    word_count INTEGER DEFAULT 0,
    reading_time_minutes INTEGER,
    
    -- Sentiment analysis
    dominant_sentiment VARCHAR(20),
    sentiment_curve JSONB, -- Sentiment over paragraphs
    
    -- Generation metadata
    generation_params JSONB,
    ai_model VARCHAR(50),
    ai_temperature DECIMAL(3,2),
    tokens_used INTEGER,
    generation_duration_seconds INTEGER,
    generation_attempts INTEGER DEFAULT 1,
    
    -- Timestamps
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    generated_at TIMESTAMP,
    last_edited_at TIMESTAMP,
    approved_at TIMESTAMP
);

CREATE UNIQUE INDEX idx_chapters_project_number ON chapters(project_id, chapter_number);
CREATE INDEX idx_chapters_status ON chapters(status);

-- Chapter revisions (history)
CREATE TABLE chapter_revisions (
    revision_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    chapter_id UUID NOT NULL REFERENCES chapters(chapter_id) ON DELETE CASCADE,
    revision_number INTEGER NOT NULL,
    
    -- Content snapshot
    title VARCHAR(255),
    content_full_text TEXT,
    word_count INTEGER,
    
    -- Change tracking
    change_type VARCHAR(50), -- initial, manual_edit, ai_regenerate, auto_rewrite
    change_reason TEXT,
    triggered_by VARCHAR(50), -- user, system, safety_flag
    
    -- If AI-generated
    ai_prompt TEXT,
    ai_completion TEXT,
    
    -- If safety-related
    safety_review_id UUID,
    
    -- User who made change
    created_by UUID REFERENCES users(user_id), -- NULL if system
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    
    -- Diff from previous (for display)
    diff_added TEXT,
    diff_removed TEXT
);

CREATE UNIQUE INDEX idx_chapter_revisions_number ON chapter_revisions(chapter_id, revision_number);

-- ============================================
-- SCENES (Granular content tracking)
-- ============================================

CREATE TABLE scenes (
    scene_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    chapter_id UUID NOT NULL REFERENCES chapters(chapter_id) ON DELETE CASCADE,
    project_id UUID NOT NULL REFERENCES projects(project_id) ON DELETE CASCADE,
    
    scene_number INTEGER NOT NULL,
    outline_scene_id VARCHAR(50), -- Reference back to parsed outline
    
    -- Location
    location VARCHAR(255),
    time_of_day VARCHAR(50),
    
    -- Characters present
    characters_present JSONB DEFAULT '[]'::jsonb, -- ["char_001", "char_002"]
    
    -- Content
    summary TEXT, -- From outline
    generated_content TEXT,
    
    -- Position in chapter
    start_paragraph INTEGER,
    end_paragraph INTEGER,
    word_count INTEGER,
    
    -- Sensory elements
    sensory_visual JSONB,
    sensory_auditory JSONB,
    sensory_tactile JSONB,
    sensory_olfactory JSONB,
    
    -- Emotional
    emotional_tone VARCHAR(50),
    emotional_intensity INTEGER, -- 1-10
    
    -- Safety
    content_flags JSONB DEFAULT '[]'::jsonb,
    safety_review_status VARCHAR(50) DEFAULT 'none', -- none, pending, resolved
    
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_scenes_chapter ON scenes(chapter_id, scene_number);

-- ============================================
-- WRITING JOBS (Async processing)
-- ============================================

CREATE TABLE writing_jobs (
    job_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    project_id UUID NOT NULL REFERENCES projects(project_id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES users(user_id) ON DELETE CASCADE,
    
    status job_status DEFAULT 'queued',
    priority INTEGER DEFAULT 5, -- 1-10, higher = more priority
    
    -- Job config
    job_type VARCHAR(50), -- full_project, single_chapter, scene_rewrite, revision
    target_chapters JSONB, -- ["chap_001", "chap_002"] or ["all"]
    writing_mode writing_mode,
    generation_options JSONB,
    
    -- Progress
    total_chapters INTEGER,
    completed_chapters INTEGER DEFAULT 0,
    current_chapter_id UUID,
    
    -- Timing
    estimated_duration_seconds INTEGER,
    started_at TIMESTAMP,
    completed_at TIMESTAMP,
    failed_at TIMESTAMP,
    failure_reason TEXT,
    
    -- Resource tracking
    tokens_allocated INTEGER,
    tokens_consumed INTEGER DEFAULT 0,
    
    -- WebSocket
    websocket_channel VARCHAR(100),
    
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_writing_jobs_status ON writing_jobs(status, priority DESC, created_at);
CREATE INDEX idx_writing_jobs_project ON writing_jobs(project_id);

-- Job logs (detailed progress)
CREATE TABLE writing_job_logs (
    log_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    job_id UUID NOT NULL REFERENCES writing_jobs(job_id) ON DELETE CASCADE,
    
    log_level VARCHAR(20), -- info, warning, error
    message TEXT NOT NULL,
    
    -- Context
    chapter_id UUID,
    scene_id VARCHAR(50),
    
    -- Additional data
    metadata JSONB,
    
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_job_logs_job ON writing_job_logs(job_id, created_at);

-- ============================================
-- CONTENT SAFETY
-- ============================================

CREATE TABLE safety_flags (
    flag_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    project_id UUID NOT NULL REFERENCES projects(project_id) ON DELETE CASCADE,
    chapter_id UUID REFERENCES chapters(chapter_id),
    scene_id UUID REFERENCES scenes(scene_id),
    
    -- Detection
    detected_by VARCHAR(50), -- ai_model, human_review, automated
    detection_confidence DECIMAL(4,3), -- 0.000 - 1.000
    
    -- Classification
    flag_category VARCHAR(50), -- sexual_content, violence, hate_speech, etc.
    flag_type VARCHAR(100), -- specific subtype
    severity content_flag_severity,
    
    -- Location
    paragraph_index INTEGER,
    original_text_snippet TEXT,
    text_context TEXT, -- Surrounding text
    
    -- AI analysis
    ai_reasoning TEXT, -- Why AI flagged this
    suggested_rewrite TEXT,
    
    -- Resolution
    status VARCHAR(50) DEFAULT 'pending', -- pending, auto_resolved, user_resolved, escalated, dismissed
    resolution_action VARCHAR(50), -- accept_rewrite, custom_rewrite, skip_scene, accept_original
    resolved_text TEXT, -- Final text after resolution
    resolved_by UUID REFERENCES users(user_id),
    resolved_at TIMESTAMP,
    
    -- User interaction
    user_viewed_at TIMESTAMP,
    user_decision_notes TEXT,
    
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_safety_flags_project ON safety_flags(project_id, status);
CREATE INDEX idx_safety_flags_pending ON safety_flags(status, severity, created_at);

-- Safety rules (configurable)
CREATE TABLE safety_rules (
    rule_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    name VARCHAR(100) NOT NULL,
    description TEXT,
    
    -- Matching
    pattern_type VARCHAR(50), -- regex, keyword, semantic, combined
    pattern_definition JSONB, -- Pattern configuration
    
    -- Action
    default_action VARCHAR(50), -- flag, block, rewrite_auto
    severity_override content_flag_severity,
    
    -- Scope
    applies_to_modes JSONB DEFAULT '["all"]'::jsonb, -- Which writing modes
    user_tier_override JSONB, -- Different rules per tier
    
    is_active BOOLEAN DEFAULT true,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- ============================================
-- EXPORTS
-- ============================================

CREATE TABLE exports (
    export_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    project_id UUID NOT NULL REFERENCES projects(project_id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES users(user_id) ON DELETE CASCADE,
    
    format export_format NOT NULL,
    status VARCHAR(50) DEFAULT 'processing', -- processing, completed, failed
    
    -- Config
    export_options JSONB NOT NULL,
    
    -- Result
    file_url VARCHAR(500),
    file_size_bytes BIGINT,
    file_checksum VARCHAR(64),
    download_token VARCHAR(255),
    download_expires_at TIMESTAMP,
    
    -- Processing
    processing_started_at TIMESTAMP,
    processing_completed_at TIMESTAMP,
    processing_duration_seconds INTEGER,
    
    error_message TEXT,
    
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_exports_project ON exports(project_id, created_at DESC);
CREATE INDEX idx_exports_download ON exports(download_token, download_expires_at);

-- ============================================
// ... (truncated for brevity, continuing with remaining tables)

CREATE TABLE user_feedback (
    feedback_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id UUID NOT NULL REFERENCES users(user_id) ON DELETE CASCADE,
    project_id UUID REFERENCES projects(project_id),
    chapter_id UUID REFERENCES chapters(chapter_id),
    
    feedback_type VARCHAR(50), -- rating, text, issue_report
    rating INTEGER, -- 1-5
    feedback_text TEXT,
    
    -- Specific issues
    reported_issues JSONB, -- Array of issue objects
    
    -- AI improvement
    used_for_training BOOLEAN DEFAULT false,
    ai_model_version VARCHAR(50),
    
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Templates
CREATE TABLE templates (
    template_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    name VARCHAR(100) NOT NULL,
    description TEXT,
    
    category VARCHAR(50), -- romance, mystery, fantasy, etc.
    subcategory VARCHAR(50),
    
    -- Structure
    template_structure JSONB NOT NULL, -- Pre-defined outline structure
    
    -- Metadata
    is_official BOOLEAN DEFAULT false,
    created_by UUID REFERENCES users(user_id),
    usage_count INTEGER DEFAULT 0,
    average_rating DECIMAL(2,1),
    
    is_active BOOLEAN DEFAULT true,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Notifications
CREATE TABLE notifications (
    notification_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id UUID NOT NULL REFERENCES users(user_id) ON DELETE CASCADE,
    
    type VARCHAR(50), -- job_complete, content_warning, subscription_expiring
    title VARCHAR(255),
    message TEXT,
    
    -- Action
    action_url VARCHAR(500),
    action_text VARCHAR(100),
    
    -- Status
    is_read BOOLEAN DEFAULT false,
    read_at TIMESTAMP,
    
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_notifications_user ON notifications(user_id, is_read, created_at DESC);

-- Analytics events
CREATE TABLE analytics_events (
    event_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id UUID REFERENCES users(user_id),
    anonymous_session_id VARCHAR(100),
    
    event_type VARCHAR(100) NOT NULL,
    event_properties JSONB,
    
    -- Context
    user_agent TEXT,
    ip_hash VARCHAR(64), -- Hashed for privacy
    country_code VARCHAR(2),
    
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) PARTITION BY RANGE (created_at);

-- Create monthly partitions for analytics
```

***

### **Redis Schema (Caching & Sessions)**

plain Copy

```
# User sessions
session:{jwt_token} -> {
    "user_id": "usr_xxx",
    "tier": "pro",
    "expires_at": 1704892200
}
TTL: 24 hours

# Rate limiting
rate_limit:{user_id}:{minute_timestamp} -> count
TTL: 2 minutes

# Project cache
project:{project_id}:metadata -> JSON
project:{project_id}:chapters -> JSON array
project:{project_id}:progress -> {
    "status": "writing",
    "completed": 8,
    "total": 20
}
TTL: 1 hour

# Job status (for WebSocket)
job:{job_id}:status -> JSON
job:{job_id}:logs -> List (LPUSH, trim to 100)
TTL: 24 hours after completion

# Export cache
export:{export_id}:status -> JSON
export:{export_id}:download_url -> string
TTL: 7 days

# AI generation cache (deduplication)
gen_hash:{sha256_of_prompt} -> {
    "result": "...",
    "model": "gpt-4",
    "created_at": 1704892200
}
TTL: 1 hour

# Real-time collaboration (if implemented)
doc:{chapter_id}:presence -> Hash of active users
doc:{chapter_id}:operations -> Stream of edits
```

***

## **PHẦN 3: UI/UX MOCKUP CHI TIẾT**

### **Design System**

#### **Color Palette**

css Copy

```css
:root {
  /* Primary */
  --primary-50: #eff6ff;
  --primary-100: #dbeafe;
  --primary-200: #bfdbfe;
  --primary-300: #93c5fd;
  --primary-400: #60a5fa;
  --primary-500: #3b82f6;  /* Main brand */
  --primary-600: #2563eb;
  --primary-700: #1d4ed8;
  --primary-800: #1e40af;
  --primary-900: #1e3a8a;
  
  /* Semantic */
  --success: #10b981;
  --warning: #f59e0b;
  --error: #ef4444;
  --info: #3b82f6;
  
  /* Content rating indicators */
  --rating-g: #22c55e;
  --rating-pg: #84cc16;
  --rating-pg13: #eab308;
  --rating-r: #f97316;
  --rating-r18: #dc2626;
  
  /* Neutral */
  --gray-50: #f9fafb;
  --gray-100: #f3f4f6;
  --gray-200: #e5e7eb;
  --gray-300: #d1d5db;
  --gray-400: #9ca3af;
  --gray-500: #6b7280;
  --gray-600: #4b5563;
  --gray-700: #374151;
  --gray-800: #1f2937;
  --gray-900: #111827;
  
  /* Typography */
  --font-sans: 'Inter', system-ui, sans-serif;
  --font-serif: 'Noto Serif', Georgia, serif;
  --font-mono: 'JetBrains Mono', monospace;
}
```

#### **Typography Scale**

Table Copy

| Token        | Size | Weight | Usage            |
| ------------ | ---- | ------ | ---------------- |
| `display-1`  | 48px | 700    | Hero titles      |
| `display-2`  | 36px | 700    | Page titles      |
| `heading-1`  | 30px | 600    | Section headers  |
| `heading-2`  | 24px | 600    | Card titles      |
| `heading-3`  | 20px | 600    | Subsection       |
| `body-large` | 18px | 400    | Lead paragraphs  |
| `body`       | 16px | 400    | Main text        |
| `body-small` | 14px | 400    | Secondary text   |
| `caption`    | 12px | 500    | Labels, metadata |

***

### **Screen Mockups**

#### **1. LANDING PAGE**

plain Copy

```
┌─────────────────────────────────────────────────────────────────────────────┐
│  [Logo: StoryForge]    Features  Pricing  Templates  [Sign In] [Get Started] │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                                                                     │   │
│  │     BIẾN Ý TƯỞNG THÀNH TRUYỆN HOÀN CHỈNH                           │   │
│  │     CHỈ TRONG VÀI PHÚT                                               │   │
│  │                                                                     │   │
│  │     Tải lên outline → AI phân tích → Chọn phong cách →             │   │
│  │     Nhận truyện chuyên nghiệp                                        │   │
│  │                                                                     │   │
│  │     [Bắt đầu viết miễn phí]  [Xem demo →]                            │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  TRUSTED BY 10,000+ NHÀ VĂN                                         │   │
│  │  [Logo Văn học trẻ] [Logo Wattpad VN] [Logo NXB Kim Đồng] ...        │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  ┌─────────────────────┐  ┌─────────────────────┐  ┌─────────────────────┐ │
│  │   [Icon: Upload]    │  │   [Icon: Robot]     │  │   [Icon: Book]      │ │
│  │                     │  │                     │  │                     │ │
│  │  1. Tải Outline     │  │  2. AI Phân tích    │  │  3. Nhận Truyện     │ │
│  │                     │  │                     │  │                     │ │
│  │  PDF, DOCX, TXT     │  │  Nhân vật, cốt      │  │  EPUB, PDF, Audio   │ │
│  │  đều được           │  │  truyện, cảm xúc    │  │  xuất ra đọc ngay   │ │
│  │                     │  │                     │  │                     │ │
│  └─────────────────────┘  └─────────────────────┘  └─────────────────────┘ │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  CÁC THỂ LOẠI ĐƯỢC HỖ TRỢ                                          │   │
│  │                                                                     │   │
│  │  [Romance] [Drama] [Rom-Com] [Thriller] [Mystery] [Fantasy] ...     │   │
│  │                                                                     │   │
│  │  Mỗi thể loại có phong cách viết riêng, đảm bảo đúng "vibe"        │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  AN TOÀN & CHUYÊN NGHIỆP                                           │   │
│  │                                                                     │   │
│  │  ✓ Tự động phát hiện & chuyển đổi nội dung nhạy cảm                │   │
│  │  ✓ Nhiều chế độ: Tình cảm tinh khiết, Drama, Hài hước...           │   │
│  │  ✓ Quyền sở hữu 100% thuộc về bạn                                  │   │
│  │  ✓ Xuất đa định dạng: EPUB, PDF, DOCX, Audio                       │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  [Bắt đầu ngay — Miễn phí 10,000 tokens]                                    │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
```

***

#### **2. DASHBOARD**

plain Copy

```
┌─────────────────────────────────────────────────────────────────────────────┐
│  [≡]  [Logo]  [🔍 Search...]  [💬]  [🔔]  [👤 User ▼]                      │
├─────────────────────────────────────────────────────────────────────────────┤
│  ┌────────────┐                                                             │
│  │  NEW       │  Xin chào, Nguyễn Văn A! 👋                                  │
│  │  PROJECT   │  Bạn có 3 dự án đang viết dở                                │
│  │            │                                                             │
│  │  [➕]      │  ┌─────────────────────────────────────────────────────┐   │
│  │  Tạo      │  │  TIẾP TỤC VIẾT                                        │   │
│  │  dự án    │  │                                                       │   │
│  │  mới      │  │  ┌─────────────────────────────────────────────┐    │   │
│  │            │  │  │ [Cover: Y tá nhỏ...]  ████████████░░░░ 80%   │    │   │
│  │            │  │  │                       Chapter 16/20           │    │   │
│  │            │  │  │  "Y tá nhỏ của tôi"                           │    │   │
│  │            │  │  │  Romance • Đang viết • Còn ~15 phút          │    │   │
│  │            │  │  │  [Tiếp tục →]                                 │    │   │
│  │            │  │  └─────────────────────────────────────────────┘    │   │
│  │            │  │                                                       │   │
│  │            │  │  ┌─────────────────────────────────────────────┐    │   │
│  │            │  │  │ [Cover: Thám tử...]   ██████░░░░░░░░░░ 30%   │    │   │
│  │            │  │  │                       Chapter 6/20            │    │   │
│  │            │  │  │  "Thám tử Sài Gòn"                            │    │   │
│  │            │  │  │  Thriller • Tạm dừng                          │    │   │
│  │            │  │  │  [Tiếp tục →]                                 │    │   │
│  │            │  │  └─────────────────────────────────────────────┘    │   │
│  │            │  │                                                       │   │
│  └────────────┘  └─────────────────────────────────────────────────────┘   │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  DỰ ÁN GẦN ĐÂY                    [Xem tất cả →]                    │   │
│  │                                                                     │   │
│  │  ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐              │   │
│  │  │ [Cover]│ │ [Cover]│ │ [Cover]│ │ [Cover]│ │ [Cover]│              │   │
│  │  │        │ │        │ │        │ │        │ │        │              │   │
│  │  │Completed│ │Completed│ │Review  │ │Draft   │ │Archived│              │   │
│  │  │Tình yêu │ │Hành trình│ │Pending │ │        │ │        │              │   │
│  │  │chị em   │ │về phía  │ │        │ │        │ │        │              │   │
│  │  │         │ │mặt trời │ │        │ │        │ │        │              │   │
│  │  │[⋮]     │ │[⋮]     │ │[⋮]     │ │[⋮]     │ │[⋮]     │              │   │
│  │  └────────┘ └────────┘ └────────┘ └────────┘ └────────┘              │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  THỐNG KÊ THÁNG NÀY                                                │   │
│  │                                                                     │   │
│  │  ┌────────────────┐  ┌────────────────┐  ┌────────────────┐         │   │
│  │  │   45,230       │  │      3         │  │    12,500      │         │   │
│  │  │   từ đã viết   │  │   truyện mới   │  │  tokens còn    │         │   │
│  │  │   ↑ 23%        │  │   ↑ 1          │  │  [Nạp thêm]    │         │   │
│  │  └────────────────┘  └────────────────┘  └────────────────┘         │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
```

***

#### **3. PROJECT CREATION - STEP 1: UPLOAD**

plain Copy

```
┌─────────────────────────────────────────────────────────────────────────────┐
│  [←]  Tạo dự án mới                              [1/3: Tải lên → 2: Thiết lập → 3: Viết] │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                                                                     │   │
│  │              ┌─────────────────────────────────────┐                │   │
│  │              │                                     │                │   │
│  │              │    [Icon: Cloud Upload]            │                │   │
│  │              │         Kéo thả file vào đây         │                │   │
│  │              │         hoặc click để chọn          │                │   │
│  │              │                                     │                │   │
│  │              │    Hỗ trợ: PDF, DOCX, TXT (Max 50MB)│                │   │
│  │              │                                     │                │   │
│  │              └─────────────────────────────────────┘                │   │
│  │                                                                     │   │
│  │  ────────────────  HOẶC  ────────────────                           │   │
│  │                                                                     │   │
│  │  [Bắt đầu từ template]  [Nhập thủ công]                            │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  📄 Y-ta-nho-cua-toi-outline.pdf          2.4 MB    [✓] Đã tải lên │   │
│  │                                                                     │   │
│  │  Đang phân tích cấu trúc...  ████████░░░░ 80%                      │   │
│  │                                                                     │   │
│  │  [Hủy]  [Phân tích lại]                                             │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  KẾT QUẢ PHÂN TÍCH                                                  │   │
│  │                                                                     │   │
│  │  Tiêu đề phát hiện:    "Y tá nhỏ của tôi"                           │   │
│  │  Thể loại:             Romance, Medical, Drama                      │   │
│  │  Độ dài ước tính:      20 chương, ~50,000 từ                        │   │
│  │                                                                     │   │
│  │  ┌─────────────────────────────────────────────────────────────┐   │   │
│  │  │  NHÂN VẬT PHÁT HIỆN                                        │   │   │
│  │  │                                                             │   │   │
│  │  │  • Trinh (20) - Nhân vật chính - Y tá mới ra trường        │   │   │
│  │  │  • An (35) - Bác sĩ phẫu thuật - Love interest             │   │   │
│  │  │                                                             │   │   │
│  │  │  [Xem chi tiết cấu trúc →]                                  │   │   │
│  │  └─────────────────────────────────────────────────────────────┘   │   │
│  │                                                                     │   │
│  │  ⚠️ Cảnh báo nội dung: Phát hiện cảnh nhạy cảm trong outline       │   │
│  │     Hệ thống sẽ đề xuất chuyển đổi phù hợp khi viết               │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  [Lưu nháp]                                    [Tiếp tục: Chọn phong cách →] │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
```

***

#### **4. PROJECT CREATION - STEP 2: CONFIGURATION**

plain Copy

```
┌─────────────────────────────────────────────────────────────────────────────┐
│  [←]  Thiết lập: Y tá nhỏ của tôi              [1 → 2/3: Thiết lập → 3: Viết] │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  CHỌN PHONG CÁCH VIẾT                                              │   │
│  │                                                                     │   │
│  │  ┌─────────────────────────────────────────────────────────────┐   │   │
│  │  │ ●  Romance Tinh khiết                                       │   │   │
│  │  │    Tập trung cảm xúc, nội tâm, kết thúc có hậu              │   │   │
│  │  │    Phù hợp: Ngôn tình sạch, truyện ngắn tình cảm            │   │   │
│  │  │    [✓ Đề xuất cho outline này]                              │   │   │
│  │  └─────────────────────────────────────────────────────────────┘   │   │
│  │                                                                     │   │
│  │  ┌─────────────────────────────────────────────────────────────┐   │   │
│  │  │ ○  Drama                                                    │   │   │
│  │  │    Xung đột, phát triển nhân vật sâu sắc                    │   │   │
│  │  │    Phù hợp: Truyện chiều sâu, văn học                      │   │   │
│  │  └─────────────────────────────────────────────────────────────┘   │   │
│  │                                                                     │   │
│  │  ┌─────────────────────────────────────────────────────────────┐   │   │
│  │  │ ○  Hài lãng mạn (Rom-Com)                                   │   │   │
│  │  │    Nhẹ nhàng, hài hước, tình huống vui nhộn                 │   │   │
│  │  │    Phù hợp: Giải trí, đọc để thư giãn                      │   │   │
│  │  └─────────────────────────────────────────────────────────────┘   │   │
│  │                                                                     │   │
│  │  [Xem thêm: Thriller, Mystery, Slice of Life...]                   │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  TÙY CHỈNH NÂNG CAO                                                │   │
│  │                                                                     │   │
│  │  Độ dài mục tiêu:    [50,000] từ    [20] chương                    │   │
│  │                                                                     │   │
│  │  Ngôn ngữ:           [Tiếng Việt ▼]                                │   │
│  │                                                                     │   │
│  │  Góc nhìn:           [Ngôi thứ ba giới hạn ▼]                      │   │
│  │                      (theo nhân vật Trinh)                         │   │
│  │                                                                     │   │
│  │  Cấp độ miêu tả:     [Giàu chi tiết ▼]                             │   │
│  │                                                                     │   │
│  │  Tốc độ cốt truyện:  [Vừa phải ▼]                                  │   │
│  │                                                                     │   │
│  │  [⚙️ Mở rộng: Tone cảm xúc, Phong cách câu văn...]                 │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  XỬ LÝ NỘI DUNG NHẠY CẢM                                           │   │
│  │                                                                     │   │
│  │  Phát hiện: 3 cảnh cần chú ý trong outline                         │   │
│  │                                                                     │   │
│  │  [●] Tự động chuyển đổi sang phong cách đã chọn                     │   │
│  │  [○] Dừng lại để tôi review từng cảnh                               │   │
│  │  [○] Giữ nguyên (yêu cầu xác nhận)                                  │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  [← Quay lại]                                  [Bắt đầu viết →]            │
│  Dự kiến: 20 phút, ~5,000 tokens                                           │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
```

***

#### **5. WRITING PROGRESS SCREEN**

plain Copy

```
┌─────────────────────────────────────────────────────────────────────────────┐
│  [←]  Đang viết: Y tá nhỏ của tôi              [Live • WebSocket connected] │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  TIẾN ĐỘ TỔNG THỂ                                                  │   │
│  │                                                                     │   │
│  │  ██████████████████████░░░░░░░░░░  16/20 chương hoàn thành (80%)   │   │
│  │                                                                     │   │
│  │  Đang viết: Chapter 17 - "Tuần trăng mật trên máy bay"             │   │
│  │  Tiến độ chương: ████████████░░░░  ~1,800 / 2,200 từ               │   │
│  │                                                                     │   │
│  │  ⏱️ Còn khoảng 8 phút nữa                                          │   │
│  │                                                                     │   │
│  │  [Tạm dừng]  [Hủy]  [⚙️ Ưu tiên cao]                               │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  NHẬT KÝ VIẾT (Real-time)                                          │   │
│  │                                                                     │   │
│  │  14:32:05  ✓ Hoàn thành Chapter 16 - "Đám cưới" (2,450 từ)         │   │
│  │  14:32:18  ℹ️ Bắt đầu Chapter 17                                    │   │
│  │  14:33:45  ✓ Viết xong Scene 17.1 - Khởi đầu chuyến bay            │   │
│  │  14:35:12  ⚠️ Phát hiện cảnh nhạy cảm ở Scene 17.3                 │   │
│  │            → Tự động chuyển đổi: "Cảnh trong cabin"                │   │
│  │            → Đã viết lại: Tập trung vào cảm xúc, không gian        │   │
│  │  14:36:30  ✓ Tiếp tục Scene 17.4                                   │   │
│  │  14:38:15  ● Đang viết Scene 17.5 (hiện tại)                       │   │
│  │                                                                     │   │
│  │  [Xem chi tiết kỹ thuật]  [Tải log]                                 │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  XEM TRƯỚC CHƯƠNG GẦN NHẤT                                         │   │
│  │                                                                     │   │
│  │  ─── Chapter 16: Đám cưới ───                                      │   │
│  │                                                                     │   │
│  │  Sau tiệc cưới, An và Trinh về phòng tân hôn. Trinh còn nguyên     │   │
│  │  váy cưới trắng, An vest đen lịch lãm. Phòng ngập hoa hồng, nến    │   │
│  │  thơm lung linh.                                                   │   │
│  │                                                                     │   │
│  │  An bế Trinh qua ngưỡng cửa, đặt cô xuống giường. Ánh mắt anh       │   │
│  │  dịu dàng như chưa từng thấy trong suốt một năm qua...              │   │
│  │                                                                     │   │
│  │  [Đọc thêm →]  [Chỉnh sửa]                                          │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  CẢNH BÁO CẦN XEM XÉT                                              │   │
│  │                                                                     │   │
│  │  ⚠️ Chapter 12 - Scene 12.3                                        │   │
│  │     Nội dung: Mô tả thân mật trong phòng bệnh                      │   │
│  │     Hành động: Đã tự động viết lại theo phong cách Romance tinh    │   │
│  │                khiết                                               │   │
│  │     [Xem so sánh]  [Chấp nhận]  [Viết lại theo ý tôi]              │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
```

***

#### **6. CHAPTER EDITOR**

plain Copy

```
┌─────────────────────────────────────────────────────────────────────────────┐
│  [←]  Y tá nhỏ của tôi  /  Chương 9: Thang máy và hầm xe                    │
│                                                                             │
│  [💾 Lưu] [↩️ Undo] [↪️ Redo] [🔄 Viết lại] [👁️ Xem trước] [⬇️ Xuất]     │
├─────────────────────────────────────────────────────────────────────────────┤
│  ┌────────────┐  ┌────────────────────────────────────────────────────────┐ │
│  │            │  │  ┌────────────────────────────────────────────────┐   │ │
│  │  CẤU TRÚC  │  │  │  CHAPTER 9: THANG MÁY VÀ HẦM XE                │   │ │
│  │            │  │  │                                                │   │ │
│  │  Scene 9.1 │  │  │  Chiều muộn, bệnh viện bắt đầu vắng bóng người. │   │ │
│  │  [✓]       │  │  │  Trinh đứng trước gương trong phòng thay đồ,   │   │ │
│  │            │  │  │  chỉnh lại chiếc váy hai dây màu xanh pastel   │   │ │
│  │  Scene 9.2 │  │  │  mà cô hiếm khi dám mặc...                      │   │ │
│  │  [✓]       │  │  │                                                │   │ │
│  │            │  │  │  ─────────────────────────────────────────────  │   │ │
│  │  Scene 9.3 │  │  │                                                │   │ │
│  │  [✓]       │  │  │  Hành lang vắng tanh. Trinh bước ra khỏi       │   │ │
│  │            │  │  │  phòng thay đồ, tay xách túi đựng đồng phục.   │   │ │
│  │  Scene 9.4 │  │  │  Tiếng giày cao gót vang lên trong không gian  │   │ │
│  │  [⚠️]      │  │  │  tĩnh mịch.                                     │   │ │
│  │  [Cần review]│ │  │                                                │   │ │
│  │            │  │  │  Cô đi về phía thang máy, định xuống hầm xe     │   │ │
│  │  Scene 9.5 │  │  │  thì một cánh cửa mở ra...                      │   │ │
│  │  [○]       │  │  │                                                │   │ │
│  │  (đang viết)│ │  │  An bước ra từ phòng thay đồ nam. Anh dừng      │   │ │
│  │            │  │  │  lại khi nhìn thấy Trinh.                       │   │ │
│  │            │  │  │                                                │   │ │
│  │            │  │  │  [Tiếp tục đọc...]                              │   │ │
│  │            │  │  │                                                │   │ │
│  │            │  │  │  ─────────────────────────────────────────────  │   │ │
│  │            │  │  │  2,340 từ  |  Đọc trong ~12 phút                │   │ │
│  │            │  │  └────────────────────────────────────────────────┘   │ │
│  │            │  │                                                     │ │
│  │            │  │  ┌────────────────────────────────────────────────┐   │ │
│  │            │  │  │  📝 GHI CHÚ CHO CHƯƠNG NÀY                     │   │ │
│  │            │  │  │                                                │   │ │
│  │            │  │  │  • Cảnh thang máy cần thêm căng thẳng hơn      │   │ │
│  │            │  │  │  • Đối thoại An hơi cứng, cần mềm mại hơn      │   │ │
│  │            │  │  │                                                │   │ │
│  │            │  │  │  [Thêm ghi chú]                                │   │ │
│  │            │  │  └────────────────────────────────────────────────┘   │ │
│  │            │  │                                                     │ │
│  └────────────┘  └────────────────────────────────────────────────────────┘ │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  THÔNG TIN CHƯƠNG                              AI: GPT-4 | Temp: 0.7 │   │
│  │                                                                     │   │
│  │  Trạng thái:  [✓ Hoàn thành]  [Sửa đổi lần cuối: 2 giờ trước]       │   │
│  │                                                                     │   │
│  │  Lịch sử phiên bản:                                                 │   │
│  │  [v1.0] [v1.1] [v1.2 ✓] [v1.3 (hiện tại)]                          │   │
│  │                                                                     │   │
│  │  [So sánh các phiên bản]  [Khôi phục v1.2]                          │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
```

***

#### **7. SAFETY REVIEW MODAL**

plain Copy

```
┌─────────────────────────────────────────────────────────────────────────────┐
│  CẢNH CẦN XEM XÉT                                          [✕]            │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  Chapter 9, Scene 9.4  |  Mức độ: Trung bình                                │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  LÝ DO PHÁT HIỆN                                                    │   │
│  │                                                                     │   │
│  │  AI phát hiện đoạn văn có thể chứa mô tả mang tính kích thích       │   │
│  │  không phù hợp với phong cách "Romance Tinh khiết" đã chọn.         │   │
│  │                                                                     │   │
│  │  Đoạn văn gốc:                                                      │   │
│  │  ┌─────────────────────────────────────────────────────────────┐   │   │
│  │  │ "Chiếc váy ôm sát lấy đường cong cơ thể, phô bày làn da      │   │   │
│  │  │  trắng ngần dưới ánh đèn hành lang..."                        │   │   │
│  │  └─────────────────────────────────────────────────────────────┘   │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  ĐỀ XUẤT CỦA AI (Đã tự động áp dụng)                                │   │
│  │                                                                     │   │
│  │  ┌─────────────────────────────────────────────────────────────┐   │   │
│  │  │ "Chiếc váy màu pastel khiến Trinh trông thật khác lạ,        │   │   │
│  │  │  như một nàng thơ bước ra từ tranh vẽ. An nhận ra mình       │   │   │
│  │  │  đã dừng lại để nhìn lâu hơn một nhịp thở..."                 │   │   │
│  │  └─────────────────────────────────────────────────────────────┘   │   │
│  │                                                                     │   │
│  │  ✓ Giữ nguyên cảm xúc rung động                                    │   │
│  │  ✓ Chuyển focus từ cơ thể sang cảm nhận của nhân vật              │   │
│  │  ✗ Loại bỏ mô tả chi tiết hình thể                                 │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  LỰA CHỌN CỦA BẠN                                                  │   │
│  │                                                                     │   │
│  │  (●) Chấp nhận đề xuất của AI                                       │   │
│  │  (○) Tự viết lại                                                    │   │
│  │      ┌─────────────────────────────────────────────────────────┐   │   │
│  │      │ [Nhập văn bản của bạn...]                               │   │   │
│  │      └─────────────────────────────────────────────────────────┘   │   │
│  │  (○) Bỏ qua cảnh này (chapter sẽ ngắn hơn ~150 từ)                │   │
│  │  (○) Giữ nguyên bản gốc                                           │   │
│  │      ⚠️ Yêu cầu xác nhận: Nội dung có thể không phù hợp          │   │
│  │      [Tôi chịu trách nhiệm về nội dung này] ☐                    │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  [Áp dụng cho tất cả cảnh tương tự trong dự án]                            │
│                                                                             │
│                              [Hủy]  [Xác nhận lựa chọn →]                  │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
```

***

#### **8. EXPORT SCREEN**

plain Copy

```
┌─────────────────────────────────────────────────────────────────────────────┐
│  [←]  Xuất truyện: Y tá nhỏ của tôi                                         │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  CHỌN ĐỊNH DẠNG                                                    │   │
│  │                                                                     │   │
│  │  ┌────────┐  ┌────────┐  ┌────────┐  ┌────────┐  ┌────────┐         │   │
│  │  │  📕   │  │  📄   │  │  📝   │  │  📱   │  │  🎧   │         │   │
│  │  │  EPUB │  │  PDF  │  │  DOCX │  │  TXT  │  │  AUDIO│         │   │
│  │  │       │  │       │  │       │  │       │  │       │         │   │
│  │  │ [✓]   │  │ [○]   │  │ [○]   │  │ [○]   │  │ [○]   │         │   │
│  │  └────────┘  └────────┘  └────────┘  └────────┘  └────────┘         │   │
│  │                                                                     │   │
│  │  Đọc trên: Kindle, Kobo, Apple Books, Google Play Books            │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  TÙY CHỈNH EPUB                                                    │   │
│  │                                                                     │   │
│  │  [✓] Bao gồm bìa tự động tạo                                       │   │
│  │      [Tải lên bìa tùy chỉnh]  hoặc  [Chỉnh sửa bìa tự động →]      │   │
│  │                                                                     │   │
│  │  [✓] Bao gồm mục lục                                               │   │
│  │  [✓] Bao gồm thông tin tác giả                                     │   │
│  │  [○] Thêm watermark                                                │   │
│  │  [○] Bật bảo vệ DRM (Không cho phép sao chép)                      │   │
│  │                                                                     │   │
│  │  Font chữ:      [Noto Serif ▼]                                     │   │
│  │  Cỡ chữ:        [16 ▼]                                             │   │
│  │  Giãn dòng:     [1.6 ▼]                                            │   │
│  │                                                                     │   │
│  │  Cách chia chương:  [Trang mới cho mỗi chương ▼]                   │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  XEM TRƯỚC BÌA                                                     │   │
│  │                                                                     │   │
│  │  ┌─────────────────────────┐                                        │   │
│  │  │    ┌─────────────┐     │  Tiêu đề: Y tá nhỏ của tôi              │   │
│  │  │    │             │     │  Tác giả: [Tên của bạn]                │   │
│  │  │    │   [Cover    │     │  Thể loại: Romance, Medical            │   │
│  │  │    │    Image]   │     │  Độ dài: 45,230 từ | 20 chương         │   │
│  │  │    │             │     │                                        │   │
│  │  │    │   Y TÁ      │     │  [Chỉnh sửa thiết kế bìa →]            │   │
│  │  │    │   NHỎ       │     │                                        │   │
│  │  │    │   CỦA       │     │                                        │   │
│  │  │    │   TÔI       │     │                                        │   │
│  │  │    │             │     │                                        │   │
│  │  │    └─────────────┘     │                                        │   │
│  │  └─────────────────────────┘                                        │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  Dung lượng ước tính: ~3.5 MB                                               │
│                                                                             │
│  [Lưu preset]                          [Bắt đầu xuất →]  ⏱️ ~2 phút        │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
```

***

#### **9. MOBILE RESPONSIVE**

plain Copy

```
┌─────────────────┐
│  [≡] StoryForge │
├─────────────────┤
│                 │
│  Xin chào! 👋   │
│                 │
│  ┌───────────┐  │
│  │  [➕]     │  │
│  │ Tạo mới   │  │
│  └───────────┘  │
│                 │
│  Đang viết:     │
│  ┌───────────┐  │
│  │ ████████░░│  │
│  │           │  │
│  │ Y tá nhỏ  │  │
│  │ 80%       │  │
│  │ [Tiếp tục]│  │
│  └───────────┘  │
│                 │
│  Gần đây:       │
│  ┌───┐ ┌───┐    │
│  │[C]│ │[C]│    │
│  │   │ │   │    │
│  │Hoàn│ │Hoàn│   │
│  │thành│ │thành│  │
│  └───┘ └───┘    │
│                 │
│  [🏠] [🔍] [💼] [👤]  │
└─────────────────┘

Tap project → 

┌─────────────────┐
│ [←] Y tá nhỏ... │
├─────────────────┤
│                 │
│  ████████░░ 80% │
│  Chương 16/20   │
│                 │
│  [Tiếp tục viết]│
│  [Xem chương]   │
│  [Chỉnh sửa]    │
│                 │
│  ─────────────  │
│                 │
│  Chương gần nhất│
│  Ch.16: Đám cưới│
│  "Sau tiệc cưới,│
│   An và Trinh..."│
│  [Đọc tiếp →]   │
│                 │
│  ─────────────  │
│                 │
│  Cảnh báo: 1    │
│  [Xem ngay →]   │
│                 │
└─────────────────┘
```

***

#### **10. COMPONENT LIBRARY**

**Buttons**

plain Copy

```
Primary:    [Lưu thay đổi]     Blue bg, white text, rounded-lg
Secondary:  [Hủy]             Gray border, gray text, transparent bg
Danger:     [Xóa dự án]       Red border, red text
Ghost:      [Chỉnh sửa →]     No border, blue text, hover underline
Icon:       [🗑️]              Icon only, circular, hover bg

States: default | hover | active | disabled | loading
```

**Form Elements**

plain Copy

```
Input:      ┌─────────────────┐  Rounded border, focus ring blue
            │ Nhập tiêu đề... │
            └─────────────────┘

Textarea:   ┌─────────────────┐  Auto-resize, min-height 100px
            │                 │
            │                 │
            └─────────────────┘

Select:     [Tiếng Việt ▼]    Custom dropdown, search enabled

Toggle:     [●────○] On/Off   Animated switch

Checkbox:   [✓] Bao gồm bìa   Custom checkmark, indeterminate state
```

**Cards**

plain Copy

```
Project Card:
┌─────────────────────────┐
│ [Cover Image 16:10]     │
│                         │
│ Title (truncate 2 lines)│
│ Genre • Status          │
│ Progress bar            │
│ [Actions ⋮]             │
└─────────────────────────┘

Chapter Card:
┌─────────────────────────┐
│ Ch.9: Title             │
│ ○ Pending / ● Completed │
│ 2,340 từ • 12 min read  │
│ [Edit] [Preview]        │
└─────────────────────────┘
```

**Feedback Components**

plain Copy

```
Toast:     ┌────────────────────────┐  Slide in from top-right
           │ ✓ Đã lưu thành công    │  Auto-dismiss 3s
           │              [✕]       │
           └────────────────────────┘

Modal:     ┌────────────────────────┐  Centered, backdrop blur
           │ Title              [✕] │
           ├────────────────────────┤
           │ Content...             │
           │                        │
           │    [Hủy]  [Xác nhận]   │
           └────────────────────────┘

Skeleton:  ┌────────────────────────┐  Animated pulse loading
           │ ████████████████████   │
           │ ██████████             │
           │ █████████████████████  │
           └────────────────────────┘
```

***

Đây là bản spec đầy đủ cho ứng dụng StoryForge. Bạn cần tôi bổ sung thêm phần nào không?
