---
globs: src/views/**/*.vue
---

# Page Layout Patterns và UI Components

## Cấu trúc trang chuẩn
Mỗi trang nên follow cấu trúc này:

```vue
<template>
  <div>
    <!-- 1. Breadcrumb Header -->
    <div class="breadcrumb-header justify-content-between">
      <div class="my-auto">
        <h4 class="page-title">Tên trang</h4>
        <ol class="breadcrumb">
          <li class="breadcrumb-item"><a href="javascript:void(0);">Trang chủ</a></li>
          <li class="breadcrumb-item active" aria-current="page">Tên trang</li>
        </ol>
      </div>
    </div>

    <!-- 2. Main Content Container -->
    <div class="row row-sm">
      <div class="col-sm-12 col-md-12 col-lg-12 col-xl-12 grid-margin">
        
        <!-- 3. Search Form Card -->
        <div class="card card-search shadow-none">
          <form class="card-body" @submit.prevent="search">
            <!-- Search form content -->
          </form>
        </div>

        <!-- 4. Data Table Card -->
        <div class="card card-table shadow-none">
          <div class="card-body card-table-body">
            <!-- Table content -->
          </div>
        </div>

      </div>
    </div>

    <!-- 5. Modals -->
    <LadaDialog :visible="modalControl.CreateOrUpdate">
      <!-- Modal content -->
    </LadaDialog>
  </div>
</template>
```

## Search Form Layout
```vue
<template>
  <form class="card-body" @submit.prevent="search">
    <div class="row">
      <!-- Text Input -->
      <div class="col-lg-3 col-md-4 mb-3">
        <label>Từ khóa</label>
        <input 
          type="text" 
          v-model="searchModel.Search.PredicateObject.Keyword" 
          class="form-control"
          placeholder="từ khóa..."
        >
      </div>

      <!-- Date Range Picker -->
      <div class="col-lg-3 col-md-4 mb-3">
        <label>Ngày tạo</label>
        <VueDatePicker 
          ref="createDatePicker" 
          v-model="date" 
          :enable-time-picker="false" 
          range
          multi-calendars 
          auto-apply 
          :format="format" 
          @update:model-value="handleDate" 
          utc="preserve"
        >
          <template #left-sidebar>
            <DateRangeSidebar @change-date="onChangeDate"></DateRangeSidebar>
          </template>
        </VueDatePicker>
      </div>

      <!-- Select Dropdown -->
      <div class="col-lg-3 col-md-4 mb-3">
        <label>Trạng thái</label>
        <v-select 
          label="Name" 
          :options="statuses"
          v-model="searchModel.Search.PredicateObject.Status"
          :reduce="(option) => option.Id"
        ></v-select>
      </div>

      <!-- Select with Custom No Options -->
      <div class="col-lg-3 col-md-4 mb-3">
        <label>Menu cha</label>
        <v-select 
          label="Label" 
          :options="parents"
          v-model="searchModel.Search.PredicateObject.ParentId" 
          :reduce="(option) => option.Id"
        >
          <template v-slot:no-options="{ search, searching }">
            <template v-if="searching">
              Không có kết quả cho <em>{{ search }}</em>.
            </template>
            <span v-else style="opacity: 0.8">Không có bản ghi nào</span>
          </template>
        </v-select>
      </div>
    </div>

    <!-- Search Button -->
    <div class="row">
      <div class="col-lg mb-3">
        <button type="submit" class="btn btn-dark" :disabled="loading">
          <i v-show="!loading" class="fe fe-search"></i>
          <span v-show="loading" class="spinner-border spinner-border-sm"></span>
          <span>Tìm kiếm</span>
        </button>
      </div>
    </div>
  </form>
</template>
```

## Data Table Layout
```vue
<template>
  <div class="card card-table shadow-none">
    <div class="card-body card-table-body">
      <!-- Action Buttons -->
      <div class="d-flex flex-row justify-content-between mg-b-10">
        <button 
          @click="fnCreateOrUpdate(0)" 
          type="button"
          class="btn btn-outline-primary mx-2 button-icon mb-1"
        >
          <i class="fe fe-plus me-2"></i>Tạo mới
        </button>
      </div>

      <!-- Table -->
      <div class="table-responsive border-top mg-b-10">
        <table class="table table-vcenter mb-0" style="min-height:300px">
          <thead>
            <tr>
              <th class="wd-lg-12p min-width-2"><span>Tên cột</span></th>
              <th class="wd-lg-8p"></th>
            </tr>
          </thead>
          
          <!-- Loading State -->
          <tbody v-if="loading">
            <tr>
              <td colspan="7" class="tx-semibold text-center text-default">
                <span class="spinner-border spinner-border-sm"></span>
              </td>
            </tr>
          </tbody>

          <!-- Empty State -->
          <tbody v-if="!loading && items.length <= 0">
            <tr>
              <td colspan="7" class="tx-semibold text-center text-danger">
                Không tìm thấy bản ghi nào!
              </td>
            </tr>
          </tbody>

          <!-- Data Rows -->
          <tbody v-if="!loading && items.length > 0">
            <tr v-for="item in items" :key="item.Id">
              <td class="text-wrap align-top">
                <!-- Cell content -->
              </td>
              <td class="align-top pt-2">
                <!-- Action Dropdown -->
                <button 
                  type="button" 
                  data-bs-toggle="dropdown"
                  class="mb-1 mt-1 mr-1 btn btn-light dropdown-toggle btn-sm"
                  aria-expanded="false"
                >
                  <i class="fas fa-cog"></i> Quản lý
                </button>
                <div class="dropdown-menu dropdown-menu-arrow">
                  <a @click="fnCreateOrUpdate(item.Id)" class="dropdown-item" href="javascript:void(0);">
                    <i class="fe fe-edit-2"></i>
                    <span class="mg-l-6">Cập nhật</span>
                  </a>
                  <a @click="deleteItem(item.Id)" class="dropdown-item" href="javascript:void(0);">
                    <i class="fe fe-trash"></i>
                    <span class="mg-l-6">Xóa</span>
                  </a>
                </div>
              </td>
            </tr>
          </tbody>
        </table>
      </div>

      <!-- Pagination -->
      <pager 
        ref="ctrlPager" 
        v-show="!loading && items.length > 0" 
        @PageSizeChanged="PageSizeChanged"
        @PageIndexChanged="PageIndexChanged" 
        :Total="searchModel.Pagination.Total"
        :TotalCurrent="items.length"
      ></pager>
    </div>
  </div>
</template>
```

## Modal Patterns
```vue
<template>
  <LadaDialog :visible="modalControl.CreateOrUpdate">
    <template #header>
      <div class="modal-header">
        <h5 class="modal-title" v-if="selectedId <= 0">Tạo mới</h5>
        <h5 class="modal-title" v-if="selectedId > 0">Cập nhật</h5>
        <button @click="closeModal" type="button" class="close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
    </template>
    <template #body>
      <CreateOrUpdate @cancel="closeModal" :id="selectedId"></CreateOrUpdate>
    </template>
  </LadaDialog>
</template>

<script setup>
// Modal Control
const modalControl = reactive({
  CreateOrUpdate: false
});

const selectedId = ref(0);

function closeModal() {
  modalControl.CreateOrUpdate = false;
  loadData(); // Reload data after modal close
}

async function fnCreateOrUpdate(id) {
  selectedId.value = id;
  modalControl.CreateOrUpdate = true;
}
</script>
```

## Date Picker Implementation
```vue
<script setup>
import DatepickerInstance from "@vuepic/vue-datepicker";
import { DateRangeSidebar } from '@/components/index';

const date = ref();
const createDatePicker = ref(DatepickerInstance);

const format = (dates) => {
  let start = dates[0];
  let end = dates[1];
  const dayStart = start.getDate();
  const monthStart = start.getMonth() + 1;
  const yearStart = start.getFullYear();
  const dayEnd = end.getDate();
  const monthEnd = end.getMonth() + 1;
  const yearEnd = end.getFullYear();

  return `${dayStart}/${monthStart}/${yearStart} - ${dayEnd}/${monthEnd}/${yearEnd}`;
}

async function onChangeDate(newDates) {
  date.value = newDates;
  createDatePicker.value.closeMenu();
  handleDate(newDates);
}

function handleDate(modelData) {
  if (modelData != null) {
    searchModel.Search.PredicateObject.CreatedStart = modelData[0];
    searchModel.Search.PredicateObject.CreatedEnd = modelData[1];
  } else {
    searchModel.Search.PredicateObject.CreatedStart = null;
    searchModel.Search.PredicateObject.CreatedEnd = null;
  }
}
</script>
```

## Select Dropdown Patterns
```vue
<script setup>
// Simple Select
const statuses = [
  { Id: 1, Name: 'Đang hoạt động' },
  { Id: 2, Name: 'Tạm dừng' }
];

// Reactive Select Options
const appTypes = reactive([
  { Id: 1, Name: 'Hệ thống quản trị nội bộ' },
  { Id: 8, Name: 'Hệ thống khách hàng' },
  { Id: 2, Name: 'Hệ thống đào tạo' },
  { Id: 3, Name: 'Hệ thống quản lý tài khoản' }
]);

// Dynamic Options from API
const parents = reactive([]);

async function getParents() {
  parents.splice(0, parents.length);
  var parentResult = await menuService.getTree();
  if (parentResult != null && parentResult.length > 0) {
    parentResult.forEach(menu => {
      parents.push(menu);
    });
  }
}
</script>

<template>
  <!-- Basic Select -->
  <v-select 
    label="Name" 
    :options="statuses"
    v-model="searchModel.Search.PredicateObject.Status"
    :reduce="(option) => option.Id"
  ></v-select>

  <!-- Select with Custom No Options Template -->
  <v-select 
    label="Label" 
    :options="parents"
    v-model="searchModel.Search.PredicateObject.ParentId" 
    :reduce="(option) => option.Id"
  >
    <template v-slot:no-options="{ search, searching }">
      <template v-if="searching">
        Không có kết quả cho <em>{{ search }}</em>.
      </template>
      <span v-else style="opacity: 0.8">Không có bản ghi nào</span>
    </template>
  </v-select>
</template>
```

## Search Model Structure
```vue
<script setup>
const searchModel = reactive({
  Pagination: {
    Total: 0,
    PageIndex: 1,
    PageSize: 25
  },
  Sort: {
    Predicate: '',
    Reverse: true
  },
  Search: {
    PredicateObject: {
      Keyword: '',
      CreatedStart: null,
      CreatedEnd: null,
      Status: null,
      // Add other search fields as needed
    }
  }
});

const items = reactive([]);
const loading = ref(false);

// Search Functions
async function search() {
  searchModel.Pagination.PageIndex = 1;
  ctrlPager.value.Refresh();
  loadData();
}

async function loadData() {
  loading.value = true;
  items.splice(0, items.length);
  var searchResult = await service.search(searchModel);
  if (searchResult.Items != null && searchResult.Items.length > 0) {
    searchResult.Items.forEach(item => {
      items.push(item);
    });
  }
  searchModel.Pagination.Total = searchResult.TotalRecord;
  loading.value = false;
}

// Pagination
function PageSizeChanged(pagesize) {
  searchModel.Pagination.PageSize = pagesize;
  searchModel.Pagination.PageIndex = 1;
  loadData();
}

function PageIndexChanged(pageIndex) {
  searchModel.Pagination.PageIndex = pageIndex;
  loadData();
}
</script>
```

## Action Functions Pattern
```vue
<script setup>
import { useToast } from "vue-toastification";
const toast = useToast();

// CRUD Actions
async function lock(id) {
  var cf = confirm("Bạn chắc chắn muốn khóa?");
  if (cf) {
    loading.value = true;
    var result = await service.lock(id);
    if (result != null && result.Result === 1) {
      toast.success('Thành công!');
    } else {
      toast.error('Thất bại: ' + result.Message);
    }
    loading.value = false;
    search();
  }
}

async function deleteItem(id) {
  var cf = confirm("Bạn chắc chắn muốn xóa?");
  if (cf) {
    loading.value = true;
    var result = await service.delete(id);
    if (result != null && result.Result === 1) {
      toast.success('Thành công!');
    } else {
      toast.error('Thất bại: ' + result.Message);
    }
    loading.value = false;
    search();
  }
}
</script>
```

## Status Badge Patterns
```vue
<template>
  <!-- Status Badges -->
  <span v-if="item.Status === 2" class="tag tag-indigo">Bị khóa</span>
  <span v-if="item.Status === 1" class="tag tag-green">Đang hoạt động</span>
  
  <!-- Priority Badges -->
  <span v-if="item.Priority === 1" class="badge bg-danger">Cao</span>
  <span v-if="item.Priority === 2" class="badge bg-warning">Trung bình</span>
  <span v-if="item.Priority === 3" class="badge bg-success">Thấp</span>
</template>
```

## Import Organization
```vue
<script setup>
//#region Imports
import { ref, reactive, onMounted } from 'vue'
import service from "@/services/serviceName";
//#endregion

//#region Components
import DatepickerInstance from "@vuepic/vue-datepicker";
import { LadaDialog, DateRangeSidebar } from '@/components/index';
import CreateOrUpdate from './components/CreateOrUpdate.vue';
import { useToast } from "vue-toastification";
const toast = useToast();
//#endregion

//#region Modals
const modalControl = reactive({
  CreateOrUpdate: false
});
//#endregion

//#region Actions
// Action functions here
//#endregion

//#region SEARCH
// Search related code here
//#endregion
</script>
```