🎉 Complete AI-Enhanced University Portal - Ready for Production
✨ Major Features Added: - AI Chat with conversation memory and university-specific knowledge base - Multi-tenant university support with white-label capabilities - Professional admin interface for knowledge base management - Advanced database schema with Prisma ORM - Comprehensive documentation and guides - Modern Next.js 15 + React 19 architecture - Bilingual support (English/Arabic) - Role-based access control - Real-time chat interface with loading states 🔧 Technical Improvements: - Fixed all linter errors and TypeScript issues - Cleaned up codebase and removed legacy files - Added comprehensive .gitignore - Updated README with detailed setup instructions - Optimized database schema and migrations - Enhanced error handling and user experience 📚 Documentation: - AI Conversation Memory Guide - AI Enhancement Summary - Developer Guide - User Guide - Complete setup and deployment instructions 🚀 Ready for GitHub deployment and production use!
This commit is contained in:
@@ -0,0 +1,275 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
interface University {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
domain: string;
|
||||
subdomain: string;
|
||||
branding: any;
|
||||
contact: any;
|
||||
features: any;
|
||||
ai: any;
|
||||
}
|
||||
|
||||
interface Content {
|
||||
id: string;
|
||||
title: string;
|
||||
titleAr: string;
|
||||
contentType: string;
|
||||
content: string;
|
||||
contentAr: string;
|
||||
isPublished: boolean;
|
||||
universityId: string;
|
||||
}
|
||||
|
||||
interface Program {
|
||||
id: string;
|
||||
title: string;
|
||||
titleAr: string;
|
||||
level: string;
|
||||
duration: string;
|
||||
fees: string;
|
||||
entryRequirements: string;
|
||||
universityId: string;
|
||||
}
|
||||
|
||||
export default function DemoPage() {
|
||||
const [universities, setUniversities] = useState<University[]>([]);
|
||||
const [content, setContent] = useState<Content[]>([]);
|
||||
const [programs, setPrograms] = useState<Program[]>([]);
|
||||
const [selectedUniversity, setSelectedUniversity] = useState<string>('');
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const [unisRes, contentRes, programsRes] = await Promise.all([
|
||||
fetch('/api/universities'),
|
||||
fetch('/api/content'),
|
||||
fetch('/api/programs')
|
||||
]);
|
||||
|
||||
const unisData = await unisRes.json();
|
||||
const contentData = await contentRes.json();
|
||||
const programsData = await programsRes.json();
|
||||
|
||||
setUniversities(unisData.data);
|
||||
setContent(contentData.data);
|
||||
setPrograms(programsData.data);
|
||||
setSelectedUniversity(unisData.data[0]?.id || '');
|
||||
} catch (error) {
|
||||
console.error('Error fetching data:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const selectedUni = universities.find(u => u.id === selectedUniversity);
|
||||
const universityContent = content.filter(c => c.universityId === selectedUniversity);
|
||||
const universityPrograms = programs.filter(p => p.universityId === selectedUniversity);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="animate-spin rounded-full h-32 w-32 border-b-2 border-blue-600 mx-auto"></div>
|
||||
<p className="mt-4 text-lg text-gray-600">Loading demo data...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
|
||||
{/* Header */}
|
||||
<div className="bg-white shadow-lg">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
||||
<h1 className="text-4xl font-bold text-gray-900 mb-2">
|
||||
🎓 White-Label University Portal Demo
|
||||
</h1>
|
||||
<p className="text-xl text-gray-600">
|
||||
Multi-Tenant Architecture with Real Database Data
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
{/* University Selector */}
|
||||
<div className="bg-white rounded-lg shadow-md p-6 mb-8">
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-4">
|
||||
🏫 Select University (Multi-Tenant Demo)
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
{universities.map((uni) => (
|
||||
<div
|
||||
key={uni.id}
|
||||
onClick={() => setSelectedUniversity(uni.id)}
|
||||
className={`p-4 rounded-lg border-2 cursor-pointer transition-all ${
|
||||
selectedUniversity === uni.id
|
||||
? 'border-blue-500 bg-blue-50'
|
||||
: 'border-gray-200 hover:border-blue-300'
|
||||
}`}
|
||||
>
|
||||
<h3 className="font-semibold text-lg text-gray-900">{uni.name}</h3>
|
||||
<p className="text-sm text-gray-600">Domain: {uni.domain}</p>
|
||||
<p className="text-sm text-gray-600">Subdomain: {uni.subdomain}</p>
|
||||
<div className="mt-2">
|
||||
<span className="inline-block px-2 py-1 text-xs font-medium bg-green-100 text-green-800 rounded-full">
|
||||
Active
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{selectedUni && (
|
||||
<>
|
||||
{/* University Info */}
|
||||
<div className="bg-white rounded-lg shadow-md p-6 mb-8">
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-4">
|
||||
🎯 {selectedUni.name} - Configuration
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-3">Branding</h3>
|
||||
<div className="space-y-2">
|
||||
<p><span className="font-medium">Primary Color:</span> {selectedUni.branding.primaryColor}</p>
|
||||
<p><span className="font-medium">Tagline:</span> {selectedUni.branding.tagline}</p>
|
||||
<p><span className="font-medium">Theme:</span> {selectedUni.branding.theme}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-3">Contact</h3>
|
||||
<div className="space-y-2">
|
||||
<p><span className="font-medium">Email:</span> {selectedUni.contact.email}</p>
|
||||
<p><span className="font-medium">Phone:</span> {selectedUni.contact.phone}</p>
|
||||
<p><span className="font-medium">Address:</span> {selectedUni.contact.address}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="bg-white rounded-lg shadow-md p-6 mb-8">
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-4">
|
||||
📝 Content Management ({universityContent.length} items)
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{universityContent.map((item) => (
|
||||
<div key={item.id} className="border rounded-lg p-4">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<h3 className="font-semibold text-gray-900">{item.title}</h3>
|
||||
<span className="px-2 py-1 text-xs font-medium bg-blue-100 text-blue-800 rounded-full">
|
||||
{item.contentType}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 mb-2">{item.content.substring(0, 100)}...</p>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs text-gray-500">
|
||||
{item.isPublished ? '✅ Published' : '⏳ Draft'}
|
||||
</span>
|
||||
<span className="text-xs text-gray-500">Arabic: {item.titleAr ? '✅' : '❌'}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Programs */}
|
||||
<div className="bg-white rounded-lg shadow-md p-6 mb-8">
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-4">
|
||||
🎓 Academic Programs ({universityPrograms.length} programs)
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{universityPrograms.map((program) => (
|
||||
<div key={program.id} className="border rounded-lg p-4">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<h3 className="font-semibold text-gray-900">{program.title}</h3>
|
||||
<span className="px-2 py-1 text-xs font-medium bg-green-100 text-green-800 rounded-full">
|
||||
{program.level}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 mb-2">{program.duration}</p>
|
||||
<p className="text-sm font-medium text-gray-900 mb-2">Fees: {program.fees}</p>
|
||||
<p className="text-xs text-gray-500">Arabic: {program.titleAr ? '✅' : '❌'}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* AI Configuration */}
|
||||
<div className="bg-white rounded-lg shadow-md p-6 mb-8">
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-4">
|
||||
🤖 AI Configuration
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-3">AI Settings</h3>
|
||||
<div className="space-y-2">
|
||||
<p><span className="font-medium">Provider:</span> {selectedUni.ai.provider}</p>
|
||||
<p><span className="font-medium">Model:</span> {selectedUni.ai.model}</p>
|
||||
<p><span className="font-medium">Languages:</span> {selectedUni.ai.personality.language.join(', ')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-3">University Context</h3>
|
||||
<div className="space-y-2">
|
||||
<p><span className="font-medium">Location:</span> {selectedUni.ai.universityContext.location}</p>
|
||||
<p><span className="font-medium">Specializations:</span> {selectedUni.ai.universityContext.specializations.slice(0, 2).join(', ')}...</p>
|
||||
<p><span className="font-medium">Research Areas:</span> {selectedUni.ai.universityContext.researchAreas.slice(0, 2).join(', ')}...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Demo Instructions */}
|
||||
<div className="bg-blue-50 rounded-lg p-6">
|
||||
<h2 className="text-2xl font-bold text-blue-900 mb-4">
|
||||
🚀 How This Multi-Tenant System Works
|
||||
</h2>
|
||||
<div className="space-y-4 text-blue-800">
|
||||
<div>
|
||||
<h3 className="font-semibold">1. Data Isolation</h3>
|
||||
<p>Each university has its own data - content, programs, and configuration are completely separated.</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold">2. Dynamic Branding</h3>
|
||||
<p>Colors, logos, contact info, and AI personality are customized per university.</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold">3. Multi-Language Support</h3>
|
||||
<p>All content supports both English and Arabic with RTL layout.</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold">4. AI Customization</h3>
|
||||
<p>Each university has its own AI context, specializations, and knowledge base.</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold">5. Domain Management</h3>
|
||||
<p>Each university can have custom domains and subdomains with SSL certificates.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Navigation */}
|
||||
<div className="mt-8 text-center">
|
||||
<a
|
||||
href="/admin"
|
||||
className="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700"
|
||||
>
|
||||
🛠️ Go to Admin Dashboard
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user