# Vue Select Component Rule

## Quy tắc sử dụng Select Components

### ✅ Sử dụng v-select
Trong dự án LADA Express Management Portal, **LUÔN sử dụng `v-select`** thay vì `LadaSelect` cho tất cả các dropdown/select components để đồng bộ với hệ thống.

### Cấu trúc chuẩn cho v-select:

```vue
<template>
  <!-- Basic v-select -->
  <v-select
    v-model="selectedValue"
    :options="options"
    :reduce="(option) => option.value"
    label="label"
    placeholder="Chọn..."
    :clearable="true"
  />

  <!-- v-select với validation -->
  <v-select
    v-model="formData.field"
    :options="options"
    :reduce="(option) => option.value"
    label="label"
    placeholder="Chọn..."
    :class="{ 'is-invalid': errors.field }"
  />
  <div class="invalid-feedback d-block" v-if="errors.field">{{ errors.field }}</div>
</template>

<script setup>
// Cấu trúc options chuẩn
const options = ref([
  { value: 1, label: 'Tùy chọn 1' },
  { value: 2, label: 'Tùy chọn 2' }
])

// Hoặc từ API
const loadOptions = async () => {
  const result = await service.getOptions()
  options.value = result.map(item => ({
    value: item.Id,
    label: item.Name
  }))
}
</script>
```

### ❌ Không sử dụng LadaSelect
```vue
<!-- KHÔNG sử dụng -->
<LadaSelect
  v-model="value"
  :options="options"
  placeholder="Chọn..."
/>
```

### Các thuộc tính quan trọng của v-select:

- `:options` - Mảng các tùy chọn
- `:reduce="(option) => option.value"` - Lấy giá trị từ object
- `label="label"` - Hiển thị text từ object
- `:clearable="true"` - Cho phép xóa lựa chọn
- `placeholder` - Text gợi ý
- `:class` - CSS classes (đặc biệt cho validation)

### Ví dụ thực tế:

```vue
<!-- Expense Category Select -->
<v-select
  v-model="searchModel.Search.PredicateObject.ExpenseCategoryId"
  :options="categoryOptions"
  :reduce="(option) => option.value"
  label="label"
  placeholder="Chọn danh mục..."
  :clearable="true"
/>

<!-- Status Select -->
<v-select
  v-model="searchModel.Search.PredicateObject.Status"
  :options="statusOptions"
  :reduce="(option) => option.value"
  label="label"
  placeholder="Chọn trạng thái..."
  :clearable="true"
/>

<!-- User Select với Suggest (Cách chuẩn) -->
<v-select
  v-model="selectedUser"
  :reduce="(option) => option.Id"
  label="UserName"
  :filterable="false"
  :options="users"
  @search="getUsers"
>
  <template #no-options="{ search, searching, loading }">
    Gõ từ khóa để tìm....
  </template>
  <template v-slot:option="option">
    {{ option.UserName }} ({{ option.FullName }})
  </template>
</v-select>
```

### API Suggest Pattern (Quan trọng - Áp dụng cho TẤT CẢ v-select lấy dữ liệu từ API):

**Nguyên tắc**: Tất cả các v-select lấy dữ liệu từ API đều phải sử dụng suggest pattern (search as you type) thay vì load toàn bộ dữ liệu.

#### 1. User Suggest Pattern:

```vue
<template>
  <v-select
    v-model="selectedUser"
    :reduce="(option) => option.Id"
    label="UserName"
    :filterable="false"
    :options="users"
    @search="getUsers"
  >
    <template #no-options="{ search, searching, loading }">
      Gõ từ khóa để tìm....
    </template>
    <template v-slot:option="option">
      {{ option.UserName }} ({{ option.FullName }})
    </template>
  </v-select>
</template>

<script setup>
import _ from 'lodash'

const users = reactive([])

const getUsers = async (keyword, loading) => {
  if (keyword.length) {
    loading(true)
    await getUsersDebounce(loading, keyword)
  }
}

let getUsersDebounce = _.debounce(async (loading, keyword) => {
  users.splice(0, users.length)
  
  const staffResult = await userService.sugStaff(keyword)
  if (staffResult != null && staffResult.length > 0) {
    staffResult.forEach(staff => {
      users.push(staff)
    })
  }
  loading(false)
}, 500)
</script>
```

#### 2. Category Suggest Pattern:

```vue
<template>
  <v-select
    v-model="selectedCategory"
    :reduce="(option) => option.Id"
    label="Name"
    :filterable="false"
    :options="categories"
    @search="getCategories"
  >
    <template #no-options="{ search, searching, loading }">
      Gõ từ khóa để tìm danh mục....
    </template>
    <template v-slot:option="option">
      {{ option.Name }}
    </template>
  </v-select>
</template>

<script setup>
import _ from 'lodash'

const categories = reactive([])

const getCategories = async (keyword, loading) => {
  if (keyword.length) {
    loading(true)
    await getCategoriesDebounce(loading, keyword)
  }
}

let getCategoriesDebounce = _.debounce(async (loading, keyword) => {
  categories.splice(0, categories.length)
  
  const categoryResult = await categoryService.search({
    Search: {
      PredicateObject: {
        Keyword: keyword
      }
    },
    Pagination: { PageIndex: 1, PageSize: 50 }
  })
  
  if (categoryResult && categoryResult.Items && categoryResult.Items.length > 0) {
    categoryResult.Items.forEach(category => {
      categories.push(category)
    })
  }
  loading(false)
}, 500)
</script>
```

#### 3. Generic API Suggest Pattern:

```vue
<template>
  <v-select
    v-model="selectedItem"
    :reduce="(option) => option.Id"
    label="Name"
    :filterable="false"
    :options="items"
    @search="getItems"
  >
    <template #no-options="{ search, searching, loading }">
      Gõ từ khóa để tìm....
    </template>
    <template v-slot:option="option">
      {{ option.Name }}
    </template>
  </v-select>
</template>

<script setup>
import _ from 'lodash'

const items = reactive([])

const getItems = async (keyword, loading) => {
  if (keyword.length) {
    loading(true)
    await getItemsDebounce(loading, keyword)
  }
}

let getItemsDebounce = _.debounce(async (loading, keyword) => {
  items.splice(0, items.length)
  
  const result = await service.search({
    Search: {
      PredicateObject: {
        Keyword: keyword
      }
    },
    Pagination: { PageIndex: 1, PageSize: 50 }
  })
  
  if (result && result.Items && result.Items.length > 0) {
    result.Items.forEach(item => {
      items.push(item)
    })
  }
  loading(false)
}, 500)
</script>
```

### Quy tắc chung cho API Suggest Pattern:

1. **Bắt buộc sử dụng suggest pattern** cho tất cả v-select lấy dữ liệu từ API
2. **Không load toàn bộ dữ liệu** khi component mount
3. **Sử dụng debounce** với delay 500ms để tối ưu performance
4. **Sử dụng reactive array** để lưu trữ options
5. **Template #no-options** để hướng dẫn user
6. **Template v-slot:option** để hiển thị đẹp
7. **Pagination** với PageSize = 50 cho API calls

### Các thuộc tính bắt buộc:
- `:filterable="false"` - Tắt filter client-side
- `@search="getItems"` - Handle search event
- `:reduce="(option) => option.Id"` - Lấy ID từ object
- `label="Name"` hoặc `label="UserName"` - Field hiển thị
- `reactive([])` - Array lưu trữ options
- `_.debounce(..., 500)` - Debounce function

### Lý do sử dụng API Suggest Pattern:
1. **Performance**: Giảm tải server và client
2. **UX**: Tìm kiếm nhanh chóng, không cần load hết dữ liệu
3. **Scalability**: Hoạt động tốt với dữ liệu lớn
4. **Network**: Giảm bandwidth và response time
5. **Memory**: Tiết kiệm memory trên client

### Lý do sử dụng v-select:
1. **Đồng bộ**: Tất cả components trong hệ thống đều sử dụng v-select
2. **Tính năng**: v-select có nhiều tính năng hơn (search, clearable, etc.)
3. **Styling**: Dễ dàng customize và tích hợp với Bootstrap
4. **Performance**: Tối ưu hơn cho danh sách lớn
5. **Maintenance**: Dễ bảo trì và cập nhật

### Ghi chú:
- **KHÔNG** sử dụng static options cho dữ liệu từ API
- **LUÔN** sử dụng suggest pattern cho API data
- Sử dụng `:reduce` để lấy giá trị thực từ object
- Sử dụng `label` để hiển thị text cho người dùng
- Thêm `:clearable="true"` khi cần cho phép xóa lựa chọn
- Import `lodash` để sử dụng debounce
