feat: add new composables for category, course, and authentication management.
This commit is contained in:
parent
fb4f345483
commit
8b403f906a
3 changed files with 59 additions and 12 deletions
|
|
@ -126,10 +126,17 @@ export const useAuth = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ฟังก์ชันดึงข้อมูลโปรไฟล์ผู้ใช้ล่าสุด
|
// Shared state สำหรับเช็คว่ากำลังโหลดโปรไฟล์อยู่หรือไม่ เพื่อป้องกันการยิงซ้อน
|
||||||
const fetchUserProfile = async () => {
|
const isProfileLoading = useState<boolean>('auth_profile_loading', () => false)
|
||||||
if (!token.value) return
|
const isProfileLoaded = useState<boolean>('auth_profile_loaded', () => false)
|
||||||
|
|
||||||
|
// ฟังก์ชันดึงข้อมูลโปรไฟล์ผู้ใช้ล่าสุด
|
||||||
|
const fetchUserProfile = async (forceRefresh = false) => {
|
||||||
|
// ถ้าไม่มี Token หรือกำลังโหลดอยู่ หรือโหลดข้อมูลมาแล้ว (และไม่ได้สั่ง Refresh) ให้ข้าม
|
||||||
|
if (!token.value || isProfileLoading.value) return
|
||||||
|
if (isProfileLoaded.value && !forceRefresh) return
|
||||||
|
|
||||||
|
isProfileLoading.value = true
|
||||||
try {
|
try {
|
||||||
const data = await $fetch<User>(`${API_BASE_URL}/user/me`, {
|
const data = await $fetch<User>(`${API_BASE_URL}/user/me`, {
|
||||||
headers: {
|
headers: {
|
||||||
|
|
@ -139,14 +146,13 @@ export const useAuth = () => {
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
user.value = data
|
user.value = data
|
||||||
|
isProfileLoaded.value = true
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
// กรณี Token หมดอายุ (401)
|
// กรณี Token หมดอายุ (401)
|
||||||
if (error.statusCode === 401) {
|
if (error.statusCode === 401) {
|
||||||
// พยายามขอ Token ใหม่ (Refresh Token)
|
|
||||||
const refreshed = await refreshAccessToken()
|
const refreshed = await refreshAccessToken()
|
||||||
if (refreshed) {
|
if (refreshed) {
|
||||||
// ถ้าได้ Token ใหม่ ให้ลองดึงข้อมูลอีกครั้ง
|
|
||||||
try {
|
try {
|
||||||
const retryData = await $fetch<User>(`${API_BASE_URL}/user/me`, {
|
const retryData = await $fetch<User>(`${API_BASE_URL}/user/me`, {
|
||||||
headers: {
|
headers: {
|
||||||
|
|
@ -156,18 +162,20 @@ export const useAuth = () => {
|
||||||
|
|
||||||
if (retryData) {
|
if (retryData) {
|
||||||
user.value = retryData
|
user.value = retryData
|
||||||
|
isProfileLoaded.value = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} catch (retryErr) {
|
} catch (retryErr) {
|
||||||
console.error('Failed to fetch user profile after refresh:', retryErr)
|
console.error('Failed to fetch user profile after refresh:', retryErr)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// ถ้า Refresh ไม่ผ่าน ให้ Logout
|
|
||||||
logout()
|
logout()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.error('Failed to fetch user profile:', error)
|
console.error('Failed to fetch user profile:', error)
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
isProfileLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,22 @@ export const useCategory = () => {
|
||||||
const API_BASE_URL = config.public.apiBase as string
|
const API_BASE_URL = config.public.apiBase as string
|
||||||
const { token } = useAuth()
|
const { token } = useAuth()
|
||||||
|
|
||||||
|
// ใช้ useState เพื่อเก็บ Cached Data ระดับ Global (แชร์กันทุก Component)
|
||||||
|
const categoriesState = useState<Category[]>('categories_cache', () => [])
|
||||||
|
const isLoaded = useState<boolean>('categories_loaded', () => false)
|
||||||
|
|
||||||
// ฟังก์ชันดึงข้อมูลหมวดหมู่ทั้งหมด
|
// ฟังก์ชันดึงข้อมูลหมวดหมู่ทั้งหมด
|
||||||
// Endpoint: GET /categories
|
// Endpoint: GET /categories
|
||||||
const fetchCategories = async () => {
|
const fetchCategories = async (forceRefresh = false) => {
|
||||||
|
// ถ้ามีข้อมูลอยู่แล้วและไม่สั่งบังคับโหลดใหม่ ให้ใช้ข้อมูลเดิมทันที
|
||||||
|
if (isLoaded.value && !forceRefresh && categoriesState.value.length > 0) {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: categoriesState.value,
|
||||||
|
total: categoriesState.value.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await $fetch<CategoryResponse>(`${API_BASE_URL}/categories`, {
|
const response = await $fetch<CategoryResponse>(`${API_BASE_URL}/categories`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
|
@ -47,9 +60,15 @@ export const useCategory = () => {
|
||||||
} : {}
|
} : {}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const categories = response.data?.categories || []
|
||||||
|
|
||||||
|
// เก็บข้อมูลลง State
|
||||||
|
categoriesState.value = categories
|
||||||
|
isLoaded.value = true
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
data: response.data?.categories || [],
|
data: categories,
|
||||||
total: response.data?.total || 0
|
total: response.data?.total || 0
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
|
|
@ -62,6 +81,7 @@ export const useCategory = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fetchCategories
|
fetchCategories,
|
||||||
|
categories: computed(() => categoriesState.value) // ส่งข้อมูลออกมาเป็น Computed เพื่อให้ UI อัปเดตตามอัตโนมัติ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,22 +77,41 @@ export const useCourse = () => {
|
||||||
const API_BASE_URL = config.public.apiBase as string
|
const API_BASE_URL = config.public.apiBase as string
|
||||||
const { token } = useAuth()
|
const { token } = useAuth()
|
||||||
|
|
||||||
|
// ใช้ useState เพื่อเก็บรายชื่อคอร์สทั้งหมดใน Memory
|
||||||
|
const coursesState = useState<Course[]>('courses_cache', () => [])
|
||||||
|
const isCoursesLoaded = useState<boolean>('courses_loaded', () => false)
|
||||||
|
|
||||||
// ฟังก์ชันดึงรายชื่อคอร์สทั้งหมด (Catalog)
|
// ฟังก์ชันดึงรายชื่อคอร์สทั้งหมด (Catalog)
|
||||||
// ใช้สำหรับหน้า Discover/Browse
|
// ใช้สำหรับหน้า Discover/Browse
|
||||||
// Endpoint: GET /courses
|
// Endpoint: GET /courses
|
||||||
const fetchCourses = async () => {
|
const fetchCourses = async (forceRefresh = false) => {
|
||||||
|
// ถ้าโหลดไปแล้ว และไม่ได้บังคับ Refresh ให้ใช้ข้อมูลจาก State
|
||||||
|
if (isCoursesLoaded.value && !forceRefresh && coursesState.value.length > 0) {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: coursesState.value,
|
||||||
|
total: coursesState.value.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await $fetch<CourseResponse>(`${API_BASE_URL}/courses`, {
|
const data = await $fetch<CourseResponse>(`${API_BASE_URL}/courses`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
// ส่ง Token ไปด้วยถ้ามี (เผื่อ Logic ในอนาคตที่ต้องเช็คสิทธิ์)
|
// ส่ง Token ไปด้วยถ้ามี
|
||||||
headers: token.value ? {
|
headers: token.value ? {
|
||||||
Authorization: `Bearer ${token.value}`
|
Authorization: `Bearer ${token.value}`
|
||||||
} : {}
|
} : {}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const courses = data.data || []
|
||||||
|
|
||||||
|
// เก็บลง State
|
||||||
|
coursesState.value = courses
|
||||||
|
isCoursesLoaded.value = true
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
data: data.data || [],
|
data: courses,
|
||||||
total: data.total || 0
|
total: data.total || 0
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue