Update Mohamed Salah's profile to display PDF content as slides

Integrate pdf.js to render PDF pages as individual slides for the Mohamed Salah report page, replacing static slide data.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 9a264234-c5d7-4dcc-adf3-a954b149b30d
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3df548ff-50ae-432f-9be4-25d34eccc983/9a264234-c5d7-4dcc-adf3-a954b149b30d/yjv3HO6
This commit is contained in:
kimjaehyeon0101
2025-10-14 06:40:58 +00:00
parent 745e41c6ce
commit 026f6ef055

View File

@ -7,12 +7,15 @@ import { Input } from "@/components/ui/input";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { useAuth } from "@/hooks/useAuth"; import { useAuth } from "@/hooks/useAuth";
import { useState } from "react"; import { useState, useEffect, useRef } from "react";
import type { MediaOutlet } from "@shared/schema"; import type { MediaOutlet } from "@shared/schema";
import Footer from "@/components/Footer"; import Footer from "@/components/Footer";
import SearchModal from "@/components/SearchModal"; import SearchModal from "@/components/SearchModal";
import * as pdfjsLib from 'pdfjs-dist';
const reportContent: Record<string, { htmlPath: string; pptPath?: string; customComponent?: boolean }> = { pdfjsLib.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjsLib.version}/pdf.worker.min.js`;
const reportContent: Record<string, { htmlPath: string; pdfPath?: string; pptPath?: string; customComponent?: boolean }> = {
'chayan-asli': { 'chayan-asli': {
htmlPath: '/attached_assets/chayan asli report_1759208054055.html', htmlPath: '/attached_assets/chayan asli report_1759208054055.html',
pptPath: '/attached_assets/chayan asli slides_1759213492580.pptx' pptPath: '/attached_assets/chayan asli slides_1759213492580.pptx'
@ -23,207 +26,77 @@ const reportContent: Record<string, { htmlPath: string; pptPath?: string; custom
}, },
'mohamed-salah': { 'mohamed-salah': {
htmlPath: '/attached_assets/Mohamed Salah_Report_en_1760420154846.html', htmlPath: '/attached_assets/Mohamed Salah_Report_en_1760420154846.html',
pdfPath: '/attached_assets/mohamed_salah_pdf_en_1760419721874.pdf',
customComponent: true customComponent: true
} }
}; };
function MohamedSalahSlides() { function MohamedSalahSlides() {
const [currentSlide, setCurrentSlide] = useState(0); const [currentSlide, setCurrentSlide] = useState(0);
const [numPages, setNumPages] = useState(0);
const [pdfDoc, setPdfDoc] = useState<any>(null);
const [loading, setLoading] = useState(true);
const canvasRef = useRef<HTMLCanvasElement>(null);
const slides = [ useEffect(() => {
{ const loadPdf = async () => {
title: "MOHAMED SALAH", try {
subtitle: "The Complete Biography: Life, Career, and Legacy of Egypt's Football King", const loadingTask = pdfjsLib.getDocument('/attached_assets/mohamed_salah_pdf_en_1760419721874.pdf');
content: null, const pdf = await loadingTask.promise;
bgColor: "bg-gradient-to-br from-red-600 to-red-800" setPdfDoc(pdf);
}, setNumPages(pdf.numPages);
{ setLoading(false);
title: "Early Life & Birth Details", } catch (error) {
content: ( console.error('Error loading PDF:', error);
<div className="space-y-4"> setLoading(false);
<div className="grid grid-cols-2 gap-6"> }
<div data-testid="info-born"> };
<p className="text-sm text-gray-500 mb-1" data-testid="label-born">Born</p> loadPdf();
<p className="text-lg font-semibold" data-testid="text-birth-date">June 15, 1992</p> }, []);
</div>
<div data-testid="info-birthplace"> useEffect(() => {
<p className="text-sm text-gray-500 mb-1" data-testid="label-birthplace">Birthplace</p> if (!pdfDoc || !canvasRef.current) return;
<p className="text-lg font-semibold" data-testid="text-birthplace">Nagrig, Basyoun, Egypt</p>
</div> const renderPage = async () => {
</div> const page = await pdfDoc.getPage(currentSlide + 1);
<div data-testid="info-fullname"> const canvas = canvasRef.current!;
<p className="text-sm text-gray-500 mb-1" data-testid="label-fullname">Full Name</p> const context = canvas.getContext('2d')!;
<p className="text-lg font-semibold" data-testid="text-fullname">Mohamed Salah Hamed Mahrous Ghaly</p>
</div> const containerWidth = canvas.parentElement?.clientWidth || 800;
<p className="text-gray-700 mt-4" data-testid="text-early-life-description"> const viewport = page.getViewport({ scale: 1 });
Born to a middle-class Egyptian family in a small farming village north of Cairo, Mohamed Salah grew up in modest surroundings that gave no indication of the football stardom that awaited him. const scale = containerWidth / viewport.width;
</p> const scaledViewport = page.getViewport({ scale });
</div>
) canvas.width = scaledViewport.width;
}, canvas.height = scaledViewport.height;
{
title: "Education & Early Football Training", await page.render({
content: ( canvasContext: context,
<div className="space-y-4"> viewport: scaledViewport
<div className="bg-yellow-50 p-4 rounded-lg" data-testid="section-extraordinary-commute"> }).promise;
<h3 className="font-semibold mb-2" data-testid="heading-commute">Extraordinary Commute</h3> };
<p className="text-gray-700 mb-3" data-testid="text-commute-description">
At age 14, Salah made a 4.5-hour journey (each way) five days a week from his village to train with El Mokawloon in Cairo. renderPage();
</p> }, [pdfDoc, currentSlide]);
<blockquote className="italic border-l-4 border-yellow-400 pl-3 text-gray-600 text-sm" data-testid="quote-school-miss">
"I missed quite a lot of school because it was the only way that I could get to training on time..." if (loading) {
</blockquote> return (
</div> <div className="flex items-center justify-center h-96">
</div> <p data-testid="text-loading-slides">Loading slides...</p>
) </div>
}, );
{ }
title: "European Career Progression",
content: (
<div className="space-y-3">
<div className="border-l-4 border-blue-500 pl-4" data-testid="career-basel">
<h3 className="font-semibold" data-testid="heading-basel">FC Basel (2012-2014)</h3>
<p className="text-sm text-gray-600" data-testid="text-basel-stats">79 Games | 20 Goals | Swiss Golden Player award</p>
</div>
<div className="border-l-4 border-blue-700 pl-4" data-testid="career-chelsea">
<h3 className="font-semibold" data-testid="heading-chelsea">Chelsea FC (2014-2015)</h3>
<p className="text-sm text-gray-600" data-testid="text-chelsea-stats">19 Games | 2 Goals | Limited opportunities</p>
</div>
<div className="border-l-4 border-purple-600 pl-4" data-testid="career-roma">
<h3 className="font-semibold" data-testid="heading-roma">AS Roma (2015-2017)</h3>
<p className="text-sm text-gray-600" data-testid="text-roma-stats">83 Games | 34 Goals | Roma Player of the Year 2016</p>
</div>
</div>
)
},
{
title: "Liverpool Achievements",
content: (
<div className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div className="bg-red-50 p-4 rounded-lg text-center" data-testid="stat-liverpool-goals">
<div className="text-3xl font-bold text-red-600" data-testid="text-liverpool-goals-number">180+</div>
<div className="text-sm text-gray-600" data-testid="text-liverpool-goals-label">Liverpool PL era goals</div>
</div>
<div className="bg-red-50 p-4 rounded-lg text-center" data-testid="stat-season-record">
<div className="text-3xl font-bold text-red-600" data-testid="text-season-record-number">32</div>
<div className="text-sm text-gray-600" data-testid="text-season-record-label">Goals in single PL season (2017-18 record)</div>
</div>
</div>
<div className="mt-4">
<h3 className="font-semibold mb-2" data-testid="heading-major-honours">Major Honours</h3>
<ul className="list-disc list-inside text-sm space-y-1 text-gray-700">
<li data-testid="honour-champions-league">UEFA Champions League winner (2019)</li>
<li data-testid="honour-premier-league">Premier League champion (2019-20)</li>
<li data-testid="honour-golden-boot">3× Premier League Golden Boot (2017-18, 2018-19, 2021-22)</li>
<li data-testid="honour-foreign-scorer">All-time leading foreign goalscorer in Premier League history</li>
</ul>
</div>
</div>
)
},
{
title: "Business Ventures & Investments",
content: (
<div className="space-y-4">
<div className="bg-green-50 p-4 rounded-lg" data-testid="section-investment-portfolio">
<h3 className="font-semibold mb-2" data-testid="heading-investments">Investment Portfolio</h3>
<ul className="space-y-2 text-sm">
<li data-testid="investment-mos-real-estate"><strong>MOS Real Estate Ltd</strong> - UK-based property development</li>
<li data-testid="investment-salah-commercial"><strong>Salah UK Commercial</strong> - £25 million in assets (2024)</li>
<li data-testid="investment-dubai-property"><strong>Dubai Property</strong> - Luxury real estate near Burj Khalifa</li>
</ul>
</div>
<div className="text-center" data-testid="section-net-worth">
<div className="text-3xl font-bold text-green-600" data-testid="text-net-worth-value">$90M</div>
<div className="text-sm text-gray-600" data-testid="text-net-worth-label">Estimated Net Worth (2025)</div>
</div>
</div>
)
},
{
title: "Personal Life & Family",
content: (
<div className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div data-testid="info-wife">
<p className="text-sm text-gray-500 mb-1" data-testid="label-wife">Wife</p>
<p className="font-semibold" data-testid="text-wife-name">Magi Sadeq (married 2013)</p>
<p className="text-sm text-gray-600" data-testid="text-wife-note">Childhood sweetheart</p>
</div>
<div data-testid="info-children">
<p className="text-sm text-gray-500 mb-1" data-testid="label-children">Children</p>
<p className="font-semibold" data-testid="text-children-names">Makka (2014) & Kayan (2020)</p>
</div>
</div>
<div className="bg-blue-50 p-4 rounded-lg" data-testid="section-family-values">
<p className="text-sm text-gray-700" data-testid="text-family-description">
Despite global fame, Salah prioritizes family life. The family resides in a modest Liverpool suburb, emphasizing simplicity over ostentation.
</p>
</div>
</div>
)
},
{
title: "Social Impact & Charity Work",
content: (
<div className="space-y-4">
<div className="bg-purple-50 p-4 rounded-lg" data-testid="section-charity">
<h3 className="font-semibold mb-2" data-testid="heading-charity">Major Charitable Initiatives</h3>
<p className="text-sm text-gray-700 mb-2" data-testid="text-charity-description">
Salah has funded numerous projects in his hometown of Nagrig, including schools, youth centers, and medical equipment.
</p>
<div className="text-2xl font-bold text-purple-600 text-center" data-testid="text-donation-amount">$3M+</div>
<p className="text-sm text-gray-600 text-center" data-testid="text-donation-target">
Donated to National Cancer Institute in Cairo
</p>
</div>
</div>
)
},
{
title: "Legacy & The Salah Effect",
content: (
<div className="space-y-4">
<div className="bg-gradient-to-r from-yellow-50 to-orange-50 p-4 rounded-lg" data-testid="section-salah-effect">
<h3 className="font-semibold mb-2" data-testid="heading-salah-effect">The Salah Effect</h3>
<p className="text-sm text-gray-700" data-testid="text-salah-effect-description">
Research from Stanford University documented how his prominence at Liverpool led to a 16% decrease in hate crimes and Islamophobia in Merseyside.
</p>
</div>
<div className="space-y-2 text-sm" data-testid="section-legacy-achievements">
<div className="flex items-start space-x-2" data-testid="legacy-item-1">
<span className="font-bold text-blue-600" data-testid="legacy-number-1">1.</span>
<p data-testid="text-legacy-1">First Egyptian to win the Premier League and Champions League</p>
</div>
<div className="flex items-start space-x-2" data-testid="legacy-item-2">
<span className="font-bold text-blue-600" data-testid="legacy-number-2">2.</span>
<p data-testid="text-legacy-2">Major investments in healthcare, education and infrastructure in Egypt</p>
</div>
</div>
</div>
)
}
];
return ( return (
<div className="space-y-6"> <div className="space-y-6">
<div className="w-full relative overflow-hidden" style={{ paddingTop: '56.25%' }} data-testid="slide-container-16-9"> <div className="w-full relative overflow-hidden" style={{ paddingTop: '56.25%' }} data-testid="slide-container-16-9">
<Card className={`${slides[currentSlide].bgColor || 'bg-white'} absolute inset-0`}> <Card className="absolute inset-0 bg-white">
<CardContent className={`p-6 h-full ${slides[currentSlide].bgColor ? 'text-white' : 'text-gray-900'} overflow-auto`}> <CardContent className="p-0 h-full w-full flex items-center justify-center">
<div className="h-full flex flex-col justify-center"> <canvas
<h2 className="text-2xl font-bold mb-2" data-testid="text-slide-title"> ref={canvasRef}
{slides[currentSlide].title} className="max-w-full max-h-full object-contain"
</h2> data-testid="canvas-pdf-slide"
{slides[currentSlide].subtitle && ( />
<p className="text-base opacity-90 mb-3">{slides[currentSlide].subtitle}</p>
)}
{slides[currentSlide].content && (
<div className={slides[currentSlide].bgColor ? 'bg-white/10 backdrop-blur p-3 rounded-lg text-sm' : 'text-sm'}>
{slides[currentSlide].content}
</div>
)}
</div>
</CardContent> </CardContent>
</Card> </Card>
</div> </div>
@ -233,17 +106,17 @@ function MohamedSalahSlides() {
variant="outline" variant="outline"
onClick={() => setCurrentSlide(Math.max(0, currentSlide - 1))} onClick={() => setCurrentSlide(Math.max(0, currentSlide - 1))}
disabled={currentSlide === 0} disabled={currentSlide === 0}
data-testid="button-prev-slide" data-testid="button-previous-slide"
> >
Previous Previous
</Button> </Button>
<div className="text-sm text-gray-600"> <span className="text-sm text-gray-600" data-testid="text-slide-counter">
Slide {currentSlide + 1} of {slides.length} Slide {currentSlide + 1} of {numPages}
</div> </span>
<Button <Button
variant="outline" variant="outline"
onClick={() => setCurrentSlide(Math.min(slides.length - 1, currentSlide + 1))} onClick={() => setCurrentSlide(Math.min(numPages - 1, currentSlide + 1))}
disabled={currentSlide === slides.length - 1} disabled={currentSlide === numPages - 1}
data-testid="button-next-slide" data-testid="button-next-slide"
> >
Next Next