lms/resources/js/pages/exams/show.tsx
2025-12-15 12:26:23 +01:00

168 lines
6.7 KiB
TypeScript

import Tabs from '@/components/tabs';
import { Separator } from '@/components/ui/separator';
import { TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import LandingLayout from '@/layouts/landing-layout';
import { systemCurrency } from '@/lib/utils';
import { ExamPreviewProps } from '@/types/page';
import { Head, Link } from '@inertiajs/react';
import { ReactNode } from 'react';
import Details from './partials/details';
import ExamHeader from './partials/exam-header';
import CoursePreview from './partials/exam-preview';
import Instructor from './partials/instructor';
import Overview from './partials/overview';
import CourseReviews from './partials/reviews';
const Show = ({ tab, exam, system, translate }: ExamPreviewProps) => {
const { button } = translate;
const tabs = [
{
value: 'overview',
label: button.overview,
Component: <Overview />,
},
{
value: 'details',
label: button.details,
Component: <Details />,
},
{
value: 'instructor',
label: button.instructor,
Component: <Instructor />,
},
{
value: 'reviews',
label: button.reviews,
Component: <CourseReviews />,
},
];
// Generate meta information for the exam
const pageTitle = exam.meta_title || `${exam.title} | ${system.fields?.name}`;
const pageDescription = exam.meta_description || exam.short_description || exam.description || 'Professional certification exam';
const pageKeywords = exam.meta_keywords || `${exam.title}, certification exam, professional test, ${system.fields?.keywords || 'LMS'}`;
const ogTitle = exam.og_title || exam.title;
const ogDescription = exam.og_description || pageDescription;
const examImage = exam.thumbnail || exam.banner || '';
const siteName = system.fields?.name;
const siteUrl = typeof window !== 'undefined' ? window.location.href : '';
const currency = systemCurrency(system.fields['selling_currency']);
return (
<>
<Head>
<title>{pageTitle}</title>
<meta name="description" content={pageDescription} />
<meta name="keywords" content={pageKeywords} />
<meta name="author" content={system.fields?.author || 'UiLib'} />
{/* Open Graph Tags */}
<meta property="og:type" content="article" />
<meta property="og:url" content={siteUrl} />
<meta property="og:title" content={ogTitle} />
<meta property="og:description" content={ogDescription} />
<meta property="og:site_name" content={siteName} />
{/* Open Graph Image */}
<meta property="og:image" content={examImage} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content={exam.title} />
{/* Twitter Card Tags */}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={ogTitle} />
<meta name="twitter:description" content={ogDescription} />
{examImage && <meta name="twitter:image" content={examImage} />}
{/* Exam-specific meta */}
<meta name="exam:title" content={exam.title} />
<meta name="exam:level" content={exam.level || ''} />
<meta name="exam:price" content={exam.price?.toString() || '0'} />
<meta name="exam:pricing_type" content={exam.pricing_type} />
{exam.instructor && <meta name="exam:instructor" content={exam.instructor.user?.name || ''} />}
{/* Schema.org structured data */}
<script type="application/ld+json">
{JSON.stringify({
'@context': 'https://schema.org',
'@type': 'ExaminationTest',
name: exam.title,
description: pageDescription,
image: examImage,
provider: {
'@type': 'Organization',
name: siteName,
url: typeof window !== 'undefined' ? window.location.origin : '',
},
instructor: exam.instructor
? {
'@type': 'Person',
name: exam.instructor.user?.name || '',
}
: undefined,
educationalLevel: exam.level,
offers:
exam.pricing_type === 'paid'
? {
'@type': 'Offer',
price: exam.price || 0,
priceCurrency: 'USD',
availability: 'https://schema.org/InStock',
}
: {
'@type': 'Offer',
price: 0,
priceCurrency: 'USD',
availability: 'https://schema.org/InStock',
},
aggregateRating: {
'@type': 'AggregateRating',
ratingValue: exam.average_rating,
},
})}
</script>
</Head>
<div className="container grid grid-cols-1 gap-7 py-10 md:grid-cols-3">
<div className="space-y-8 md:col-span-2">
<ExamHeader />
<Tabs value={tab} className="bg-card overflow-hidden rounded-md border shadow">
<div className="overflow-x-auto overflow-y-hidden">
<TabsList className="vertical-tabs-list">
{tabs.map(({ label, value }) => (
<Link key={value} href={route('exams.details', { slug: exam.slug, id: exam.id, tab: value })}>
<TabsTrigger key={value} value={value} className="vertical-tabs-trigger">
<span>{label}</span>
</TabsTrigger>
</Link>
))}
</TabsList>
</div>
<Separator className="mt-[1px]" />
{tabs.map(({ value, Component }) => (
<TabsContent key={value} value={value} className="m-0 p-5">
{Component}
</TabsContent>
))}
</Tabs>
</div>
<div>
<CoursePreview />
</div>
</div>
</>
);
};
Show.layout = (page: ReactNode) => <LandingLayout children={page} customizable={false} />;
export default Show;