# Incident Management API Documentation

**For Frontend Developers**

---

## Overview

Các API này cung cấp chức năng quản lý báo cáo sự cố (bảo trì, sửa chữa) tại các căn hộ. Hỗ trợ xem danh sách sự cố, chi tiết sự cố, và thông tin liên hệ của người báo cáo và kỹ thuật viên.

**Base URL:** `/api/v1/incidents`

**Authentication:** Required (Bearer Token)

---

## 1. Danh Sách Sự Cố

### Endpoint
```
GET /api/v1/incidents
```

### Authorization
- **Yêu cầu:** Admin role
- **Headers:** `Authorization: Bearer <token>`

### Query Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `page` | number | 1 | Trang (bắt đầu từ 1) |
| `pageSize` | number | 20 | Số bản ghi mỗi trang |
| `sortField` | string | `created_at` | Trường sắp xếp |
| `sortOrder` | string | `DESC` | Thứ tự (`ASC` hoặc `DESC`) |

### Request Example
```bash
curl -X GET "http://api.example.com/api/v1/incidents?page=1&pageSize=10&sortOrder=DESC" \
  -H "Authorization: Bearer <token>"
```

### Response Example (200 OK)
```json
{
  "status": true,
  "data": {
    "count": 5,
    "rows": [
      {
        "id": "b2c2c555-3b02-4b47-986f-4c0772924d4f",
        "title": "Máy chiếu không lên hình",
        "code": "INC20260515-0001",
        "type_id": "92a0ec40-a4d7-413e-8336-e6bb0ebc7e3d",
        "type_name": "Thiết bị",
        "room_id": "00000001-0001-0001-0001-000000000012",
        "room_name": "Phòng 202",
        "apartment_name": "Căn hộ B202",
        "building_name": "Tòa nhà B",
        "reporter_id": "0e4f9062-0c72-4a5e-8a8b-6b37a6418088",
        "reporter_name": "Nguyễn Văn A",
        "technician_id": "fe2abfb2-8c9f-43e5-a781-04064a7157b7",
        "technician_name": "Vũ Thị Em",
        "priority": "medium",
        "status": "pending_material",
        "deadline": "2026-05-20T23:59:59.000Z",
        "created_at": "2026-05-15T04:57:12.109Z",
        "updated_at": "2026-05-15T04:57:12.109Z"
      }
    ],
    "page": 1,
    "pageSize": 10
  }
}
```

### Response Fields

| Field | Type | Description |
|-------|------|-------------|
| `id` | UUID | ID duy nhất của sự cố |
| `title` | string | Tiêu đề/tên sự cố |
| `code` | string | Mã sự cố tự tạo (ví dụ: INC20260515-0001) |
| `type_id` | UUID | ID loại sự cố |
| `type_name` | string | Tên loại sự cố (Điện, Nước, Điều hòa, v.v.) |
| `room_id` | UUID | ID phòng |
| `room_name` | string | Tên phòng (vd: "Phòng 202") |
| `apartment_name` | string | Tên căn hộ (vd: "Căn hộ B202") |
| `building_name` | string | Tên tòa nhà (vd: "Tòa nhà B") |
| `reporter_id` | UUID | ID người báo cáo sự cố |
| `reporter_name` | string | Tên người báo cáo (first_name + last_name) |
| `technician_id` | UUID | ID kỹ thuật viên được giao (nullable) |
| `technician_name` | string | Tên kỹ thuật viên (nullable) |
| `priority` | string | Mức độ ưu tiên: `low`, `medium`, `high`, `urgent` |
| `status` | string | Trạng thái: `new`, `assigned`, `in_progress`, `pending_material`, `completed`, `cancelled`, `on_hold` |
| `deadline` | ISO 8601 | Hạn chót hoàn thành (nullable) |
| `created_at` | ISO 8601 | Ngày tạo |
| `updated_at` | ISO 8601 | Ngày cập nhật cuối cùng |

### Trạng Thái Sự Cố

| Status | Mô Tả Tiếng Việt |
|--------|-----------------|
| `new` | Mới báo cáo |
| `assigned` | Đã giao cho kỹ thuật viên |
| `in_progress` | Đang xử lý |
| `pending_material` | Chờ vật liệu |
| `completed` | Hoàn thành |
| `cancelled` | Đã hủy |
| `on_hold` | Tạm dừng |

### Mức Độ Ưu Tiên

| Priority | Mô Tả |
|----------|-------|
| `low` | Thấp |
| `medium` | Trung bình |
| `high` | Cao |
| `urgent` | Khẩn cấp |

### Error Responses

**401 Unauthorized**
```json
{
  "status": false,
  "error": "Unauthorized"
}
```

**403 Forbidden** (Không phải Admin)
```json
{
  "status": false,
  "error": "Forbidden"
}
```

---

## 2. Chi Tiết Sự Cố

### Endpoint
```
GET /api/v1/incidents/{id}
```

### Authorization
- **Yêu cầu:** Authenticated user
- **Headers:** `Authorization: Bearer <token>`
- **Ghi chú:** Admin có thể xem mọi sự cố, user thường chỉ xem sự cố của chính mình

### Path Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | UUID | ID của sự cố |

### Request Example
```bash
curl -X GET "http://api.example.com/api/v1/incidents/b2c2c555-3b02-4b47-986f-4c0772924d4f" \
  -H "Authorization: Bearer <token>"
```

### Response Example (200 OK)
```json
{
  "status": true,
  "data": {
    "id": "b2c2c555-3b02-4b47-986f-4c0772924d4f",
    "title": "Máy chiếu không lên hình",
    "code": "INC20260515-0001",
    "description": "Máy chiếu ở phòng họp không lên hình, kiểm tra dây cấp điện và kết nối HDMI",
    "type_id": "92a0ec40-a4d7-413e-8336-e6bb0ebc7e3d",
    "type_name": "Thiết bị",
    "room_id": "00000001-0001-0001-0001-000000000012",
    "room_name": "Phòng 202",
    "apartment_name": "Căn hộ B202",
    "building_name": "Tòa nhà B",
    "priority": "medium",
    "status": "pending_material",
    "deadline": "2026-05-20T23:59:59.000Z",
    "reporter": {
      "id": "0e4f9062-0c72-4a5e-8a8b-6b37a6418088",
      "name": "Nguyễn Văn A",
      "phone": "+84 901 234 527",
      "email": "nguyenvana@bekind.com"
    },
    "technician": {
      "id": "fe2abfb2-8c9f-43e5-a781-04064a7157b7",
      "name": "Vũ Thị Em",
      "phone": "0933445566",
      "email": "em.vt@example.com"
    },
    "created_at": "2026-05-15T04:57:12.109Z",
    "updated_at": "2026-05-15T04:57:12.109Z"
  }
}
```

### Response Fields

| Field | Type | Description |
|-------|------|-------------|
| `id` | UUID | ID duy nhất của sự cố |
| `title` | string | Tiêu đề/tên sự cố |
| `code` | string | Mã sự cố tự tạo (ví dụ: INC20260515-0001) |
| `description` | string | Mô tả chi tiết sự cố |
| `type_id` | UUID | ID loại sự cố |
| `type_name` | string | Tên loại sự cố |
| `room_id` | UUID | ID phòng |
| `room_name` | string | Tên phòng |
| `apartment_name` | string | Tên căn hộ |
| `building_name` | string | Tên tòa nhà |
| `priority` | string | Mức độ ưu tiên |
| `status` | string | Trạng thái sự cố |
| `deadline` | ISO 8601 | Hạn chót hoàn thành (nullable) |
| `reporter` | object | Thông tin người báo cáo |
| `reporter.id` | UUID | ID người báo cáo |
| `reporter.name` | string | Tên người báo cáo |
| `reporter.phone` | string | Số điện thoại người báo cáo |
| `reporter.email` | string | Email người báo cáo |
| `technician` | object | Thông tin kỹ thuật viên (nullable) |
| `technician.id` | UUID | ID kỹ thuật viên |
| `technician.name` | string | Tên kỹ thuật viên |
| `technician.phone` | string | Số điện thoại kỹ thuật viên |
| `technician.email` | string | Email kỹ thuật viên |
| `created_at` | ISO 8601 | Ngày tạo |
| `updated_at` | ISO 8601 | Ngày cập nhật cuối cùng |

### Error Responses

**404 Not Found**
```json
{
  "status": false,
  "error": "Incident not found"
}
```

**403 Forbidden** (Không có quyền xem)
```json
{
  "status": false,
  "error": "Forbidden"
}
```

---

## 3. Hướng Dẫn Sử Dụng

### 3.1 Hiển Thị Danh Sách Sự Cố

**Scenario:** Admin muốn xem danh sách tất cả sự cố, sắp xếp theo ngày tạo mới nhất trước.

```javascript
// React/Vue example
async function fetchIncidents() {
  const response = await fetch(
    '/api/v1/incidents?page=1&pageSize=20&sortField=created_at&sortOrder=DESC',
    {
      headers: {
        'Authorization': `Bearer ${authToken}`
      }
    }
  );
  
  const result = await response.json();
  return result.data;
}
```

### 3.2 Xem Chi Tiết Sự Cố

**Scenario:** Click vào một sự cố để xem toàn bộ chi tiết (bao gồm thông tin liên hệ của người báo cáo và kỹ thuật viên).

```javascript
async function fetchIncidentDetail(incidentId) {
  const response = await fetch(
    `/api/v1/incidents/${incidentId}`,
    {
      headers: {
        'Authorization': `Bearer ${authToken}`
      }
    }
  );
  
  const result = await response.json();
  return result.data;
}
```

### 3.3 Xây Dựng UI Card Danh Sách

**Ví dụ cấu trúc HTML:**

```html
<div class="incident-card">
  <div class="incident-header">
    <h3>{{ incident.title }}</h3>
    <span class="incident-code">{{ incident.code }}</span>
    <span class="priority-badge" :class="`priority-${incident.priority}`">
      {{ incident.priority }}
    </span>
  </div>
  
  <div class="incident-body">
    <p><strong>Loại:</strong> {{ incident.type_name }}</p>
    <p><strong>Vị trí:</strong> {{ incident.building_name }} - {{ incident.apartment_name }} - {{ incident.room_name }}</p>
    <p><strong>Người báo cáo:</strong> {{ incident.reporter_name }}</p>
    <p><strong>Kỹ thuật viên:</strong> {{ incident.technician_name || 'Chưa giao' }}</p>
    <p v-if="incident.deadline"><strong>Hạn:</strong> {{ formatDate(incident.deadline) }}</p>
  </div>
  
  <div class="incident-footer">
    <span class="status-badge" :class="`status-${incident.status}`">
      {{ formatStatus(incident.status) }}
    </span>
    <span class="date">{{ formatDate(incident.created_at) }}</span>
  </div>
</div>
```

### 3.4 Xây Dựng UI Chi Tiết Sự Cố

**Ví dụ cấu trúc HTML:**

```html
<div class="incident-detail">
  <div class="detail-header">
    <h1>{{ incident.title }}</h1>
    <div class="code-section">
      <span class="incident-code">{{ incident.code }}</span>
    </div>
    <div class="status-priority">
      <span class="status-badge" :class="`status-${incident.status}`">
        {{ formatStatus(incident.status) }}
      </span>
      <span class="priority-badge" :class="`priority-${incident.priority}`">
        {{ incident.priority }}
      </span>
    </div>
  </div>

  <!-- Mô tả -->
  <section class="detail-section">
    <h3>Mô Tả</h3>
    <p>{{ incident.description }}</p>
  </section>

  <!-- Vị trí -->
  <section class="detail-section">
    <h3>Vị Trí</h3>
    <p>
      <strong>Tòa nhà:</strong> {{ incident.building_name }}<br>
      <strong>Căn hộ:</strong> {{ incident.apartment_name }}<br>
      <strong>Phòng:</strong> {{ incident.room_name }}
    </p>
  </section>

  <!-- Loại sự cố -->
  <section class="detail-section">
    <h3>Loại Sự Cố</h3>
    <p>{{ incident.type_name }}</p>
  </section>

  <!-- Hạn chót -->
  <section class="detail-section" v-if="incident.deadline">
    <h3>Hạn Chót</h3>
    <p>{{ formatDateTime(incident.deadline) }}</p>
  </section>

  <!-- Người báo cáo -->
  <section class="detail-section">
    <h3>Người Báo Cáo</h3>
    <div class="contact-info">
      <p><strong>Tên:</strong> {{ incident.reporter.name }}</p>
      <p><strong>Điện thoại:</strong> <a href="tel:{{ incident.reporter.phone }}">{{ incident.reporter.phone }}</a></p>
      <p><strong>Email:</strong> <a href="mailto:{{ incident.reporter.email }}">{{ incident.reporter.email }}</a></p>
    </div>
  </section>

  <!-- Kỹ thuật viên -->
  <section class="detail-section" v-if="incident.technician">
    <h3>Kỹ Thuật Viên Phụ Trách</h3>
    <div class="contact-info">
      <p><strong>Tên:</strong> {{ incident.technician.name }}</p>
      <p><strong>Điện thoại:</strong> <a href="tel:{{ incident.technician.phone }}">{{ incident.technician.phone }}</a></p>
      <p><strong>Email:</strong> <a href="mailto:{{ incident.technician.email }}">{{ incident.technician.email }}</a></p>
    </div>
  </section>
  <section class="detail-section" v-else>
    <h3>Kỹ Thuật Viên</h3>
    <p class="text-muted">Chưa được giao cho kỹ thuật viên</p>
  </section>

  <!-- Timestamps -->
  <section class="detail-section">
    <h3>Thông Tin Khác</h3>
    <p>
      <strong>Tạo lúc:</strong> {{ formatDateTime(incident.created_at) }}<br>
      <strong>Cập nhật lúc:</strong> {{ formatDateTime(incident.updated_at) }}
    </p>
  </section>
</div>
```

---

## 4. Loại Sự Cố (Incident Types)

Dưới đây là danh sách các loại sự cố có sẵn trong hệ thống:

| Loại | Mô Tả |
|------|--------|
| Điện | Vấn đề liên quan đến hệ thống điện |
| Nước | Vấn đề liên quan đến hệ thống nước |
| Điều hòa | Vấn đề liên quan đến hệ thống điều hòa không khí |
| Cấu trúc | Vấn đề liên quan đến cấu trúc, dột, nứt tường |
| Thiết bị | Vấn đề liên quan đến thiết bị, nội thất |
| Côn trùng | Vấn đề về côn trùng, chuột |
| Bảo mật | Vấn đề liên quan đến an ninh, khóa cửa |
| Vệ sinh | Yêu cầu vệ sinh, làm sạch |
| Khác | Các vấn đề khác |

---

## 5. Hướng Dẫn Xử Lý Lỗi

### 5.1 Xử Lý Response Lỗi

```javascript
async function fetchIncidents() {
  try {
    const response = await fetch('/api/v1/incidents', {
      headers: { 'Authorization': `Bearer ${authToken}` }
    });

    if (!response.ok) {
      if (response.status === 401) {
        // Token hết hạn, redirect to login
        window.location.href = '/login';
      } else if (response.status === 403) {
        // Không có quyền
        showError('Bạn không có quyền truy cập');
      } else if (response.status === 404) {
        // Không tìm thấy
        showError('Sự cố không tồn tại');
      }
      return;
    }

    const result = await response.json();
    if (result.status) {
      return result.data;
    } else {
      showError(result.error || 'Lỗi không xác định');
    }
  } catch (error) {
    console.error('Fetch error:', error);
    showError('Lỗi kết nối đến server');
  }
}
```

---

## 6. Best Practices

### 6.1 Caching
```javascript
// Cache danh sách sự cố để giảm tải server
const cache = new Map();

async function fetchIncidentsWithCache() {
  const cacheKey = 'incidents_list';
  if (cache.has(cacheKey)) {
    return cache.get(cacheKey);
  }

  const data = await fetchIncidents();
  cache.set(cacheKey, data);
  
  // Clear cache sau 5 phút
  setTimeout(() => cache.delete(cacheKey), 5 * 60 * 1000);
  
  return data;
}
```

### 6.2 Pagination
```javascript
// Load từng trang một khi scroll
async function loadNextPage(currentPage) {
  const data = await fetchIncidents({
    page: currentPage + 1,
    pageSize: 20
  });
  
  return data;
}
```

### 6.3 Hiển Thị Trạng Thái
```javascript
const statusMap = {
  'new': { label: 'Mới', color: 'blue' },
  'assigned': { label: 'Đã giao', color: 'yellow' },
  'in_progress': { label: 'Đang xử lý', color: 'orange' },
  'pending_material': { label: 'Chờ vật liệu', color: 'purple' },
  'completed': { label: 'Hoàn thành', color: 'green' },
  'cancelled': { label: 'Đã hủy', color: 'red' },
  'on_hold': { label: 'Tạm dừng', color: 'gray' }
};

const priorityMap = {
  'low': { label: 'Thấp', color: 'green' },
  'medium': { label: 'Trung bình', color: 'yellow' },
  'high': { label: 'Cao', color: 'orange' },
  'urgent': { label: 'Khẩn cấp', color: 'red' }
};
```

---

## 7. Liên Hệ & Support

Nếu có câu hỏi hoặc vấn đề với API, vui lòng liên hệ với team backend.

---

**Last Updated:** May 15, 2026  
**Version:** 1.0
