mirror of
https://github.com/th30d4y/OpenLearnX.git
synced 2026-05-26 19:26:33 +00:00
dashboard & cource certificate Block chain
This commit is contained in:
@@ -10,18 +10,31 @@ interface Certificate {
|
||||
share_code: string
|
||||
user_name: string
|
||||
student_name: string
|
||||
studentName?: string // camelCase variant
|
||||
userName?: string // camelCase variant
|
||||
course_title: string
|
||||
courseTitle?: string // camelCase variant
|
||||
mentor_name: string
|
||||
instructor_name: string
|
||||
instructorName?: string // camelCase variant
|
||||
mentorName?: string // camelCase variant
|
||||
completion_date: string
|
||||
completionDate?: string // camelCase variant
|
||||
wallet_address?: string
|
||||
walletAddress?: string // camelCase variant
|
||||
issued_by: string
|
||||
issuedBy?: string // camelCase variant
|
||||
view_count: number
|
||||
viewCount?: number // camelCase variant
|
||||
blockchain_hash?: string
|
||||
blockchainHash?: string // camelCase variant
|
||||
public_url?: string
|
||||
publicUrl?: string // camelCase variant
|
||||
verification_url?: string
|
||||
is_verified: boolean
|
||||
isVerified?: boolean // camelCase variant
|
||||
is_revoked: boolean
|
||||
isRevoked?: boolean // camelCase variant
|
||||
}
|
||||
|
||||
export default function CertificatePage() {
|
||||
@@ -55,8 +68,23 @@ export default function CertificatePage() {
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
console.log('🔍 Verify endpoint response:', data)
|
||||
|
||||
if (data.success && data.verified) {
|
||||
console.log('✅ Found certificate by verify endpoint')
|
||||
console.log('🎓 Student name fields:', {
|
||||
student_name: data.certificate?.student_name,
|
||||
user_name: data.certificate?.user_name,
|
||||
studentName: data.certificate?.studentName,
|
||||
userName: data.certificate?.userName
|
||||
})
|
||||
console.log('👨🏫 Instructor name fields:', {
|
||||
instructor_name: data.certificate?.instructor_name,
|
||||
mentor_name: data.certificate?.mentor_name,
|
||||
instructorName: data.certificate?.instructorName,
|
||||
mentorName: data.certificate?.mentorName
|
||||
})
|
||||
|
||||
setCertificate(data.certificate)
|
||||
setLoading(false)
|
||||
return
|
||||
@@ -73,8 +101,23 @@ export default function CertificatePage() {
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
console.log('🔍 Direct endpoint response:', data)
|
||||
|
||||
if (data.success) {
|
||||
console.log('✅ Found certificate by direct endpoint')
|
||||
console.log('🎓 Student name fields:', {
|
||||
student_name: data.certificate?.student_name,
|
||||
user_name: data.certificate?.user_name,
|
||||
studentName: data.certificate?.studentName,
|
||||
userName: data.certificate?.userName
|
||||
})
|
||||
console.log('👨🏫 Instructor name fields:', {
|
||||
instructor_name: data.certificate?.instructor_name,
|
||||
mentor_name: data.certificate?.mentor_name,
|
||||
instructorName: data.certificate?.instructorName,
|
||||
mentorName: data.certificate?.mentorName
|
||||
})
|
||||
|
||||
setCertificate(data.certificate)
|
||||
setLoading(false)
|
||||
return
|
||||
@@ -95,15 +138,62 @@ export default function CertificatePage() {
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ FIXED: Helper functions to get names with multiple fallbacks
|
||||
const getStudentName = () => {
|
||||
if (!certificate) return 'Student Name Missing'
|
||||
|
||||
const name = certificate.student_name ||
|
||||
certificate.user_name ||
|
||||
certificate.studentName ||
|
||||
certificate.userName ||
|
||||
'Student Name Missing'
|
||||
|
||||
console.log('🎓 Final student name:', name)
|
||||
return name
|
||||
}
|
||||
|
||||
const getInstructorName = () => {
|
||||
if (!certificate) return 'OpenLearnX Instructor'
|
||||
|
||||
const name = certificate.instructor_name ||
|
||||
certificate.mentor_name ||
|
||||
certificate.instructorName ||
|
||||
certificate.mentorName ||
|
||||
'OpenLearnX Instructor'
|
||||
|
||||
console.log('👨🏫 Final instructor name:', name)
|
||||
return name
|
||||
}
|
||||
|
||||
const getCourseTitle = () => {
|
||||
if (!certificate) return 'Course Title Missing'
|
||||
|
||||
return certificate.course_title ||
|
||||
certificate.courseTitle ||
|
||||
'Course Title Missing'
|
||||
}
|
||||
|
||||
const getCompletionDate = () => {
|
||||
if (!certificate) return new Date()
|
||||
|
||||
const dateStr = certificate.completion_date || certificate.completionDate || new Date().toISOString()
|
||||
return new Date(dateStr)
|
||||
}
|
||||
|
||||
const handleDownloadPDF = () => {
|
||||
if (!certificate) return
|
||||
|
||||
try {
|
||||
const studentName = getStudentName()
|
||||
const instructorName = getInstructorName()
|
||||
const courseTitle = getCourseTitle()
|
||||
const completionDate = getCompletionDate()
|
||||
|
||||
const certificateHTML = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Certificate - ${certificate.user_name}</title>
|
||||
<title>Certificate - ${studentName}</title>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;700&family=Inter:wght@400;500;600&display=swap');
|
||||
@@ -192,13 +282,13 @@ export default function CertificatePage() {
|
||||
|
||||
<div style="font-size: 18px; color: #6b7280; margin-bottom: 30px;">This is to certify that</div>
|
||||
|
||||
<div class="student-name">${certificate.user_name}</div>
|
||||
<div class="student-name">${studentName}</div>
|
||||
|
||||
<div style="font-size: 18px; color: #6b7280; margin-bottom: 20px;">has successfully completed the course</div>
|
||||
<div class="course-title">"${certificate.course_title}"</div>
|
||||
<div class="course-title">"${courseTitle}"</div>
|
||||
|
||||
<div style="font-size: 16px; color: #374151; margin: 20px 0;">
|
||||
✅ Completed on: ${new Date(certificate.completion_date).toLocaleDateString('en-US', {
|
||||
✅ Completed on: ${completionDate.toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
@@ -207,7 +297,7 @@ export default function CertificatePage() {
|
||||
|
||||
<div class="mentor-section">
|
||||
<div style="width: 200px; height: 2px; background: #6b7280; margin: 0 auto 10px auto;"></div>
|
||||
<div class="mentor-name">${certificate.mentor_name}</div>
|
||||
<div class="mentor-name">${instructorName}</div>
|
||||
<div style="font-size: 14px; color: #6b7280; margin-top: 5px;">Course Instructor</div>
|
||||
</div>
|
||||
|
||||
@@ -247,13 +337,16 @@ export default function CertificatePage() {
|
||||
const handleShare = async () => {
|
||||
if (!certificate) return
|
||||
|
||||
const shareText = `🎓 Check out my certificate of completion for "${certificate.course_title}" from OpenLearnX!\n\nStudent: ${certificate.user_name}\nCertificate ID: ${certificate.certificate_id}\n\n#OpenLearnX #Certificate #Learning`
|
||||
const studentName = getStudentName()
|
||||
const courseTitle = getCourseTitle()
|
||||
|
||||
const shareText = `🎓 Check out my certificate of completion for "${courseTitle}" from OpenLearnX!\n\nStudent: ${studentName}\nCertificate ID: ${certificate.certificate_id}\n\n#OpenLearnX #Certificate #Learning`
|
||||
const shareUrl = window.location.href
|
||||
|
||||
if (navigator.share) {
|
||||
try {
|
||||
await navigator.share({
|
||||
title: `Certificate - ${certificate.course_title}`,
|
||||
title: `Certificate - ${courseTitle}`,
|
||||
text: shareText,
|
||||
url: shareUrl
|
||||
})
|
||||
@@ -329,6 +422,12 @@ export default function CertificatePage() {
|
||||
)
|
||||
}
|
||||
|
||||
// ✅ Get the final names to display
|
||||
const studentName = getStudentName()
|
||||
const instructorName = getInstructorName()
|
||||
const courseTitle = getCourseTitle()
|
||||
const completionDate = getCompletionDate()
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-purple-50 to-indigo-100 py-12 px-4">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
@@ -359,15 +458,18 @@ export default function CertificatePage() {
|
||||
<p className="text-xl text-gray-600 mb-8">This is to certify that</p>
|
||||
|
||||
<div className="mb-8">
|
||||
{/* ✅ FIXED: Using helper function for guaranteed name display */}
|
||||
<div className="text-6xl font-bold text-gray-900 mb-4 border-t-4 border-b-4 border-indigo-300 py-6 capitalize font-serif">
|
||||
{certificate.user_name}
|
||||
{studentName}
|
||||
</div>
|
||||
<p className="text-sm text-gray-500">Student</p>
|
||||
</div>
|
||||
|
||||
<p className="text-xl text-gray-600 mb-4">has successfully completed the course</p>
|
||||
|
||||
{/* ✅ FIXED: Using helper function for course title */}
|
||||
<h3 className="text-3xl font-semibold text-gray-900 mb-8 italic font-serif">
|
||||
"{certificate.course_title}"
|
||||
"{courseTitle}"
|
||||
</h3>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 my-8 p-6 bg-indigo-50 rounded-xl">
|
||||
@@ -375,7 +477,7 @@ export default function CertificatePage() {
|
||||
<Calendar className="w-8 h-8 text-indigo-600 mx-auto mb-2" />
|
||||
<p className="text-sm text-gray-600">Completion Date</p>
|
||||
<p className="font-semibold text-gray-900">
|
||||
{new Date(certificate.completion_date).toLocaleDateString('en-US', {
|
||||
{completionDate.toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
@@ -394,7 +496,9 @@ export default function CertificatePage() {
|
||||
<div className="text-center">
|
||||
<User className="w-8 h-8 text-indigo-600 mx-auto mb-2" />
|
||||
<p className="text-sm text-gray-600">Views</p>
|
||||
<p className="font-semibold text-gray-900">{certificate.view_count}</p>
|
||||
<p className="font-semibold text-gray-900">
|
||||
{certificate.view_count || certificate.viewCount || 0}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -402,8 +506,9 @@ export default function CertificatePage() {
|
||||
<div className="flex justify-center">
|
||||
<div className="text-center">
|
||||
<div className="w-48 h-0.5 bg-gray-400 mb-3 mx-auto"></div>
|
||||
{/* ✅ FIXED: Using helper function for instructor name */}
|
||||
<p className="text-xl font-semibold text-gray-700">
|
||||
{certificate.mentor_name}
|
||||
{instructorName}
|
||||
</p>
|
||||
<p className="text-sm text-gray-500">Course Instructor</p>
|
||||
</div>
|
||||
@@ -412,13 +517,13 @@ export default function CertificatePage() {
|
||||
|
||||
<div className="mt-8 pt-6 border-t border-gray-200">
|
||||
<p className="text-sm text-gray-500">
|
||||
<strong>{certificate.issued_by}</strong><br/>
|
||||
<strong>{certificate.issued_by || certificate.issuedBy || 'OpenLearnX'}</strong><br/>
|
||||
Digital Certificate of Achievement<br/>
|
||||
<span className="text-purple-600">🔒 Blockchain Verified</span>
|
||||
</p>
|
||||
{certificate.blockchain_hash && (
|
||||
{(certificate.blockchain_hash || certificate.blockchainHash) && (
|
||||
<p className="text-xs text-gray-400 mt-2 font-mono break-all">
|
||||
Blockchain Hash: {certificate.blockchain_hash}
|
||||
Blockchain Hash: {certificate.blockchain_hash || certificate.blockchainHash}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
@@ -447,6 +552,20 @@ export default function CertificatePage() {
|
||||
<p>This certificate can be verified at any time using the certificate ID above.</p>
|
||||
<p className="mt-2">Powered by OpenLearnX • Secured by Blockchain Technology</p>
|
||||
</div>
|
||||
|
||||
{/* ✅ DEBUG: Show raw certificate data in development */}
|
||||
{process.env.NODE_ENV === 'development' && (
|
||||
<div className="mt-8 p-4 bg-gray-100 rounded-lg">
|
||||
<details>
|
||||
<summary className="text-sm font-medium text-gray-700 cursor-pointer">
|
||||
Debug: Raw Certificate Data
|
||||
</summary>
|
||||
<pre className="mt-2 text-xs text-gray-600 whitespace-pre-wrap">
|
||||
{JSON.stringify(certificate, null, 2)}
|
||||
</pre>
|
||||
</details>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user