feat: Implement My Courses page with course cards, filtering, and certificate download functionality.
This commit is contained in:
parent
9ed2347843
commit
c4f68eb927
4 changed files with 107 additions and 157 deletions
|
|
@ -17,7 +17,6 @@ useHead({
|
|||
|
||||
const route = useRoute()
|
||||
const showEnrollModal = ref(false)
|
||||
const showCertModal = ref(false)
|
||||
const activeFilter = ref<'all' | 'progress' | 'completed'>('all')
|
||||
const { currentUser } = useAuth()
|
||||
|
||||
|
|
@ -36,9 +35,10 @@ const getLocalizedText = (text: string | { th: string; en: string } | undefined)
|
|||
}
|
||||
|
||||
// Data Handling
|
||||
const { fetchEnrolledCourses } = useCourse()
|
||||
const { fetchEnrolledCourses, getCertificate, generateCertificate } = useCourse()
|
||||
const enrolledCourses = ref<any[]>([])
|
||||
const isLoading = ref(false)
|
||||
const isDownloadingCert = ref(false)
|
||||
|
||||
const loadEnrolledCourses = async () => {
|
||||
isLoading.value = true
|
||||
|
|
@ -85,17 +85,40 @@ onMounted(() => {
|
|||
})
|
||||
|
||||
// Certificate Handling
|
||||
const selectedCertCourse = ref<any>(null)
|
||||
const downloadingCourseId = ref<number | null>(null)
|
||||
// Certificate Handling
|
||||
// Certificate Handling
|
||||
const downloadCertificate = async (course: any) => {
|
||||
if (!course) return
|
||||
downloadingCourseId.value = course.id
|
||||
|
||||
try {
|
||||
// 1. Try to GET existing certificate
|
||||
let res = await getCertificate(course.id)
|
||||
|
||||
const openCertModal = (course: any) => {
|
||||
selectedCertCourse.value = course
|
||||
showCertModal.value = true
|
||||
}
|
||||
// 2. If not found (or error), try to GENERATE new one
|
||||
if (!res.success) {
|
||||
res = await generateCertificate(course.id)
|
||||
}
|
||||
|
||||
// Mock certificate download action
|
||||
const downloadCertificate = () => {
|
||||
showCertModal.value = false
|
||||
alert(`Start downloading certificate for ${selectedCertCourse.value?.title || 'course'}...`)
|
||||
// 3. Handle Result
|
||||
if (res.success && res.data) {
|
||||
const cert = res.data
|
||||
if (cert.download_url) {
|
||||
window.open(cert.download_url, '_blank')
|
||||
} else {
|
||||
// Fallback if no URL but success (maybe show message)
|
||||
console.warn('Certificate ready but no URL')
|
||||
}
|
||||
} else {
|
||||
// Silent fail or minimal log, or maybe use a toast if available, but avoid $q if undefined
|
||||
console.error(res.error || 'Failed to get certificate')
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
downloadingCourseId.value = null
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
@ -148,7 +171,8 @@ const downloadCertificate = () => {
|
|||
:completed="true"
|
||||
show-certificate
|
||||
show-study-again
|
||||
@view-certificate="openCertModal(course)"
|
||||
:loading="downloadingCourseId === course.id"
|
||||
@view-certificate="downloadCertificate(course)"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
|
|
@ -190,50 +214,7 @@ const downloadCertificate = () => {
|
|||
</q-card>
|
||||
</q-dialog>
|
||||
|
||||
<!-- MODAL: Certificate Preview -->
|
||||
<q-dialog v-model="showCertModal" backdrop-filter="blur(8px)" full-width full-height>
|
||||
<div class="relative bg-white text-slate-900 w-full max-w-5xl aspect-[1.414/1] shadow-2xl rounded-sm p-12 flex flex-col items-center text-center overflow-hidden m-auto">
|
||||
<!-- Close Button -->
|
||||
<q-btn icon="close" flat round dense class="absolute top-4 right-4 text-slate-400" @click="showCertModal = false" />
|
||||
|
||||
<!-- Border Decoration -->
|
||||
<div class="absolute inset-4 border-4 border-double border-slate-200 pointer-events-none"></div>
|
||||
|
||||
<div class="relative z-10 w-full h-full flex flex-col justify-center">
|
||||
<div class="w-16 h-16 mx-auto mb-6 bg-blue-600 text-white flex items-center justify-center rounded-full font-serif font-bold text-3xl">E</div>
|
||||
|
||||
<h1 class="text-4xl font-serif font-bold mb-2 uppercase tracking-widest text-slate-800">{{ $t('certificate.title') }}</h1>
|
||||
<div class="w-32 h-1 bg-amber-400 mx-auto mb-8"></div>
|
||||
|
||||
<p class="text-slate-500 mb-4 text-lg italic font-serif">{{ $t('certificate.presentedTo') }}</p>
|
||||
|
||||
<h2 class="text-5xl font-serif font-bold text-blue-900 mb-6 font-handwriting">{{ currentUser?.firstName }} {{ currentUser?.lastName }}</h2>
|
||||
|
||||
<p class="text-slate-500 mb-6 text-lg italic font-serif">{{ $t('certificate.completedDesc') }}</p>
|
||||
|
||||
<h3 class="text-2xl font-bold text-slate-800 mb-12">{{ selectedCertCourse?.title || 'Course Title' }}</h3>
|
||||
|
||||
<div class="flex justify-end items-end px-12 mt-auto">
|
||||
<div class="text-center">
|
||||
<div class="w-48 border-b border-slate-800 mb-2 pb-2">{{ new Date().toLocaleDateString('th-TH', { year: 'numeric', month: '2-digit', day: '2-digit' }) }}</div>
|
||||
<div class="text-xs text-slate-500 uppercase tracking-wider">{{ $t('certificate.issueDate') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="absolute bottom-6 left-0 w-full text-center">
|
||||
<q-btn
|
||||
unelevated
|
||||
rounded
|
||||
color="primary"
|
||||
icon="download"
|
||||
:label="$t('certificate.downloadPDF')"
|
||||
class="font-bold px-6"
|
||||
@click="downloadCertificate"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</q-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue