feat: Implement core authentication and course management logic with new discovery and profile pages.

This commit is contained in:
supalerk-ar66 2026-01-16 10:03:04 +07:00
parent 1aa3190ca4
commit 2ffcc36fe4
12 changed files with 397 additions and 89 deletions

View file

@ -20,36 +20,42 @@ const showDetail = ref(false);
const searchQuery = ref("");
const isCategoryOpen = ref(true);
// Mock Course Data
const courses = [
{
id: 1,
title: "เบื้องต้นการออกแบบ UX/UI",
levelType: "neutral" as const,
price: "ฟรี",
description: "เรียนรู้พื้นฐานการวาดโครงร่าง...",
rating: "4.8",
lessons: "12",
},
{
id: 2,
title: "รูปแบบ React ขั้นสูง",
levelType: "warning" as const,
price: "ฟรี",
description: "เจาะลึก HOC, Hooks และอื่นๆ...",
rating: "4.9",
lessons: "24",
},
{
id: 3,
title: "การตลาดดิจิทัล 101",
levelType: "success" as const,
price: "ฟรี",
description: "คู่มือสมบูรณ์ SEO/SEM...",
rating: "4.7",
lessons: "18",
},
];
// Courses Data
const { fetchCourses, fetchCourseById } = useCourse();
const courses = ref<any[]>([]);
const isLoading = ref(false);
const selectedCourse = ref<any>(null);
const isLoadingDetail = ref(false);
const loadCourses = async () => {
isLoading.value = true;
const res = await fetchCourses();
if (res.success) {
courses.value = (res.data || []).map((c: any) => ({
...c,
rating: "0.0",
lessons: "0",
levelType: c.levelType || "neutral"
}));
}
isLoading.value = false;
};
const selectCourse = async (id: number) => {
isLoadingDetail.value = true;
selectedCourse.value = null;
showDetail.value = true;
const res = await fetchCourseById(id);
if (res.success) {
selectedCourse.value = res.data;
}
isLoadingDetail.value = false;
};
onMounted(() => {
loadCourses();
});
// Categories Data
const categories = [
@ -79,12 +85,12 @@ const visibleCategories = computed(() => {
// Filter Logic based on search query
const filteredCourses = computed(() => {
if (!searchQuery.value) return courses;
if (!searchQuery.value) return courses.value;
const query = searchQuery.value.toLowerCase();
return courses.filter(
return courses.value.filter(
(c) =>
c.title.toLowerCase().includes(query) ||
c.description.toLowerCase().includes(query)
c.description?.toLowerCase().includes(query)
);
});
</script>
@ -206,8 +212,9 @@ const filteredCourses = computed(() => {
:description="course.description"
:rating="course.rating"
:lessons="course.lessons"
:image="course.thumbnail_url"
show-view-details
@view-details="showDetail = true"
@view-details="selectCourse(course.id)"
/>
</div>
@ -245,11 +252,20 @@ const filteredCourses = computed(() => {
<!-- COURSE DETAIL VIEW: Detailed information about a specific course -->
<div v-else>
<NuxtLink to="/browse" class="btn btn-secondary mb-6 inline-block">
กลบหนารายการคอร
</NuxtLink>
<button
@click="showDetail = false"
class="btn btn-secondary mb-6 inline-flex items-center gap-2"
>
<span></span> กลบหนารายการคอร
</button>
<div class="grid-12">
<div v-if="isLoadingDetail" class="flex justify-center py-20">
<div class="spinner-border animate-spin inline-block w-8 h-8 border-4 rounded-full" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
<div v-else-if="selectedCourse" class="grid-12">
<!-- Main Content (Left Column) -->
<div class="col-span-8">
<!-- Hero Video Placeholder -->
@ -265,10 +281,17 @@ const filteredCourses = computed(() => {
margin-bottom: 24px;
position: relative;
border: 1px solid var(--border-color);
overflow: hidden;
"
>
<img
v-if="selectedCourse.thumbnail_url"
:src="selectedCourse.thumbnail_url"
class="absolute inset-0 w-full h-full object-cover opacity-50"
/>
<!-- Play Button -->
<div
class="relative z-10"
style="
width: 80px;
height: 80px;
@ -295,15 +318,13 @@ const filteredCourses = computed(() => {
</div>
<h1 class="text-[32px] font-bold mb-4 text-slate-900 dark:text-white">
เบองตนการออกแบบ UX/UI
{{ selectedCourse.title }}
</h1>
<p
class="text-slate-700 dark:text-slate-400 mb-6"
style="font-size: 1.1em; line-height: 1.7"
>
เนอหาครอบคลมทกอยางตงแตการวยผใช (User Research)
ไปจนถงการทำตนแบบความละเอยดส (High-fidelity Prototyping)
เหมาะสำหรบผเรมตนทองการเขาสสายงานออกแบบผลตภณฑ
{{ selectedCourse.description }}
</p>
<!-- Learning Objectives -->
@ -391,12 +412,12 @@ const filteredCourses = computed(() => {
class="text-primary font-bold"
style="font-size: 32px; margin: 0"
>
ฟร
{{ selectedCourse.price || 'ฟรี' }}
</h2>
</div>
<NuxtLink
to="/dashboard/my-courses?enrolled=true"
:to="`/dashboard/my-courses?enroll=${selectedCourse.id}`"
class="btn btn-primary w-full mb-4 text-white"
style="height: 48px; font-size: 16px"
>