// --- DATA ---
const detailedPrograms = {
"BS Programs": {
"Faculty of Arts": ["BS Economics", "BS English (Applied Linguistics)", "BS Islamic Studies", "BS Mass Communication", "BS Psychology", "BS Sociology"],
"Faculty of Computer Science and Information Technology": ["BS Computer Science", "BS Data Science", "BS Information Technology", "BS Software Engineering"],
"Faculty of Management": ["BS Accounting & Finance", "BS Bachelor of Business & Information Technology (BBIT)", "BS Business Administration", "BS Commerce", "BS Public Administration"],
"Faculty of Science & Technology": ["BS Bioinformatics", "BS Biotechnology", "BS Mathematics", "BS Statistics", "BS Zoology"]
},
"BS-Lateral Programs (BS 5th)": {
"Faculty of Management": ["Accounting & Finance", "Bachelor of Business & Information Technology (BBIT)", "Business Administration", "Commerce", "Public Administration"],
"Faculty of Education": ["B.Ed. (Hons) Elementary", "B.Ed. (Hons.) Early Childhood Care and Education"],
"Faculty of Science & Technology": ["Bioinformatics", "Biotechnology", "Mathematics", "Zoology"],
"Faculty of Computer Science and Information Technology": ["Computer Science", "Information Technology", "Software Engineering"],
"Faculty of Arts": ["Economics", "English (Applied Linguistics)", "Mass Communication", "Psychology", "Sociology"]
},
"Associate Degree Programs": {
"Faculty of Management": ["ADP in Accounting & Finance", "ADP in Business Administration", "ADP in Business Analytics", "ADP in Commerce", "ADP in Human Resource Management", "ADP in Islamic Banking", "ADP in Operations Management", "ADP in Public Administration", "ADP in Sales and Marketing", "ADP in Supply Chain Management"],
"Faculty of Arts": ["ADP in Arts", "ADP in Economics", "ADP in English (Applied Linguistics)", "ADP in Mass Communication", "ADP in Psychology", "ADP in Sociology"],
"Faculty of Science & Technology": ["ADP in Biotechnology", "ADP in Mathematics", "ADP in Science", "ADP in Statistics", "ADP in Zoology"],
"Faculty of Computer Science and Information Technology": ["ADP in Computer Networking", "ADP in Computer Science", "ADP in Data Science", "ADP in Database Management System", "ADP in Web Design and Development"],
"Faculty of Education": ["ADP in Early Childhood Care and Education", "ADP in Education"]
},
"B.Ed Programs": {
"Faculty of Education": [ "BS B.Ed. (Hons) Elementary", "BS B.Ed. (Hons.) Early Childhood Care and Education", "B.Ed. Elementary (2.5-Year)", "B.Ed. Secondary (1.5-Year Program)" ]
},
"MS/M.Phil Programs": {
"Faculty of Computer Science and Information Technology": [ "MS in Computer Science" ],
"Faculty of Management": [ "Master of Business Administration (Equivalent to MS)" ]
},
"Diploma Programs": {
"Faculty of Arts": [ "Diploma in Applied Psychology", "Diploma in English Language Teaching", "Diploma in Linguistics", "Diploma in Television Production" ],
"Faculty of Computer Science and Information Technology": [ "Diploma in Computer Science", "Diploma in Information Technology" ],
"Faculty of Management": [ "Diploma in Accounting", "Diploma in Accounting & Finance", "Diploma in Banking & Finance", "Diploma in Business Administration", "Diploma in Entrepreneurship & SME Management", "Diploma in Finance", "Diploma in Human Resource Management", "Diploma in Marketing Management", "Diploma in Public Administration" ],
"Faculty of Science & Technology": [ "Diploma in Bioinformatics", "Diploma in Molecular Biology" ]
},
"Specialization Programs": {
"Faculty of Management": [ "Specialization in Banking", "Specialization in Finance", "Specialization in Human Resource Management", "Specialization in Management", "Specialization in Marketing" ]
},
"Zero Semester": {},
"Short Courses": {},
};
// --- UTILITY FUNCTIONS ---
// Gemini API Caller
const callGeminiAPI = async (prompt) => {
const apiKey = ""; // Leave empty, will be handled by the environment
const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key=${apiKey}`;
const payload = {
contents: [{ role: "user", parts: [{ text: prompt }] }]
};
try {
const response = await fetch(apiUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (!response.ok) {
throw new Error(`API call failed with status: ${response.status}`);
}
const result = await response.json();
if (result.candidates && result.candidates.length > 0 && result.candidates[0].content.parts.length > 0) {
return result.candidates[0].content.parts[0].text;
}
return "Sorry, I couldn't get a response. Please try again.";
} catch (error) {
console.error("Gemini API call error:", error);
return "An error occurred. Please check your network connection or the console for details.";
}
};
// --- UI COMPONENTS ---
// Custom WhatsApp Icon Component
const WhatsAppIcon = (props) => (
<svg aria-label="WhatsApp" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" {...props}>
<path fill="currentColor" d="M12.04 2.01A10.03 10.03 0 0 0 2 12.05a10.03 10.03 0 0 0 10.04 10.03c2.4 0 4.75-1.03 6.4-2.5l.1-.07.07-.1.12-.16.18-.2.15-.15.06-.07.05-.05c.02-.02.03-.03.05-.05.07-.08.13-.15.2-.23a10.03 10.03 0 0 0 2.01-6.17A10.03 10.03 0 0 0 12.04 2.01zM17.6 15.95c-.22.63-1.22 1.16-1.68 1.22-.46.06-1.02.06-1.57-.18-.55-.24-1.13-.52-1.68-.85-.6-.35-1.14-.78-1.6-1.2-.5-.45-.9-1-1.2-1.57-.3-.58-.48-1.2-.48-1.84 0-.65.18-1.28.5-1.8.3-.5.7-1 1.18-1.33.4-.3.8-.4 1.1-.4h.1c.1 0 .2 0 .3.02.2.03.3.04.38.06.1.03.2.06.27.1.1.04.18.1.25.15.1.06.2.13.25.2.1.1.18.22.23.34.05.12.08.25.08.4 0 .1-.02.2-.04.3-.02.1-.05.2-.08.27-.03.07-.08.14-.12.2-.05.06-.1.1-.15.15-.05.04-.1.08-.15.12-.05.04-.08.08-.12.12l-.08.07c-.02.02-.05.04-.07.06-.2.18-.4.36-.6.53-.2.17-.3.25-.3.3s0 .1.02.18c.02.07.04.15.07.2.03.06.06.1.1.15.03.05.07.1.1.13.04.04.08.08.12.1.04.03.08.06.12.08.08.06.17.1.25.15.28.14.58.22.9.22.3 0 .6-.04.88-.13.28-.1.55-.23.8-.4.25-.17.48-.38.7-.62.2-.2.4-.4.58-.62.17-.2.33-.4.48-.6.1-.14.2-.28.28-.43.08-.15.13-.3.15-.45.02-.15.02-.3 0-.45-.02-.15-.07-.3-.13-.43-.06-.13-.14-.25-.24-.36-.1-.1-.2-.2-.3-.28-.1-.08-.2-.15-.3-.2s-.2-.1-.3-.13c-.1-.03-.2-.05-.3-.06-.1-.02-.2-.03-.3-.03h-.1c-.3 0-.7.1-1.1.4-.5.33-.9.8-1.18 1.33-.32.52-.5 1.15-.5 1.8 0 .65.17 1.27.48 1.85.3.58.7 1.07 1.2 1.52.48.45.98.85 1.6 1.2.55.33 1.13.6 1.68.85.55.24 1.1.24 1.57.18.46-.06 1.46-.6 1.68-1.22.22-.62.22-1.2 0-1.8s-.5-1.1-1-1.5c-.5-.4-1.1-.6-1.8-.6s-1.2.2-1.8.6c-.55.4-1.03.9-1.4 1.4s-.7 1-1 1.4c-.3.4-.6.7-1 .9s-.8.3-1.2.3c-.4 0-.8-.1-1.2-.3s-.7-.5-1-.9c-.3-.4-.5-.8-.6-1.2s-.2-.8-.2-1.2.1- .8.2-1.2.3-.8.6-1.2c.3-.4.7-.7 1.1-1 .4-.3.8-.5 1.2-.6.4-.1.8-.1 1.2-.1h.1c.45 0 .88.1 1.3.3.4.2.8.5 1.1.8.3.3.6.7.8 1.1s.3.8.3 1.2c0 .4-.1.8-.2 1.2z"/>
</svg>
);
// Custom Instagram Icon
const InstagramIcon = (props) => (
<svg aria-label="Instagram" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" {...props}>
<path fill="currentColor" d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.689-.073-4.948-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.162 6.162 6.162 6.162-2.759 6.162-6.162-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4s1.791-4 4-4 4 1.79 4 4-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/>
</svg>
);
// Simple VU Logo Component
const VULogo = (props) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" {...props}>
<circle cx="50" cy="50" r="48" fill="#FFFFFF" stroke="#00AEEF" strokeWidth="4" />
<text x="50%" y="50%" dominantBaseline="middle" textAnchor="middle" fontSize="50" fontWeight="bold" fill="#002D62" fontFamily="Arial, sans-serif">VU</text>
</svg>
);
// Modal Component for showing AI results
const Modal = ({ title, content, onClose }) => (
<div className="fixed inset-0 bg-black bg-opacity-50 flex justify-center items-center p-4 z-50">
<div className="bg-white rounded-lg shadow-xl w-full max-w-md max-h-[90vh] flex flex-col">
<div className="flex justify-between items-center p-4 border-b">
<h3 className="text-lg font-bold text-gray-800">{title}</h3>
<button onClick={onClose} className="text-gray-500 hover:text-gray-800"><X size={24} /></button>
</div>
<div className="p-6 overflow-y-auto" dangerouslySetInnerHTML={{ __html: content }}></div>
<div className="p-4 border-t text-right">
<button onClick={onClose} className="bg-blue-500 text-white font-bold py-2 px-4 rounded-lg hover:bg-blue-600">Close</button>
</div>
</div>
</div>
);
// Top Navigation Component
const TopNav = ({ activeScreen, navigateTo }) => {
const [programsOpen, setProgramsOpen] = useState(false);
const dropdownRef = useRef(null);
useEffect(() => {
const handleClickOutside = (event) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
setProgramsOpen(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [dropdownRef]);
const navItems = [
{ id: 'home', label: 'Home' },
{ id: 'programs', label: 'Programs', subItems: [
{ id: 'bs-programs', label: 'BS' },
{ id: 'bs-lateral-programs', label: 'BS-Lateral' },
{ id: 'associate-degree-programs', label: 'Associate Degree' },
{ id: 'bed-programs', label: 'B.Ed' },
{ id: 'ms-mphil-programs', label: 'MS/M.Phil' },
{ id: 'zero-semester-programs', label: 'Zero Semester' },
{ id: 'short-courses-programs', label: 'Short Courses' },
]},
{ id: 'gallery', label: 'Gallery' },
{ id: 'download', label: 'Download' },
{ id: 'tools', label: 'Tools' },
{ id: 'job-portal', label: 'Job Portal' },
{ id: 'contact', label: 'Contact' },
];
return (
<div className="flex justify-around items-center bg-blue-100 border-b border-blue-200 shadow-inner">
{navItems.map(item => {
const isActive = activeScreen === item.id || (item.subItems && item.subItems.some(sub => sub.id === activeScreen));
const baseStyle = "w-full text-sm font-semibold py-3 px-2 transition-all duration-150 border-b-4 active:border-b-0 active:translate-y-1 relative";
const activeStyle = "bg-white text-blue-600 border-blue-600";
const inactiveStyle = "bg-blue-50 text-gray-700 border-blue-300 hover:bg-blue-100 hover:border-blue-400";
if (item.subItems) {
return (
<div key={item.id} className="relative w-full" ref={dropdownRef}>
<button
onClick={() => setProgramsOpen(!programsOpen)}
className={`${baseStyle} ${isActive ? activeStyle : inactiveStyle}`}
>
{item.label}
</button>
{programsOpen && (
<div className="absolute top-full left-0 w-48 bg-white rounded-md shadow-lg z-30 border border-gray-200">
{item.subItems.map(subItem => (
<a href="#" key={subItem.label} onClick={(e) => { e.preventDefault(); navigateTo(subItem.id); setProgramsOpen(false); }} className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">{subItem.label}</a>
))}
</div>
)}
</div>
)
}
return (
<button
key={item.id}
onClick={() => navigateTo(item.id)}
className={`${baseStyle} ${isActive ? activeStyle : inactiveStyle}`}
>
{item.label}
</button>
);
})}
</div>
);
};
// Footer Component
const Footer = () => (
<footer className="bg-gray-800 text-white p-6 text-center">
<div className="flex justify-center space-x-6 mb-4">
<a href="#" aria-label="Facebook" className="text-gray-400 hover:text-white transition-colors"><Facebook size={24} /></a>
<a href="#" aria-label="Twitter" className="text-gray-400 hover:text-white transition-colors"><Twitter size={24} /></a>
<a href="https://www.youtube.com/@VUkhanpur" target="_blank" rel="noopener noreferrer" aria-label="YouTube" className="text-gray-400 hover:text-white transition-colors"><Youtube size={24} /></a>
<a href="https://wa.me/923217212792" target="_blank" rel="noopener noreferrer" aria-label="WhatsApp" className="text-gray-400 hover:text-white transition-colors"><WhatsAppIcon className="h-6 w-6" /></a>
<a href="#" aria-label="Instagram" className="text-gray-400 hover:text-white transition-colors"><InstagramIcon className="h-6 w-6" /></a>
<a href="#" aria-label="LinkedIn" className="text-gray-400 hover:text-white transition-colors"><Linkedin size={24} /></a>
</div>
<p className="text-sm text-gray-400">© 2025 Virtual University Khanpur Campus. All Rights Reserved.</p>
</footer>
);
// --- MAIN APP COMPONENT ---
export default function App() {
const [activeScreen, setActiveScreen] = useState('home');
const [previousScreen, setPreviousScreen] = useState(null);
const [isAiHelperOpen, setIsAiHelperOpen] = useState(false);
const navigateTo = (screen) => {
setPreviousScreen(activeScreen);
setActiveScreen(screen);
};
const goBack = () => {
if (previousScreen && !['home', 'programs', 'gallery', 'contact', 'download', 'tools', 'job-portal'].includes(previousScreen)) {
setActiveScreen(previousScreen);
setPreviousScreen(null);
} else {
setActiveScreen('home');
}
};
const screenConfig = {
home: { title: 'VU Khanpur', showNav: true },
programs: { title: 'Academic Programs', showNav: true },
gallery: { title: 'Campus Gallery', showNav: true },
contact: { title: 'Contact & Support', showNav: true },
download: { title: 'Downloads', showNav: true },
tools: { title: 'Student Tools', showNav: true },
'job-portal': { title: 'Job Portal', showNav: true },
apply: { title: 'Apply Now', showNav: false },
'academic-calendar': { title: 'Academic Calendar', showNav: false },
'admission-schedule': { title: 'Admission Schedule', showNav: false },
'cgpa-calculator': { title: 'CGPA Calculator', showNav: false },
'ai-study-planner': { title: 'AI Study Planner', showNav: false },
'pdf-converter': { title: 'PDF Converter', showNav: false },
'currency-converter': { title: 'Currency Converter', showNav: false },
'mcqs-generator': { title: 'MCQs Generator', showNav: false },
'bs-programs': { title: 'BS Programs', showNav: false },
'bs-lateral-programs': { title: 'BS-Lateral Programs', showNav: false },
'associate-degree-programs': { title: 'Associate Degree Programs', showNav: false },
'bed-programs': { title: 'B.Ed Programs', showNav: false },
'ms-mphil-programs': { title: 'MS/M.Phil Programs', showNav: false },
'zero-semester-programs': { title: 'Zero Semester', showNav: false },
'short-courses-programs': { title: 'Short Courses', showNav: false },
};
const currentScreenConfig = screenConfig[activeScreen] || { title: 'VU Khanpur', showNav: true };
const showBackButton = !currentScreenConfig.showNav;
const Header = () => (
<div className="bg-blue-500 p-4 text-white flex items-center justify-between shadow-md">
{showBackButton ? <button onClick={goBack} className="text-white"><ArrowLeft size={24} /></button> : <div className="w-8"></div>}
<div className="flex items-center gap-2">
<VULogo className="h-8 w-8" />
<h1 className="text-xl font-bold">{currentScreenConfig.title}</h1>
</div>
<Bell size={24} className="text-white cursor-pointer" />
</div>
);
const renderScreen = () => {
switch (activeScreen) {
case 'programs': return <ProgramsScreen />;
case 'contact': return <ContactScreen />;
case 'apply': return <ApplyScreen goBack={goBack} />;
case 'gallery': return <GalleryScreen />;
case 'admission-schedule': return <AdmissionScheduleScreen />;
case 'academic-calendar': return <AcademicCalendarScreen />;
case 'cgpa-calculator': return <CgpaCalculatorScreen />;
case 'download': return <DownloadScreen />;
case 'ai-study-planner': return <AIStudyPlannerScreen />;
case 'tools': return <ToolsScreen navigateTo={navigateTo} />;
case 'pdf-converter': return <PdfConverterScreen />;
case 'currency-converter': return <CurrencyConverterScreen />;
case 'mcqs-generator': return <McqsGeneratorScreen />;
case 'job-portal': return <JobPortalScreen />;
case 'bs-programs': return <ProgramDisplayPage title="BS Programs" programs={detailedPrograms["BS Programs"]} />;
case 'bs-lateral-programs': return <ProgramDisplayPage title="BS-Lateral Programs" programs={detailedPrograms["BS-Lateral Programs (BS 5th)"]} />;
case 'associate-degree-programs': return <ProgramDisplayPage title="Associate Degree Programs" programs={detailedPrograms["Associate Degree Programs"]} />;
case 'bed-programs': return <ProgramDisplayPage title="B.Ed Programs" programs={detailedPrograms["B.Ed Programs"]} />;
case 'ms-mphil-programs': return <ProgramDisplayPage title="MS/M.Phil Programs" programs={detailedPrograms["MS/M.Phil Programs"]} />;
case 'zero-semester-programs': return <ProgramDisplayPage title="Zero Semester" programs={detailedPrograms["Zero Semester"]} />;
case 'short-courses-programs': return <ProgramDisplayPage title="Short Courses" programs={detailedPrograms["Short Courses"]} />;
case 'home':
default: return <HomeScreen navigateTo={navigateTo} />;
}
};
return (
<div className="bg-gray-100 min-h-screen font-sans flex flex-col">
<div className="w-full max-w-7xl mx-auto bg-white flex-grow flex flex-col relative">
<div className="sticky top-0 z-20 bg-white shadow-md">
<Header />
{currentScreenConfig.showNav && <TopNav activeScreen={activeScreen} navigateTo={navigateTo} />}
</div>
<main className="p-4 md:p-8 flex-grow">
{renderScreen()}
</main>
<Footer />
{isAiHelperOpen && <AIHelperScreen onClose={() => setIsAiHelperOpen(false)} />}
<button
onClick={() => setIsAiHelperOpen(true)}
className="fixed bottom-6 right-6 bg-blue-600 text-white w-16 h-16 rounded-full flex items-center justify-center shadow-lg z-30 transform hover:scale-110 transition-transform"
aria-label="AI Campus Helper"
>
<Bot size={32} />
</button>
</div>
</div>
);
}
// --- SCREEN COMPONENTS ---
// Home Screen Component
const HomeScreen = ({ navigateTo }) => {
const quickLinks = [
{ title: "LMS Login", icon: <GraduationCap size={24} />, color: "bg-teal-500", url: "https://vulms.vu.edu.pk" },
{ title: "VU Email", icon: <Mail size={24} />, color: "bg-red-500", url: "https://accounts.google.com/v3/signin/identifier?continue=https%3A%2F%2Fmail.google.com%2Fmail%2F&hd=vu.edu.pk&osid=1&sacu=1&service=mail&flowName=GlifWebSignIn&flowEntry=AddSession&dsh=S1577548343%3A1751698400066131" },
{ title: "Academic Calendar", icon: <Calendar size={24} />, color: "bg-green-500", action: () => navigateTo('academic-calendar') },
{ title: "Admission Info", icon: <FileText size={24} />, color: "bg-orange-500", action: () => navigateTo('admission-schedule') },
];
const announcements = [
{ id: 1, text: "Fall 2025 admissions are now open. Apply now!" },
{ id: 2, text: "Mid-term examination schedule has been announced." },
{ id: 3, text: "New short courses available for registration." },
];
const buttonBaseStyle = "w-full text-white font-bold py-4 px-4 rounded-lg text-lg focus:outline-none transition-all duration-200 shadow-md hover:shadow-lg flex items-center justify-center gap-2 border-b-4 active:border-b-2 active:translate-y-0.5";
return (
<div className="bg-gray-50 p-4 rounded-lg">
<div className="bg-gradient-to-r from-blue-500 to-blue-400 text-white p-6 rounded-xl mb-8 text-center">
<h2 className="text-2xl md:text-3xl font-bold">Welcome to Virtual University Khanpur Campus</h2>
<p className="mt-2 text-blue-100">Your digital study companion.</p>
</div>
<div className="mb-8 rounded-xl overflow-hidden shadow-lg">
<img src="https://placehold.co/1200x400/003366/FFFFFF?text=Campus+Banner" alt="Campus Banner" className="w-full h-auto" />
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-8">
<button onClick={() => navigateTo('apply')} className={`${buttonBaseStyle} bg-green-500 hover:bg-green-600 border-green-700 active:border-green-600`}>Apply Now</button>
<button onClick={() => navigateTo('programs')} className={`${buttonBaseStyle} bg-purple-600 hover:bg-purple-700 border-purple-800 active:border-purple-700`}><Sparkles size={20} />AI Program Advisor</button>
<button onClick={() => navigateTo('admission-schedule')} className={`${buttonBaseStyle} bg-sky-500 hover:bg-sky-600 border-sky-700 active:border-sky-600`}><Calendar size={20} />Admission Schedule</button>
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
{quickLinks.map(link => {
const commonProps = {
key: link.title,
className: `${link.color} text-white p-4 rounded-lg flex flex-col items-center justify-center text-center h-28 transform hover:scale-105 transition-transform`,
};
return link.url ? (
<a href={link.url} target="_blank" rel="noopener noreferrer" {...commonProps}>
{link.icon}
<span className="mt-2 font-semibold text-sm">{link.title}</span>
</a>
) : (
<button onClick={link.action} {...commonProps}>
{link.icon}
<span className="mt-2 font-semibold text-sm">{link.title}</span>
</button>
);
})}
</div>
<div>
<div className="flex justify-between items-center mb-3">
<h3 className="text-lg font-bold text-gray-800">Announcements</h3>
<button onClick={() => alert('News & Events page coming soon!')} className="text-sm text-blue-600 hover:underline font-semibold">News & Events</button>
</div>
<div className="space-y-3">
{announcements.map(ann => (
<div key={ann.id} className="bg-white border border-gray-200 p-3 rounded-lg flex items-start space-x-3">
<Bell size={18} className="text-blue-500 mt-1 flex-shrink-0" />
<p className="text-gray-700 text-sm">{ann.text}</p>
</div>
))}
</div>
</div>
</div>
);
};
// Generic Program Display Page
const ProgramDisplayPage = ({ title, programs }) => {
const programEntries = Object.entries(programs);
return (
<div className="p-4 bg-gray-50">
<h3 className="text-3xl font-bold text-blue-800 border-b-2 border-blue-500 pb-2 mb-6">{title}</h3>
{programEntries.length > 0 ? (
programEntries.map(([faculty, programList]) => (
<div key={faculty} className="mb-8">
<h4 className="text-xl font-semibold text-gray-700 bg-gray-100 p-3 rounded-t-lg border-l-4 border-blue-400">{faculty}</h4>
<div className="bg-white p-4 rounded-b-lg shadow-sm">
<ul className="grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-3 list-disc list-inside">
{programList.map(program => (
<li key={program} className="text-gray-600">
{program}
</li>
))}
</ul>
</div>
</div>
))
) : (
<p className="text-md text-gray-500 italic">No programs currently listed for this category.</p>
)}
</div>
);
};
// Programs Screen Component (Main Page)
const ProgramsScreen = () => {
const [interests, setInterests] = useState('');
const [recommendation, setRecommendation] = useState('');
const [isLoadingRecommendation, setIsLoadingRecommendation] = useState(false);
const [modalContent, setModalContent] = useState('');
const [modalTitle, setModalTitle] = useState('');
const [isModalOpen, setIsModalOpen] = useState(false);
const [isLoadingModal, setIsLoadingModal] = useState(false);
const allPrograms = Object.values(detailedPrograms).flatMap(Object.values).flat().join(', ');
const handleGetRecommendation = async () => {
if (!interests.trim()) { setRecommendation("Please enter your interests first."); return; }
setIsLoadingRecommendation(true);
setRecommendation('');
const prompt = `Based on these interests: "${interests}", which of the following degree programs would be the best fit? Give me just the program name and a one-sentence explanation. Available programs are: ${allPrograms}.`;
const result = await callGeminiAPI(prompt);
setRecommendation(result.replace(/\n/g, '<br/>'));
setIsLoadingRecommendation(false);
};
const handleGetCareerPaths = async (program) => {
setIsModalOpen(true);
setModalTitle(`Career Paths for ${program}`);
setIsLoadingModal(true);
setModalContent('');
const prompt = `What are the potential career paths for a student with a "${program}" degree? List the top 5 career options. For each option, provide a title and a one-sentence description. Format the output as a simple HTML list.`;
const result = await callGeminiAPI(prompt);
setModalContent(result);
setIsLoadingModal(false);
};
return (
<>
{isModalOpen && <Modal title={modalTitle} onClose={() => setIsModalOpen(false)} content={isLoadingModal ? '<div class="flex justify-center items-center p-8"><div class="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500"></div></div>' : modalContent} />}
<div className="p-4 bg-gray-50">
<div className="bg-white border border-blue-200 p-4 rounded-lg mb-6 shadow">
<h3 className="text-lg font-bold text-blue-800 mb-2 flex items-center"><Sparkles className="mr-2 text-yellow-500" /> AI Program Advisor</h3>
<p className="text-sm text-gray-600 mb-3">Tell us your interests, and we'll suggest a program for you!</p>
<textarea value={interests} onChange={(e) => setInterests(e.target.value)} placeholder="e.g., I love coding, designing websites, and solving problems." className="w-full p-2 border border-gray-300 rounded-md h-24" />
<button onClick={handleGetRecommendation} disabled={isLoadingRecommendation} className="w-full mt-3 bg-blue-500 text-white font-bold py-2 px-4 rounded-lg hover:bg-blue-600 disabled:bg-blue-300 flex items-center justify-center border-b-4 border-blue-700 active:border-b-2 active:translate-y-0.5 transition-all">
{isLoadingRecommendation ? <Loader className="animate-spin mr-2" /> : <Sparkles className="mr-2" />}
{isLoadingRecommendation ? 'Thinking...' : '✨ Get Recommendation'}
</button>
{recommendation && <div className="mt-4 p-3 bg-blue-100 border border-blue-200 rounded-md text-blue-800" dangerouslySetInnerHTML={{ __html: recommendation }}></div>}
</div>
{Object.entries(detailedPrograms).map(([degreeType, faculties]) => (
<div key={degreeType} className="mb-6">
<h3 className="text-xl font-bold text-blue-800 border-b-2 border-blue-500 pb-2 mb-3">{degreeType}</h3>
{Object.keys(faculties).length > 0 ? (
Object.entries(faculties).map(([faculty, programs]) => (
<div key={faculty} className="mb-4">
<h4 className="text-md font-semibold text-gray-700 mb-2">{faculty}</h4>
<ul className="space-y-3">
{programs.map(program => (
<li key={program} className="bg-white border border-gray-200 p-3 rounded-lg text-gray-700 flex justify-between items-center">
<span className="text-sm">{program}</span>
<button onClick={() => handleGetCareerPaths(program)} className="text-xs bg-blue-100 text-blue-700 font-semibold py-1 px-2 rounded-full hover:bg-blue-200 flex items-center gap-1 flex-shrink-0">
<Sparkles size={14} /> Career Paths
</button>
</li>
))}
</ul>
</div>
))
) : (
<p className="text-sm text-gray-500 italic">No programs currently listed for this category.</p>
)}
</div>
))}
</div>
</>
);
};
// Contact Screen Component
const ContactScreen = () => {
return (
<div className="p-4 bg-gray-50 space-y-4">
<div className="bg-white border border-gray-200 p-4 rounded-lg"><h3 className="font-bold text-lg mb-2 flex items-center"><MapPin size={20} className="mr-2 text-blue-500"/>Address</h3><p className="text-gray-700">Virtual University Khanpur Campus, Near Cafe Sajawal, Khanpur, Punjab</p></div>
<div className="bg-white border border-gray-200 p-4 rounded-lg"><h3 className="font-bold text-lg mb-2 flex items-center"><Phone size={20} className="mr-2 text-blue-500"/>Phone</h3><a href="tel:0321-7212792" className="text-blue-600 hover:underline">0321-7212792</a></div>
<div className="bg-white border border-gray-200 p-4 rounded-lg"><h3 className="font-bold text-lg mb-2 flex items-center"><Mail size={20} className="mr-2 text-blue-500"/>Email</h3><a href="mailto:vukhanpur@gmail.com" className="text-blue-600 hover:underline">vukhanpur@gmail.com</a></div>
<div className="bg-white border border-gray-200 p-4 rounded-lg"><h3 className="font-bold text-lg mb-2 flex items-center"><Globe size={20} className="mr-2 text-blue-500"/>Website</h3><a href="https://vukhanpur.com" target="_blank" rel="noopener noreferrer" className="text-blue-600 hover:underline">https://vukhanpur.com</a></div>
<div className="bg-white border border-gray-200 p-4 rounded-lg"><h3 className="font-bold text-lg mb-3">Follow Us</h3><div className="flex items-center space-x-6"><a href="#" aria-label="Facebook" className="text-gray-500 hover:text-blue-600 transition-colors"><Facebook size={32} /></a><a href="#" aria-label="Twitter" className="text-gray-500 hover:text-sky-500 transition-colors"><Twitter size={32} /></a><a href="https://www.youtube.com/@VUkhanpur" target="_blank" rel="noopener noreferrer" aria-label="YouTube" className="text-gray-500 hover:text-red-600 transition-colors"><Youtube size={32} /></a><a href="https://wa.me/923217212792" target="_blank" rel="noopener noreferrer" aria-label="WhatsApp" className="text-gray-500 hover:text-green-500 transition-colors"><WhatsAppIcon className="h-8 w-8" /></a></div></div>
<div className="mt-6"><iframe src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3468.809999468114!2d70.6558489151051!3d28.64481548248358!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x393719815352df85%3A0x7cfc7def137d2a1!2sVirtual%20University%20Campus%20Khanpur!5e0!3m2!1sen!2s!4v1672583345671!5m2!1sen!2s" width="100%" height="250" style={{ border: 0 }} allowFullScreen="" loading="lazy" className="rounded-lg" referrerPolicy="no-referrer-when-downgrade"></iframe></div>
</div>
);
};
// Gallery Screen Component
const GalleryScreen = () => {
const images = [ { id: 1, src: 'https://placehold.co/400x400/3b82f6/ffffff?text=Classroom', alt: 'A modern classroom' }, { id: 2, src: 'https://placehold.co/400x400/10b981/ffffff?text=Library', alt: 'University library with students' }, { id: 3, src: 'https://placehold.co/400x400/8b5cf6/ffffff?text=Campus', alt: 'Campus building exterior' }, { id: 4, src: 'https://placehold.co/400x400/ef4444/ffffff?text=Students', alt: 'Group of students collaborating' }, { id: 5, src: 'https://placehold.co/400x400/f97316/ffffff?text=Event', alt: 'A campus event or seminar' }, { id: 6, src: 'https://placehold.co/400x400/14b8a6/ffffff?text=Lab', alt: 'A computer lab with equipment' }, ];
return (
<div className="p-4 bg-gray-50">
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
{images.map(image => (
<div key={image.id} className="relative overflow-hidden rounded-lg shadow-md group">
<img src={image.src} alt={image.alt} className="w-full h-full object-cover aspect-square" />
<div className="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-40 transition-all duration-300 flex items-end p-2">
<p className="text-white text-sm opacity-0 group-hover:opacity-100 transition-opacity duration-300">{image.alt}</p>
</div>
</div>
))}
</div>
</div>
);
};
// Academic Calendar Screen Component
const AcademicCalendarScreen = () => {
const calendarData = [
{ desc: "Option to Change of Study Program for existing Students", day: "Thursday", date: "September 04, 2025" },
{ desc: "Course Selection Link Open for enrollment Fall 2025", day: "Wednesday", date: "September 17, 2025" },
{ desc: "First Merit List For MS Programs", day: "Monday", date: "September 29, 2025" },
{ desc: "Last Date to apply for Course Exemption / Transfer of Course Credits", day: "Tuesday", date: "September 30, 2025" },
{ desc: "Last Date to Pay Fee for First Merit List For MS Programs", day: "Thursday", date: "October 02, 2025" },
{ desc: "Orientation for newly admitted students", day: "Monday to Monday", date: "October 06 - 13, 2025" },
{ desc: "Second Merit List For MS Programs", day: "Monday", date: "October 06, 2025" },
{ desc: "Last date to apply for Change of Study Program for existing and newly admit students", day: "Friday", date: "October 10, 2025" },
{ desc: "Last Date to apply for Need based Scholarship", day: "Friday", date: "October 10, 2025" },
{ desc: "Last Date to Pay Fee for Second Merit List For MS Programs", day: "Saturday", date: "October 11, 2025" },
{ desc: "Commencement of Classes - Fall 2025", day: "Monday", date: "October 13, 2025" },
{ desc: "Last date to apply for semester unfreeze", day: "Wednesday", date: "October 22, 2025" },
{ desc: "Last date for Course Selection to add/drop/replace course(s)", day: "Wednesday", date: "October 22, 2025" },
{ desc: "Mid-Term Exam - Fall 2025", day: "Monday", date: "December 08, 2025" },
{ desc: "Birthday of Quaid-e-Azam Muhammad Ali Jinnah (Holiday)", day: "Thursday", date: "December 25, 2025" },
{ desc: "Final-Term Exam - Fall 2025", day: "Monday", date: "January 26, 2026" },
{ desc: "Kashmir day (Holiday)", day: "Thursday", date: "February 05, 2026" },
{ desc: "Final-Term Result Announcement - Fall 2025", day: "Wednesday", date: "March 04, 2026" },
];
const datesToRemember = [
{ desc: "Last date to apply for Change of Study Program for existing and newly admit students", day: "Friday", date: "October 10, 2025" },
{ desc: "Last Date to apply for Need based Scholarship", day: "Friday", date: "October 10, 2025" },
{ desc: "Commencement of Classes - Fall 2025", day: "Monday", date: "October 13, 2025" },
{ desc: "Last date for Course Selection to add/drop/replace course(s)", day: "Wednesday", date: "October 22, 2025" },
{ desc: "Mid-Term Exam - Fall 2025", day: "Monday", date: "December 08, 2025" },
{ desc: "Final-Term Exam - Fall 2025", day: "Monday", date: "January 26, 2026" },
];
return (
<div className="p-4 bg-gray-50">
<div className="bg-white p-4 rounded-lg shadow mb-6">
<h3 className="text-xl font-bold text-blue-800 mb-3">Academic Calendar (Fall 2025)</h3>
<div className="overflow-x-auto">
<table className="w-full text-sm text-left text-gray-500">
<thead className="text-xs text-gray-700 uppercase bg-gray-100">
<tr>
<th scope="col" className="px-4 py-3">Description</th>
<th scope="col" className="px-4 py-3">Day</th>
<th scope="col" className="px-4 py-3">Date</th>
</tr>
</thead>
<tbody>
{calendarData.map((item, index) => (
<tr key={index} className="bg-white border-b">
<td className="px-4 py-3 font-medium text-gray-900">{item.desc}</td>
<td className="px-4 py-3">{item.day}</td>
<td className="px-4 py-3">{item.date}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
<div className="bg-white p-4 rounded-lg shadow mb-6">
<h3 className="text-xl font-bold text-blue-800 mb-3">Dates to Remember</h3>
<div className="overflow-x-auto">
<table className="w-full text-sm text-left text-gray-500">
<thead className="text-xs text-gray-700 uppercase bg-gray-100">
<tr>
<th scope="col" className="px-4 py-3">Description</th>
<th scope="col" className="px-4 py-3">Day</th>
<th scope="col" className="px-4 py-3">Date</th>
</tr>
</thead>
<tbody>
{datesToRemember.map((item, index) => (
<tr key={index} className="bg-white border-b">
<td className="px-4 py-3 font-medium text-gray-900">{item.desc}</td>
<td className="px-4 py-3">{item.day}</td>
<td className="px-4 py-3">{item.date}</td>
</tr>
))}
</tbody>
</table>
</div>
<p className="text-xs text-gray-500 mt-4">* Subject to appearance of moon</p>
<p className="text-xs text-gray-500">• The University reserves the right to make any change in Academic Calendar, as required.</p>
</div>
</div>
);
};
// Admission Schedule Screen Component
const AdmissionScheduleScreen = () => {
const scheduleData = [ { desc: "Admissions Open - Fall 2025", day: "Monday", date: "July 07, 2025" }, { desc: "Last Date to apply for Admissions", day: "Friday", date: "September 05, 2025" }, { desc: "Last Date to apply for Course Exemption / Transfer of Credits", day: "Friday", date: "September 05, 2025" }, { desc: "Last Date to Apply for MS/M.Phil Admissions", day: "Friday", date: "September 05, 2025" }, { desc: "Last Date to deposit Admission Processing Fee (Rs. 500)", day: "Wednesday", date: "September 10, 2025" }, { desc: "Entry Test for MS/M.Phil Programs Admissions", day: "Tuesday", date: "September 16, 2025" }, { desc: "First Merit List For MS Programs", day: "Monday", date: "September 29, 2025" }, { desc: "Last Date to Pay Fee for First Merit List For MS Programs", day: "Friday", date: "October 03, 2025" }, { desc: "Commencement of Classes - Fall 2025", day: "Monday", date: "October 06, 2025" }, ];
const labCities = ["Abbottabad", "Bahawalnagar", "Bahawalpur", "D.G.Khan", "Faisalabad", "Gujar Khan", "Gujranwala", "Hyderabad", "Jalalpur Pirwala", "Jhelum", "Karachi", "Lahore", "Multan", "Peshawar", "Quetta", "Rawalpindi"];
return (
<div className="p-4 bg-gray-50">
<div className="bg-white p-4 rounded-lg shadow mb-6"><h3 className="text-xl font-bold text-blue-800 mb-3">Admission Schedule - Fall 2025</h3><p className="text-sm text-gray-600 mb-4">Virtual University of Pakistan offers admission twice in an academic year namely spring in the month of February/March and fall in the month of August/September each year.</p><div className="overflow-x-auto"><table className="w-full text-sm text-left text-gray-500"><thead className="text-xs text-gray-700 uppercase bg-gray-100"><tr><th scope="col" className="px-4 py-3">Description</th><th scope="col" className="px-4 py-3">Day</th><th scope="col" className="px-4 py-3">Date</th></tr></thead><tbody>{scheduleData.map((item, index) => (<tr key={index} className="bg-white border-b"><td className="px-4 py-3 font-medium text-gray-900">{item.desc}</td><td className="px-4 py-3">{item.day}</td><td className="px-4 py-3">{item.date}</td></tr>))}</tbody></table></div><p className="text-xs text-gray-500 mt-4">Note: The University reserves the right to make any change(s) in the admission schedule.</p></div>
<div className="bg-white p-4 rounded-lg shadow mb-6"><h3 className="text-xl font-bold text-blue-800 mb-3">Lab Cities (For Biological Sciences Programs)</h3><ul className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-2 text-sm text-gray-700">{labCities.map(city => <li key={city} className="bg-gray-100 p-2 rounded-md">{city}</li>)}</ul><p className="text-xs text-gray-500 mt-4">*Limited Seats on Merit Basis - Only within Pakistan.<br/>** Open Merit. However, selection of lab preference is necessary.<br/>Note: Students can apply only in designated city/region. Each city will have its own merit. If any student wants to apply for more than one city, he/she must apply separately. They will not allow to go for practical other than merit city after admission.</p></div>
</div>
);
};
// Apply Screen Component
const ApplyScreen = ({ goBack }) => {
const [formData, setFormData] = useState({ name: '', number: '', city: '', whatsapp: '', degree: 'ADP' });
const [isSubmitted, setIsSubmitted] = useState(false);
const degreePrograms = ["ADP", "BS", "BS 5th", "B.Ed", "MS", "Diploma", "Short Course", "Zero Semester"];
const handleChange = (e) => setFormData(prev => ({ ...prev, [e.target.name]: e.target.value }));
const handleSubmit = (e) => { e.preventDefault(); console.log("Form Data Submitted:", formData); setIsSubmitted(true); };
if (isSubmitted) {
return (
<div className="p-6 flex flex-col items-center justify-center text-center bg-gray-50 h-full">
<CheckCircle className="text-green-500 w-24 h-24 mb-6" />
<h2 className="text-2xl font-bold text-gray-800 mb-2">Thank You!</h2>
<p className="text-gray-600 mb-6">Your application has been received. We will contact you shortly.</p>
<div className="text-left bg-white p-4 rounded-lg border w-full mb-6"><h3 className="font-bold mb-2">Submitted Information:</h3><p><strong>Name:</strong> {formData.name}</p><p><strong>Number:</strong> {formData.number}</p><p><strong>City:</strong> {formData.city}</p><p><strong>WhatsApp:</strong> {formData.whatsapp}</p><p><strong>Degree:</strong> {formData.degree}</p></div>
<p className="text-sm text-gray-500">A confirmation email would be sent to vukhanpur@gmail.com.</p>
<button onClick={goBack} className="mt-8 bg-blue-500 text-white font-bold py-2 px-6 rounded-lg hover:bg-blue-600">Back to Home</button>
</div>
);
}
return (
<div className="p-4 md:p-8 max-w-2xl mx-auto bg-gray-50">
<form onSubmit={handleSubmit} className="space-y-4">
<div><label htmlFor="name" className="block text-sm font-medium text-gray-700">Name</label><input type="text" name="name" id="name" required value={formData.name} onChange={handleChange} className="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500" /></div>
<div><label htmlFor="number" className="block text-sm font-medium text-gray-700">Phone Number</label><input type="tel" name="number" id="number" required value={formData.number} onChange={handleChange} className="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500" /></div>
<div><label htmlFor="city" className="block text-sm font-medium text-gray-700">City</label><input type="text" name="city" id="city" required value={formData.city} onChange={handleChange} className="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500" /></div>
<div><label htmlFor="whatsapp" className="block text-sm font-medium text-gray-700">WhatsApp Number</label><input type="tel" name="whatsapp" id="whatsapp" required value={formData.whatsapp} onChange={handleChange} className="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500" /></div>
<div><label htmlFor="degree" className="block text-sm font-medium text-gray-700">Degree Program</label><select name="degree" id="degree" value={formData.degree} onChange={handleChange} className="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500">{degreePrograms.map(program => (<option key={program} value={program}>{program}</option>))}</select></div>
<div className="pt-4"><button type="submit" className="w-full bg-green-500 text-white font-bold py-3 px-6 rounded-lg text-lg hover:bg-green-600 focus:outline-none focus:ring-4 focus:ring-green-500/50 transition-all duration-300">Submit Application</button></div>
</form>
</div>
);
};
// AI Helper Screen Component
const AIHelperScreen = ({ onClose }) => {
const [messages, setMessages] = useState([ { from: 'ai', text: 'Hello! I am the VU Khanpur AI Helper. How can I assist you today?' } ]);
const [input, setInput] = useState('');
const [isLoading, setIsLoading] = useState(false);
const chatEndRef = useRef(null);
useEffect(() => {
chatEndRef.current?.scrollIntoView({ behavior: "smooth" });
}, [messages]);
const handleSend = async () => {
if (!input.trim()) return;
const userMessage = { from: 'user', text: input };
setMessages(prev => [...prev, userMessage]);
setInput('');
setIsLoading(true);
const context = `You are a friendly and helpful AI assistant for the Virtual University Khanpur Campus. Your goal is to answer student questions accurately. Use the following information to help you answer: Campus Address: Near Cafe Sajawal, Khanpur, Punjab. Phone: 0321-7212792. Email: vukhanpur@gmail.com. Website: vukhanpur.com. Admissions are open for Fall 2025. If you don't know the answer, politely say that you don't have that information. Keep your answers concise and helpful. The student's question is: "${input}"`;
const aiResponseText = await callGeminiAPI(context);
const aiMessage = { from: 'ai', text: aiResponseText };
setMessages(prev => [...prev, aiMessage]);
setIsLoading(false);
};
return (
<div className="fixed bottom-24 right-6 w-80 h-96 bg-white rounded-lg shadow-2xl flex flex-col z-40">
<div className="flex justify-between items-center p-3 bg-blue-600 text-white rounded-t-lg">
<h3 className="font-bold flex items-center gap-2"><Bot size={20} /> AI Campus Helper</h3>
<button onClick={onClose} className="text-white hover:text-gray-200"><X size={20} /></button>
</div>
<div className="flex-grow p-3 overflow-y-auto">
{messages.map((msg, index) => (
<div key={index} className={`flex ${msg.from === 'user' ? 'justify-end' : 'justify-start'} mb-2`}>
<div className={`p-2 rounded-lg max-w-xs ${msg.from === 'user' ? 'bg-blue-500 text-white' : 'bg-gray-200 text-gray-800'}`}>
{msg.text}
</div>
</div>
))}
{isLoading && ( <div className="flex justify-start mb-2"><div className="p-2 rounded-lg bg-gray-200 text-gray-500"><Loader className="animate-spin h-5 w-5" /></div></div> )}
<div ref={chatEndRef} />
</div>
<div className="p-2 border-t flex">
<input type="text" value={input} onChange={(e) => setInput(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && handleSend()} placeholder="Ask a question..." className="flex-grow p-2 border rounded-l-md focus:outline-none focus:ring-2 focus:ring-blue-500" />
<button onClick={handleSend} className="bg-blue-600 text-white p-2 rounded-r-md hover:bg-blue-700"><Send size={20} /></button>
</div>
</div>
);
};
// CGPA Calculator Screen Component
const CgpaCalculatorScreen = () => {
const [courses, setCourses] = useState([{ id: 1, subjectName: '', credits: 3, marks: 85 }]);
const [result, setResult] = useState(null);
const getGradeDetails = (marks) => {
const score = parseFloat(marks);
if (isNaN(score) || score < 0 || score > 100) return { grade: 'N/A', points: 0 };
if (score >= 90) return { grade: 'A', points: 4.0 };
if (score >= 85) return { grade: 'A-', points: 3.67 };
if (score >= 80) return { grade: 'B+', points: 3.33 };
if (score >= 75) return { grade: 'B', points: 3.0 };
if (score >= 70) return { grade: 'B-', points: 2.67 };
if (score >= 65) return { grade: 'C+', points: 2.33 };
if (score >= 60) return { grade: 'C', points: 2.0 };
if (score >= 55) return { grade: 'C-', points: 1.67 };
if (score >= 50) return { grade: 'D+', points: 1.33 };
if (score >= 45) return { grade: 'D', points: 1.0 };
return { grade: 'F', points: 0.0 };
};
const handleCourseChange = (id, field, value) => {
setCourses(courses.map(course => course.id === id ? { ...course, [field]: value } : course));
};
const addCourse = () => {
setCourses([...courses, { id: Date.now(), subjectName: '', credits: 3, marks: 85 }]);
};
const removeCourse = (id) => {
setCourses(courses.filter(course => course.id !== id));
};
const calculateCgpa = () => {
let totalPoints = 0;
let totalCredits = 0;
const coursesWithGrades = courses.map(course => {
const credits = parseFloat(course.credits);
const { grade, points } = getGradeDetails(course.marks);
if (!isNaN(credits) && points !== undefined) {
totalPoints += credits * points;
totalCredits += credits;
}
return { ...course, grade };
});
if (totalCredits === 0) {
setResult({ cgpa: 0, courses: coursesWithGrades });
} else {
setResult({
cgpa: (totalPoints / totalCredits).toFixed(2),
courses: coursesWithGrades
});
}
};
return (
<div className="p-4 md:p-8 max-w-4xl mx-auto bg-gray-50">
<div className="bg-white p-6 rounded-lg shadow-md">
<h3 className="text-2xl font-bold text-blue-800 mb-4">CGPA Calculator</h3>
<div className="space-y-4 mb-4">
<div className="hidden md:grid grid-cols-4 gap-4 text-sm font-medium text-gray-500">
<label>Subject Name (Optional)</label>
<label>Credit Hours</label>
<label>Obtained Marks (out of 100)</label>
<label>Action</label>
</div>
{courses.map((course, index) => (
<div key={course.id} className="grid grid-cols-1 md:grid-cols-4 gap-4 items-center p-2 border rounded-md">
<div>
<label className="block text-sm font-medium text-gray-700 md:hidden">Subject Name</label>
<input
type="text"
placeholder={`Subject ${index + 1}`}
value={course.subjectName}
onChange={(e) => handleCourseChange(course.id, 'subjectName', e.target.value)}
className="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 md:hidden">Credit Hours</label>
<input
type="number"
value={course.credits}
onChange={(e) => handleCourseChange(course.id, 'credits', e.target.value)}
className="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm"
min="1"
max="4"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 md:hidden">Obtained Marks</label>
<input
type="number"
value={course.marks}
onChange={(e) => handleCourseChange(course.id, 'marks', e.target.value)}
className="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm"
min="0"
max="100"
/>
</div>
<div className="flex items-end h-full">
{courses.length > 1 && (
<button onClick={() => removeCourse(course.id)} className="bg-red-500 text-white p-2 rounded-md hover:bg-red-600">
<Trash2 size={20} />
</button>
)}
</div>
</div>
))}
</div>
<button onClick={addCourse} className="flex items-center gap-2 text-sm bg-gray-200 text-gray-700 py-2 px-4 rounded-md hover:bg-gray-300 mb-6">
<Plus size={16} /> Add Course
</button>
<button onClick={calculateCgpa} className="w-full bg-blue-600 text-white font-bold py-3 px-6 rounded-lg text-lg hover:bg-blue-700">
Calculate CGPA
</button>
{result && (
<div className="mt-6">
<div className="text-center bg-blue-100 p-4 rounded-lg">
<h4 className="text-lg font-semibold text-blue-800">Your Calculated CGPA is:</h4>
<p className="text-4xl font-bold text-blue-600">{result.cgpa}</p>
</div>
<div className="mt-4 overflow-x-auto">
<h4 className="text-lg font-semibold text-gray-800 mb-2">Calculation Summary:</h4>
<table className="w-full text-sm text-left text-gray-500">
<thead className="text-xs text-gray-700 uppercase bg-gray-100">
<tr>
<th className="px-4 py-3">Subject</th>
<th className="px-4 py-3">Credits</th>
<th className="px-4 py-3">Marks</th>
<th className="px-4 py-3">Grade</th>
</tr>
</thead>
<tbody>
{result.courses.map(course => (
<tr key={course.id} className="bg-white border-b">
<td className="px-4 py-3 font-medium text-gray-900">{course.subjectName || 'N/A'}</td>
<td className="px-4 py-3">{course.credits}</td>
<td className="px-4 py-3">{course.marks}</td>
<td className="px-4 py-3 font-bold">{course.grade}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)}
</div>
</div>
);
};
// Download Screen Component
const DownloadScreen = () => {
return (
<div className="p-4 md:p-8 max-w-4xl mx-auto bg-gray-50">
<div className="bg-white p-6 rounded-lg shadow-md">
<h3 className="text-2xl font-bold text-blue-800 mb-6">Downloads</h3>
<div className="space-y-4">
<a
href="https://vukhanpur.com/VUHandouts.php"
target="_blank"
rel="noopener noreferrer"
className="w-full bg-orange-500 text-white font-bold py-4 px-4 rounded-lg text-lg hover:bg-orange-600 focus:outline-none focus:ring-4 focus:ring-orange-500/50 transform hover:scale-105 transition-all duration-300 shadow-lg flex items-center justify-center gap-2"
>
<Download size={20} />
Download Prospectus
</a>
<a
href="https://vukhanpur.com/VUHandouts.php"
target="_blank"
rel="noopener noreferrer"
className="w-full bg-teal-500 text-white font-bold py-4 px-4 rounded-lg text-lg hover:bg-teal-600 focus:outline-none focus:ring-4 focus:ring-teal-500/50 transform hover:scale-105 transition-all duration-300 shadow-lg flex items-center justify-center gap-2"
>
<Download size={20} />
Highlighted Handouts
</a>
<a
href="https://vukhanpur.com/VUHandouts.php"
target="_blank"
rel="noopener noreferrer"
className="w-full bg-blue-500 text-white font-bold py-4 px-4 rounded-lg text-lg hover:bg-blue-600 focus:outline-none focus:ring-4 focus:ring-blue-500/50 transform hover:scale-105 transition-all duration-300 shadow-lg flex items-center justify-center gap-2"
>
<Download size={20} />
Handouts
</a>
<a
href="https://vukhanpur.com/VUHandouts.php"
target="_blank"
rel="noopener noreferrer"
className="w-full bg-red-500 text-white font-bold py-4 px-4 rounded-lg text-lg hover:bg-red-600 focus:outline-none focus:ring-4 focus:ring-red-500/50 transform hover:scale-105 transition-all duration-300 shadow-lg flex items-center justify-center gap-2"
>
<Download size={20} />
Past Papers
</a>
<a
href="#"
onClick={(e) => { e.preventDefault(); alert("Software download link coming soon!"); }}
className="w-full bg-gray-500 text-white font-bold py-4 px-4 rounded-lg text-lg hover:bg-gray-600 focus:outline-none focus:ring-4 focus:ring-gray-500/50 transform hover:scale-105 transition-all duration-300 shadow-lg flex items-center justify-center gap-2"
>
<Download size={20} />
Software
</a>
</div>
</div>
</div>
);
};
// AI Study Planner Screen Component
const AIStudyPlannerScreen = () => {
const [subjects, setSubjects] = useState('');
const [examDate, setExamDate] = useState('');
const [studyHours, setStudyHours] = useState('');
const [studyPlan, setStudyPlan] = useState('');
const [isLoading, setIsLoading] = useState(false);
const handleGeneratePlan = async () => {
if (!subjects || !examDate || !studyHours) {
alert("Please fill in all fields.");
return;
}
setIsLoading(true);
setStudyPlan('');
const prompt = `Create a weekly study plan for a student preparing for exams.
Subjects: ${subjects}
Exam Date: ${examDate}
Available study time: ${studyHours} per day.
Generate a structured, weekly timetable in an HTML table format. The table should have columns for 'Day', 'Time Slot', and 'Subject/Topic to Study'.
Break down the subjects into important topics where possible. Include short breaks. Make the plan realistic and effective.`;
const result = await callGeminiAPI(prompt);
setStudyPlan(result);
setIsLoading(false);
};
return (
<div className="p-4 md:p-8 max-w-4xl mx-auto bg-gray-50">
<div className="bg-white p-6 rounded-lg shadow-md">
<h3 className="text-2xl font-bold text-blue-800 mb-2 flex items-center gap-2"><BookOpen size={24} /> AI Study Planner</h3>
<p className="text-gray-600 mb-6">Get a personalized study plan to ace your exams!</p>
<div className="space-y-4">
<div>
<label htmlFor="subjects" className="block text-sm font-medium text-gray-700">Your Subjects</label>
<input type="text" id="subjects" value={subjects} onChange={(e) => setSubjects(e.target.value)} placeholder="e.g., Physics, Chemistry, Math" className="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm" />
</div>
<div>
<label htmlFor="examDate" className="block text-sm font-medium text-gray-700">Exam Start Date</label>
<input type="date" id="examDate" value={examDate} onChange={(e) => setExamDate(e.target.value)} className="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm" />
</div>
<div>
<label htmlFor="studyHours" className="block text-sm font-medium text-gray-700">Daily Study Hours</label>
<input type="text" id="studyHours" value={studyHours} onChange={(e) => setStudyHours(e.target.value)} placeholder="e.g., 4 hours" className="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm" />
</div>
</div>
<button onClick={handleGeneratePlan} disabled={isLoading} className="w-full mt-6 bg-teal-500 text-white font-bold py-3 px-6 rounded-lg text-lg hover:bg-teal-600 disabled:bg-teal-300 flex items-center justify-center border-b-4 border-teal-700 active:border-b-2 active:translate-y-0.5 transition-all">
{isLoading ? <Loader className="animate-spin mr-2" /> : <Sparkles className="mr-2" />}
{isLoading ? 'Generating Plan...' : 'Generate My Study Plan'}
</button>
{studyPlan && (
<div className="mt-8">
<h4 className="text-xl font-bold text-blue-800 mb-4">Your Personalized Study Plan</h4>
<div className="prose max-w-none" dangerouslySetInnerHTML={{ __html: studyPlan }}></div>
</div>
)}
</div>
</div>
);
};
// Tools Screen Component
const ToolsScreen = ({ navigateTo }) => {
const buttonBaseStyle = "w-full text-white font-bold py-4 px-4 rounded-lg text-lg focus:outline-none transition-all duration-200 shadow-md hover:shadow-lg flex items-center justify-center gap-2 border-b-4 active:border-b-2 active:translate-y-0.5";
return (
<div className="p-4 md:p-8 max-w-4xl mx-auto bg-gray-50">
<div className="bg-white p-6 rounded-lg shadow-md">
<h3 className="text-2xl font-bold text-blue-800 mb-6">Student Tools</h3>
<div className="space-y-4">
<button onClick={() => navigateTo('cgpa-calculator')} className={`${buttonBaseStyle} bg-indigo-500 hover:bg-indigo-600 border-indigo-700 active:border-indigo-600`}><Calculator size={20} />CGPA Calculator</button>
<button onClick={() => navigateTo('ai-study-planner')} className={`${buttonBaseStyle} bg-teal-500 hover:bg-teal-600 border-teal-700 active:border-teal-600`}><BookOpen size={20} />AI Study Planner</button>
<button onClick={() => navigateTo('pdf-converter')} className={`${buttonBaseStyle} bg-rose-500 hover:bg-rose-600 border-rose-700 active:border-rose-600`}><FileText size={20} />PDF Converter</button>
<button onClick={() => navigateTo('currency-converter')} className={`${buttonBaseStyle} bg-amber-500 hover:bg-amber-600 border-amber-700 active:border-amber-600`}><RefreshCw size={20} />Currency Converter</button>
<button onClick={() => navigateTo('mcqs-generator')} className={`${buttonBaseStyle} bg-cyan-500 hover:bg-cyan-600 border-cyan-700 active:border-cyan-600`}><HelpCircle size={20} />MCQs Generator</button>
</div>
</div>
</div>
);
};
// PDF Converter Screen Component
const PdfConverterScreen = () => {
const [file, setFile] = useState(null);
const [isConverting, setIsConverting] = useState(false);
const [downloadLink, setDownloadLink] = useState('');
const handleFileChange = (e) => {
const selectedFile = e.target.files[0];
if (selectedFile) {
setFile(selectedFile);
setDownloadLink('');
}
};
const handleConvert = () => {
if (!file) {
alert("Please select a file first.");
return;
}
setIsConverting(true);
setDownloadLink('');
// Simulate a file conversion process
setTimeout(() => {
setIsConverting(false);
// In a real app, this would be the URL to the converted PDF
setDownloadLink('#');
}, 2000);
};
return (
<div className="p-4 md:p-8 max-w-4xl mx-auto bg-gray-50">
<div className="bg-white p-6 rounded-lg shadow-md text-center">
<h3 className="text-2xl font-bold text-blue-800 mb-2 flex items-center justify-center gap-2"><FileText size={24} /> PDF Converter</h3>
<p className="text-gray-600 mb-6">Convert your Word, Excel, and Image files to PDF.</p>
<div className="border-2 border-dashed border-gray-300 rounded-lg p-8 mb-4">
<UploadCloud className="mx-auto h-12 w-12 text-gray-400" />
<label htmlFor="file-upload" className="relative cursor-pointer bg-white rounded-md font-medium text-blue-600 hover:text-blue-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-blue-500">
<span>Upload a file</span>
<input id="file-upload" name="file-upload" type="file" className="sr-only" onChange={handleFileChange} accept=".doc,.docx,.xls,.xlsx,.png,.jpg,.jpeg" />
</label>
<p className="text-xs text-gray-500 mt-1">DOC, DOCX, XLS, XLSX, PNG, JPG up to 10MB</p>
</div>
{file && (
<div className="text-sm text-gray-700 mb-4">
Selected file: <strong>{file.name}</strong>
</div>
)}
<button
onClick={handleConvert}
disabled={!file || isConverting}
className="w-full bg-rose-500 text-white font-bold py-3 px-6 rounded-lg text-lg hover:bg-rose-600 disabled:bg-rose-300 flex items-center justify-center border-b-4 border-rose-700 active:border-b-2 active:translate-y-0.5 transition-all"
>
{isConverting ? <Loader className="animate-spin mr-2" /> : <FileText className="mr-2" />}
{isConverting ? 'Converting...' : 'Convert to PDF'}
</button>
{downloadLink && (
<div className="mt-6">
<h4 className="text-lg font-semibold text-green-700">Conversion Successful!</h4>
<a
href={downloadLink}
download="converted-file.pdf"
className="mt-2 inline-block bg-green-500 text-white font-bold py-3 px-6 rounded-lg text-lg hover:bg-green-600"
>
Download PDF
</a>
<p className="text-xs text-gray-500 mt-2">(This is a demo. No file was actually converted.)</p>
</div>
)}
</div>
</div>
);
};
// Currency Converter Screen Component
const CurrencyConverterScreen = () => {
const [amount, setAmount] = useState('1');
const [fromCurrency, setFromCurrency] = useState('USD');
const [toCurrency, setToCurrency] = useState('PKR');
const [convertedAmount, setConvertedAmount] = useState(null);
const [rates, setRates] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchRates = async () => {
try {
const response = await fetch('https://open.er-api.com/v6/latest/USD');
if (!response.ok) {
throw new Error('Failed to fetch exchange rates.');
}
const data = await response.json();
setRates(data.rates);
setIsLoading(false);
} catch (err) {
setError(err.message);
setIsLoading(false);
}
};
fetchRates();
}, []);
const handleConvert = () => {
if (!rates || !amount) return;
const numericAmount = parseFloat(amount);
if (isNaN(numericAmount)) return;
const amountInUSD = numericAmount / rates[fromCurrency];
const finalAmount = amountInUSD * rates[toCurrency];
setConvertedAmount(finalAmount.toFixed(2));
};
useEffect(() => {
if (rates) {
handleConvert();
}
}, [amount, fromCurrency, toCurrency, rates]);
if (isLoading) {
return <div className="flex justify-center items-center p-8"><Loader className="animate-spin h-12 w-12 text-blue-500" /></div>;
}
if (error) {
return <div className="text-center text-red-500 p-8">{error}</div>;
}
const currencies = rates ? Object.keys(rates) : [];
return (
<div className="p-4 md:p-8 max-w-2xl mx-auto bg-gray-50">
<div className="bg-white p-6 rounded-lg shadow-md">
<h3 className="text-2xl font-bold text-blue-800 mb-2 flex items-center justify-center gap-2"><RefreshCw size={24}/> Currency Converter</h3>
<p className="text-gray-600 mb-6 text-center">Get real-time exchange rates.</p>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 items-end">
<div>
<label htmlFor="amount" className="block text-sm font-medium text-gray-700">Amount</label>
<input type="number" id="amount" value={amount} onChange={(e) => setAmount(e.target.value)} className="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm" />
</div>
<div>
<label htmlFor="from" className="block text-sm font-medium text-gray-700">From</label>
<select id="from" value={fromCurrency} onChange={(e) => setFromCurrency(e.target.value)} className="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm">
{currencies.map(c => <option key={c}>{c}</option>)}
</select>
</div>
<div>
<label htmlFor="to" className="block text-sm font-medium text-gray-700">To</label>
<select id="to" value={toCurrency} onChange={(e) => setToCurrency(e.target.value)} className="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm">
{currencies.map(c => <option key={c}>{c}</option>)}
</select>
</div>
</div>
{convertedAmount !== null && (
<div className="mt-6 text-center bg-green-100 p-4 rounded-lg">
<p className="text-lg text-gray-700">{amount} {fromCurrency} =</p>
<p className="text-4xl font-bold text-green-700">{convertedAmount} {toCurrency}</p>
<p className="text-xs text-gray-500 mt-2">Rates are updated in real-time.</p>
</div>
)}
</div>
</div>
);
};
// MCQs Generator Screen Component
const McqsGeneratorScreen = () => {
const [file, setFile] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [mcqs, setMcqs] = useState([]);
const [error, setError] = useState('');
const [numMcqs, setNumMcqs] = useState(5);
const [difficulty, setDifficulty] = useState('Medium');
const [showAnswers, setShowAnswers] = useState(false);
const handleFileChange = (e) => {
const selectedFile = e.target.files[0];
if (selectedFile && selectedFile.type === 'application/pdf') {
setFile(selectedFile);
setMcqs([]);
setError('');
} else {
alert("Please select a PDF file.");
}
};
const handleGenerateMcqs = async () => {
if (!file) {
alert("Please upload a PDF file first.");
return;
}
setIsLoading(true);
setMcqs([]);
setError('');
setShowAnswers(false);
const sampleText = `Computer Science is the study of computers and computational systems. Unlike electrical and computer engineers, computer scientists deal mostly with software and software systems; this includes their theory, design, development, and application. Principal areas of study within Computer Science include artificial intelligence, computer systems and networks, security, database systems, human computer interaction, vision and graphics, numerical analysis, programming languages, software engineering, bioinformatics and theory of computing.`;
const prompt = `Based on the following text, create exactly ${numMcqs} multiple-choice questions (MCQs) with a ${difficulty} difficulty level.
Text: "${sampleText}"
Format the output as a valid JSON array where each object has three keys: "question" (string), "options" (an array of 4 strings), and "correctAnswer" (the string of the correct option).`;
const result = await callGeminiAPI(prompt);
try {
// Clean the response to make it valid JSON
const cleanedResult = result.replace(/```json/g, '').replace(/```/g, '').trim();
const parsedMcqs = JSON.parse(cleanedResult);
setMcqs(parsedMcqs);
} catch (e) {
console.error("Failed to parse MCQs JSON:", e);
setError("Sorry, there was an issue generating the MCQs. Please try again.");
}
setIsLoading(false);
};
return (
<div className="p-4 md:p-8 max-w-4xl mx-auto bg-gray-50">
<div className="bg-white p-6 rounded-lg shadow-md">
<div className="text-center">
<h3 className="text-2xl font-bold text-blue-800 mb-2 flex items-center justify-center gap-2"><HelpCircle size={24} /> MCQs Generator</h3>
<p className="text-gray-600 mb-6">Upload your PDF handout and get practice MCQs for your exams.</p>
</div>
<div className="border-2 border-dashed border-gray-300 rounded-lg p-8 mb-4 text-center">
<UploadCloud className="mx-auto h-12 w-12 text-gray-400" />
<label htmlFor="pdf-upload" className="relative cursor-pointer bg-white rounded-md font-medium text-blue-600 hover:text-blue-500">
<span>Upload a PDF file</span>
<input id="pdf-upload" name="pdf-upload" type="file" className="sr-only" onChange={handleFileChange} accept=".pdf" />
</label>
<p className="text-xs text-gray-500 mt-1">PDF files up to 10MB</p>
</div>
{file && <p className="text-sm text-gray-700 mb-4 text-center">Selected file: <strong>{file.name}</strong></p>}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
<div>
<label htmlFor="numMcqs" className="block text-sm font-medium text-gray-700">Number of Questions</label>
<input type="number" id="numMcqs" value={numMcqs} onChange={(e) => setNumMcqs(e.target.value)} min="1" max="10" className="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm" />
</div>
<div>
<label htmlFor="difficulty" className="block text-sm font-medium text-gray-700">Difficulty</label>
<select id="difficulty" value={difficulty} onChange={(e) => setDifficulty(e.target.value)} className="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm">
<option>Easy</option>
<option>Medium</option>
<option>Hard</option>
</select>
</div>
</div>
<button
onClick={handleGenerateMcqs}
disabled={!file || isLoading}
className="w-full bg-cyan-500 text-white font-bold py-3 px-6 rounded-lg text-lg hover:bg-cyan-600 disabled:bg-cyan-300 flex items-center justify-center border-b-4 border-cyan-700 active:border-b-2 active:translate-y-0.5 transition-all"
>
{isLoading ? <Loader className="animate-spin mr-2" /> : <Sparkles className="mr-2" />}
{isLoading ? 'Generating MCQs...' : 'Generate MCQs'}
</button>
{error && <p className="text-red-500 text-center mt-4">{error}</p>}
{mcqs.length > 0 && (
<div className="mt-8 text-left">
<div className="flex justify-between items-center mb-4">
<h4 className="text-xl font-bold text-blue-800">Generated Quiz</h4>
<button onClick={() => setShowAnswers(!showAnswers)} className="flex items-center gap-2 text-sm bg-gray-200 text-gray-700 py-1 px-3 rounded-md hover:bg-gray-300">
{showAnswers ? <EyeOff size={16} /> : <Eye size={16} />}
{showAnswers ? 'Hide Answers' : 'Show Answers'}
</button>
</div>
<div className="space-y-6">
{mcqs.map((mcq, index) => (
<div key={index} className="border-b pb-4">
<p className="font-semibold">{index + 1}. {mcq.question}</p>
<ul className="list-none mt-2 space-y-1">
{mcq.options.map((option, i) => (
<li key={i} className={`p-2 rounded-md ${showAnswers && option === mcq.correctAnswer ? 'bg-green-100 border border-green-300 text-green-800 font-bold' : 'bg-gray-50'}`}>
{String.fromCharCode(65 + i)}. {option}
</li>
))}
</ul>
</div>
))}
</div>
<p className="text-xs text-gray-500 mt-4 italic text-center">(This is a demo. MCQs were generated from sample text, not the actual PDF.)</p>
</div>
)}
</div>
</div>
);
};
// Job Portal Screen Component
const JobPortalScreen = () => {
const [jobs, setJobs] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [searchTerm, setSearchTerm] = useState('');
const [activeTab, setActiveTab] = useState('all');
useEffect(() => {
setIsLoading(true);
const sampleJobsFromSources = [
{ id: 1, source: 'PPSC', title: 'Lecturer in Computer Science', department: 'Higher Education Department', location: 'Punjab', url: 'https://www.ppsc.gop.pk/' },
{ id: 2, source: 'NTS', title: 'Project Director (IT)', department: 'Federal Government Agency', location: 'Islamabad', url: 'https://www.nts.org.pk/new/' },
{ id: 3, source: 'PPSC', title: 'Assistant Director (Admin)', department: 'Finance Department', location: 'Lahore', url: 'https://www.ppsc.gop.pk/' },
{ id: 4, source: 'NTS', title: 'Data Entry Operator', department: 'Ministry of IT & Telecom', location: 'Pakistan-wide', url: 'https://www.nts.org.pk/new/' },
{ id: 5, source: 'PPSC', title: 'Tehsildar/Consolidation Officer', department: 'Board of Revenue', location: 'Various Districts, Punjab', url: 'https://www.ppsc.gop.pk/' },
];
setTimeout(() => {
setJobs(sampleJobsFromSources);
setIsLoading(false);
}, 1500);
}, []);
const filteredJobs = jobs.filter(job =>
(job.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
job.department.toLowerCase().includes(searchTerm.toLowerCase())) &&
(activeTab === 'all' || job.source.toLowerCase() === activeTab)
);
const TabButton = ({ tabName, label }) => (
<button
onClick={() => setActiveTab(tabName)}
className={`py-2 px-4 rounded-full text-sm font-semibold transition-colors ${activeTab === tabName ? 'bg-blue-600 text-white' : 'bg-gray-200 text-gray-700 hover:bg-gray-300'}`}
>
{label}
</button>
);
return (
<div className="p-4 md:p-8 max-w-4xl mx-auto bg-gray-50">
<div className="bg-white p-6 rounded-lg shadow-md">
<h3 className="text-2xl font-bold text-blue-800 mb-2 flex items-center gap-2"><Briefcase size={24} /> Job Portal</h3>
<p className="text-gray-600 mb-6">Find your next career opportunity from official sources.</p>
<div className="relative mb-6">
<input
type="text"
placeholder="Search for jobs by title or department..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-full"
/>
<Search className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" size={20} />
</div>
<div className="flex space-x-2 mb-6 border-b pb-4">
<TabButton tabName="all" label="All Jobs" />
<TabButton tabName="ppsc" label="PPSC Jobs" />
<TabButton tabName="nts" label="NTS Jobs" />
</div>
{isLoading ? (
<div className="flex justify-center items-center p-8"><Loader className="animate-spin h-12 w-12 text-blue-500" /></div>
) : (
<div className="space-y-4">
{filteredJobs.map((job) => (
<div key={job.id} className="border border-gray-200 rounded-lg p-4 hover:shadow-md transition-shadow">
<div className="flex justify-between items-start">
<div>
<h4 className="font-bold text-lg text-blue-700">{job.title}</h4>
<p className="text-sm text-gray-600 font-semibold">{job.department}</p>
<p className="text-xs text-gray-500 mb-2">{job.location}</p>
</div>
<span className={`text-xs font-bold px-2 py-1 rounded-full ${job.source === 'PPSC' ? 'bg-green-100 text-green-800' : 'bg-blue-100 text-blue-800'}`}>
{job.source}
</span>
</div>
<a href={job.url} target="_blank" rel="noopener noreferrer" className="mt-3 inline-block bg-green-500 text-white font-bold py-2 px-4 rounded-lg text-sm hover:bg-green-600">
Apply Online
</a>
</div>
))}
</div>
)}
<p className="text-xs text-gray-500 mt-4 text-center italic">(Note: This is a demo with sample job listings. Click 'Apply Online' for official sites.)</p>
</div>
</div>
);
};