|
| 1 | +import { createServerSupabaseClient } from '@/lib/database/supabase-server' |
| 2 | +import { notFound } from 'next/navigation' |
| 3 | +import { AnalyticsTracker } from '@/components/resume/AnalyticsTracker' |
| 4 | +import { ResumeData } from '@/lib/types/resume' |
| 5 | +import { Metadata } from 'next' |
| 6 | +import { ResumeA4 } from '@/components/resume/ResumeA4' |
| 7 | + |
| 8 | +export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise<Metadata> { |
| 9 | + const { id } = await params |
| 10 | + const supabase = await createServerSupabaseClient() |
| 11 | + |
| 12 | + const { data: resume } = await supabase |
| 13 | + .from('resumes') |
| 14 | + .select('title, personal_info') |
| 15 | + .filter('id', 'eq', id) |
| 16 | + .single() |
| 17 | + |
| 18 | + if (!resume) return { title: 'Not Found' } |
| 19 | + |
| 20 | + return { |
| 21 | + title: `${resume.personal_info?.fullName || 'Portfolio'} | ${resume.title}`, |
| 22 | + description: 'A professional portfolio created with Lab68Dev Platform', |
| 23 | + } |
| 24 | +} |
| 25 | + |
| 26 | +export default async function PublicPortfolioPage({ params }: { params: Promise<{ id: string }> }) { |
| 27 | + const { id } = await params |
| 28 | + const supabase = await createServerSupabaseClient() |
| 29 | + |
| 30 | + const { data: resume, error } = await supabase |
| 31 | + .from('resumes') |
| 32 | + .select('*') |
| 33 | + .filter('id', 'eq', id) |
| 34 | + .single() |
| 35 | + |
| 36 | + if (!resume || error) { |
| 37 | + return notFound() |
| 38 | + } |
| 39 | + |
| 40 | + const resumeData: ResumeData = resume.data |
| 41 | + |
| 42 | + return ( |
| 43 | + <div className="min-h-screen bg-neutral-100 flex justify-center py-12 print:py-0 print:bg-white"> |
| 44 | + <AnalyticsTracker resumeId={id} /> |
| 45 | + |
| 46 | + {/* We reuse ResumeA4 but give it dummy handlers to make it read-only. |
| 47 | + The print CSS handles hiding non-public things if needed. |
| 48 | + We also wrap it in a pointer-events-none layer for areas we don't want edited. |
| 49 | + For a true read-only, we should ideally disable inline editing. |
| 50 | + But pointer-events-none works for a quick static preview renderer! |
| 51 | + */} |
| 52 | + <div className="pointer-events-none shadow-2xl print:shadow-none bg-white"> |
| 53 | + <ResumeA4 |
| 54 | + editorRef={{ current: null } as any} |
| 55 | + resumeData={resumeData} |
| 56 | + setResumeData={() => {}} |
| 57 | + selectedTemplate={resume.template || 'modern'} |
| 58 | + zoomLevel={1} |
| 59 | + showPhotoUpload={false} |
| 60 | + handlePhotoUpload={() => {}} |
| 61 | + toggleSectionVisibility={() => {}} |
| 62 | + handleDragStart={() => {}} |
| 63 | + handleDragOver={() => {}} |
| 64 | + handleDragEnd={() => {}} |
| 65 | + updateExperience={() => {}} |
| 66 | + removeExperience={() => {}} |
| 67 | + addExperience={() => {}} |
| 68 | + updateEducation={() => {}} |
| 69 | + removeEducation={() => {}} |
| 70 | + addEducation={() => {}} |
| 71 | + updateSkill={() => {}} |
| 72 | + removeSkill={() => {}} |
| 73 | + addSkill={() => {}} |
| 74 | + updateCertification={() => {}} |
| 75 | + removeCertification={() => {}} |
| 76 | + addCertification={() => {}} |
| 77 | + updateProject={() => {}} |
| 78 | + removeProject={() => {}} |
| 79 | + addProject={() => {}} |
| 80 | + getFontSizeClass={() => resumeData.styleSettings.fontSize === 'small' ? 'text-xs' : resumeData.styleSettings.fontSize === 'large' ? 'text-base' : 'text-sm'} |
| 81 | + /> |
| 82 | + </div> |
| 83 | + </div> |
| 84 | + ) |
| 85 | +} |
0 commit comments