feat: implement course discovery page with browsing, filtering, and detailed course views.

This commit is contained in:
supalerk-ar66 2026-01-27 10:32:13 +07:00
parent b8547f83b3
commit fb4f345483
3 changed files with 15 additions and 33 deletions

View file

@ -273,16 +273,6 @@ onMounted(() => {
:src="selectedCourse.thumbnail_url"
class="absolute inset-0 w-full h-full object-cover opacity-60 group-hover:opacity-40 transition-opacity"
/>
<!-- Play Button -->
<div class="absolute inset-0 flex items-center justify-center">
<div class="w-20 h-20 bg-white/20 backdrop-blur-md rounded-full flex items-center justify-center group-hover:scale-110 transition-transform cursor-pointer">
<div class="w-16 h-16 bg-white rounded-full flex items-center justify-center shadow-xl">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-blue-600 ml-1" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z" clip-rule="evenodd" />
</svg>
</div>
</div>
</div>
</div>
<h1 class="text-3xl md:text-4xl font-bold mb-4 text-slate-900 dark:text-white tracking-tight">

View file

@ -240,16 +240,22 @@ onBeforeUnmount(() => {
</script>
<template>
<q-layout view="hHh LpR lFf" class="bg-white dark:bg-slate-900 text-slate-900 dark:text-slate-100 font-inter">
<q-layout view="hHh LpR lFf" class="bg-white dark:bg-slate-900 text-slate-900 dark:text-slate-100">
<!-- Header -->
<q-header bordered class="bg-white dark:bg-slate-900 border-b border-gray-200 dark:border-white/5 text-slate-900 dark:text-white h-14">
<q-toolbar>
<q-btn flat round dense icon="menu" class="lg:hidden mr-2" @click="toggleSidebar" />
<q-btn flat dense no-caps icon="arrow_back" :label="$t('classroom.backToDashboard')" to="/dashboard/my-courses" class="text-slate-600 dark:text-slate-300 mobile-hide-label" />
<NuxtLink
to="/dashboard/my-courses"
class="inline-flex items-center gap-2 text-slate-900 dark:text-white hover:text-blue-600 dark:hover:text-blue-300 transition-all font-black text-sm md:text-base group mr-4"
>
<q-icon name="arrow_back" size="24px" class="transition-transform group-hover:-translate-x-1" />
<span>{{ $t('classroom.backToDashboard') }}</span>
</NuxtLink>
<q-toolbar-title class="text-sm font-bold text-center lg:text-left truncate">
<q-toolbar-title class="text-base font-bold text-center lg:text-left truncate">
{{ courseData ? getLocalizedText(courseData.course.title) : $t('classroom.loadingTitle') }}
</q-toolbar-title>
@ -272,7 +278,7 @@ onBeforeUnmount(() => {
<div v-if="courseData" class="h-full scroll">
<q-list class="pb-10">
<template v-for="chapter in courseData.chapters" :key="chapter.id">
<q-item-label header class="bg-slate-100 dark:bg-slate-800 text-slate-700 dark:text-slate-300 font-bold sticky top-0 z-10 border-b dark:border-white/5 text-xs py-3">
<q-item-label header class="bg-slate-100 dark:bg-slate-800 text-slate-700 dark:text-slate-300 font-bold sticky top-0 z-10 border-b dark:border-white/5 text-sm py-4">
{{ getLocalizedText(chapter.title) }}
</q-item-label>
@ -292,7 +298,7 @@ onBeforeUnmount(() => {
</q-item-section>
<q-item-section>
<q-item-label class="text-xs md:text-sm line-clamp-2">
<q-item-label class="text-sm md:text-base line-clamp-2">
{{ getLocalizedText(lesson.title) }}
</q-item-label>
</q-item-section>
@ -350,12 +356,12 @@ onBeforeUnmount(() => {
<!-- Lesson Info -->
<div v-if="currentLesson" class="bg-white dark:bg-slate-800 p-6 rounded-2xl shadow-sm border border-slate-100 dark:border-white/5">
<h1 class="text-2xl font-bold mb-2">{{ getLocalizedText(currentLesson.title) }}</h1>
<p class="text-slate-500 dark:text-slate-400" v-if="currentLesson.description">{{ getLocalizedText(currentLesson.description) }}</p>
<h1 class="text-2xl md:text-3xl font-bold mb-3">{{ getLocalizedText(currentLesson.title) }}</h1>
<p class="text-slate-500 dark:text-slate-400 text-base md:text-lg" v-if="currentLesson.description">{{ getLocalizedText(currentLesson.description) }}</p>
<!-- Lesson Content Area -->
<div v-if="!videoSrc && currentLesson.content" class="mt-6 prose dark:prose-invert max-w-none p-6 bg-slate-50 dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/5">
<div v-html="getLocalizedText(currentLesson.content)"></div>
<div v-html="getLocalizedText(currentLesson.content)" class="text-base md:text-lg leading-relaxed"></div>
</div>
</div>
</div>

View file

@ -62,12 +62,6 @@ useHead({
<template>
<div class="container mx-auto max-w-7xl px-4 py-8">
<NuxtLink to="/browse/discovery" class="inline-flex items-center gap-2 text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white mb-6 transition-colors font-medium">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg>
{{ $t('discovery.backToCatalog') }}
</NuxtLink>
<div v-if="course" class="grid grid-cols-1 lg:grid-cols-12 gap-8">
@ -76,14 +70,6 @@ useHead({
<div v-if="course.thumbnail_url" class="absolute inset-0">
<img :src="course.thumbnail_url" class="w-full h-full object-cover opacity-90 group-hover:opacity-75 transition-opacity" />
</div>
<!-- Play Icon Overlay -->
<div class="absolute inset-0 flex items-center justify-center">
<div class="w-16 h-16 md:w-20 md:h-20 bg-blue-600 rounded-full flex items-center justify-center shadow-lg shadow-blue-600/40 group-hover:scale-110 transition-transform duration-300">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 md:h-10 md:w-10 text-white ml-1" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z" clip-rule="evenodd" />
</svg>
</div>
</div>
</div>
<h1 class="text-3xl md:text-4xl font-bold text-slate-900 dark:text-white mb-4 leading-tight">{{ getLocalizedText(course.title) }}</h1>