elearning/Frontend-Learner/composables/useCategory.ts

114 lines
4.5 KiB
TypeScript

/**
* @interface Category
* @description โครงสร้างข้อมูลหมวดหมู่ (Category)
*/
export interface Category {
id: number
name: { // ชื่อหมวดหมู่รองรับ 2 ภาษา
th: string
en: string
[key: string]: string
}
slug: string // Slug สำหรับใช้งานบน URL
description: { // รายละเอียดหมวดหมู่
th: string
en: string
[key: string]: string
}
icon: string // ไอคอนของหมวดหมู่อ้างอิงจาก Material Icons
sort_order: number
is_active: boolean
created_at: string
updated_at: string
}
export interface CategoryData {
total: number // จำนวนหมวดหมู่ทั้งหมด
categories: Category[]
}
interface CategoryResponse {
code: number
message: string
data: CategoryData
}
/**
* @composable useCategory
* @description จัดการข้อมูลหมวดหมู่ (Categories) การดึงข้อมูล และการจัดการ Cache (เก็บข้อมูลจำลองชั่วคราว)
*/
export const useCategory = () => {
const config = useRuntimeConfig()
const API_BASE_URL = config.public.apiBase as string
const { token } = useAuth()
// เก็บ Cache การดึงข้อมูลหมวดหมู่ในระดับ Global (ใช้ข้าม Component ได้โดยไม่ต้องโหลดใหม่)
const categoriesState = useState<Category[]>('categories_cache', () => [])
const isLoaded = useState<boolean>('categories_loaded', () => false)
/**
* @function fetchCategories
* @description ดึงรายการหมวดหมู่ทั้งหมดจาก API (GET /categories)
* หากมีแคชอยู่แล้วจะดึงจาก State ทันที แต่อาจบังคับให้โหลดใหม่ (forceRefresh) ได้
*/
const fetchCategories = async (forceRefresh = false) => {
// ถ้ามีข้อมูลอยู่แล้วและไม่สั่งบังคับโหลดใหม่ ให้ใช้ข้อมูลเดิมทันที
if (isLoaded.value && !forceRefresh && categoriesState.value.length > 0) {
return {
success: true,
data: categoriesState.value,
total: categoriesState.value.length
}
}
try {
const response = await $fetch<CategoryResponse>(`${API_BASE_URL}/categories`, {
method: 'GET',
headers: token.value ? {
Authorization: `Bearer ${token.value}`
} : {}
})
const categories = response.data?.categories || []
// บันทึกรายการหมวดหมู่ลง State Cache
categoriesState.value = categories
isLoaded.value = true
return {
success: true,
data: categories,
total: response.data?.total || 0
}
} catch (err: any) {
console.error('Fetch categories failed:', err)
// กรณีเกิด Error 429 ระบบจะทำการหน่วงเวลา (1.5 วิ) และลองโหลดข้อมูลใหม่อีก 1 ครั้ง (Retry)
if (err.statusCode === 429 || err.status === 429) {
await new Promise(resolve => setTimeout(resolve, 1500)); // หน่วงเวลา 1.5 วินาที
try {
const retryRes = await $fetch<CategoryResponse>(`${API_BASE_URL}/categories`, {
method: 'GET',
headers: token.value ? { Authorization: `Bearer ${token.value}` } : {}
})
const cats = retryRes.data?.categories || []
categoriesState.value = cats
isLoaded.value = true
return { success: true, data: cats, total: retryRes.data?.total || 0 }
} catch (retryErr) {
console.error('Retry fetch categories failed:', retryErr)
}
}
return {
success: false,
error: err.data?.message || err.message || 'Error fetching categories'
}
}
}
return {
fetchCategories,
categories: computed(() => categoriesState.value) // ส่งข้อมูลออกมาเป็น Computed เพื่อให้ UI อัปเดตตามอัตโนมัติ
}
}