main
This commit is contained in:
@@ -0,0 +1,392 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { useAuth } from '@/components/providers/MockAuthProvider'
|
||||
import { useLanguage } from '@/components/providers/LanguageProvider'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import {
|
||||
Upload,
|
||||
Image as ImageIcon,
|
||||
CheckCircle,
|
||||
XCircle,
|
||||
AlertCircle,
|
||||
Eye,
|
||||
LogOut,
|
||||
Languages,
|
||||
ArrowLeft
|
||||
} from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
|
||||
interface AccessibilityAudit {
|
||||
id: string
|
||||
altText: string
|
||||
wcagScore: number
|
||||
issues: string[]
|
||||
suggestions: string[]
|
||||
}
|
||||
|
||||
export default function AccessibilityPage() {
|
||||
const { user, userProfile, logout } = useAuth()
|
||||
const { t, language, setLanguage } = useLanguage()
|
||||
const router = useRouter()
|
||||
const [selectedFile, setSelectedFile] = useState<File | null>(null)
|
||||
const [preview, setPreview] = useState<string | null>(null)
|
||||
const [audit, setAudit] = useState<AccessibilityAudit | null>(null)
|
||||
const [isAnalyzing, setIsAnalyzing] = useState(false)
|
||||
const [recentAudits, setRecentAudits] = useState<AccessibilityAudit[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
if (!user) {
|
||||
router.push('/')
|
||||
}
|
||||
}, [user, router])
|
||||
|
||||
useEffect(() => {
|
||||
fetchRecentAudits()
|
||||
}, [])
|
||||
|
||||
const fetchRecentAudits = async () => {
|
||||
try {
|
||||
const response = await fetch('/api/accessibility')
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
setRecentAudits(data.audits || [])
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching audits:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleFileSelect = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = event.target.files?.[0]
|
||||
if (file) {
|
||||
setSelectedFile(file)
|
||||
|
||||
// Create preview
|
||||
const reader = new FileReader()
|
||||
reader.onload = (e) => {
|
||||
setPreview(e.target?.result as string)
|
||||
}
|
||||
reader.readAsDataURL(file)
|
||||
}
|
||||
}
|
||||
|
||||
const handleAnalyze = async () => {
|
||||
if (!selectedFile) return
|
||||
|
||||
setIsAnalyzing(true)
|
||||
try {
|
||||
const formData = new FormData()
|
||||
formData.append('file', selectedFile)
|
||||
|
||||
const response = await fetch('/api/accessibility', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
})
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
setAudit(data.audit)
|
||||
fetchRecentAudits()
|
||||
} else {
|
||||
console.error('Failed to analyze image')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error analyzing image:', error)
|
||||
} finally {
|
||||
setIsAnalyzing(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleLogout = async () => {
|
||||
await logout()
|
||||
}
|
||||
|
||||
const toggleLanguage = () => {
|
||||
setLanguage(language === 'en' ? 'ar' : 'en')
|
||||
}
|
||||
|
||||
const getScoreColor = (score: number) => {
|
||||
if (score >= 0.9) return 'text-green-600'
|
||||
if (score >= 0.7) return 'text-yellow-600'
|
||||
return 'text-red-600'
|
||||
}
|
||||
|
||||
const getScoreBackground = (score: number) => {
|
||||
if (score >= 0.9) return 'bg-green-50 border-green-200'
|
||||
if (score >= 0.7) return 'bg-yellow-50 border-yellow-200'
|
||||
return 'bg-red-50 border-red-200'
|
||||
}
|
||||
|
||||
if (!user || !userProfile) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
|
||||
<p className="text-gray-600">Loading accessibility tools...</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
{/* Header */}
|
||||
<header className="bg-white shadow-sm border-b">
|
||||
<div className="container mx-auto px-4 py-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center space-x-4">
|
||||
<Link href="/dashboard" className="flex items-center space-x-2 text-blue-600 hover:text-blue-800">
|
||||
<ArrowLeft size={20} />
|
||||
<span>Back to Dashboard</span>
|
||||
</Link>
|
||||
<div className="text-gray-300">|</div>
|
||||
<h1 className="text-xl font-bold text-gray-900">
|
||||
{t('accessibility')} Tools
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-4">
|
||||
<button
|
||||
onClick={toggleLanguage}
|
||||
className="flex items-center space-x-2 px-3 py-2 rounded-lg bg-gray-100 hover:bg-gray-200 transition-colors"
|
||||
>
|
||||
<Languages size={16} />
|
||||
<span className="text-sm font-medium">{language.toUpperCase()}</span>
|
||||
</button>
|
||||
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="w-8 h-8 bg-blue-600 rounded-full flex items-center justify-center">
|
||||
<span className="text-white text-sm font-medium">
|
||||
{userProfile.name.charAt(0)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="hidden md:block">
|
||||
<p className="text-sm font-medium text-gray-900">{userProfile.name}</p>
|
||||
<p className="text-xs text-gray-500">{userProfile.role}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="flex items-center space-x-2 px-3 py-2 rounded-lg bg-red-100 hover:bg-red-200 transition-colors text-red-700"
|
||||
>
|
||||
<LogOut size={16} />
|
||||
<span className="text-sm font-medium">{t('logout')}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Main Content */}
|
||||
<main className="container mx-auto px-4 py-8">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
{/* Upload Section */}
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6 mb-8">
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-6">
|
||||
Image Accessibility Analyzer
|
||||
</h2>
|
||||
<p className="text-gray-600 mb-6">
|
||||
Upload an image to automatically generate alt text and check WCAG 2.2 compliance.
|
||||
Our AI-powered tool helps ensure your content is accessible to everyone.
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
{/* Upload Area */}
|
||||
<div>
|
||||
<div className="border-2 border-dashed border-gray-300 rounded-lg p-8 text-center">
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={handleFileSelect}
|
||||
className="hidden"
|
||||
id="image-upload"
|
||||
/>
|
||||
<label
|
||||
htmlFor="image-upload"
|
||||
className="cursor-pointer flex flex-col items-center"
|
||||
>
|
||||
<Upload size={48} className="text-gray-400 mb-4" />
|
||||
<p className="text-lg font-medium text-gray-900 mb-2">
|
||||
{t('upload_image')}
|
||||
</p>
|
||||
<p className="text-sm text-gray-600">
|
||||
PNG, JPG, GIF up to 10MB
|
||||
</p>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{selectedFile && (
|
||||
<div className="mt-4">
|
||||
<p className="text-sm text-gray-600 mb-2">
|
||||
Selected: {selectedFile.name}
|
||||
</p>
|
||||
<button
|
||||
onClick={handleAnalyze}
|
||||
disabled={isAnalyzing}
|
||||
className="w-full bg-blue-600 text-white py-3 px-4 rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{isAnalyzing ? 'Analyzing...' : 'Analyze Image'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Preview */}
|
||||
{preview && (
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">
|
||||
Image Preview
|
||||
</h3>
|
||||
<div className="bg-gray-100 rounded-lg p-4">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={preview}
|
||||
alt="Preview"
|
||||
className="max-w-full h-auto rounded-lg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Analysis Results */}
|
||||
{audit && (
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6 mb-8">
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-6">
|
||||
Analysis Results
|
||||
</h2>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{/* Alt Text */}
|
||||
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
|
||||
<h3 className="text-lg font-semibold text-blue-900 mb-2">
|
||||
{t('alt_text_suggestion')}
|
||||
</h3>
|
||||
<p className="text-blue-800">{audit.altText}</p>
|
||||
</div>
|
||||
|
||||
{/* WCAG Score */}
|
||||
<div className={`border rounded-lg p-4 ${getScoreBackground(audit.wcagScore)}`}>
|
||||
<h3 className="text-lg font-semibold mb-2">
|
||||
{t('wcag_score')}
|
||||
</h3>
|
||||
<div className="flex items-center space-x-2">
|
||||
<span className={`text-2xl font-bold ${getScoreColor(audit.wcagScore)}`}>
|
||||
{Math.round(audit.wcagScore * 100)}%
|
||||
</span>
|
||||
{audit.wcagScore >= 0.9 ? (
|
||||
<CheckCircle className="text-green-600" size={24} />
|
||||
) : audit.wcagScore >= 0.7 ? (
|
||||
<AlertCircle className="text-yellow-600" size={24} />
|
||||
) : (
|
||||
<XCircle className="text-red-600" size={24} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Issues and Suggestions */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mt-6">
|
||||
{audit.issues.length > 0 && (
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-3">
|
||||
Issues Found
|
||||
</h3>
|
||||
<ul className="space-y-2">
|
||||
{audit.issues.map((issue, index) => (
|
||||
<li key={index} className="flex items-start space-x-2">
|
||||
<XCircle className="text-red-500 mt-0.5" size={16} />
|
||||
<span className="text-sm text-gray-700">{issue}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{audit.suggestions.length > 0 && (
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-3">
|
||||
Suggestions
|
||||
</h3>
|
||||
<ul className="space-y-2">
|
||||
{audit.suggestions.map((suggestion, index) => (
|
||||
<li key={index} className="flex items-start space-x-2">
|
||||
<CheckCircle className="text-green-500 mt-0.5" size={16} />
|
||||
<span className="text-sm text-gray-700">{suggestion}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Recent Audits */}
|
||||
{recentAudits.length > 0 && (
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-6">
|
||||
Recent Audits
|
||||
</h2>
|
||||
<div className="space-y-4">
|
||||
{recentAudits.slice(0, 5).map((recentAudit, index) => (
|
||||
<div key={index} className="flex items-center justify-between p-4 bg-gray-50 rounded-lg">
|
||||
<div className="flex items-center space-x-3">
|
||||
<ImageIcon className="text-gray-400" size={20} />
|
||||
<div>
|
||||
<p className="font-medium text-gray-900">
|
||||
{recentAudit.altText.substring(0, 50)}...
|
||||
</p>
|
||||
<p className="text-sm text-gray-600">
|
||||
Score: {Math.round(recentAudit.wcagScore * 100)}%
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Eye className="text-gray-400" size={16} />
|
||||
<span className="text-sm text-gray-600">View Details</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Guidelines */}
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6 mt-8">
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-6">
|
||||
Accessibility Guidelines
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-3">
|
||||
Alt Text Best Practices
|
||||
</h3>
|
||||
<ul className="space-y-2 text-sm text-gray-700">
|
||||
<li>• Be concise but descriptive</li>
|
||||
<li>• Focus on important details</li>
|
||||
<li>• Avoid redundant phrases like "image of"</li>
|
||||
<li>• Keep under 125 characters when possible</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-3">
|
||||
WCAG 2.2 Compliance
|
||||
</h3>
|
||||
<ul className="space-y-2 text-sm text-gray-700">
|
||||
<li>• Color contrast ratio of 4.5:1 minimum</li>
|
||||
<li>• Keyboard navigation support</li>
|
||||
<li>• Screen reader compatibility</li>
|
||||
<li>• Clear focus indicators</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,430 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { useAuth } from '@/components/providers/MockAuthProvider'
|
||||
import { useLanguage } from '@/components/providers/LanguageProvider'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import {
|
||||
BarChart3,
|
||||
Users,
|
||||
MessageSquare,
|
||||
Star,
|
||||
TrendingUp,
|
||||
Clock,
|
||||
Shield,
|
||||
Heart,
|
||||
LogOut,
|
||||
Languages,
|
||||
ArrowLeft,
|
||||
Eye,
|
||||
CheckCircle,
|
||||
AlertTriangle
|
||||
} from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, BarChart, Bar } from 'recharts'
|
||||
|
||||
interface SurveyStats {
|
||||
totalSurveys: number
|
||||
averageRating: number
|
||||
ratingDistribution: Record<number, number>
|
||||
}
|
||||
|
||||
export default function AdminPage() {
|
||||
const { user, userProfile, logout } = useAuth()
|
||||
const { t, language, setLanguage } = useLanguage()
|
||||
const router = useRouter()
|
||||
const [surveyStats, setSurveyStats] = useState<SurveyStats | null>(null)
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
if (!user) {
|
||||
router.push('/')
|
||||
return
|
||||
}
|
||||
|
||||
if (userProfile && userProfile.role !== 'ADMIN') {
|
||||
router.push('/dashboard')
|
||||
return
|
||||
}
|
||||
|
||||
fetchDashboardData()
|
||||
}, [user, userProfile, router])
|
||||
|
||||
const fetchDashboardData = async () => {
|
||||
try {
|
||||
const response = await fetch('/api/survey')
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
setSurveyStats(data.statistics)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching dashboard data:', error)
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleLogout = async () => {
|
||||
await logout()
|
||||
}
|
||||
|
||||
const toggleLanguage = () => {
|
||||
setLanguage(language === 'en' ? 'ar' : 'en')
|
||||
}
|
||||
|
||||
// Mock data for charts
|
||||
const responseTimeData = [
|
||||
{ name: 'Mon', time: 1.2 },
|
||||
{ name: 'Tue', time: 0.8 },
|
||||
{ name: 'Wed', time: 1.5 },
|
||||
{ name: 'Thu', time: 1.1 },
|
||||
{ name: 'Fri', time: 0.9 },
|
||||
{ name: 'Sat', time: 1.3 },
|
||||
{ name: 'Sun', time: 1.0 },
|
||||
]
|
||||
|
||||
const satisfactionData = [
|
||||
{ name: 'Week 1', satisfaction: 92 },
|
||||
{ name: 'Week 2', satisfaction: 88 },
|
||||
{ name: 'Week 3', satisfaction: 95 },
|
||||
{ name: 'Week 4', satisfaction: 91 },
|
||||
]
|
||||
|
||||
const ratingData = surveyStats ?
|
||||
Object.entries(surveyStats.ratingDistribution).map(([rating, count]) => ({
|
||||
rating: `${rating} Stars`,
|
||||
count
|
||||
})) : []
|
||||
|
||||
if (!user || !userProfile || isLoading) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
|
||||
<p className="text-gray-600">Loading admin dashboard...</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (userProfile.role !== 'ADMIN') {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<AlertTriangle className="mx-auto h-12 w-12 text-red-500 mb-4" />
|
||||
<h1 className="text-xl font-bold text-gray-900 mb-2">Access Denied</h1>
|
||||
<p className="text-gray-600">You don't have permission to access this page.</p>
|
||||
<Link href="/dashboard" className="mt-4 inline-block bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700">
|
||||
Go to Dashboard
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
{/* Header */}
|
||||
<header className="bg-white shadow-sm border-b">
|
||||
<div className="container mx-auto px-4 py-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center space-x-4">
|
||||
<Link href="/dashboard" className="flex items-center space-x-2 text-blue-600 hover:text-blue-800">
|
||||
<ArrowLeft size={20} />
|
||||
<span>Back to Dashboard</span>
|
||||
</Link>
|
||||
<div className="text-gray-300">|</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<BarChart3 className="text-blue-600" size={24} />
|
||||
<h1 className="text-xl font-bold text-gray-900">
|
||||
Admin Dashboard
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-4">
|
||||
<button
|
||||
onClick={toggleLanguage}
|
||||
className="flex items-center space-x-2 px-3 py-2 rounded-lg bg-gray-100 hover:bg-gray-200 transition-colors"
|
||||
>
|
||||
<Languages size={16} />
|
||||
<span className="text-sm font-medium">{language.toUpperCase()}</span>
|
||||
</button>
|
||||
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="w-8 h-8 bg-blue-600 rounded-full flex items-center justify-center">
|
||||
<span className="text-white text-sm font-medium">
|
||||
{userProfile.name.charAt(0)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="hidden md:block">
|
||||
<p className="text-sm font-medium text-gray-900">{userProfile.name}</p>
|
||||
<p className="text-xs text-gray-500">{userProfile.role}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="flex items-center space-x-2 px-3 py-2 rounded-lg bg-red-100 hover:bg-red-200 transition-colors text-red-700"
|
||||
>
|
||||
<LogOut size={16} />
|
||||
<span className="text-sm font-medium">{t('logout')}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Main Content */}
|
||||
<main className="container mx-auto px-4 py-8">
|
||||
{/* KPI Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
||||
<KPICard
|
||||
title="Active Users"
|
||||
value="12,543"
|
||||
change="+5.2%"
|
||||
trend="up"
|
||||
icon={<Users size={24} />}
|
||||
color="blue"
|
||||
/>
|
||||
<KPICard
|
||||
title="Avg Response Time"
|
||||
value="1.2s"
|
||||
change="-0.3s"
|
||||
trend="down"
|
||||
icon={<Clock size={24} />}
|
||||
color="green"
|
||||
/>
|
||||
<KPICard
|
||||
title="Satisfaction Rate"
|
||||
value={surveyStats ? `${Math.round(surveyStats.averageRating * 20)}%` : '94%'}
|
||||
change="+2.1%"
|
||||
trend="up"
|
||||
icon={<Star size={24} />}
|
||||
color="yellow"
|
||||
/>
|
||||
<KPICard
|
||||
title="Ticket Deflection"
|
||||
value="87%"
|
||||
change="+4.3%"
|
||||
trend="up"
|
||||
icon={<MessageSquare size={24} />}
|
||||
color="purple"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Charts Section */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 mb-8">
|
||||
{/* Response Time Chart */}
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">
|
||||
Average Response Time (This Week)
|
||||
</h3>
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<LineChart data={responseTimeData}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="name" />
|
||||
<YAxis />
|
||||
<Tooltip />
|
||||
<Line type="monotone" dataKey="time" stroke="#3B82F6" strokeWidth={2} />
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
|
||||
{/* Satisfaction Trend */}
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">
|
||||
User Satisfaction Trend
|
||||
</h3>
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<LineChart data={satisfactionData}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="name" />
|
||||
<YAxis />
|
||||
<Tooltip />
|
||||
<Line type="monotone" dataKey="satisfaction" stroke="#10B981" strokeWidth={2} />
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Rating Distribution */}
|
||||
{surveyStats && ratingData.length > 0 && (
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6 mb-8">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">
|
||||
Rating Distribution ({surveyStats.totalSurveys} responses)
|
||||
</h3>
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<BarChart data={ratingData}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="rating" />
|
||||
<YAxis />
|
||||
<Tooltip />
|
||||
<Bar dataKey="count" fill="#F59E0B" />
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
{/* System Health */}
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">
|
||||
System Health
|
||||
</h3>
|
||||
<div className="space-y-4">
|
||||
<HealthIndicator
|
||||
label="Chat Service"
|
||||
status="healthy"
|
||||
value="99.8% uptime"
|
||||
/>
|
||||
<HealthIndicator
|
||||
label="Database"
|
||||
status="healthy"
|
||||
value="Response time: 12ms"
|
||||
/>
|
||||
<HealthIndicator
|
||||
label="AI Assistant"
|
||||
status="healthy"
|
||||
value="Processing normally"
|
||||
/>
|
||||
<HealthIndicator
|
||||
label="Accessibility Scanner"
|
||||
status="warning"
|
||||
value="High load detected"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Recent Activity */}
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">
|
||||
Recent Activity
|
||||
</h3>
|
||||
<div className="space-y-4">
|
||||
<ActivityItem
|
||||
icon={<MessageSquare size={16} />}
|
||||
title="New chat session started"
|
||||
time="2 minutes ago"
|
||||
type="chat"
|
||||
/>
|
||||
<ActivityItem
|
||||
icon={<Star size={16} />}
|
||||
title="Survey response: 5 stars"
|
||||
time="5 minutes ago"
|
||||
type="survey"
|
||||
/>
|
||||
<ActivityItem
|
||||
icon={<Shield size={16} />}
|
||||
title="Accessibility scan completed"
|
||||
time="12 minutes ago"
|
||||
type="accessibility"
|
||||
/>
|
||||
<ActivityItem
|
||||
icon={<Heart size={16} />}
|
||||
title="Mental health escalation"
|
||||
time="25 minutes ago"
|
||||
type="mental-health"
|
||||
/>
|
||||
<ActivityItem
|
||||
icon={<Users size={16} />}
|
||||
title="New user registration"
|
||||
time="1 hour ago"
|
||||
type="user"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Helper Components
|
||||
const KPICard: React.FC<{
|
||||
title: string
|
||||
value: string
|
||||
change: string
|
||||
trend: 'up' | 'down'
|
||||
icon: React.ReactNode
|
||||
color: 'blue' | 'green' | 'yellow' | 'purple'
|
||||
}> = ({ title, value, change, trend, icon, color }) => {
|
||||
const colorClasses = {
|
||||
blue: 'bg-blue-50 border-blue-200 text-blue-600',
|
||||
green: 'bg-green-50 border-green-200 text-green-600',
|
||||
yellow: 'bg-yellow-50 border-yellow-200 text-yellow-600',
|
||||
purple: 'bg-purple-50 border-purple-200 text-purple-600',
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className={`p-3 rounded-lg ${colorClasses[color]}`}>
|
||||
{icon}
|
||||
</div>
|
||||
<div className={`flex items-center space-x-1 text-sm ${
|
||||
trend === 'up' ? 'text-green-600' : 'text-red-600'
|
||||
}`}>
|
||||
<TrendingUp size={16} className={trend === 'down' ? 'rotate-180' : ''} />
|
||||
<span>{change}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-2xl font-bold text-gray-900">{value}</p>
|
||||
<p className="text-sm text-gray-600">{title}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const HealthIndicator: React.FC<{
|
||||
label: string
|
||||
status: 'healthy' | 'warning' | 'error'
|
||||
value: string
|
||||
}> = ({ label, status, value }) => {
|
||||
const statusColors = {
|
||||
healthy: 'text-green-600 bg-green-50',
|
||||
warning: 'text-yellow-600 bg-yellow-50',
|
||||
error: 'text-red-600 bg-red-50',
|
||||
}
|
||||
|
||||
const StatusIcon = status === 'healthy' ? CheckCircle : AlertTriangle
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
||||
<div className="flex items-center space-x-3">
|
||||
<StatusIcon className={`${statusColors[status].split(' ')[0]}`} size={16} />
|
||||
<span className="font-medium text-gray-900">{label}</span>
|
||||
</div>
|
||||
<span className="text-sm text-gray-600">{value}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const ActivityItem: React.FC<{
|
||||
icon: React.ReactNode
|
||||
title: string
|
||||
time: string
|
||||
type: string
|
||||
}> = ({ icon, title, time, type }) => {
|
||||
const typeColors = {
|
||||
chat: 'text-blue-600 bg-blue-50',
|
||||
survey: 'text-yellow-600 bg-yellow-50',
|
||||
accessibility: 'text-green-600 bg-green-50',
|
||||
'mental-health': 'text-pink-600 bg-pink-50',
|
||||
user: 'text-purple-600 bg-purple-50',
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center space-x-3 p-3 bg-gray-50 rounded-lg">
|
||||
<div className={`p-2 rounded-lg ${typeColors[type as keyof typeof typeColors]}`}>
|
||||
{icon}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-medium text-gray-900">{title}</p>
|
||||
<p className="text-xs text-gray-500">{time}</p>
|
||||
</div>
|
||||
<Eye className="text-gray-400" size={16} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,545 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { useLanguage } from '@/components/providers/LanguageProvider';
|
||||
import {
|
||||
GraduationCap,
|
||||
Calendar,
|
||||
Users,
|
||||
CheckCircle,
|
||||
Clock,
|
||||
DollarSign,
|
||||
Globe,
|
||||
MapPin,
|
||||
Mail,
|
||||
Phone,
|
||||
ExternalLink
|
||||
} from 'lucide-react';
|
||||
import { mockCourses } from '@/lib/mockData';
|
||||
|
||||
export default function AdmissionsPage() {
|
||||
const { language, dir } = useLanguage();
|
||||
const [activeTab, setActiveTab] = useState('requirements');
|
||||
|
||||
// Application process steps
|
||||
const applicationSteps = [
|
||||
{
|
||||
step: 1,
|
||||
titleEn: 'Choose Your Program',
|
||||
titleAr: 'اختر برنامجك',
|
||||
descriptionEn: 'Browse our extensive range of undergraduate and postgraduate programs.',
|
||||
descriptionAr: 'تصفح مجموعتنا الواسعة من البرامج الجامعية والدراسات العليا.',
|
||||
timelineEn: '1-2 weeks',
|
||||
timelineAr: '1-2 أسابيع'
|
||||
},
|
||||
{
|
||||
step: 2,
|
||||
titleEn: 'Submit Application',
|
||||
titleAr: 'تقديم الطلب',
|
||||
descriptionEn: 'Complete your online application with all required documents.',
|
||||
descriptionAr: 'أكمل طلبك عبر الإنترنت مع جميع الوثائق المطلوبة.',
|
||||
timelineEn: '2-3 days',
|
||||
timelineAr: '2-3 أيام'
|
||||
},
|
||||
{
|
||||
step: 3,
|
||||
titleEn: 'Application Review',
|
||||
titleAr: 'مراجعة الطلب',
|
||||
descriptionEn: 'Our admissions team reviews your application and documents.',
|
||||
descriptionAr: 'يراجع فريق القبول لدينا طلبك ووثائقك.',
|
||||
timelineEn: '2-4 weeks',
|
||||
timelineAr: '2-4 أسابيع'
|
||||
},
|
||||
{
|
||||
step: 4,
|
||||
titleEn: 'Receive Offer',
|
||||
titleAr: 'استلام العرض',
|
||||
descriptionEn: 'If successful, you\'ll receive a conditional or unconditional offer.',
|
||||
descriptionAr: 'في حالة النجاح، ستتلقى عرضاً مشروطاً أو غير مشروط.',
|
||||
timelineEn: '1 week',
|
||||
timelineAr: '1 أسبوع'
|
||||
},
|
||||
{
|
||||
step: 5,
|
||||
titleEn: 'Accept & Enroll',
|
||||
titleAr: 'القبول والتسجيل',
|
||||
descriptionEn: 'Accept your offer and complete enrollment procedures.',
|
||||
descriptionAr: 'اقبل عرضك وأكمل إجراءات التسجيل.',
|
||||
timelineEn: '1-2 weeks',
|
||||
timelineAr: '1-2 أسابيع'
|
||||
}
|
||||
];
|
||||
|
||||
// Entry requirements by level
|
||||
const entryRequirements = {
|
||||
undergraduate: {
|
||||
titleEn: 'Undergraduate Requirements',
|
||||
titleAr: 'متطلبات البكالوريوس',
|
||||
requirements: {
|
||||
en: [
|
||||
'Australian Year 12 or equivalent international qualification',
|
||||
'English language proficiency (IELTS 6.0 or equivalent)',
|
||||
'Prerequisites specific to chosen program',
|
||||
'Personal statement (for some programs)',
|
||||
'Portfolio (for creative programs)'
|
||||
],
|
||||
ar: [
|
||||
'الصف الثاني عشر الأسترالي أو مؤهل دولي معادل',
|
||||
'إجادة اللغة الإنجليزية (IELTS 6.0 أو ما يعادله)',
|
||||
'المتطلبات المسبقة الخاصة بالبرنامج المختار',
|
||||
'بيان شخصي (لبعض البرامج)',
|
||||
'محفظة أعمال (للبرامج الإبداعية)'
|
||||
]
|
||||
}
|
||||
},
|
||||
postgraduate: {
|
||||
titleEn: 'Postgraduate Requirements',
|
||||
titleAr: 'متطلبات الدراسات العليا',
|
||||
requirements: {
|
||||
en: [
|
||||
'Bachelor\'s degree or equivalent from recognized institution',
|
||||
'English language proficiency (IELTS 6.5 or equivalent)',
|
||||
'Academic transcripts and certificates',
|
||||
'CV/Resume and personal statement',
|
||||
'Letters of recommendation (for research degrees)',
|
||||
'Research proposal (for PhD programs)'
|
||||
],
|
||||
ar: [
|
||||
'درجة البكالوريوس أو ما يعادلها من مؤسسة معترف بها',
|
||||
'إجادة اللغة الإنجليزية (IELTS 6.5 أو ما يعادله)',
|
||||
'كشوف الدرجات والشهادات الأكاديمية',
|
||||
'السيرة الذاتية والبيان الشخصي',
|
||||
'خطابات التوصية (لدرجات البحث)',
|
||||
'مقترح البحث (لبرامج الدكتوراه)'
|
||||
]
|
||||
}
|
||||
},
|
||||
international: {
|
||||
titleEn: 'International Student Requirements',
|
||||
titleAr: 'متطلبات الطلاب الدوليين',
|
||||
requirements: {
|
||||
en: [
|
||||
'Valid passport and student visa',
|
||||
'Academic credentials assessment',
|
||||
'English language test results',
|
||||
'Financial capacity evidence',
|
||||
'Health insurance coverage',
|
||||
'Overseas Student Health Cover (OSHC)'
|
||||
],
|
||||
ar: [
|
||||
'جواز سفر صالح وتأشيرة طالب',
|
||||
'تقييم الأوراق الأكاديمية',
|
||||
'نتائج اختبار اللغة الإنجليزية',
|
||||
'دليل القدرة المالية',
|
||||
'تغطية التأمين الصحي',
|
||||
'تغطية صحية للطلاب الأجانب (OSHC)'
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Important dates
|
||||
const importantDates = [
|
||||
{
|
||||
dateEn: 'October 31, 2024',
|
||||
dateAr: '31 أكتوبر 2024',
|
||||
eventEn: 'Applications Open',
|
||||
eventAr: 'فتح التطبيقات',
|
||||
typeEn: 'Application',
|
||||
typeAr: 'التطبيق'
|
||||
},
|
||||
{
|
||||
dateEn: 'January 15, 2025',
|
||||
dateAr: '15 يناير 2025',
|
||||
eventEn: 'Semester 1 Applications Close',
|
||||
eventAr: 'إغلاق طلبات الفصل الأول',
|
||||
typeEn: 'Deadline',
|
||||
typeAr: 'الموعد النهائي'
|
||||
},
|
||||
{
|
||||
dateEn: 'February 24, 2025',
|
||||
dateAr: '24 فبراير 2025',
|
||||
eventEn: 'Semester 1 Commences',
|
||||
eventAr: 'بداية الفصل الأول',
|
||||
typeEn: 'Start Date',
|
||||
typeAr: 'تاريخ البداية'
|
||||
},
|
||||
{
|
||||
dateEn: 'June 15, 2025',
|
||||
dateAr: '15 يونيو 2025',
|
||||
eventEn: 'Semester 2 Applications Close',
|
||||
eventAr: 'إغلاق طلبات الفصل الثاني',
|
||||
typeEn: 'Deadline',
|
||||
typeAr: 'الموعد النهائي'
|
||||
},
|
||||
{
|
||||
dateEn: 'July 28, 2025',
|
||||
dateAr: '28 يوليو 2025',
|
||||
eventEn: 'Semester 2 Commences',
|
||||
eventAr: 'بداية الفصل الثاني',
|
||||
typeEn: 'Start Date',
|
||||
typeAr: 'تاريخ البداية'
|
||||
}
|
||||
];
|
||||
|
||||
// Support services
|
||||
const supportServices = [
|
||||
{
|
||||
titleEn: 'Academic Support',
|
||||
titleAr: 'الدعم الأكاديمي',
|
||||
descriptionEn: 'Tutoring, study groups, and academic skills workshops.',
|
||||
descriptionAr: 'التدريس والمجموعات الدراسية وورش المهارات الأكاديمية.',
|
||||
iconColor: 'text-blue-500'
|
||||
},
|
||||
{
|
||||
titleEn: 'Student Accommodation',
|
||||
titleAr: 'سكن الطلاب',
|
||||
descriptionEn: 'On-campus and off-campus housing options with support.',
|
||||
descriptionAr: 'خيارات السكن داخل وخارج الحرم الجامعي مع الدعم.',
|
||||
iconColor: 'text-green-500'
|
||||
},
|
||||
{
|
||||
titleEn: 'Career Services',
|
||||
titleAr: 'خدمات الوظائف',
|
||||
descriptionEn: 'Job placement, internships, and career development programs.',
|
||||
descriptionAr: 'التوظيف والتدريب وبرامج تطوير المهنة.',
|
||||
iconColor: 'text-purple-500'
|
||||
},
|
||||
{
|
||||
titleEn: 'International Student Support',
|
||||
titleAr: 'دعم الطلاب الدوليين',
|
||||
descriptionEn: 'Visa assistance, orientation programs, and cultural integration.',
|
||||
descriptionAr: 'مساعدة التأشيرة وبرامج التوجيه والاندماج الثقافي.',
|
||||
iconColor: 'text-orange-500'
|
||||
},
|
||||
{
|
||||
titleEn: 'Financial Aid',
|
||||
titleAr: 'المساعدة المالية',
|
||||
descriptionEn: 'Scholarships, grants, and financial planning assistance.',
|
||||
descriptionAr: 'المنح الدراسية والمنح والمساعدة في التخطيط المالي.',
|
||||
iconColor: 'text-green-600'
|
||||
},
|
||||
{
|
||||
titleEn: 'Disability Services',
|
||||
titleAr: 'خدمات الإعاقة',
|
||||
descriptionEn: 'Accessibility support and reasonable adjustments.',
|
||||
descriptionAr: 'دعم إمكانية الوصول والتعديلات المعقولة.',
|
||||
iconColor: 'text-red-500'
|
||||
}
|
||||
];
|
||||
|
||||
// Filter popular courses
|
||||
const popularCourses = mockCourses.slice(0, 6);
|
||||
|
||||
return (
|
||||
<div className={`min-h-screen bg-gray-50 ${dir === 'rtl' ? 'font-arabic' : ''}`} dir={dir}>
|
||||
{/* Hero Section */}
|
||||
<div className="bg-gradient-to-r from-purple-600 to-blue-600 text-white py-16">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<h1 className="text-4xl md:text-5xl font-bold mb-6">
|
||||
{language === 'en' ? 'Admissions' : 'القبول'}
|
||||
</h1>
|
||||
<p className="text-xl opacity-90 mb-8">
|
||||
{language === 'en'
|
||||
? 'Join UTAS and embark on your educational journey. We\'re here to guide you through every step of the application process.'
|
||||
: 'انضم إلى UTAS وابدأ رحلتك التعليمية. نحن هنا لإرشادك خلال كل خطوة من خطوات عملية التقديم.'
|
||||
}
|
||||
</p>
|
||||
<div className="flex justify-center space-x-8 text-center">
|
||||
<div>
|
||||
<div className="text-3xl font-bold">150+</div>
|
||||
<div className="opacity-80">{language === 'en' ? 'Programs' : 'برنامج'}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-3xl font-bold">95%</div>
|
||||
<div className="opacity-80">{language === 'en' ? 'Graduate Employment' : 'توظيف الخريجين'}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-3xl font-bold">100+</div>
|
||||
<div className="opacity-80">{language === 'en' ? 'Countries Represented' : 'دولة ممثلة'}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
{/* Navigation Tabs */}
|
||||
<div className="flex justify-center mb-8">
|
||||
<div className="bg-white rounded-lg p-1 shadow-lg">
|
||||
{[
|
||||
{ id: 'requirements', labelEn: 'Requirements', labelAr: 'المتطلبات' },
|
||||
{ id: 'process', labelEn: 'Application Process', labelAr: 'عملية التقديم' },
|
||||
{ id: 'dates', labelEn: 'Important Dates', labelAr: 'تواريخ مهمة' },
|
||||
{ id: 'support', labelEn: 'Student Support', labelAr: 'دعم الطلاب' }
|
||||
].map(tab => (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setActiveTab(tab.id)}
|
||||
className={`px-6 py-3 rounded-md font-semibold transition-colors ${
|
||||
activeTab === tab.id
|
||||
? 'bg-purple-600 text-white'
|
||||
: 'text-gray-600 hover:text-purple-600'
|
||||
}`}
|
||||
>
|
||||
{language === 'en' ? tab.labelEn : tab.labelAr}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Requirements Tab */}
|
||||
{activeTab === 'requirements' && (
|
||||
<div className="space-y-8">
|
||||
{Object.values(entryRequirements).map((category, index) => (
|
||||
<div key={index} className="bg-white rounded-xl shadow-lg p-8">
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-6 flex items-center">
|
||||
<GraduationCap className="mr-3 text-purple-600" size={28} />
|
||||
{language === 'en' ? category.titleEn : category.titleAr}
|
||||
</h2>
|
||||
<div className="grid lg:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<ul className="space-y-4">
|
||||
{(language === 'en' ? category.requirements.en : category.requirements.ar).map((requirement, reqIndex) => (
|
||||
<li key={reqIndex} className="flex items-start">
|
||||
<CheckCircle className="w-5 h-5 text-green-500 mr-3 mt-0.5 flex-shrink-0" />
|
||||
<span className="text-gray-700">{requirement}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="bg-purple-50 rounded-lg p-6">
|
||||
<h3 className="font-semibold text-purple-900 mb-3">
|
||||
{language === 'en' ? 'Quick Tips' : 'نصائح سريعة'}
|
||||
</h3>
|
||||
<ul className="space-y-2 text-sm text-purple-700">
|
||||
<li>• {language === 'en' ? 'Apply early for better chances' : 'تقدم مبكراً لفرص أفضل'}</li>
|
||||
<li>• {language === 'en' ? 'Prepare documents in advance' : 'حضر الوثائق مسبقاً'}</li>
|
||||
<li>• {language === 'en' ? 'Contact our admissions team for help' : 'اتصل بفريق القبول للمساعدة'}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Application Process Tab */}
|
||||
{activeTab === 'process' && (
|
||||
<div className="space-y-8">
|
||||
<div className="bg-white rounded-xl shadow-lg p-8">
|
||||
<h2 className="text-3xl font-bold text-gray-900 mb-8 text-center">
|
||||
{language === 'en' ? 'Application Process' : 'عملية التقديم'}
|
||||
</h2>
|
||||
<div className="space-y-8">
|
||||
{applicationSteps.map((step, index) => (
|
||||
<div key={step.step} className="flex items-start">
|
||||
<div className="flex-shrink-0 w-12 h-12 bg-purple-600 text-white rounded-full flex items-center justify-center font-bold text-lg mr-6">
|
||||
{step.step}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between mb-2">
|
||||
<h3 className="text-xl font-bold text-gray-900">
|
||||
{language === 'en' ? step.titleEn : step.titleAr}
|
||||
</h3>
|
||||
<div className="flex items-center text-sm text-purple-600 font-medium">
|
||||
<Clock className="w-4 h-4 mr-1" />
|
||||
{language === 'en' ? step.timelineEn : step.timelineAr}
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-gray-600">
|
||||
{language === 'en' ? step.descriptionEn : step.descriptionAr}
|
||||
</p>
|
||||
{index < applicationSteps.length - 1 && (
|
||||
<div className="w-px h-8 bg-gray-300 ml-6 mt-4"></div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-8 text-center">
|
||||
<button className="bg-purple-600 text-white px-8 py-3 rounded-lg font-semibold hover:bg-purple-700 transition-colors">
|
||||
{language === 'en' ? 'Start Your Application' : 'ابدأ طلبك'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Popular Programs */}
|
||||
<div className="bg-white rounded-xl shadow-lg p-8">
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-6">
|
||||
{language === 'en' ? 'Popular Programs' : 'البرامج الشائعة'}
|
||||
</h2>
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{popularCourses.map(course => (
|
||||
<div key={course.id} className="border border-gray-200 rounded-lg p-6 hover:shadow-md transition-shadow">
|
||||
<h3 className="text-lg font-bold text-gray-900 mb-2">
|
||||
{language === 'en' ? course.title : course.titleAr}
|
||||
</h3>
|
||||
<div className="space-y-2 text-sm text-gray-600 mb-4">
|
||||
<div className="flex items-center">
|
||||
<Clock className="w-4 h-4 mr-2" />
|
||||
<span>{course.duration}</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<MapPin className="w-4 h-4 mr-2" />
|
||||
<span>{language === 'en' ? course.campus : course.campusAr}</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<DollarSign className="w-4 h-4 mr-2" />
|
||||
<span>{course.fees}</span>
|
||||
</div>
|
||||
</div>
|
||||
<button className="w-full bg-purple-50 text-purple-700 py-2 px-4 rounded-lg font-medium hover:bg-purple-100 transition-colors">
|
||||
{language === 'en' ? 'Learn More' : 'تعلم المزيد'}
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Important Dates Tab */}
|
||||
{activeTab === 'dates' && (
|
||||
<div className="bg-white rounded-xl shadow-lg p-8">
|
||||
<h2 className="text-3xl font-bold text-gray-900 mb-8 text-center">
|
||||
{language === 'en' ? 'Important Dates 2024-2025' : 'تواريخ مهمة 2024-2025'}
|
||||
</h2>
|
||||
<div className="space-y-6">
|
||||
{importantDates.map((date, index) => (
|
||||
<div key={index} className="flex items-center justify-between p-6 bg-gray-50 rounded-lg hover:bg-gray-100 transition-colors">
|
||||
<div className="flex items-center">
|
||||
<div className="w-12 h-12 bg-purple-600 text-white rounded-full flex items-center justify-center mr-4">
|
||||
<Calendar size={20} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-gray-900">
|
||||
{language === 'en' ? date.eventEn : date.eventAr}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600">
|
||||
{language === 'en' ? date.typeEn : date.typeAr}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<div className="font-semibold text-purple-600">
|
||||
{language === 'en' ? date.dateEn : date.dateAr}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-8 text-center">
|
||||
<button className="bg-purple-600 text-white px-6 py-3 rounded-lg font-semibold hover:bg-purple-700 transition-colors mr-4">
|
||||
{language === 'en' ? 'Download Academic Calendar' : 'تحميل التقويم الأكاديمي'}
|
||||
</button>
|
||||
<button className="bg-gray-200 text-gray-700 px-6 py-3 rounded-lg font-semibold hover:bg-gray-300 transition-colors">
|
||||
{language === 'en' ? 'Set Reminders' : 'تعيين التذكيرات'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Student Support Tab */}
|
||||
{activeTab === 'support' && (
|
||||
<div className="space-y-8">
|
||||
<div className="bg-white rounded-xl shadow-lg p-8">
|
||||
<h2 className="text-3xl font-bold text-gray-900 mb-8 text-center">
|
||||
{language === 'en' ? 'Student Support Services' : 'خدمات دعم الطلاب'}
|
||||
</h2>
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{supportServices.map((service, index) => (
|
||||
<div key={index} className="text-center">
|
||||
<div className={`w-16 h-16 rounded-full bg-gray-100 flex items-center justify-center mx-auto mb-4 ${service.iconColor}`}>
|
||||
<Users size={32} />
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-gray-900 mb-3">
|
||||
{language === 'en' ? service.titleEn : service.titleAr}
|
||||
</h3>
|
||||
<p className="text-gray-600 text-sm">
|
||||
{language === 'en' ? service.descriptionEn : service.descriptionAr}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Contact Information */}
|
||||
<div className="bg-white rounded-xl shadow-lg p-8">
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-6">
|
||||
{language === 'en' ? 'Contact Admissions' : 'اتصل بالقبول'}
|
||||
</h2>
|
||||
<div className="grid md:grid-cols-2 gap-8">
|
||||
<div>
|
||||
<h3 className="font-semibold text-gray-900 mb-4">
|
||||
{language === 'en' ? 'General Inquiries' : 'الاستفسارات العامة'}
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center">
|
||||
<Mail className="w-5 h-5 text-purple-600 mr-3" />
|
||||
<span>admissions@utas.edu.au</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<Phone className="w-5 h-5 text-purple-600 mr-3" />
|
||||
<span>+61 3 6226 2999</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<Globe className="w-5 h-5 text-purple-600 mr-3" />
|
||||
<span>www.utas.edu.au/admissions</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-gray-900 mb-4">
|
||||
{language === 'en' ? 'International Students' : 'الطلاب الدوليون'}
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center">
|
||||
<Mail className="w-5 h-5 text-blue-600 mr-3" />
|
||||
<span>international@utas.edu.au</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<Phone className="w-5 h-5 text-blue-600 mr-3" />
|
||||
<span>+61 3 6226 1800</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-6 flex gap-4">
|
||||
<button className="flex-1 bg-purple-600 text-white py-3 px-4 rounded-lg font-semibold hover:bg-purple-700 transition-colors">
|
||||
{language === 'en' ? 'Schedule a Call' : 'جدولة مكالمة'}
|
||||
</button>
|
||||
<button className="flex-1 bg-gray-200 text-gray-700 py-3 px-4 rounded-lg font-semibold hover:bg-gray-300 transition-colors flex items-center justify-center gap-2">
|
||||
<ExternalLink size={16} />
|
||||
{language === 'en' ? 'Live Chat' : 'الدردشة المباشرة'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Call to Action */}
|
||||
<div className="mt-16 bg-gradient-to-r from-purple-600 to-blue-600 rounded-xl p-8 text-white text-center">
|
||||
<h2 className="text-3xl font-bold mb-4">
|
||||
{language === 'en' ? 'Ready to Start Your Journey?' : 'هل أنت مستعد لبدء رحلتك؟'}
|
||||
</h2>
|
||||
<p className="text-xl opacity-90 mb-8 max-w-2xl mx-auto">
|
||||
{language === 'en'
|
||||
? 'Join thousands of students who have chosen UTAS for their education. Apply now and take the first step towards your future.'
|
||||
: 'انضم إلى آلاف الطلاب الذين اختاروا UTAS لتعليمهم. تقدم الآن واتخذ الخطوة الأولى نحو مستقبلك.'
|
||||
}
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<button className="bg-white text-purple-600 px-8 py-3 rounded-lg font-semibold hover:bg-gray-100 transition-colors">
|
||||
{language === 'en' ? 'Apply Now' : 'تقدم الآن'}
|
||||
</button>
|
||||
<button className="bg-purple-700 text-white px-8 py-3 rounded-lg font-semibold hover:bg-purple-800 transition-colors">
|
||||
{language === 'en' ? 'Book Information Session' : 'احجز جلسة معلومات'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
'use client';
|
||||
|
||||
import { useLanguage } from '@/components/providers/LanguageProvider';
|
||||
import { Snowflake, Mountain, Ship, Microscope, Users } from 'lucide-react';
|
||||
|
||||
const AntarcticPage = () => {
|
||||
const { language } = useLanguage();
|
||||
const isRTL = language === 'ar';
|
||||
|
||||
const expeditions = [
|
||||
{
|
||||
year: "2024",
|
||||
title: "Antarctic Climate Monitoring Mission",
|
||||
description: "Deploying advanced sensors to track ice sheet changes and their global impact",
|
||||
participants: 45,
|
||||
duration: "6 months",
|
||||
image: "🌨️"
|
||||
},
|
||||
{
|
||||
year: "2023",
|
||||
title: "Deep Sea Antarctic Exploration",
|
||||
description: "Discovering new marine species in the Southern Ocean depths",
|
||||
participants: 32,
|
||||
duration: "4 months",
|
||||
image: "🐟"
|
||||
},
|
||||
{
|
||||
year: "2023",
|
||||
title: "Ice Core Historical Analysis",
|
||||
description: "Extracting 100,000 years of climate data from Antarctic ice cores",
|
||||
participants: 28,
|
||||
duration: "5 months",
|
||||
image: "🧊"
|
||||
}
|
||||
];
|
||||
|
||||
const discoveries = [
|
||||
{
|
||||
title: "New Antarctic Fish Species",
|
||||
description: "UTAS researchers discovered 15 new fish species adapted to extreme cold, revolutionizing our understanding of polar marine biodiversity.",
|
||||
impact: "Published in Nature: Marine Biology",
|
||||
year: "2024"
|
||||
},
|
||||
{
|
||||
title: "Ice Sheet Stability Model",
|
||||
description: "Breakthrough computer modeling predicting Antarctic ice sheet behavior over the next century, informing global sea level rise projections.",
|
||||
impact: "Cited by IPCC Climate Reports",
|
||||
year: "2023"
|
||||
},
|
||||
{
|
||||
title: "Polar Microorganism Medicine",
|
||||
description: "Antarctic bacteria showing promise for new antibiotics resistant to current drug-resistant infections.",
|
||||
impact: "3 patents filed, clinical trials pending",
|
||||
year: "2023"
|
||||
}
|
||||
];
|
||||
|
||||
const facilities = [
|
||||
{
|
||||
name: "Australian Antarctic Division HQ",
|
||||
location: "Hobart, Tasmania",
|
||||
description: "Australia's primary Antarctic research coordination center",
|
||||
icon: <Mountain className="w-8 h-8" />
|
||||
},
|
||||
{
|
||||
name: "Research Vessel Aurora Australis",
|
||||
location: "Southern Ocean",
|
||||
description: "State-of-the-art polar research vessel for Antarctic expeditions",
|
||||
icon: <Ship className="w-8 h-8" />
|
||||
},
|
||||
{
|
||||
name: "Casey Station Collaboration",
|
||||
location: "Antarctic Territory",
|
||||
description: "Year-round research station for climate and marine studies",
|
||||
icon: <Snowflake className="w-8 h-8" />
|
||||
},
|
||||
{
|
||||
name: "Polar Medicine Centre",
|
||||
location: "UTAS Campus",
|
||||
description: "World-leading research in polar medicine and extreme environment health",
|
||||
icon: <Microscope className="w-8 h-8" />
|
||||
}
|
||||
];
|
||||
|
||||
const stats = [
|
||||
{ value: "40+", label: "Years in Antarctica", description: "Continuous research presence" },
|
||||
{ value: "200+", label: "Expeditions Led", description: "Scientific missions completed" },
|
||||
{ value: "1,500+", label: "Research Papers", description: "Published Antarctic studies" },
|
||||
{ value: "50+", label: "Countries Partnered", description: "International collaborations" }
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={`min-h-screen bg-gradient-to-br from-blue-50 via-white to-cyan-50 ${isRTL ? 'rtl' : 'ltr'}`}>
|
||||
{/* Hero Section */}
|
||||
<div className="relative bg-gradient-to-r from-blue-900 via-cyan-900 to-blue-800 text-white overflow-hidden">
|
||||
<div className="absolute inset-0 bg-black/20"></div>
|
||||
<div className="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-24">
|
||||
<div className="text-center">
|
||||
<div className="flex justify-center mb-6">
|
||||
<div className="bg-white/10 backdrop-blur-sm rounded-full p-6">
|
||||
<Snowflake className="w-16 h-16 text-cyan-300" />
|
||||
</div>
|
||||
</div>
|
||||
<h1 className="text-5xl md:text-7xl font-bold mb-6 bg-gradient-to-r from-white to-cyan-200 bg-clip-text text-transparent">
|
||||
Antarctic Excellence
|
||||
</h1>
|
||||
<p className="text-xl md:text-2xl mb-8 text-cyan-100 max-w-4xl mx-auto">
|
||||
Leading the world in polar research, climate science, and Antarctic exploration
|
||||
for over four decades
|
||||
</p>
|
||||
<div className="text-6xl mb-8">🇦🇶</div>
|
||||
<p className="text-lg text-cyan-200">
|
||||
Australia's Gateway to the Antarctic
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stats Section */}
|
||||
<div className="bg-white py-16">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-8">
|
||||
{stats.map((stat, index) => (
|
||||
<div key={index} className="text-center">
|
||||
<div className="text-4xl md:text-5xl font-bold text-blue-600 mb-2">
|
||||
{stat.value}
|
||||
</div>
|
||||
<div className="text-lg font-semibold text-gray-900 mb-1">
|
||||
{stat.label}
|
||||
</div>
|
||||
<div className="text-sm text-gray-600">
|
||||
{stat.description}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Current Expeditions */}
|
||||
<div className="py-16 bg-gradient-to-r from-blue-50 to-cyan-50">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<h2 className="text-4xl font-bold text-center mb-12 text-gray-900">
|
||||
Current Expeditions
|
||||
</h2>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
{expeditions.map((expedition, index) => (
|
||||
<div key={index} className="bg-white rounded-xl shadow-lg overflow-hidden hover:shadow-xl transition-shadow duration-300">
|
||||
<div className="p-6">
|
||||
<div className="text-6xl text-center mb-4">{expedition.image}</div>
|
||||
<div className="bg-blue-600 text-white px-3 py-1 rounded-full text-sm font-semibold inline-block mb-3">
|
||||
{expedition.year}
|
||||
</div>
|
||||
<h3 className="text-xl font-bold mb-3 text-gray-900">
|
||||
{expedition.title}
|
||||
</h3>
|
||||
<p className="text-gray-700 mb-4">
|
||||
{expedition.description}
|
||||
</p>
|
||||
<div className="flex justify-between text-sm text-gray-600">
|
||||
<div className="flex items-center gap-1">
|
||||
<Users className="w-4 h-4" />
|
||||
{expedition.participants} researchers
|
||||
</div>
|
||||
<div>
|
||||
Duration: {expedition.duration}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Major Discoveries */}
|
||||
<div className="py-16 bg-white">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<h2 className="text-4xl font-bold text-center mb-12 text-gray-900">
|
||||
Groundbreaking Discoveries
|
||||
</h2>
|
||||
|
||||
<div className="space-y-8">
|
||||
{discoveries.map((discovery, index) => (
|
||||
<div key={index} className="bg-gradient-to-r from-blue-50 to-cyan-50 rounded-xl p-8 hover:shadow-lg transition-shadow duration-300">
|
||||
<div className="flex flex-col md:flex-row justify-between items-start gap-6">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<h3 className="text-2xl font-bold text-gray-900">
|
||||
{discovery.title}
|
||||
</h3>
|
||||
<span className="bg-blue-600 text-white px-3 py-1 rounded-full text-sm font-semibold">
|
||||
{discovery.year}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-gray-700 mb-4 text-lg">
|
||||
{discovery.description}
|
||||
</p>
|
||||
<div className="bg-green-100 text-green-800 px-4 py-2 rounded-lg inline-block">
|
||||
<strong>Impact:</strong> {discovery.impact}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Research Facilities */}
|
||||
<div className="py-16 bg-gradient-to-r from-cyan-50 to-blue-50">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<h2 className="text-4xl font-bold text-center mb-12 text-gray-900">
|
||||
World-Class Facilities
|
||||
</h2>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
{facilities.map((facility, index) => (
|
||||
<div key={index} className="bg-white rounded-xl p-6 shadow-lg hover:shadow-xl transition-shadow duration-300">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="bg-blue-100 text-blue-600 p-3 rounded-lg">
|
||||
{facility.icon}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-xl font-bold mb-2 text-gray-900">
|
||||
{facility.name}
|
||||
</h3>
|
||||
<div className="text-sm text-blue-600 mb-2 font-semibold">
|
||||
📍 {facility.location}
|
||||
</div>
|
||||
<p className="text-gray-700">
|
||||
{facility.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Call to Action */}
|
||||
<div className="bg-gradient-to-r from-blue-600 to-cyan-600 py-16">
|
||||
<div className="max-w-4xl mx-auto text-center px-4 sm:px-6 lg:px-8">
|
||||
<h2 className="text-4xl font-bold text-white mb-6">
|
||||
Join the Antarctic Adventure
|
||||
</h2>
|
||||
<p className="text-xl text-blue-100 mb-8">
|
||||
Be part of the next generation of polar researchers shaping our understanding of climate change
|
||||
</p>
|
||||
<div className="flex flex-wrap justify-center gap-4">
|
||||
<button className="bg-white text-blue-600 px-8 py-4 rounded-lg font-semibold hover:bg-blue-50 transition-colors text-lg">
|
||||
Explore Antarctic Programs
|
||||
</button>
|
||||
<button className="border-2 border-white text-white px-8 py-4 rounded-lg font-semibold hover:bg-white hover:text-blue-600 transition-colors text-lg">
|
||||
Research Opportunities
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AntarcticPage;
|
||||
@@ -0,0 +1,73 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
// Mock data for demo purposes
|
||||
const mockAudits = [
|
||||
{
|
||||
id: '1',
|
||||
fileName: 'homepage-banner.jpg',
|
||||
originalAltText: '',
|
||||
suggestedAltText: 'University campus building with students walking in the foreground',
|
||||
wcagScore: 85,
|
||||
improvements: [
|
||||
'Add descriptive alt text to improve accessibility',
|
||||
'Ensure sufficient color contrast',
|
||||
'Consider adding captions for better understanding'
|
||||
],
|
||||
createdAt: new Date('2024-12-01T10:00:00Z'),
|
||||
userId: '1'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
fileName: 'course-diagram.png',
|
||||
originalAltText: 'diagram',
|
||||
suggestedAltText: 'Flow chart showing course prerequisites with arrows connecting related subjects',
|
||||
wcagScore: 92,
|
||||
improvements: [
|
||||
'Current alt text is good',
|
||||
'Consider adding more descriptive details',
|
||||
'Ensure text is readable at all zoom levels'
|
||||
],
|
||||
createdAt: new Date('2024-12-02T14:30:00Z'),
|
||||
userId: '1'
|
||||
}
|
||||
];
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
// Return mock data instead of database query
|
||||
return NextResponse.json(mockAudits);
|
||||
} catch (error) {
|
||||
console.error('Error fetching accessibility audits:', error);
|
||||
return NextResponse.json({ error: 'Failed to fetch audits' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const formData = await request.formData();
|
||||
const file = formData.get('file') as File;
|
||||
|
||||
if (!file) {
|
||||
return NextResponse.json({ error: 'No file uploaded' }, { status: 400 });
|
||||
}
|
||||
|
||||
// Mock AI analysis results
|
||||
const mockAnalysis = {
|
||||
fileName: file.name,
|
||||
suggestedAltText: `Professional photograph showing ${file.name.replace(/\.[^/.]+$/, "").replace(/[-_]/g, ' ')} in a clear, well-lit environment`,
|
||||
wcagScore: Math.floor(Math.random() * 20) + 80, // Random score between 80-100
|
||||
improvements: [
|
||||
'Add descriptive alt text for screen readers',
|
||||
'Ensure image has sufficient color contrast',
|
||||
'Consider adding captions for complex images',
|
||||
'Verify image is meaningful and not decorative'
|
||||
],
|
||||
confidence: 0.95
|
||||
};
|
||||
|
||||
return NextResponse.json(mockAnalysis);
|
||||
} catch (error) {
|
||||
console.error('Error processing accessibility audit:', error);
|
||||
return NextResponse.json({ error: 'Failed to process audit' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { OpenAI } from 'openai'
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
|
||||
const openai = new OpenAI({
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
})
|
||||
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const formData = await request.formData()
|
||||
const file = formData.get('file') as File
|
||||
const url = formData.get('url') as string
|
||||
|
||||
if (!file && !url) {
|
||||
return NextResponse.json({ error: 'File or URL is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
let altText = ''
|
||||
let wcagScore = 0
|
||||
const issues: string[] = []
|
||||
const suggestions: string[] = []
|
||||
|
||||
if (file) {
|
||||
// Convert file to base64 for OpenAI Vision API
|
||||
const bytes = await file.arrayBuffer()
|
||||
const buffer = Buffer.from(bytes)
|
||||
const base64 = buffer.toString('base64')
|
||||
|
||||
// Generate alt text using OpenAI Vision
|
||||
const response = await openai.chat.completions.create({
|
||||
model: 'gpt-4o-mini',
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: 'Generate concise, descriptive alt text for this image that would be useful for screen readers. Focus on the most important visual elements and context.',
|
||||
},
|
||||
{
|
||||
type: 'image_url',
|
||||
image_url: {
|
||||
url: `data:${file.type};base64,${base64}`,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
max_tokens: 100,
|
||||
})
|
||||
|
||||
altText = response.choices[0]?.message?.content || 'Unable to generate alt text'
|
||||
}
|
||||
|
||||
// Simulate WCAG compliance checking
|
||||
if (!altText || altText.length < 10) {
|
||||
issues.push('Missing or inadequate alt text')
|
||||
suggestions.push('Add descriptive alt text for all images')
|
||||
wcagScore = 0.3
|
||||
} else if (altText.length > 125) {
|
||||
issues.push('Alt text is too long')
|
||||
suggestions.push('Keep alt text under 125 characters')
|
||||
wcagScore = 0.7
|
||||
} else {
|
||||
wcagScore = 0.95
|
||||
}
|
||||
|
||||
// Additional WCAG checks (simulated)
|
||||
if (url) {
|
||||
// Simulate checking color contrast, heading structure, etc.
|
||||
const randomFactor = Math.random()
|
||||
if (randomFactor < 0.3) {
|
||||
issues.push('Low color contrast detected')
|
||||
suggestions.push('Ensure color contrast ratio is at least 4.5:1')
|
||||
wcagScore = Math.min(wcagScore, 0.6)
|
||||
}
|
||||
if (randomFactor < 0.2) {
|
||||
issues.push('Missing heading structure')
|
||||
suggestions.push('Use proper heading hierarchy (h1, h2, h3, etc.)')
|
||||
wcagScore = Math.min(wcagScore, 0.5)
|
||||
}
|
||||
}
|
||||
|
||||
// Save audit to database
|
||||
const audit = await prisma.accessibilityAudit.create({
|
||||
data: {
|
||||
url: url || 'uploaded-image',
|
||||
imagePath: file ? file.name : null,
|
||||
altText,
|
||||
wcagScore,
|
||||
issues: JSON.stringify(issues),
|
||||
suggestions: JSON.stringify(suggestions),
|
||||
},
|
||||
})
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
audit: {
|
||||
id: audit.id,
|
||||
altText,
|
||||
wcagScore,
|
||||
issues,
|
||||
suggestions,
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error in accessibility API:', error)
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to process accessibility audit' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const audits = await prisma.accessibilityAudit.findMany({
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
take: 50,
|
||||
})
|
||||
|
||||
const avgScore = audits.length > 0
|
||||
? audits.reduce((sum: number, audit: { wcagScore: number | null }) =>
|
||||
sum + (audit.wcagScore || 0), 0) / audits.length
|
||||
: 0
|
||||
|
||||
return NextResponse.json({
|
||||
audits,
|
||||
statistics: {
|
||||
totalAudits: audits.length,
|
||||
averageScore: Math.round(avgScore * 100) / 100,
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error fetching accessibility audits:', error)
|
||||
return NextResponse.json({ error: 'Failed to fetch audits' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
// Mock data for demo purposes
|
||||
const mockAudits = [
|
||||
{
|
||||
id: '1',
|
||||
fileName: 'homepage-banner.jpg',
|
||||
originalAltText: '',
|
||||
suggestedAltText: 'University campus building with students walking in the foreground',
|
||||
wcagScore: 85,
|
||||
improvements: [
|
||||
'Add descriptive alt text to improve accessibility',
|
||||
'Ensure sufficient color contrast',
|
||||
'Consider adding captions for better understanding'
|
||||
],
|
||||
createdAt: new Date('2024-12-01T10:00:00Z'),
|
||||
userId: '1'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
fileName: 'course-diagram.png',
|
||||
originalAltText: 'diagram',
|
||||
suggestedAltText: 'Flow chart showing course prerequisites with arrows connecting related subjects',
|
||||
wcagScore: 92,
|
||||
improvements: [
|
||||
'Current alt text is good',
|
||||
'Consider adding more descriptive details',
|
||||
'Ensure text is readable at all zoom levels'
|
||||
],
|
||||
createdAt: new Date('2024-12-02T14:30:00Z'),
|
||||
userId: '1'
|
||||
}
|
||||
];
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
// Return mock data instead of database query
|
||||
return NextResponse.json(mockAudits);
|
||||
} catch (error) {
|
||||
console.error('Error fetching accessibility audits:', error);
|
||||
return NextResponse.json({ error: 'Failed to fetch audits' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const formData = await request.formData();
|
||||
const file = formData.get('file') as File;
|
||||
|
||||
if (!file) {
|
||||
return NextResponse.json({ error: 'No file uploaded' }, { status: 400 });
|
||||
}
|
||||
|
||||
// Mock AI analysis results
|
||||
const mockAnalysis = {
|
||||
fileName: file.name,
|
||||
suggestedAltText: `Professional photograph showing ${file.name.replace(/\.[^/.]+$/, "").replace(/[-_]/g, ' ')} in a clear, well-lit environment`,
|
||||
wcagScore: Math.floor(Math.random() * 20) + 80, // Random score between 80-100
|
||||
improvements: [
|
||||
'Add descriptive alt text for screen readers',
|
||||
'Ensure image has sufficient color contrast',
|
||||
'Consider adding captions for complex images',
|
||||
'Verify image is meaningful and not decorative'
|
||||
],
|
||||
confidence: 0.95
|
||||
};
|
||||
|
||||
return NextResponse.json(mockAnalysis);
|
||||
} catch (error) {
|
||||
console.error('Error processing accessibility audit:', error);
|
||||
return NextResponse.json({ error: 'Failed to process audit' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
interface ApplicationData {
|
||||
type: 'undergraduate' | 'postgraduate' | 'research' | 'international';
|
||||
personalInfo: {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
dateOfBirth: string;
|
||||
citizenship: string;
|
||||
address: string;
|
||||
};
|
||||
academicInfo: {
|
||||
previousEducation: string;
|
||||
atar?: number;
|
||||
transcripts: string[];
|
||||
englishProficiency?: string;
|
||||
};
|
||||
coursePreferences: {
|
||||
firstChoice: string;
|
||||
secondChoice?: string;
|
||||
thirdChoice?: string;
|
||||
campus: string;
|
||||
startDate: string;
|
||||
};
|
||||
documents: string[];
|
||||
scholarshipInterest: boolean;
|
||||
}
|
||||
|
||||
// Mock application database
|
||||
const applications: Array<ApplicationData & { id: string; status: string; submittedAt: string }> = [];
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const { action, ...data } = await request.json();
|
||||
|
||||
switch (action) {
|
||||
case 'submit':
|
||||
const applicationId = `APP-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
const newApplication = {
|
||||
id: applicationId,
|
||||
...data as ApplicationData,
|
||||
status: 'submitted',
|
||||
submittedAt: new Date().toISOString()
|
||||
};
|
||||
|
||||
applications.push(newApplication);
|
||||
|
||||
// Send confirmation email (mock)
|
||||
console.log('Application submitted:', {
|
||||
id: applicationId,
|
||||
email: data.personalInfo?.email,
|
||||
course: data.coursePreferences?.firstChoice
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
applicationId,
|
||||
message: 'Application submitted successfully',
|
||||
nextSteps: [
|
||||
'Check your email for confirmation',
|
||||
'Upload required documents if not already provided',
|
||||
'Monitor application status in your portal',
|
||||
'Await assessment (typically 2-4 weeks)',
|
||||
'Respond to offer if successful'
|
||||
],
|
||||
estimatedProcessingTime: '2-4 weeks',
|
||||
contactInfo: {
|
||||
phone: '+61 3 6226 6200',
|
||||
email: 'admissions@utas.edu.au',
|
||||
hours: 'Monday-Friday 9:00 AM - 5:00 PM'
|
||||
}
|
||||
});
|
||||
|
||||
case 'getStatus':
|
||||
const { applicationId: statusId } = data;
|
||||
const application = applications.find(app => app.id === statusId);
|
||||
|
||||
if (!application) {
|
||||
return NextResponse.json({
|
||||
error: 'Application not found'
|
||||
}, { status: 404 });
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
application: {
|
||||
id: application.id,
|
||||
status: application.status,
|
||||
submittedAt: application.submittedAt,
|
||||
course: application.coursePreferences.firstChoice,
|
||||
campus: application.coursePreferences.campus
|
||||
},
|
||||
timeline: [
|
||||
{ step: 'Application Submitted', completed: true, date: application.submittedAt },
|
||||
{ step: 'Document Verification', completed: false, estimated: '1-2 weeks' },
|
||||
{ step: 'Academic Assessment', completed: false, estimated: '2-3 weeks' },
|
||||
{ step: 'Offer Decision', completed: false, estimated: '3-4 weeks' },
|
||||
{ step: 'Enrollment', completed: false, estimated: 'Upon acceptance' }
|
||||
]
|
||||
});
|
||||
|
||||
case 'getRequirements':
|
||||
const { courseType, citizenship } = data;
|
||||
|
||||
const requirements = {
|
||||
undergraduate: {
|
||||
domestic: [
|
||||
'Completed Year 12 or equivalent',
|
||||
'ATAR score or alternative entry pathway',
|
||||
'Prerequisite subjects for specific courses',
|
||||
'English language proficiency',
|
||||
'Valid identification documents'
|
||||
],
|
||||
international: [
|
||||
'Completed secondary education equivalent to Australian Year 12',
|
||||
'Academic transcripts (officially translated)',
|
||||
'English proficiency (IELTS 6.0+ or equivalent)',
|
||||
'Student visa documentation',
|
||||
'Financial capacity evidence',
|
||||
'Health insurance (OSHC)'
|
||||
]
|
||||
},
|
||||
postgraduate: {
|
||||
domestic: [
|
||||
'Completed bachelor degree or equivalent',
|
||||
'Academic transcripts',
|
||||
'Work experience (for some programs)',
|
||||
'Professional references',
|
||||
'English language proficiency'
|
||||
],
|
||||
international: [
|
||||
'Completed bachelor degree equivalent to Australian standard',
|
||||
'Academic transcripts (officially translated)',
|
||||
'English proficiency (IELTS 6.5+ or equivalent)',
|
||||
'Student visa documentation',
|
||||
'Financial capacity evidence',
|
||||
'Health insurance (OSHC)',
|
||||
'Professional experience (where required)'
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const citizenshipType = citizenship === 'australian' || citizenship === 'permanent_resident'
|
||||
? 'domestic' : 'international';
|
||||
|
||||
return NextResponse.json({
|
||||
requirements: requirements[courseType as keyof typeof requirements]?.[citizenshipType] || [],
|
||||
deadlines: {
|
||||
semester1: {
|
||||
domestic: 'December 31, 2024',
|
||||
international: 'October 31, 2024'
|
||||
},
|
||||
semester2: {
|
||||
domestic: 'May 31, 2025',
|
||||
international: 'March 31, 2025'
|
||||
}
|
||||
},
|
||||
fees: {
|
||||
undergraduate: {
|
||||
domestic: 'Commonwealth Supported Places available',
|
||||
international: '$32,000 - $45,000 per year'
|
||||
},
|
||||
postgraduate: {
|
||||
domestic: '$25,000 - $40,000 per year',
|
||||
international: '$35,000 - $50,000 per year'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
default:
|
||||
return NextResponse.json({
|
||||
error: 'Invalid action'
|
||||
}, { status: 400 });
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Application API error:', error);
|
||||
return NextResponse.json({
|
||||
error: 'Failed to process application request'
|
||||
}, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(request: Request) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const type = searchParams.get('type') || 'info';
|
||||
|
||||
if (type === 'info') {
|
||||
return NextResponse.json({
|
||||
applicationTypes: [
|
||||
{
|
||||
type: 'undergraduate',
|
||||
title: 'Undergraduate Applications',
|
||||
description: 'Bachelor degrees, diplomas, and certificates',
|
||||
eligibility: 'Year 12 completion or equivalent',
|
||||
portal: 'UAC UTAS portal'
|
||||
},
|
||||
{
|
||||
type: 'postgraduate',
|
||||
title: 'Postgraduate Applications',
|
||||
description: 'Masters, graduate certificates and diplomas',
|
||||
eligibility: 'Bachelor degree or equivalent + work experience',
|
||||
portal: 'Direct UTAS application'
|
||||
},
|
||||
{
|
||||
type: 'research',
|
||||
title: 'Research Degrees',
|
||||
description: 'PhD, Masters by Research',
|
||||
eligibility: 'Honours degree or masters + research proposal',
|
||||
portal: 'Research degree portal'
|
||||
},
|
||||
{
|
||||
type: 'international',
|
||||
title: 'International Applications',
|
||||
description: 'For students requiring a student visa',
|
||||
eligibility: 'Varies by course + English proficiency',
|
||||
portal: 'International student portal'
|
||||
}
|
||||
],
|
||||
support: {
|
||||
phone: '+61 3 6226 6200',
|
||||
email: 'admissions@utas.edu.au',
|
||||
chat: 'Available 24/7 through this portal',
|
||||
hours: 'Monday-Friday 9:00 AM - 5:00 PM AEST'
|
||||
},
|
||||
scholarships: {
|
||||
available: true,
|
||||
types: [
|
||||
'Merit-based scholarships up to $5,000/year',
|
||||
'Tasmanian scholarships up to $15,000/year',
|
||||
'International student scholarships',
|
||||
'Program-specific scholarships',
|
||||
'Indigenous student support',
|
||||
'Rural and regional scholarships'
|
||||
],
|
||||
deadline: 'Apply early for best scholarship opportunities'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
error: 'Invalid request type'
|
||||
}, { status: 400 });
|
||||
|
||||
} catch (error) {
|
||||
console.error('Application info error:', error);
|
||||
return NextResponse.json({
|
||||
error: 'Failed to get application information'
|
||||
}, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
// Mock chat responses
|
||||
const mockResponses = [
|
||||
{
|
||||
trigger: ['library', 'hours', 'open'],
|
||||
response: {
|
||||
en: 'The library is open Monday-Friday 8:00 AM - 10:00 PM, Saturday 9:00 AM - 6:00 PM, and Sunday 12:00 PM - 8:00 PM. During exam periods, we have extended hours until midnight.',
|
||||
ar: 'المكتبة مفتوحة من الاثنين إلى الجمعة من 8:00 صباحاً حتى 10:00 مساءً، يوم السبت من 9:00 صباحاً حتى 6:00 مساءً، والأحد من 12:00 ظهراً حتى 8:00 مساءً. خلال فترات الامتحانات، لدينا ساعات ممتدة حتى منتصف الليل.'
|
||||
}
|
||||
},
|
||||
{
|
||||
trigger: ['password', 'change', 'reset'],
|
||||
response: {
|
||||
en: 'To change your password, go to Settings > Account > Change Password. You can also reset it using the "Forgot Password" link on the login page.',
|
||||
ar: 'لتغيير كلمة المرور الخاصة بك، اذهب إلى الإعدادات > الحساب > تغيير كلمة المرور. يمكنك أيضاً إعادة تعيينها باستخدام رابط "نسيت كلمة المرور" في صفحة تسجيل الدخول.'
|
||||
}
|
||||
},
|
||||
{
|
||||
trigger: ['registration', 'semester', 'enroll'],
|
||||
response: {
|
||||
en: 'Registration for the next semester opens on January 15th for continuing students and February 1st for new students. Please check your academic calendar for specific dates.',
|
||||
ar: 'التسجيل للفصل الدراسي القادم يفتح في 15 يناير للطلاب المستمرين و 1 فبراير للطلاب الجدد. يرجى مراجعة التقويم الأكاديمي للتواريخ المحددة.'
|
||||
}
|
||||
},
|
||||
{
|
||||
trigger: ['help', 'support', 'contact'],
|
||||
response: {
|
||||
en: 'For academic support, contact Student Services at support@university.edu or call (555) 123-4567. For technical issues, email IT help desk at it@university.edu.',
|
||||
ar: 'للدعم الأكاديمي، اتصل بخدمات الطلاب على support@university.edu أو اتصل بالرقم (555) 123-4567. للمشاكل التقنية، راسل مكتب المساعدة التقنية على it@university.edu.'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
// Mental health keywords that trigger escalation
|
||||
const mentalHealthKeywords = [
|
||||
'depressed', 'depression', 'anxiety', 'anxious', 'stressed', 'stress',
|
||||
'overwhelmed', 'suicide', 'self-harm', 'hurt myself', 'kill myself',
|
||||
'hopeless', 'worthless', 'sad', 'crying', 'panic', 'fear',
|
||||
'مكتئب', 'اكتئاب', 'قلق', 'قلقان', 'متوتر', 'توتر',
|
||||
'مرهق', 'انتحار', 'إيذاء النفس', 'أؤذي نفسي', 'أقتل نفسي',
|
||||
'يائس', 'عديم القيمة', 'حزين', 'بكاء', 'هلع', 'خوف'
|
||||
];
|
||||
|
||||
function findBestResponse(message: string, language: string = 'en') {
|
||||
const lowerMessage = message.toLowerCase();
|
||||
|
||||
// Check for mental health keywords first
|
||||
const hasMentalHealthKeyword = mentalHealthKeywords.some(keyword =>
|
||||
lowerMessage.includes(keyword.toLowerCase())
|
||||
);
|
||||
|
||||
if (hasMentalHealthKeyword) {
|
||||
return {
|
||||
response: language === 'ar'
|
||||
? 'أفهم أنك تمر بوقت صعب. من المهم أن تطلب المساعدة من المختصين. يمكنك التواصل مع خدمة الاستشارة الجامعية على الرقم (555) 123-4567 أو زيارة مركز الصحة النفسية في الحرم الجامعي. في حالات الطوارئ، اتصل بالرقم 911 أو خط المساعدة الوطني للأزمات النفسية.'
|
||||
: 'I understand you\'re going through a difficult time. It\'s important to seek help from professionals. You can contact the university counseling service at (555) 123-4567 or visit the mental health center on campus. In emergencies, call 911 or the National Crisis Helpline.',
|
||||
escalate: true,
|
||||
category: 'mental_health'
|
||||
};
|
||||
}
|
||||
|
||||
// Look for FAQ matches
|
||||
for (const faq of mockResponses) {
|
||||
const hasMatch = faq.trigger.some(trigger =>
|
||||
lowerMessage.includes(trigger.toLowerCase())
|
||||
);
|
||||
|
||||
if (hasMatch) {
|
||||
return {
|
||||
response: faq.response[language as keyof typeof faq.response],
|
||||
escalate: false,
|
||||
category: 'faq'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Default response
|
||||
return {
|
||||
response: language === 'ar'
|
||||
? 'شكراً لك على سؤالك. يمكنني مساعدتك في العثور على المعلومات التي تحتاجها. جرب أن تسأل عن ساعات المكتبة، أو تغيير كلمة المرور، أو التسجيل للفصل الدراسي.'
|
||||
: 'Thank you for your question. I can help you find the information you need. Try asking about library hours, changing your password, or semester registration.',
|
||||
escalate: false,
|
||||
category: 'general'
|
||||
};
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const { message, language = 'en' } = body;
|
||||
|
||||
if (!message) {
|
||||
return NextResponse.json({ error: 'Message is required' }, { status: 400 });
|
||||
}
|
||||
|
||||
const result = findBestResponse(message, language);
|
||||
|
||||
return NextResponse.json({
|
||||
response: result.response,
|
||||
escalate: result.escalate,
|
||||
category: result.category,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error processing chat:', error);
|
||||
return NextResponse.json({ error: 'Failed to process message' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import UTASChatBot from '@/lib/chatbot';
|
||||
|
||||
// Initialize the chatbot (will use OpenRouter if API key is available)
|
||||
const chatbot = new UTASChatBot();
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { message, conversationHistory = [] } = await request.json();
|
||||
|
||||
if (!message || typeof message !== 'string') {
|
||||
return NextResponse.json({
|
||||
error: 'Message is required and must be a string'
|
||||
}, { status: 400 });
|
||||
}
|
||||
|
||||
// Generate AI response using RAG and potentially OpenRouter
|
||||
const response = await chatbot.generateResponse(message, conversationHistory);
|
||||
|
||||
// Log the interaction for demo purposes
|
||||
console.log('UTAS Chat:', {
|
||||
timestamp: new Date().toISOString(),
|
||||
message: message.substring(0, 100),
|
||||
response: response.substring(0, 100)
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
message: response,
|
||||
timestamp: new Date().toISOString(),
|
||||
source: 'UTAS AI Assistant'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Chat API Error:', error);
|
||||
|
||||
return NextResponse.json({
|
||||
message: "I apologize, but I'm experiencing technical difficulties. Please try again or contact UTAS directly at +61 3 6226 6200 or info@utas.edu.au for immediate assistance.",
|
||||
timestamp: new Date().toISOString(),
|
||||
source: 'UTAS AI Assistant',
|
||||
error: true
|
||||
}, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
return NextResponse.json({
|
||||
service: 'UTAS AI Chat Assistant',
|
||||
status: 'active',
|
||||
features: [
|
||||
'RAG-powered responses using UTAS knowledge base',
|
||||
'OpenRouter AI integration (when API key provided)',
|
||||
'Real-time course and program information',
|
||||
'Application guidance and support',
|
||||
'Campus and research information'
|
||||
],
|
||||
endpoints: {
|
||||
POST: 'Send message and conversation history',
|
||||
GET: 'Service status and information'
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
// Mock chat responses
|
||||
const mockResponses = [
|
||||
{
|
||||
trigger: ['library', 'hours', 'open'],
|
||||
response: {
|
||||
en: 'The library is open Monday-Friday 8:00 AM - 10:00 PM, Saturday 9:00 AM - 6:00 PM, and Sunday 12:00 PM - 8:00 PM. During exam periods, we have extended hours until midnight.',
|
||||
ar: 'المكتبة مفتوحة من الاثنين إلى الجمعة من 8:00 صباحاً حتى 10:00 مساءً، يوم السبت من 9:00 صباحاً حتى 6:00 مساءً، والأحد من 12:00 ظهراً حتى 8:00 مساءً. خلال فترات الامتحانات، لدينا ساعات ممتدة حتى منتصف الليل.'
|
||||
}
|
||||
},
|
||||
{
|
||||
trigger: ['password', 'change', 'reset'],
|
||||
response: {
|
||||
en: 'To change your password, go to Settings > Account > Change Password. You can also reset it using the "Forgot Password" link on the login page.',
|
||||
ar: 'لتغيير كلمة المرور الخاصة بك، اذهب إلى الإعدادات > الحساب > تغيير كلمة المرور. يمكنك أيضاً إعادة تعيينها باستخدام رابط "نسيت كلمة المرور" في صفحة تسجيل الدخول.'
|
||||
}
|
||||
},
|
||||
{
|
||||
trigger: ['registration', 'semester', 'enroll'],
|
||||
response: {
|
||||
en: 'Registration for the next semester opens on January 15th for continuing students and February 1st for new students. Please check your academic calendar for specific dates.',
|
||||
ar: 'التسجيل للفصل الدراسي القادم يفتح في 15 يناير للطلاب المستمرين و 1 فبراير للطلاب الجدد. يرجى مراجعة التقويم الأكاديمي للتواريخ المحددة.'
|
||||
}
|
||||
},
|
||||
{
|
||||
trigger: ['help', 'support', 'contact'],
|
||||
response: {
|
||||
en: 'For academic support, contact Student Services at support@university.edu or call (555) 123-4567. For technical issues, email IT help desk at it@university.edu.',
|
||||
ar: 'للدعم الأكاديمي، اتصل بخدمات الطلاب على support@university.edu أو اتصل بالرقم (555) 123-4567. للمشاكل التقنية، راسل مكتب المساعدة التقنية على it@university.edu.'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
// Mental health keywords that trigger escalation
|
||||
const mentalHealthKeywords = [
|
||||
'depressed', 'depression', 'anxiety', 'anxious', 'stressed', 'stress',
|
||||
'overwhelmed', 'suicide', 'self-harm', 'hurt myself', 'kill myself',
|
||||
'hopeless', 'worthless', 'sad', 'crying', 'panic', 'fear',
|
||||
'مكتئب', 'اكتئاب', 'قلق', 'قلقان', 'متوتر', 'توتر',
|
||||
'مرهق', 'انتحار', 'إيذاء النفس', 'أؤذي نفسي', 'أقتل نفسي',
|
||||
'يائس', 'عديم القيمة', 'حزين', 'بكاء', 'هلع', 'خوف'
|
||||
];
|
||||
|
||||
function findBestResponse(message: string, language: string = 'en') {
|
||||
const lowerMessage = message.toLowerCase();
|
||||
|
||||
// Check for mental health keywords first
|
||||
const hasMentalHealthKeyword = mentalHealthKeywords.some(keyword =>
|
||||
lowerMessage.includes(keyword.toLowerCase())
|
||||
);
|
||||
|
||||
if (hasMentalHealthKeyword) {
|
||||
return {
|
||||
response: language === 'ar'
|
||||
? 'أفهم أنك تمر بوقت صعب. من المهم أن تطلب المساعدة من المختصين. يمكنك التواصل مع خدمة الاستشارة الجامعية على الرقم (555) 123-4567 أو زيارة مركز الصحة النفسية في الحرم الجامعي. في حالات الطوارئ، اتصل بالرقم 911 أو خط المساعدة الوطني للأزمات النفسية.'
|
||||
: 'I understand you\'re going through a difficult time. It\'s important to seek help from professionals. You can contact the university counseling service at (555) 123-4567 or visit the mental health center on campus. In emergencies, call 911 or the National Crisis Helpline.',
|
||||
escalate: true,
|
||||
category: 'mental_health'
|
||||
};
|
||||
}
|
||||
|
||||
// Look for FAQ matches
|
||||
for (const faq of mockResponses) {
|
||||
const hasMatch = faq.trigger.some(trigger =>
|
||||
lowerMessage.includes(trigger.toLowerCase())
|
||||
);
|
||||
|
||||
if (hasMatch) {
|
||||
return {
|
||||
response: faq.response[language as keyof typeof faq.response],
|
||||
escalate: false,
|
||||
category: 'faq'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Default response
|
||||
return {
|
||||
response: language === 'ar'
|
||||
? 'شكراً لك على سؤالك. يمكنني مساعدتك في العثور على المعلومات التي تحتاجها. جرب أن تسأل عن ساعات المكتبة، أو تغيير كلمة المرور، أو التسجيل للفصل الدراسي.'
|
||||
: 'Thank you for your question. I can help you find the information you need. Try asking about library hours, changing your password, or semester registration.',
|
||||
escalate: false,
|
||||
category: 'general'
|
||||
};
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const { message, language = 'en' } = body;
|
||||
|
||||
if (!message) {
|
||||
return NextResponse.json({ error: 'Message is required' }, { status: 400 });
|
||||
}
|
||||
|
||||
const result = findBestResponse(message, language);
|
||||
|
||||
return NextResponse.json({
|
||||
response: result.response,
|
||||
escalate: result.escalate,
|
||||
category: result.category,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error processing chat:', error);
|
||||
return NextResponse.json({ error: 'Failed to process message' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { OpenAI } from 'openai'
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
|
||||
const openai = new OpenAI({
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
})
|
||||
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
type Message = {
|
||||
sender: 'user' | 'bot'
|
||||
text: string
|
||||
timestamp: Date
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { message, mode = 'general', history = [] } = await request.json()
|
||||
|
||||
// Get FAQs from database
|
||||
const faqs = await prisma.fAQ.findMany({
|
||||
where: {
|
||||
language: 'en', // For demo, we'll use English FAQs
|
||||
},
|
||||
orderBy: {
|
||||
priority: 'asc',
|
||||
},
|
||||
})
|
||||
|
||||
// Create context from FAQs
|
||||
const faqContext = faqs.map((faq: { question: string; answer: string }) =>
|
||||
`Q: ${faq.question}\nA: ${faq.answer}`
|
||||
).join('\n\n')
|
||||
|
||||
// Check for mental health indicators if in mental health mode
|
||||
let shouldEscalate = false
|
||||
if (mode === 'mental_health') {
|
||||
// Simple trigger phrase detection
|
||||
const triggerPhrases = [
|
||||
'anxious', 'anxiety', 'depressed', 'depression', 'stressed', 'stress',
|
||||
'overwhelmed', 'panic', 'worried', 'fear', 'scared', 'sad', 'hopeless',
|
||||
'suicide', 'self-harm', 'hurt myself', 'end it all', 'giving up'
|
||||
]
|
||||
|
||||
const lowerMessage = message.toLowerCase()
|
||||
shouldEscalate = triggerPhrases.some(phrase => lowerMessage.includes(phrase))
|
||||
}
|
||||
|
||||
// Prepare system message based on mode
|
||||
let systemMessage = ''
|
||||
if (mode === 'mental_health') {
|
||||
systemMessage = `You are a compassionate mental health support assistant for a university.
|
||||
Provide empathetic, supportive responses. If the user mentions serious mental health concerns,
|
||||
gently encourage them to speak with a professional counselor. Keep responses warm and understanding.`
|
||||
} else {
|
||||
systemMessage = `You are a helpful university assistant. Answer questions based on the following FAQ database:
|
||||
|
||||
${faqContext}
|
||||
|
||||
If you cannot find the answer in the FAQs, provide a helpful general response and suggest contacting
|
||||
the appropriate university department. Keep responses concise and helpful.`
|
||||
}
|
||||
|
||||
const completion = await openai.chat.completions.create({
|
||||
model: 'gpt-4o-mini',
|
||||
messages: [
|
||||
{ role: 'system', content: systemMessage },
|
||||
...history.map((msg: Message) => ({
|
||||
role: msg.sender === 'user' ? 'user' as const : 'assistant' as const,
|
||||
content: msg.text,
|
||||
})),
|
||||
{ role: 'user', content: message },
|
||||
],
|
||||
max_tokens: 500,
|
||||
temperature: 0.7,
|
||||
})
|
||||
|
||||
const response = completion.choices[0]?.message?.content || 'I apologize, but I cannot provide a response at this time.'
|
||||
|
||||
// Log the chat session
|
||||
await prisma.chatSession.create({
|
||||
data: {
|
||||
type: mode === 'mental_health' ? 'MENTAL_HEALTH' : 'GENERAL',
|
||||
messages: JSON.stringify([
|
||||
...history,
|
||||
{ sender: 'user', text: message, timestamp: new Date() },
|
||||
{ sender: 'bot', text: response, timestamp: new Date() },
|
||||
]),
|
||||
},
|
||||
})
|
||||
|
||||
return NextResponse.json({
|
||||
response,
|
||||
shouldEscalate,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error in chat API:', error)
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to process chat message' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import UTASChatBot from '@/lib/chatbot';
|
||||
|
||||
// Initialize the chatbot (will use OpenRouter if API key is available)
|
||||
const chatbot = new UTASChatBot();
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const {
|
||||
message,
|
||||
conversationHistory = [],
|
||||
systemPrompt = null,
|
||||
language = 'en'
|
||||
} = await request.json();
|
||||
|
||||
if (!message || typeof message !== 'string') {
|
||||
return NextResponse.json({
|
||||
error: 'Message is required and must be a string'
|
||||
}, { status: 400 });
|
||||
}
|
||||
|
||||
// Generate AI response using RAG and potentially OpenRouter with role-based context
|
||||
const response = await chatbot.generateResponse(
|
||||
message,
|
||||
conversationHistory,
|
||||
systemPrompt,
|
||||
language
|
||||
);
|
||||
|
||||
// Log the interaction for demo purposes
|
||||
console.log('UTAS Chat:', {
|
||||
timestamp: new Date().toISOString(),
|
||||
message: message.substring(0, 100),
|
||||
response: response.substring(0, 100)
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
message: response,
|
||||
timestamp: new Date().toISOString(),
|
||||
source: 'UTAS AI Assistant'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Chat API Error:', error);
|
||||
|
||||
return NextResponse.json({
|
||||
message: "I apologize, but I'm experiencing technical difficulties. Please try again or contact UTAS directly at +61 3 6226 6200 or info@utas.edu.au for immediate assistance.",
|
||||
timestamp: new Date().toISOString(),
|
||||
source: 'UTAS AI Assistant',
|
||||
error: true
|
||||
}, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
return NextResponse.json({
|
||||
service: 'UTAS AI Chat Assistant',
|
||||
status: 'active',
|
||||
features: [
|
||||
'RAG-powered responses using UTAS knowledge base',
|
||||
'OpenRouter AI integration (when API key provided)',
|
||||
'Real-time course and program information',
|
||||
'Application guidance and support',
|
||||
'Campus and research information'
|
||||
],
|
||||
endpoints: {
|
||||
POST: 'Send message and conversation history',
|
||||
GET: 'Service status and information'
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { mockCourses, searchCourses, studyAreas } from '@/lib/mockData';
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const query = searchParams.get('q') || '';
|
||||
const area = searchParams.get('area') || '';
|
||||
const level = searchParams.get('level') || '';
|
||||
const campus = searchParams.get('campus') || '';
|
||||
|
||||
// Convert level filter to studyMode
|
||||
let studyMode = '';
|
||||
if (level) {
|
||||
if (level.toLowerCase() === 'undergraduate') {
|
||||
studyMode = 'undergraduate';
|
||||
} else if (level.toLowerCase() === 'postgraduate') {
|
||||
studyMode = 'postgraduate';
|
||||
} else if (level.toLowerCase() === 'research') {
|
||||
studyMode = 'research';
|
||||
}
|
||||
}
|
||||
|
||||
// Build filters object
|
||||
const filters: { area?: string; studyMode?: string; availability?: string } = {};
|
||||
if (area) filters.area = area;
|
||||
if (studyMode) filters.studyMode = studyMode;
|
||||
|
||||
let results = searchCourses(query, filters);
|
||||
|
||||
// Filter by campus if specified
|
||||
if (campus) {
|
||||
results = results.filter(course =>
|
||||
course.campus.some(c => c.toLowerCase() === campus.toLowerCase())
|
||||
);
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
courses: results,
|
||||
total: results.length,
|
||||
filters: {
|
||||
query,
|
||||
area,
|
||||
level,
|
||||
campus
|
||||
},
|
||||
areas: studyAreas.map(area => area.name),
|
||||
campuses: ["Hobart", "Launceston", "Burnie", "Sydney"]
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Courses API error:', error);
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'Failed to fetch courses',
|
||||
courses: mockCourses.slice(0, 5), // Return some courses as fallback
|
||||
total: 5
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { courseId, action } = await request.json();
|
||||
|
||||
if (action === 'getDetails') {
|
||||
const course = mockCourses.find(c => c.id === courseId);
|
||||
|
||||
if (!course) {
|
||||
return NextResponse.json({
|
||||
error: 'Course not found'
|
||||
}, { status: 404 });
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
course: course,
|
||||
relatedCourses: mockCourses
|
||||
.filter(c => c.area === course.area && c.id !== courseId)
|
||||
.slice(0, 3),
|
||||
applicationInfo: {
|
||||
process: course.area.includes('Medicine')
|
||||
? 'Competitive entry with UCAT and interview required'
|
||||
: 'Standard application through UAC UTAS portal',
|
||||
requirements: course.entry,
|
||||
deadlines: {
|
||||
semester1: 'December 31, 2024',
|
||||
semester2: 'May 31, 2025'
|
||||
},
|
||||
scholarships: [
|
||||
'UTAS Merit Scholarship - $5,000/year',
|
||||
'Tasmanian Scholarship - $15,000/year (for mainland students)',
|
||||
`${course.area} specific scholarships available`
|
||||
]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (action === 'apply') {
|
||||
// Mock application process
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: 'Application submitted successfully',
|
||||
applicationId: `APP-${Date.now()}`,
|
||||
nextSteps: [
|
||||
'Check your email for confirmation',
|
||||
'Complete required documents',
|
||||
'Attend orientation session'
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
error: 'Invalid action'
|
||||
}, { status: 400 });
|
||||
|
||||
} catch (error) {
|
||||
console.error('Course action error:', error);
|
||||
return NextResponse.json({
|
||||
error: 'Failed to process request'
|
||||
}, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
// Mock survey data
|
||||
const mockSurveys = [
|
||||
{
|
||||
id: '1',
|
||||
rating: 5,
|
||||
feedback: 'Great chatbot experience!',
|
||||
category: 'chatbot',
|
||||
createdAt: new Date('2024-12-01T10:00:00Z'),
|
||||
userId: '1'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
rating: 4,
|
||||
feedback: 'Dashboard is very helpful',
|
||||
category: 'dashboard',
|
||||
createdAt: new Date('2024-12-02T14:30:00Z'),
|
||||
userId: '1'
|
||||
}
|
||||
];
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
return NextResponse.json(mockSurveys);
|
||||
} catch (error) {
|
||||
console.error('Error fetching surveys:', error);
|
||||
return NextResponse.json({ error: 'Failed to fetch surveys' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const { rating, feedback, category } = body;
|
||||
|
||||
if (!rating || !feedback || !category) {
|
||||
return NextResponse.json({ error: 'Missing required fields' }, { status: 400 });
|
||||
}
|
||||
|
||||
// Mock survey creation
|
||||
const newSurvey = {
|
||||
id: Date.now().toString(),
|
||||
rating,
|
||||
feedback,
|
||||
category,
|
||||
createdAt: new Date(),
|
||||
userId: '1'
|
||||
};
|
||||
|
||||
mockSurveys.push(newSurvey);
|
||||
|
||||
return NextResponse.json(newSurvey);
|
||||
} catch (error) {
|
||||
console.error('Error creating survey:', error);
|
||||
return NextResponse.json({ error: 'Failed to create survey' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { rating, type, feedback, sessionId, userId } = await request.json()
|
||||
|
||||
const survey = await prisma.survey.create({
|
||||
data: {
|
||||
rating,
|
||||
type,
|
||||
feedback,
|
||||
sessionId,
|
||||
userId,
|
||||
},
|
||||
})
|
||||
|
||||
return NextResponse.json({ success: true, survey })
|
||||
} catch (error) {
|
||||
console.error('Error creating survey:', error)
|
||||
return NextResponse.json({ error: 'Failed to submit survey' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const surveys = await prisma.survey.findMany({
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
take: 100,
|
||||
})
|
||||
|
||||
// Calculate statistics
|
||||
const totalSurveys = surveys.length
|
||||
const averageRating = totalSurveys > 0
|
||||
? surveys.reduce((sum: number, survey: { rating: number }) => sum + survey.rating, 0) / totalSurveys
|
||||
: 0
|
||||
const ratingDistribution = surveys.reduce((acc: Record<number, number>, survey: { rating: number }) => {
|
||||
acc[survey.rating] = (acc[survey.rating] || 0) + 1
|
||||
return acc
|
||||
}, {} as Record<number, number>)
|
||||
|
||||
return NextResponse.json({
|
||||
surveys,
|
||||
statistics: {
|
||||
totalSurveys,
|
||||
averageRating: Math.round(averageRating * 10) / 10,
|
||||
ratingDistribution,
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error fetching surveys:', error)
|
||||
return NextResponse.json({ error: 'Failed to fetch surveys' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
// Mock survey data
|
||||
const mockSurveys = [
|
||||
{
|
||||
id: '1',
|
||||
rating: 5,
|
||||
feedback: 'Great chatbot experience!',
|
||||
category: 'chatbot',
|
||||
createdAt: new Date('2024-12-01T10:00:00Z'),
|
||||
userId: '1'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
rating: 4,
|
||||
feedback: 'Dashboard is very helpful',
|
||||
category: 'dashboard',
|
||||
createdAt: new Date('2024-12-02T14:30:00Z'),
|
||||
userId: '1'
|
||||
}
|
||||
];
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
return NextResponse.json(mockSurveys);
|
||||
} catch (error) {
|
||||
console.error('Error fetching surveys:', error);
|
||||
return NextResponse.json({ error: 'Failed to fetch surveys' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const { rating, feedback, category } = body;
|
||||
|
||||
if (!rating || !feedback || !category) {
|
||||
return NextResponse.json({ error: 'Missing required fields' }, { status: 400 });
|
||||
}
|
||||
|
||||
// Mock survey creation
|
||||
const newSurvey = {
|
||||
id: Date.now().toString(),
|
||||
rating,
|
||||
feedback,
|
||||
category,
|
||||
createdAt: new Date(),
|
||||
userId: '1'
|
||||
};
|
||||
|
||||
mockSurveys.push(newSurvey);
|
||||
|
||||
return NextResponse.json(newSurvey);
|
||||
} catch (error) {
|
||||
console.error('Error creating survey:', error);
|
||||
return NextResponse.json({ error: 'Failed to create survey' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
// Mock user profile data
|
||||
const mockUserProfile = {
|
||||
id: '1',
|
||||
email: 'student@university.edu',
|
||||
name: 'John Doe',
|
||||
role: 'STUDENT',
|
||||
studentId: 'ST001234',
|
||||
faculty: 'Arts',
|
||||
balance: 5420.50,
|
||||
enrollments: [
|
||||
{
|
||||
id: '1',
|
||||
course: {
|
||||
id: '1',
|
||||
code: 'CS101',
|
||||
title: 'Introduction to Computer Science',
|
||||
credits: 3,
|
||||
instructor: 'Dr. Smith',
|
||||
schedule: 'MWF 10:00-11:00'
|
||||
},
|
||||
grade: 'A',
|
||||
semester: 'Fall 2024'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
course: {
|
||||
id: '2',
|
||||
code: 'MATH201',
|
||||
title: 'Calculus II',
|
||||
credits: 4,
|
||||
instructor: 'Prof. Johnson',
|
||||
schedule: 'TTh 2:00-3:30'
|
||||
},
|
||||
grade: 'B+',
|
||||
semester: 'Fall 2024'
|
||||
}
|
||||
],
|
||||
createdAt: new Date('2024-09-01T00:00:00Z'),
|
||||
updatedAt: new Date('2024-12-01T00:00:00Z')
|
||||
};
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
// Return mock user profile
|
||||
return NextResponse.json(mockUserProfile);
|
||||
} catch (error) {
|
||||
console.error('Error fetching user profile:', error);
|
||||
return NextResponse.json({ error: 'Failed to fetch user profile' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const email = searchParams.get('email')
|
||||
|
||||
if (!email) {
|
||||
return NextResponse.json({ error: 'Email is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { email },
|
||||
include: {
|
||||
enrollments: {
|
||||
include: {
|
||||
course: true,
|
||||
},
|
||||
},
|
||||
advisor: true,
|
||||
},
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
return NextResponse.json({ error: 'User not found' }, { status: 404 })
|
||||
}
|
||||
|
||||
return NextResponse.json(user)
|
||||
} catch (error) {
|
||||
console.error('Error fetching user profile:', error)
|
||||
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
// Mock user profile data
|
||||
const mockUserProfile = {
|
||||
id: '1',
|
||||
email: 'student@university.edu',
|
||||
name: 'John Doe',
|
||||
role: 'STUDENT',
|
||||
studentId: 'ST001234',
|
||||
faculty: 'Arts',
|
||||
balance: 5420.50,
|
||||
enrollments: [
|
||||
{
|
||||
id: '1',
|
||||
course: {
|
||||
id: '1',
|
||||
code: 'CS101',
|
||||
title: 'Introduction to Computer Science',
|
||||
credits: 3,
|
||||
instructor: 'Dr. Smith',
|
||||
schedule: 'MWF 10:00-11:00'
|
||||
},
|
||||
grade: 'A',
|
||||
semester: 'Fall 2024'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
course: {
|
||||
id: '2',
|
||||
code: 'MATH201',
|
||||
title: 'Calculus II',
|
||||
credits: 4,
|
||||
instructor: 'Prof. Johnson',
|
||||
schedule: 'TTh 2:00-3:30'
|
||||
},
|
||||
grade: 'B+',
|
||||
semester: 'Fall 2024'
|
||||
}
|
||||
],
|
||||
createdAt: new Date('2024-09-01T00:00:00Z'),
|
||||
updatedAt: new Date('2024-12-01T00:00:00Z')
|
||||
};
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
// Return mock user profile
|
||||
return NextResponse.json(mockUserProfile);
|
||||
} catch (error) {
|
||||
console.error('Error fetching user profile:', error);
|
||||
return NextResponse.json({ error: 'Failed to fetch user profile' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Search, Filter, MapPin, Clock, DollarSign, BookOpen, Award, ChevronRight, X } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
interface Course {
|
||||
id: string;
|
||||
title: string;
|
||||
area: string;
|
||||
duration: string;
|
||||
description: string;
|
||||
entry: string;
|
||||
campus: string[];
|
||||
fees: string;
|
||||
pathways: string[];
|
||||
}
|
||||
|
||||
export default function CoursesPage() {
|
||||
const [courses, setCourses] = useState<Course[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [selectedArea, setSelectedArea] = useState('');
|
||||
const [selectedLevel, setSelectedLevel] = useState('');
|
||||
const [selectedCampus, setSelectedCampus] = useState('');
|
||||
const [showFilters, setShowFilters] = useState(false);
|
||||
|
||||
const areas = [
|
||||
"Business and Law",
|
||||
"Creative Arts and Design",
|
||||
"Earth, Sea, Antarctic and Environment",
|
||||
"Education, Humanities and Social Sciences",
|
||||
"Health and Medicine",
|
||||
"Science, Technology and Engineering"
|
||||
];
|
||||
|
||||
const levels = ["Undergraduate", "Postgraduate"];
|
||||
const campuses = ["Hobart", "Launceston", "Burnie", "Sydney"];
|
||||
|
||||
useEffect(() => {
|
||||
fetchCourses();
|
||||
}, [searchQuery, selectedArea, selectedLevel, selectedCampus]);
|
||||
|
||||
const fetchCourses = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const params = new URLSearchParams();
|
||||
if (searchQuery) params.append('q', searchQuery);
|
||||
if (selectedArea) params.append('area', selectedArea);
|
||||
if (selectedLevel) params.append('level', selectedLevel);
|
||||
if (selectedCampus) params.append('campus', selectedCampus);
|
||||
|
||||
const response = await fetch(`/api/courses?${params}`);
|
||||
const data = await response.json();
|
||||
setCourses(data.courses || []);
|
||||
} catch (error) {
|
||||
console.error('Error fetching courses:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const clearFilters = () => {
|
||||
setSearchQuery('');
|
||||
setSelectedArea('');
|
||||
setSelectedLevel('');
|
||||
setSelectedCampus('');
|
||||
};
|
||||
|
||||
const filteredCourses = courses.filter(course => {
|
||||
if (searchQuery && !course.title.toLowerCase().includes(searchQuery.toLowerCase()) &&
|
||||
!course.description.toLowerCase().includes(searchQuery.toLowerCase())) {
|
||||
return false;
|
||||
}
|
||||
if (selectedArea && course.area !== selectedArea) return false;
|
||||
if (selectedCampus && !course.campus.includes(selectedCampus)) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
{/* Header */}
|
||||
<div className="bg-gradient-to-r from-blue-600 to-teal-600 text-white">
|
||||
<div className="container mx-auto px-4 py-16">
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<h1 className="text-4xl md:text-5xl font-bold mb-4">
|
||||
Find Your Perfect Course
|
||||
</h1>
|
||||
<p className="text-xl opacity-90 mb-8">
|
||||
Explore over 350 study programs at the world's #1 university for climate action
|
||||
</p>
|
||||
|
||||
{/* Search Bar */}
|
||||
<div className="relative max-w-2xl mx-auto">
|
||||
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400" size={20} />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search courses by name, area, or keyword..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="w-full pl-12 pr-16 py-4 rounded-xl text-gray-800 text-lg focus:outline-none focus:ring-4 focus:ring-white/30"
|
||||
/>
|
||||
<button
|
||||
onClick={() => setShowFilters(!showFilters)}
|
||||
className="absolute right-2 top-1/2 transform -translate-y-1/2 bg-blue-600 text-white p-2 rounded-lg hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
<Filter size={20} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
{showFilters && (
|
||||
<div className="bg-white border-b border-gray-200 shadow-sm">
|
||||
<div className="container mx-auto px-4 py-6">
|
||||
<div className="flex flex-wrap gap-4 items-center justify-between">
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<select
|
||||
value={selectedArea}
|
||||
onChange={(e) => setSelectedArea(e.target.value)}
|
||||
className="px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="">All Study Areas</option>
|
||||
{areas.map(area => (
|
||||
<option key={area} value={area}>{area}</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
<select
|
||||
value={selectedLevel}
|
||||
onChange={(e) => setSelectedLevel(e.target.value)}
|
||||
className="px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="">All Levels</option>
|
||||
{levels.map(level => (
|
||||
<option key={level} value={level}>{level}</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
<select
|
||||
value={selectedCampus}
|
||||
onChange={(e) => setSelectedCampus(e.target.value)}
|
||||
className="px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="">All Campuses</option>
|
||||
{campuses.map(campus => (
|
||||
<option key={campus} value={campus}>{campus}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-4">
|
||||
<button
|
||||
onClick={clearFilters}
|
||||
className="flex items-center space-x-2 px-4 py-2 text-gray-600 hover:text-gray-800 transition-colors"
|
||||
>
|
||||
<X size={16} />
|
||||
<span>Clear Filters</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowFilters(false)}
|
||||
className="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition-colors"
|
||||
>
|
||||
Hide Filters
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Results */}
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h2 className="text-2xl font-bold text-gray-800">
|
||||
{loading ? 'Loading...' : `${filteredCourses.length} courses found`}
|
||||
</h2>
|
||||
{(selectedArea || selectedLevel || selectedCampus || searchQuery) && (
|
||||
<button
|
||||
onClick={clearFilters}
|
||||
className="text-blue-600 hover:text-blue-700 font-medium transition-colors"
|
||||
>
|
||||
Clear all filters
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{loading ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{[...Array(6)].map((_, i) => (
|
||||
<div key={i} className="bg-white rounded-lg p-6 shadow-sm animate-pulse">
|
||||
<div className="h-4 bg-gray-200 rounded mb-2"></div>
|
||||
<div className="h-6 bg-gray-200 rounded mb-4"></div>
|
||||
<div className="h-20 bg-gray-200 rounded mb-4"></div>
|
||||
<div className="flex space-x-2 mb-4">
|
||||
<div className="h-6 bg-gray-200 rounded flex-1"></div>
|
||||
<div className="h-6 bg-gray-200 rounded flex-1"></div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : filteredCourses.length === 0 ? (
|
||||
<div className="text-center py-12">
|
||||
<BookOpen size={64} className="mx-auto text-gray-400 mb-4" />
|
||||
<h3 className="text-xl font-semibold text-gray-600 mb-2">No courses found</h3>
|
||||
<p className="text-gray-500 mb-4">Try adjusting your search criteria or clearing filters</p>
|
||||
<button
|
||||
onClick={clearFilters}
|
||||
className="bg-blue-600 text-white px-6 py-2 rounded-lg hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
Clear Filters
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{filteredCourses.map((course) => (
|
||||
<div key={course.id} className="bg-white rounded-lg shadow-sm hover:shadow-md transition-shadow border border-gray-200">
|
||||
<div className="p-6">
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<span className="inline-block px-3 py-1 bg-blue-100 text-blue-800 text-xs font-medium rounded-full">
|
||||
{course.area}
|
||||
</span>
|
||||
<Award className="text-teal-600" size={20} />
|
||||
</div>
|
||||
|
||||
<h3 className="text-xl font-semibold text-gray-900 mb-3 line-clamp-2">
|
||||
{course.title}
|
||||
</h3>
|
||||
|
||||
<p className="text-gray-600 text-sm mb-4 line-clamp-3">
|
||||
{course.description}
|
||||
</p>
|
||||
|
||||
<div className="space-y-2 mb-4">
|
||||
<div className="flex items-center text-sm text-gray-500">
|
||||
<Clock size={16} className="mr-2" />
|
||||
<span>{course.duration}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center text-sm text-gray-500">
|
||||
<MapPin size={16} className="mr-2" />
|
||||
<span>{course.campus.join(', ')}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center text-sm text-gray-500">
|
||||
<DollarSign size={16} className="mr-2" />
|
||||
<span>{course.fees}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="text-sm text-gray-600">
|
||||
<span className="font-medium">Entry:</span> {course.entry}
|
||||
</div>
|
||||
<Link
|
||||
href={`/courses/${course.id}`}
|
||||
className="inline-flex items-center text-blue-600 hover:text-blue-700 font-medium text-sm transition-colors"
|
||||
>
|
||||
Learn More
|
||||
<ChevronRight size={16} className="ml-1" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Call to Action */}
|
||||
<div className="bg-gradient-to-r from-teal-600 to-blue-600 text-white">
|
||||
<div className="container mx-auto px-4 py-16">
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<h2 className="text-3xl font-bold mb-4">Ready to Start Your Journey?</h2>
|
||||
<p className="text-xl opacity-90 mb-8">
|
||||
Join over 35,000 students at the world's #1 university for climate action
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<Link
|
||||
href="/apply"
|
||||
className="bg-white text-blue-600 px-8 py-3 rounded-xl font-semibold hover:bg-gray-100 transition-all duration-200"
|
||||
>
|
||||
Apply Now
|
||||
</Link>
|
||||
<Link
|
||||
href="/contact"
|
||||
className="border-2 border-white text-white px-8 py-3 rounded-xl font-semibold hover:bg-white hover:text-blue-600 transition-all duration-200"
|
||||
>
|
||||
Get Advice
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Search, Filter, MapPin, Clock, DollarSign, BookOpen, Award, ChevronRight } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
interface Course {
|
||||
id: string;
|
||||
title: string;
|
||||
area: string;
|
||||
duration: string;
|
||||
description: string;
|
||||
entry: string;
|
||||
campus: string[];
|
||||
fees: string;
|
||||
pathways: string[];
|
||||
}
|
||||
|
||||
export default function CoursesPage() {
|
||||
const [courses, setCourses] = useState<Course[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [selectedArea, setSelectedArea] = useState('');
|
||||
const [selectedLevel, setSelectedLevel] = useState('');
|
||||
const [selectedCampus, setSelectedCampus] = useState('');
|
||||
const [showFilters, setShowFilters] = useState(false);
|
||||
|
||||
const areas = [
|
||||
"Business and Law",
|
||||
"Creative Arts and Design",
|
||||
"Earth, Sea, Antarctic and Environment",
|
||||
"Education, Humanities and Social Sciences",
|
||||
"Health and Medicine",
|
||||
"Science, Technology and Engineering"
|
||||
];
|
||||
|
||||
const campuses = ["Hobart", "Launceston", "Burnie", "Sydney"];
|
||||
|
||||
useEffect(() => {
|
||||
fetchCourses();
|
||||
}, [searchQuery, selectedArea, selectedLevel, selectedCampus]);
|
||||
|
||||
const fetchCourses = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const params = new URLSearchParams();
|
||||
if (searchQuery) params.append('q', searchQuery);
|
||||
if (selectedArea) params.append('area', selectedArea);
|
||||
if (selectedLevel) params.append('level', selectedLevel);
|
||||
if (selectedCampus) params.append('campus', selectedCampus);
|
||||
|
||||
const response = await fetch(`/api/courses?${params}`);
|
||||
const data = await response.json();
|
||||
setCourses(data.courses || []);
|
||||
} catch (error) {
|
||||
console.error('Error fetching courses:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [searchQuery, selectedArea, selectedLevel, selectedCampus]);
|
||||
|
||||
const clearFilters = () => {
|
||||
setSearchQuery('');
|
||||
setSelectedArea('');
|
||||
setSelectedLevel('');
|
||||
setSelectedCampus('');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
{/* Header */}
|
||||
<div className="bg-gradient-to-r from-blue-600 to-teal-600 text-white">
|
||||
<div className="container mx-auto px-4 py-16">
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<h1 className="text-4xl md:text-5xl font-bold mb-4">
|
||||
Find Your Perfect Course
|
||||
</h1>
|
||||
<p className="text-xl opacity-90 mb-8">
|
||||
Explore over 350 study programs at the world's #1 university for climate action
|
||||
</p>
|
||||
|
||||
{/* Search Bar */}
|
||||
<div className="relative max-w-2xl mx-auto">
|
||||
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400" size={20} />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search courses, keywords, or study areas..."
|
||||
className="w-full pl-12 pr-4 py-4 rounded-xl text-gray-900 text-lg focus:outline-none focus:ring-2 focus:ring-white"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
<div className="bg-white shadow-sm border-b">
|
||||
<div className="container mx-auto px-4 py-4">
|
||||
<div className="flex flex-wrap items-center gap-4">
|
||||
<button
|
||||
onClick={() => setShowFilters(!showFilters)}
|
||||
className="flex items-center space-x-2 px-4 py-2 bg-gray-100 rounded-lg hover:bg-gray-200 transition-colors"
|
||||
>
|
||||
<Filter size={16} />
|
||||
<span>Filters</span>
|
||||
</button>
|
||||
|
||||
{/* Quick Filters */}
|
||||
<select
|
||||
value={selectedArea}
|
||||
onChange={(e) => setSelectedArea(e.target.value)}
|
||||
className="px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="">All Study Areas</option>
|
||||
{areas.map(area => (
|
||||
<option key={area} value={area}>{area}</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
<select
|
||||
value={selectedLevel}
|
||||
onChange={(e) => setSelectedLevel(e.target.value)}
|
||||
className="px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="">All Levels</option>
|
||||
<option value="undergraduate">Undergraduate</option>
|
||||
<option value="postgraduate">Postgraduate</option>
|
||||
</select>
|
||||
|
||||
<select
|
||||
value={selectedCampus}
|
||||
onChange={(e) => setSelectedCampus(e.target.value)}
|
||||
className="px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="">All Campuses</option>
|
||||
{campuses.map(campus => (
|
||||
<option key={campus} value={campus}>{campus}</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
{(searchQuery || selectedArea || selectedLevel || selectedCampus) && (
|
||||
<button
|
||||
onClick={clearFilters}
|
||||
className="text-blue-600 hover:text-blue-700 text-sm font-medium"
|
||||
>
|
||||
Clear all filters
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Results */}
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h2 className="text-2xl font-bold text-gray-900">
|
||||
{loading ? 'Searching...' : `${courses.length} courses found`}
|
||||
</h2>
|
||||
|
||||
<div className="flex items-center space-x-4">
|
||||
<span className="text-gray-600">Sort by:</span>
|
||||
<select className="px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
<option>Relevance</option>
|
||||
<option>Course Name A-Z</option>
|
||||
<option>Study Area</option>
|
||||
<option>Duration</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{loading ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{[...Array(6)].map((_, i) => (
|
||||
<div key={i} className="bg-white rounded-xl shadow-sm p-6 animate-pulse">
|
||||
<div className="h-4 bg-gray-200 rounded mb-3"></div>
|
||||
<div className="h-6 bg-gray-200 rounded mb-4"></div>
|
||||
<div className="h-16 bg-gray-200 rounded mb-4"></div>
|
||||
<div className="space-y-2">
|
||||
<div className="h-3 bg-gray-200 rounded"></div>
|
||||
<div className="h-3 bg-gray-200 rounded w-3/4"></div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{courses.map(course => (
|
||||
<div key={course.id} className="bg-white rounded-xl shadow-sm hover:shadow-lg transition-all duration-300 overflow-hidden group">
|
||||
<div className="p-6">
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<span className="px-3 py-1 bg-blue-100 text-blue-700 text-sm font-medium rounded-full">
|
||||
{course.area}
|
||||
</span>
|
||||
<Award size={16} className="text-yellow-500" />
|
||||
</div>
|
||||
|
||||
<h3 className="text-xl font-bold text-gray-900 mb-3 group-hover:text-blue-600 transition-colors">
|
||||
{course.title}
|
||||
</h3>
|
||||
|
||||
<p className="text-gray-600 mb-4 line-clamp-3">
|
||||
{course.description}
|
||||
</p>
|
||||
|
||||
<div className="space-y-2 mb-4">
|
||||
<div className="flex items-center text-sm text-gray-500">
|
||||
<Clock size={14} className="mr-2" />
|
||||
<span>{course.duration}</span>
|
||||
</div>
|
||||
<div className="flex items-center text-sm text-gray-500">
|
||||
<MapPin size={14} className="mr-2" />
|
||||
<span>{course.campus.join(', ')}</span>
|
||||
</div>
|
||||
<div className="flex items-center text-sm text-gray-500">
|
||||
<DollarSign size={14} className="mr-2" />
|
||||
<span>{course.fees}</span>
|
||||
</div>
|
||||
<div className="flex items-center text-sm text-gray-500">
|
||||
<BookOpen size={14} className="mr-2" />
|
||||
<span>{course.entry}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-2 mb-4">
|
||||
{course.pathways.slice(0, 3).map(pathway => (
|
||||
<span key={pathway} className="px-2 py-1 bg-gray-100 text-gray-600 text-xs rounded">
|
||||
{pathway}
|
||||
</span>
|
||||
))}
|
||||
{course.pathways.length > 3 && (
|
||||
<span className="px-2 py-1 bg-gray-100 text-gray-600 text-xs rounded">
|
||||
+{course.pathways.length - 3} more
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex space-x-2">
|
||||
<Link
|
||||
href={`/courses/${course.id}`}
|
||||
className="flex-1 bg-blue-600 text-white px-4 py-2 rounded-lg text-center hover:bg-blue-700 transition-colors text-sm font-medium"
|
||||
>
|
||||
Learn More
|
||||
</Link>
|
||||
<Link
|
||||
href="/apply"
|
||||
className="flex-1 border border-blue-600 text-blue-600 px-4 py-2 rounded-lg text-center hover:bg-blue-50 transition-colors text-sm font-medium"
|
||||
>
|
||||
Apply Now
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!loading && courses.length === 0 && (
|
||||
<div className="text-center py-16">
|
||||
<div className="bg-gray-100 rounded-full w-24 h-24 flex items-center justify-center mx-auto mb-6">
|
||||
<Search size={32} className="text-gray-400" />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-gray-900 mb-2">No courses found</h3>
|
||||
<p className="text-gray-600 mb-6">
|
||||
Try adjusting your search criteria or browse all available courses.
|
||||
</p>
|
||||
<button
|
||||
onClick={clearFilters}
|
||||
className="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
Show All Courses
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Call to Action */}
|
||||
<div className="bg-gradient-to-r from-teal-600 to-blue-600 text-white py-16">
|
||||
<div className="container mx-auto px-4 text-center">
|
||||
<h2 className="text-3xl font-bold mb-4">Ready to Apply?</h2>
|
||||
<p className="text-xl opacity-90 mb-8">
|
||||
Join thousands of students at Australia's most sustainable university
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<Link
|
||||
href="/apply"
|
||||
className="bg-white text-blue-600 px-8 py-3 rounded-xl font-semibold hover:bg-gray-100 transition-all duration-200 flex items-center justify-center"
|
||||
>
|
||||
Start Application
|
||||
<ChevronRight size={20} className="ml-2" />
|
||||
</Link>
|
||||
<Link
|
||||
href="/chat"
|
||||
className="border-2 border-white text-white px-8 py-3 rounded-xl font-semibold hover:bg-white hover:text-blue-600 transition-all duration-200"
|
||||
>
|
||||
Chat with AI Assistant
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Search, Filter, MapPin, Clock, DollarSign, BookOpen, Award, ChevronRight, X } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
interface Course {
|
||||
id: string;
|
||||
title: string;
|
||||
area: string;
|
||||
duration: string;
|
||||
description: string;
|
||||
entry: string;
|
||||
campus: string[];
|
||||
fees: string;
|
||||
pathways: string[];
|
||||
}
|
||||
|
||||
export default function CoursesPage() {
|
||||
const [courses, setCourses] = useState<Course[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [selectedArea, setSelectedArea] = useState('');
|
||||
const [selectedLevel, setSelectedLevel] = useState('');
|
||||
const [selectedCampus, setSelectedCampus] = useState('');
|
||||
const [showFilters, setShowFilters] = useState(false);
|
||||
|
||||
const areas = [
|
||||
"Business and Law",
|
||||
"Creative Arts and Design",
|
||||
"Earth, Sea, Antarctic and Environment",
|
||||
"Education, Humanities and Social Sciences",
|
||||
"Health and Medicine",
|
||||
"Science, Technology and Engineering"
|
||||
];
|
||||
|
||||
const levels = ["Undergraduate", "Postgraduate"];
|
||||
const campuses = ["Hobart", "Launceston", "Burnie", "Sydney"];
|
||||
|
||||
const fetchCourses = React.useCallback(async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const params = new URLSearchParams();
|
||||
if (searchQuery) params.append('q', searchQuery);
|
||||
if (selectedArea) params.append('area', selectedArea);
|
||||
if (selectedLevel) params.append('level', selectedLevel);
|
||||
if (selectedCampus) params.append('campus', selectedCampus);
|
||||
|
||||
const response = await fetch(`/api/courses?${params}`);
|
||||
const data = await response.json();
|
||||
setCourses(data.courses || []);
|
||||
} catch (error) {
|
||||
console.error('Error fetching courses:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [searchQuery, selectedArea, selectedLevel, selectedCampus]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchCourses();
|
||||
}, [fetchCourses]);
|
||||
|
||||
const clearFilters = () => {
|
||||
setSearchQuery('');
|
||||
setSelectedArea('');
|
||||
setSelectedLevel('');
|
||||
setSelectedCampus('');
|
||||
};
|
||||
|
||||
const filteredCourses = courses.filter(course => {
|
||||
if (searchQuery && !course.title.toLowerCase().includes(searchQuery.toLowerCase()) &&
|
||||
!course.description.toLowerCase().includes(searchQuery.toLowerCase())) {
|
||||
return false;
|
||||
}
|
||||
if (selectedArea && course.area !== selectedArea) return false;
|
||||
if (selectedCampus && !course.campus.includes(selectedCampus)) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
{/* Header */}
|
||||
<div className="bg-gradient-to-r from-blue-600 to-teal-600 text-white">
|
||||
<div className="container mx-auto px-4 py-16">
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<h1 className="text-4xl md:text-5xl font-bold mb-4">
|
||||
Find Your Perfect Course
|
||||
</h1>
|
||||
<p className="text-xl opacity-90 mb-8">
|
||||
Explore over 350 study programs at the world's #1 university for climate action
|
||||
</p>
|
||||
|
||||
{/* Search Bar */}
|
||||
<div className="relative max-w-2xl mx-auto">
|
||||
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400" size={20} />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search courses by name, area, or keyword..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="w-full pl-12 pr-16 py-4 rounded-xl text-gray-800 text-lg focus:outline-none focus:ring-4 focus:ring-white/30"
|
||||
/>
|
||||
<button
|
||||
onClick={() => setShowFilters(!showFilters)}
|
||||
className="absolute right-2 top-1/2 transform -translate-y-1/2 bg-blue-600 text-white p-2 rounded-lg hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
<Filter size={20} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
{showFilters && (
|
||||
<div className="bg-white border-b border-gray-200 shadow-sm">
|
||||
<div className="container mx-auto px-4 py-6">
|
||||
<div className="flex flex-wrap gap-4 items-center justify-between">
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<select
|
||||
value={selectedArea}
|
||||
onChange={(e) => setSelectedArea(e.target.value)}
|
||||
className="px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="">All Study Areas</option>
|
||||
{areas.map(area => (
|
||||
<option key={area} value={area}>{area}</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
<select
|
||||
value={selectedLevel}
|
||||
onChange={(e) => setSelectedLevel(e.target.value)}
|
||||
className="px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="">All Levels</option>
|
||||
{levels.map(level => (
|
||||
<option key={level} value={level}>{level}</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
<select
|
||||
value={selectedCampus}
|
||||
onChange={(e) => setSelectedCampus(e.target.value)}
|
||||
className="px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="">All Campuses</option>
|
||||
{campuses.map(campus => (
|
||||
<option key={campus} value={campus}>{campus}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-4">
|
||||
<button
|
||||
onClick={clearFilters}
|
||||
className="flex items-center space-x-2 px-4 py-2 text-gray-600 hover:text-gray-800 transition-colors"
|
||||
>
|
||||
<X size={16} />
|
||||
<span>Clear Filters</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowFilters(false)}
|
||||
className="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition-colors"
|
||||
>
|
||||
Hide Filters
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Results */}
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h2 className="text-2xl font-bold text-gray-800">
|
||||
{loading ? 'Loading...' : `${filteredCourses.length} courses found`}
|
||||
</h2>
|
||||
{(selectedArea || selectedLevel || selectedCampus || searchQuery) && (
|
||||
<button
|
||||
onClick={clearFilters}
|
||||
className="text-blue-600 hover:text-blue-700 font-medium transition-colors"
|
||||
>
|
||||
Clear all filters
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{loading ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{[...Array(6)].map((_, i) => (
|
||||
<div key={i} className="bg-white rounded-lg p-6 shadow-sm animate-pulse">
|
||||
<div className="h-4 bg-gray-200 rounded mb-2"></div>
|
||||
<div className="h-6 bg-gray-200 rounded mb-4"></div>
|
||||
<div className="h-20 bg-gray-200 rounded mb-4"></div>
|
||||
<div className="flex space-x-2 mb-4">
|
||||
<div className="h-6 bg-gray-200 rounded flex-1"></div>
|
||||
<div className="h-6 bg-gray-200 rounded flex-1"></div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : filteredCourses.length === 0 ? (
|
||||
<div className="text-center py-12">
|
||||
<BookOpen size={64} className="mx-auto text-gray-400 mb-4" />
|
||||
<h3 className="text-xl font-semibold text-gray-600 mb-2">No courses found</h3>
|
||||
<p className="text-gray-500 mb-4">Try adjusting your search criteria or clearing filters</p>
|
||||
<button
|
||||
onClick={clearFilters}
|
||||
className="bg-blue-600 text-white px-6 py-2 rounded-lg hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
Clear Filters
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{filteredCourses.map((course) => (
|
||||
<div key={course.id} className="bg-white rounded-lg shadow-sm hover:shadow-md transition-shadow border border-gray-200">
|
||||
<div className="p-6">
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<span className="inline-block px-3 py-1 bg-blue-100 text-blue-800 text-xs font-medium rounded-full">
|
||||
{course.area}
|
||||
</span>
|
||||
<Award className="text-teal-600" size={20} />
|
||||
</div>
|
||||
|
||||
<h3 className="text-xl font-semibold text-gray-900 mb-3 line-clamp-2">
|
||||
{course.title}
|
||||
</h3>
|
||||
|
||||
<p className="text-gray-600 text-sm mb-4 line-clamp-3">
|
||||
{course.description}
|
||||
</p>
|
||||
|
||||
<div className="space-y-2 mb-4">
|
||||
<div className="flex items-center text-sm text-gray-500">
|
||||
<Clock size={16} className="mr-2" />
|
||||
<span>{course.duration}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center text-sm text-gray-500">
|
||||
<MapPin size={16} className="mr-2" />
|
||||
<span>{course.campus.join(', ')}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center text-sm text-gray-500">
|
||||
<DollarSign size={16} className="mr-2" />
|
||||
<span>{course.fees}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="text-sm text-gray-600">
|
||||
<span className="font-medium">Entry:</span> {course.entry}
|
||||
</div>
|
||||
<Link
|
||||
href={`/courses/${course.id}`}
|
||||
className="inline-flex items-center text-blue-600 hover:text-blue-700 font-medium text-sm transition-colors"
|
||||
>
|
||||
Learn More
|
||||
<ChevronRight size={16} className="ml-1" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Call to Action */}
|
||||
<div className="bg-gradient-to-r from-teal-600 to-blue-600 text-white">
|
||||
<div className="container mx-auto px-4 py-16">
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<h2 className="text-3xl font-bold mb-4">Ready to Start Your Journey?</h2>
|
||||
<p className="text-xl opacity-90 mb-8">
|
||||
Join over 35,000 students at the world's #1 university for climate action
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<Link
|
||||
href="/apply"
|
||||
className="bg-white text-blue-600 px-8 py-3 rounded-xl font-semibold hover:bg-gray-100 transition-all duration-200"
|
||||
>
|
||||
Apply Now
|
||||
</Link>
|
||||
<Link
|
||||
href="/contact"
|
||||
className="border-2 border-white text-white px-8 py-3 rounded-xl font-semibold hover:bg-white hover:text-blue-600 transition-all duration-200"
|
||||
>
|
||||
Get Advice
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,477 @@
|
||||
'use client';
|
||||
|
||||
import React, { useEffect } from 'react'
|
||||
import { useAuth } from '@/components/providers/MockAuthProvider'
|
||||
import { useLanguage } from '@/components/providers/LanguageProvider'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import {
|
||||
Calendar,
|
||||
DollarSign,
|
||||
User,
|
||||
BookOpen,
|
||||
Clock,
|
||||
AlertCircle,
|
||||
LogOut,
|
||||
Languages,
|
||||
Heart,
|
||||
Shield,
|
||||
BarChart3,
|
||||
Star
|
||||
} from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
|
||||
// Extended UserProfile type to include relationships
|
||||
interface ExtendedUserProfile {
|
||||
id: string
|
||||
email: string
|
||||
name: string
|
||||
role: string
|
||||
year?: number
|
||||
faculty?: string
|
||||
balance?: number
|
||||
enrollments?: Array<{
|
||||
id: string
|
||||
grade?: string
|
||||
course: {
|
||||
id: string
|
||||
name: string
|
||||
code: string
|
||||
schedule?: string
|
||||
credits: number
|
||||
}
|
||||
}>
|
||||
advisor?: {
|
||||
id: string
|
||||
name: string
|
||||
email: string
|
||||
}
|
||||
}
|
||||
|
||||
export default function DashboardPage() {
|
||||
const { user, userProfile, logout } = useAuth()
|
||||
const { t, language, setLanguage } = useLanguage()
|
||||
const router = useRouter()
|
||||
|
||||
// Type assertion for extended user profile
|
||||
const extendedProfile = userProfile as ExtendedUserProfile
|
||||
|
||||
useEffect(() => {
|
||||
if (!user) {
|
||||
router.push('/')
|
||||
}
|
||||
}, [user, router])
|
||||
|
||||
const handleLogout = async () => {
|
||||
await logout()
|
||||
}
|
||||
|
||||
const toggleLanguage = () => {
|
||||
setLanguage(language === 'en' ? 'ar' : 'en')
|
||||
}
|
||||
|
||||
if (!user || !userProfile) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
|
||||
<p className="text-gray-600">Loading dashboard...</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
{/* Header */}
|
||||
<header className="bg-white shadow-sm border-b">
|
||||
<div className="container mx-auto px-4 py-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center space-x-4">
|
||||
<Link href="/" className="flex items-center space-x-2">
|
||||
<div className="w-8 h-8 bg-blue-600 rounded-lg flex items-center justify-center">
|
||||
<span className="text-white font-bold text-sm">UP</span>
|
||||
</div>
|
||||
<span className="text-xl font-bold text-gray-900">University Portal</span>
|
||||
</Link>
|
||||
|
||||
<nav className="hidden md:flex space-x-6">
|
||||
<Link href="/dashboard" className="text-blue-600 font-medium">
|
||||
{t('dashboard')}
|
||||
</Link>
|
||||
<Link href="/accessibility" className="text-gray-600 hover:text-blue-600">
|
||||
{t('accessibility')}
|
||||
</Link>
|
||||
<Link href="/wellbeing" className="text-gray-600 hover:text-blue-600">
|
||||
{t('wellbeing')}
|
||||
</Link>
|
||||
{userProfile.role === 'ADMIN' && (
|
||||
<Link href="/admin" className="text-gray-600 hover:text-blue-600">
|
||||
{t('admin')}
|
||||
</Link>
|
||||
)}
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-4">
|
||||
<button
|
||||
onClick={toggleLanguage}
|
||||
className="flex items-center space-x-2 px-3 py-2 rounded-lg bg-gray-100 hover:bg-gray-200 transition-colors"
|
||||
>
|
||||
<Languages size={16} />
|
||||
<span className="text-sm font-medium">{language.toUpperCase()}</span>
|
||||
</button>
|
||||
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="w-8 h-8 bg-blue-600 rounded-full flex items-center justify-center">
|
||||
<span className="text-white text-sm font-medium">
|
||||
{userProfile.name.charAt(0)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="hidden md:block">
|
||||
<p className="text-sm font-medium text-gray-900">{userProfile.name}</p>
|
||||
<p className="text-xs text-gray-500">{userProfile.role}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="flex items-center space-x-2 px-3 py-2 rounded-lg bg-red-100 hover:bg-red-200 transition-colors text-red-700"
|
||||
>
|
||||
<LogOut size={16} />
|
||||
<span className="text-sm font-medium">{t('logout')}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Main Content */}
|
||||
<main className="container mx-auto px-4 py-8">
|
||||
{/* Welcome Section */}
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">
|
||||
Welcome back, {userProfile.name}!
|
||||
</h1>
|
||||
<p className="text-gray-600">
|
||||
Here's your personalized dashboard with important updates and quick actions.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Personalized Alert */}
|
||||
{userProfile.role === 'STUDENT' && userProfile.faculty === 'Arts' && (
|
||||
<div className="mb-8 p-4 bg-blue-50 border border-blue-200 rounded-lg">
|
||||
<div className="flex items-center space-x-3">
|
||||
<AlertCircle className="text-blue-600" size={20} />
|
||||
<div>
|
||||
<h3 className="font-medium text-blue-900">
|
||||
{t('registration_opens')}
|
||||
</h3>
|
||||
<p className="text-sm text-blue-800">
|
||||
Registration for spring semester opens on August 15th for Year 2 students.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Stats Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
||||
{userProfile.role === 'STUDENT' && (
|
||||
<>
|
||||
<StatCard
|
||||
title={t('tuition_balance')}
|
||||
value={`$${userProfile.balance?.toLocaleString() || '0'}`}
|
||||
icon={<DollarSign size={20} />}
|
||||
color="green"
|
||||
/>
|
||||
<StatCard
|
||||
title={t('my_courses')}
|
||||
value={extendedProfile.enrollments?.length.toString() || '0'}
|
||||
icon={<BookOpen size={20} />}
|
||||
color="blue"
|
||||
/>
|
||||
<StatCard
|
||||
title="GPA"
|
||||
value="3.7"
|
||||
icon={<Star size={20} />}
|
||||
color="yellow"
|
||||
/>
|
||||
<StatCard
|
||||
title="Credits"
|
||||
value="45"
|
||||
icon={<Clock size={20} />}
|
||||
color="purple"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{userProfile.role === 'ADMIN' && (
|
||||
<>
|
||||
<StatCard
|
||||
title="Active Students"
|
||||
value="12,543"
|
||||
icon={<User size={20} />}
|
||||
color="blue"
|
||||
/>
|
||||
<StatCard
|
||||
title="Avg Response Time"
|
||||
value="1.2s"
|
||||
icon={<Clock size={20} />}
|
||||
color="green"
|
||||
/>
|
||||
<StatCard
|
||||
title="Satisfaction Rate"
|
||||
value="94%"
|
||||
icon={<Star size={20} />}
|
||||
color="yellow"
|
||||
/>
|
||||
<StatCard
|
||||
title="Tickets Resolved"
|
||||
value="1,247"
|
||||
icon={<BarChart3 size={20} />}
|
||||
color="purple"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Main Content Grid */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
{/* Left Column - Student specific */}
|
||||
<div className="lg:col-span-2 space-y-6">
|
||||
{userProfile.role === 'STUDENT' && (
|
||||
<>
|
||||
{/* Course Schedule */}
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
|
||||
<div className="p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">
|
||||
Current Courses
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
{extendedProfile.enrollments?.map((enrollment) => (
|
||||
<div key={enrollment.id} className="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
||||
<div>
|
||||
<h4 className="font-medium text-gray-900">
|
||||
{enrollment.course.name}
|
||||
</h4>
|
||||
<p className="text-sm text-gray-600">
|
||||
{enrollment.course.code} • {enrollment.course.schedule}
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<span className="text-sm font-medium text-gray-900">
|
||||
{enrollment.grade || 'In Progress'}
|
||||
</span>
|
||||
<p className="text-xs text-gray-500">
|
||||
{enrollment.course.credits} credits
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Upcoming Deadlines */}
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
|
||||
<div className="p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">
|
||||
Upcoming Deadlines
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between p-3 bg-yellow-50 rounded-lg border border-yellow-200">
|
||||
<div>
|
||||
<h4 className="font-medium text-yellow-900">
|
||||
Art History Essay
|
||||
</h4>
|
||||
<p className="text-sm text-yellow-800">
|
||||
Due in 3 days
|
||||
</p>
|
||||
</div>
|
||||
<Calendar className="text-yellow-600" size={20} />
|
||||
</div>
|
||||
<div className="flex items-center justify-between p-3 bg-blue-50 rounded-lg border border-blue-200">
|
||||
<div>
|
||||
<h4 className="font-medium text-blue-900">
|
||||
Course Registration
|
||||
</h4>
|
||||
<p className="text-sm text-blue-800">
|
||||
Opens August 15th
|
||||
</p>
|
||||
</div>
|
||||
<Calendar className="text-blue-600" size={20} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Quick Actions */}
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
|
||||
<div className="p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">
|
||||
Quick Actions
|
||||
</h3>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<Link
|
||||
href="/accessibility"
|
||||
className="flex items-center space-x-3 p-4 bg-green-50 rounded-lg hover:bg-green-100 transition-colors"
|
||||
>
|
||||
<Shield className="text-green-600" size={20} />
|
||||
<span className="font-medium text-green-900">
|
||||
{t('accessibility')}
|
||||
</span>
|
||||
</Link>
|
||||
<Link
|
||||
href="/wellbeing"
|
||||
className="flex items-center space-x-3 p-4 bg-pink-50 rounded-lg hover:bg-pink-100 transition-colors"
|
||||
>
|
||||
<Heart className="text-pink-600" size={20} />
|
||||
<span className="font-medium text-pink-900">
|
||||
{t('wellbeing')}
|
||||
</span>
|
||||
</Link>
|
||||
{userProfile.role === 'ADMIN' && (
|
||||
<Link
|
||||
href="/admin"
|
||||
className="flex items-center space-x-3 p-4 bg-blue-50 rounded-lg hover:bg-blue-100 transition-colors"
|
||||
>
|
||||
<BarChart3 className="text-blue-600" size={20} />
|
||||
<span className="font-medium text-blue-900">
|
||||
{t('admin')}
|
||||
</span>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Column - Advisor & Support */}
|
||||
<div className="space-y-6">
|
||||
{/* Advisor Contact */}
|
||||
{userProfile.role === 'STUDENT' && extendedProfile.advisor && (
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
|
||||
<div className="p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">
|
||||
{t('advisor_contact')}
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="w-10 h-10 bg-blue-600 rounded-full flex items-center justify-center">
|
||||
<span className="text-white text-sm font-medium">
|
||||
{extendedProfile.advisor.name.charAt(0)}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium text-gray-900">
|
||||
{extendedProfile.advisor.name}
|
||||
</p>
|
||||
<p className="text-sm text-gray-600">
|
||||
{extendedProfile.advisor.email}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<button className="w-full bg-blue-600 text-white py-2 px-4 rounded-lg hover:bg-blue-700 transition-colors">
|
||||
Schedule Meeting
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Recent Activity */}
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
|
||||
<div className="p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">
|
||||
Recent Activity
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center space-x-3 text-sm">
|
||||
<div className="w-2 h-2 bg-green-500 rounded-full"></div>
|
||||
<span className="text-gray-600">
|
||||
Submitted Art History assignment
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3 text-sm">
|
||||
<div className="w-2 h-2 bg-blue-500 rounded-full"></div>
|
||||
<span className="text-gray-600">
|
||||
Accessed course materials
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3 text-sm">
|
||||
<div className="w-2 h-2 bg-yellow-500 rounded-full"></div>
|
||||
<span className="text-gray-600">
|
||||
Chatted with AI assistant
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Support Resources */}
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
|
||||
<div className="p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">
|
||||
Support Resources
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
<a
|
||||
href="#"
|
||||
className="flex items-center justify-between p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition-colors"
|
||||
>
|
||||
<span className="text-sm text-gray-700">Academic Support</span>
|
||||
<span className="text-xs text-gray-500">24/7</span>
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
className="flex items-center justify-between p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition-colors"
|
||||
>
|
||||
<span className="text-sm text-gray-700">Technical Help</span>
|
||||
<span className="text-xs text-gray-500">Mon-Fri</span>
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
className="flex items-center justify-between p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition-colors"
|
||||
>
|
||||
<span className="text-sm text-gray-700">Counseling Services</span>
|
||||
<span className="text-xs text-gray-500">Available</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Helper Components
|
||||
const StatCard: React.FC<{
|
||||
title: string
|
||||
value: string
|
||||
icon: React.ReactNode
|
||||
color: 'blue' | 'green' | 'yellow' | 'purple'
|
||||
}> = ({ title, value, icon, color }) => {
|
||||
const colorClasses = {
|
||||
blue: 'bg-blue-50 border-blue-200 text-blue-600',
|
||||
green: 'bg-green-50 border-green-200 text-green-600',
|
||||
yellow: 'bg-yellow-50 border-yellow-200 text-yellow-600',
|
||||
purple: 'bg-purple-50 border-purple-200 text-purple-600',
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className={`p-2 rounded-lg ${colorClasses[color]}`}>
|
||||
{icon}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-2xl font-bold text-gray-900">{value}</p>
|
||||
<p className="text-sm text-gray-600">{title}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
@import "tailwindcss";
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap');
|
||||
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
--primary-blue: #1e3a8a;
|
||||
--primary-teal: #0f766e;
|
||||
--accent-gold: #f59e0b;
|
||||
--text-primary: #111827;
|
||||
--text-secondary: #6b7280;
|
||||
--surface-light: #f8fafc;
|
||||
--border-light: #e5e7eb;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: #0a0a0a;
|
||||
--foreground: #ededed;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--background);
|
||||
color: var(--foreground);
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
|
||||
font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11';
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Custom animations */
|
||||
@keyframes fade-in-up {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade-in-up-delay {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade-in-up-delay-2 {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse-glow {
|
||||
0%, 100% {
|
||||
box-shadow: 0 0 5px rgba(59, 130, 246, 0.5);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 20px rgba(59, 130, 246, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-in-up {
|
||||
animation: fade-in-up 0.8s ease-out;
|
||||
}
|
||||
|
||||
.animate-fade-in-up-delay {
|
||||
animation: fade-in-up-delay 0.8s ease-out 0.2s both;
|
||||
}
|
||||
|
||||
.animate-fade-in-up-delay-2 {
|
||||
animation: fade-in-up-delay-2 0.8s ease-out 0.4s both;
|
||||
}
|
||||
|
||||
.animate-float {
|
||||
animation: float 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.animate-pulse-glow {
|
||||
animation: pulse-glow 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* Professional gradients */
|
||||
.gradient-primary {
|
||||
background: linear-gradient(135deg, var(--primary-blue) 0%, var(--primary-teal) 100%);
|
||||
}
|
||||
|
||||
.gradient-accent {
|
||||
background: linear-gradient(135deg, var(--accent-gold) 0%, #f97316 100%);
|
||||
}
|
||||
|
||||
.gradient-surface {
|
||||
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
|
||||
}
|
||||
|
||||
/* Glass morphism effects */
|
||||
.glass {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.glass-dark {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* Typography improvements */
|
||||
.text-hero {
|
||||
font-size: clamp(2.5rem, 8vw, 5rem);
|
||||
font-weight: 800;
|
||||
line-height: 1.1;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.text-display {
|
||||
font-size: clamp(1.875rem, 4vw, 3rem);
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.text-body-large {
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
/* Professional hover effects */
|
||||
.hover-lift {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.hover-lift:hover {
|
||||
transform: translateY(-8px);
|
||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
/* Loading states */
|
||||
.skeleton {
|
||||
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: loading 1.5s infinite;
|
||||
}
|
||||
|
||||
@keyframes loading {
|
||||
0% {
|
||||
background-position: 200% 0;
|
||||
}
|
||||
100% {
|
||||
background-position: -200% 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Responsive improvements */
|
||||
@media (max-width: 768px) {
|
||||
.text-hero {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
@import "tailwindcss";
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap');
|
||||
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
--primary-blue: #1e3a8a;
|
||||
--primary-teal: #0f766e;
|
||||
--accent-gold: #f59e0b;
|
||||
--text-primary: #111827;
|
||||
--text-secondary: #6b7280;
|
||||
--surface-light: #f8fafc;
|
||||
--border-light: #e5e7eb;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: #0a0a0a;
|
||||
--foreground: #ededed;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--background);
|
||||
color: var(--foreground);
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
|
||||
font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11';
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Custom animations */
|
||||
@keyframes fade-in-up {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse-glow {
|
||||
0%, 100% {
|
||||
box-shadow: 0 0 5px rgba(59, 130, 246, 0.5);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 20px rgba(59, 130, 246, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-in-up {
|
||||
animation: fade-in-up 0.8s ease-out;
|
||||
}
|
||||
|
||||
.animate-float {
|
||||
animation: float 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.animate-pulse-glow {
|
||||
animation: pulse-glow 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* Professional gradients */
|
||||
.gradient-primary {
|
||||
background: linear-gradient(135deg, var(--primary-blue) 0%, var(--primary-teal) 100%);
|
||||
}
|
||||
|
||||
.gradient-accent {
|
||||
background: linear-gradient(135deg, var(--accent-gold) 0%, #f97316 100%);
|
||||
}
|
||||
|
||||
/* Glass morphism effects */
|
||||
.glass {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.glass-dark {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* Typography improvements */
|
||||
.text-hero {
|
||||
font-size: clamp(2.5rem, 8vw, 5rem);
|
||||
font-weight: 800;
|
||||
line-height: 1.1;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.text-display {
|
||||
font-size: clamp(1.875rem, 4vw, 3rem);
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
/* Professional hover effects */
|
||||
.hover-lift {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.hover-lift:hover {
|
||||
transform: translateY(-8px);
|
||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
/* Responsive improvements */
|
||||
@media (max-width: 768px) {
|
||||
.text-hero {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
}
|
||||
+163
-12
@@ -1,26 +1,177 @@
|
||||
@import "tailwindcss";
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap');
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
--primary-blue: #1e3a8a;
|
||||
--primary-teal: #0f766e;
|
||||
--accent-gold: #f59e0b;
|
||||
--text-primary: #111827;
|
||||
--text-secondary: #6b7280;
|
||||
--surface-light: #f8fafc;
|
||||
--border-light: #e5e7eb;
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--font-sans: var(--font-geist-sans);
|
||||
--font-mono: var(--font-geist-mono);
|
||||
/* RTL Support */
|
||||
[dir="rtl"] {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: #0a0a0a;
|
||||
--foreground: #ededed;
|
||||
}
|
||||
[dir="rtl"] .ltr-only {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[dir="ltr"] .rtl-only {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Consistent Container Classes */
|
||||
.page-container {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 80rem;
|
||||
padding: 2rem 1rem;
|
||||
}
|
||||
|
||||
.section-container {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 72rem;
|
||||
padding: 1.5rem 1rem;
|
||||
}
|
||||
|
||||
.content-container {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 64rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
/* RTL Specific Margins and Paddings */
|
||||
[dir="rtl"] .ml-auto {
|
||||
margin-left: unset;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
[dir="rtl"] .mr-auto {
|
||||
margin-right: unset;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
[dir="rtl"] .space-x-4 > * + * {
|
||||
margin-left: unset;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
/* Base styles */
|
||||
body {
|
||||
background: var(--background);
|
||||
color: var(--foreground);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
|
||||
font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11';
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Custom animations */
|
||||
@keyframes fade-in-up {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse-glow {
|
||||
0%, 100% {
|
||||
box-shadow: 0 0 5px rgba(59, 130, 246, 0.5);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 20px rgba(59, 130, 246, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-in-up {
|
||||
animation: fade-in-up 0.8s ease-out;
|
||||
}
|
||||
|
||||
.animate-float {
|
||||
animation: float 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.animate-pulse-glow {
|
||||
animation: pulse-glow 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* Professional gradients */
|
||||
.gradient-primary {
|
||||
background: linear-gradient(135deg, var(--primary-blue) 0%, var(--primary-teal) 100%);
|
||||
}
|
||||
|
||||
.gradient-accent {
|
||||
background: linear-gradient(135deg, var(--accent-gold) 0%, #f97316 100%);
|
||||
}
|
||||
|
||||
/* Glass morphism effects */
|
||||
.glass {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.glass-dark {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* Typography improvements */
|
||||
.text-hero {
|
||||
font-size: clamp(2.5rem, 8vw, 5rem);
|
||||
font-weight: 800;
|
||||
line-height: 1.1;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.text-display {
|
||||
font-size: clamp(1.875rem, 4vw, 3rem);
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
/* Professional hover effects */
|
||||
.hover-lift {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.hover-lift:hover {
|
||||
transform: translateY(-8px);
|
||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
/* Responsive improvements */
|
||||
@media (max-width: 768px) {
|
||||
.text-hero {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,302 @@
|
||||
'use client';
|
||||
|
||||
import { useLanguage } from '@/components/providers/LanguageProvider';
|
||||
import { Lightbulb, Rocket, Brain, Zap, Target, Globe, Award, TrendingUp } from 'lucide-react';
|
||||
|
||||
const InnovationPage = () => {
|
||||
const { language } = useLanguage();
|
||||
const isRTL = language === 'ar';
|
||||
|
||||
const innovations = [
|
||||
{
|
||||
title: "Ocean Intelligence Platform",
|
||||
category: "Marine Technology",
|
||||
description: "AI-powered ocean monitoring system providing real-time data on marine ecosystems, helping predict and prevent environmental disasters.",
|
||||
impact: "$50M+ in marine conservation funding secured",
|
||||
status: "Deployed globally",
|
||||
icon: <Brain className="w-8 h-8" />,
|
||||
color: "text-blue-600",
|
||||
bgColor: "bg-blue-50"
|
||||
},
|
||||
{
|
||||
title: "Antarctic Climate Predictive Models",
|
||||
category: "Climate Science",
|
||||
description: "Revolutionary machine learning models that predict ice sheet behavior with 95% accuracy, informing global climate policy.",
|
||||
impact: "Used by 15+ government climate agencies",
|
||||
status: "Active research",
|
||||
icon: <Zap className="w-8 h-8" />,
|
||||
color: "text-cyan-600",
|
||||
bgColor: "bg-cyan-50"
|
||||
},
|
||||
{
|
||||
title: "Sustainable Aquaculture Systems",
|
||||
category: "Food Security",
|
||||
description: "Breakthrough closed-loop aquaculture technology reducing environmental impact while tripling fish production efficiency.",
|
||||
impact: "Licensed to 200+ global fish farms",
|
||||
status: "Commercial deployment",
|
||||
icon: <Target className="w-8 h-8" />,
|
||||
color: "text-green-600",
|
||||
bgColor: "bg-green-50"
|
||||
},
|
||||
{
|
||||
title: "Polar Medicine Innovations",
|
||||
category: "Healthcare",
|
||||
description: "Medical devices and treatments adapted for extreme environments, revolutionizing remote healthcare delivery.",
|
||||
impact: "Saving lives in 40+ remote locations",
|
||||
status: "Medical trials",
|
||||
icon: <Rocket className="w-8 h-8" />,
|
||||
color: "text-purple-600",
|
||||
bgColor: "bg-purple-50"
|
||||
}
|
||||
];
|
||||
|
||||
const partnerships = [
|
||||
{
|
||||
name: "Antarctic Treaty System",
|
||||
type: "International Treaty",
|
||||
role: "Research Coordinator",
|
||||
impact: "Leading climate research for 54 nations"
|
||||
},
|
||||
{
|
||||
name: "Commonwealth Scientific Research",
|
||||
type: "Government Partnership",
|
||||
role: "Strategic Research Partner",
|
||||
impact: "£100M+ joint research funding"
|
||||
},
|
||||
{
|
||||
name: "International Ocean Discovery",
|
||||
type: "Global Consortium",
|
||||
role: "Core Institution",
|
||||
impact: "20+ breakthrough marine discoveries"
|
||||
},
|
||||
{
|
||||
name: "World Climate Research Programme",
|
||||
type: "UN Partnership",
|
||||
role: "Lead Research Institution",
|
||||
impact: "Informing global climate policy"
|
||||
}
|
||||
];
|
||||
|
||||
const startups = [
|
||||
{
|
||||
name: "OceanMind Analytics",
|
||||
founded: "2022",
|
||||
sector: "Marine AI",
|
||||
valuation: "$15M",
|
||||
description: "AI platform predicting fish migration patterns for sustainable fishing"
|
||||
},
|
||||
{
|
||||
name: "Antarctic Genomics",
|
||||
founded: "2023",
|
||||
sector: "Biotechnology",
|
||||
valuation: "$8M",
|
||||
description: "Developing new medicines from Antarctic microorganisms"
|
||||
},
|
||||
{
|
||||
name: "Climate Futures Tech",
|
||||
founded: "2021",
|
||||
sector: "Climate Tech",
|
||||
valuation: "$25M",
|
||||
description: "Software solutions for climate adaptation planning"
|
||||
},
|
||||
{
|
||||
name: "Polar Healthcare Solutions",
|
||||
founded: "2023",
|
||||
sector: "HealthTech",
|
||||
valuation: "$12M",
|
||||
description: "Medical devices for extreme environment healthcare"
|
||||
}
|
||||
];
|
||||
|
||||
const metrics = [
|
||||
{ value: "150+", label: "Patents Filed", description: "Active intellectual property portfolio" },
|
||||
{ value: "$500M+", label: "Research Impact", description: "Annual economic impact of research" },
|
||||
{ value: "40+", label: "Spin-off Companies", description: "Successful research commercialization" },
|
||||
{ value: "85%", label: "Industry Adoption", description: "Research translated to real-world applications" }
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={`min-h-screen bg-gradient-to-br from-purple-50 to-blue-50 ${isRTL ? 'rtl' : 'ltr'}`}>
|
||||
{/* Hero Section */}
|
||||
<div className="bg-gradient-to-r from-purple-900 via-blue-900 to-indigo-900 text-white">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-20">
|
||||
<div className="text-center">
|
||||
<div className="flex justify-center mb-6">
|
||||
<div className="bg-white/10 backdrop-blur-sm rounded-full p-6">
|
||||
<Lightbulb className="w-16 h-16 text-yellow-300" />
|
||||
</div>
|
||||
</div>
|
||||
<h1 className="text-5xl md:text-7xl font-bold mb-6">
|
||||
Innovation Hub
|
||||
</h1>
|
||||
<p className="text-xl md:text-2xl mb-8 text-purple-100 max-w-4xl mx-auto">
|
||||
Where breakthrough research meets real-world impact. Discover how UTAS
|
||||
innovations are solving global challenges and creating tomorrow's solutions.
|
||||
</p>
|
||||
<div className="flex flex-wrap justify-center gap-6">
|
||||
<div className="bg-white/10 backdrop-blur-sm rounded-lg px-6 py-4">
|
||||
<div className="text-3xl font-bold text-yellow-300">150+</div>
|
||||
<div className="text-sm text-purple-100">Patents</div>
|
||||
</div>
|
||||
<div className="bg-white/10 backdrop-blur-sm rounded-lg px-6 py-4">
|
||||
<div className="text-3xl font-bold text-yellow-300">40+</div>
|
||||
<div className="text-sm text-purple-100">Startups</div>
|
||||
</div>
|
||||
<div className="bg-white/10 backdrop-blur-sm rounded-lg px-6 py-4">
|
||||
<div className="text-3xl font-bold text-yellow-300">$500M+</div>
|
||||
<div className="text-sm text-purple-100">Impact</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Key Metrics */}
|
||||
<div className="bg-white py-16">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<h2 className="text-3xl font-bold text-center mb-12 text-gray-900">
|
||||
Innovation Impact
|
||||
</h2>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-8">
|
||||
{metrics.map((metric, index) => (
|
||||
<div key={index} className="text-center">
|
||||
<div className="text-4xl md:text-5xl font-bold text-purple-600 mb-2">
|
||||
{metric.value}
|
||||
</div>
|
||||
<div className="text-lg font-semibold text-gray-900 mb-1">
|
||||
{metric.label}
|
||||
</div>
|
||||
<div className="text-sm text-gray-600">
|
||||
{metric.description}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Breakthrough Innovations */}
|
||||
<div className="py-16 bg-gradient-to-r from-purple-50 to-blue-50">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<h2 className="text-4xl font-bold text-center mb-12 text-gray-900">
|
||||
Breakthrough Innovations
|
||||
</h2>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
{innovations.map((innovation, index) => (
|
||||
<div key={index} className="bg-white rounded-xl shadow-lg p-6 hover:shadow-xl transition-shadow duration-300">
|
||||
<div className="flex items-start gap-4 mb-4">
|
||||
<div className={`${innovation.bgColor} ${innovation.color} p-3 rounded-lg`}>
|
||||
{innovation.icon}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="bg-purple-100 text-purple-800 px-3 py-1 rounded-full text-sm font-semibold inline-block mb-2">
|
||||
{innovation.category}
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-gray-900 mb-2">
|
||||
{innovation.title}
|
||||
</h3>
|
||||
<div className="bg-green-100 text-green-800 px-2 py-1 rounded text-sm font-medium inline-block">
|
||||
{innovation.status}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-gray-700 mb-4">
|
||||
{innovation.description}
|
||||
</p>
|
||||
<div className="bg-gradient-to-r from-blue-500 to-purple-500 text-white px-4 py-2 rounded-lg text-sm font-semibold">
|
||||
<TrendingUp className="w-4 h-4 inline mr-2" />
|
||||
{innovation.impact}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Global Partnerships */}
|
||||
<div className="py-16 bg-white">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<h2 className="text-4xl font-bold text-center mb-12 text-gray-900">
|
||||
Global Research Partnerships
|
||||
</h2>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
{partnerships.map((partnership, index) => (
|
||||
<div key={index} className="bg-gradient-to-r from-blue-50 to-purple-50 rounded-xl p-6">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<Globe className="w-6 h-6 text-blue-600" />
|
||||
<h3 className="text-xl font-bold text-gray-900">
|
||||
{partnership.name}
|
||||
</h3>
|
||||
</div>
|
||||
<div className="text-sm text-blue-600 font-semibold mb-2">
|
||||
{partnership.type} • {partnership.role}
|
||||
</div>
|
||||
<p className="text-gray-700">
|
||||
{partnership.impact}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* UTAS Spin-offs */}
|
||||
<div className="py-16 bg-gradient-to-r from-blue-50 to-purple-50">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<h2 className="text-4xl font-bold text-center mb-12 text-gray-900">
|
||||
UTAS-Born Startups
|
||||
</h2>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
{startups.map((startup, index) => (
|
||||
<div key={index} className="bg-white rounded-xl shadow-lg p-6 hover:shadow-xl transition-shadow duration-300">
|
||||
<div className="flex justify-between items-start mb-4">
|
||||
<div>
|
||||
<h3 className="text-xl font-bold text-gray-900 mb-1">
|
||||
{startup.name}
|
||||
</h3>
|
||||
<div className="text-sm text-gray-600">
|
||||
Founded {startup.founded} • {startup.sector}
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-green-100 text-green-800 px-3 py-1 rounded-full text-sm font-semibold">
|
||||
{startup.valuation}
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-gray-700">
|
||||
{startup.description}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Call to Action */}
|
||||
<div className="bg-gradient-to-r from-purple-600 to-blue-600 py-16">
|
||||
<div className="max-w-4xl mx-auto text-center px-4 sm:px-6 lg:px-8">
|
||||
<Award className="w-16 h-16 text-yellow-300 mx-auto mb-6" />
|
||||
<h2 className="text-4xl font-bold text-white mb-6">
|
||||
Join the Innovation Revolution
|
||||
</h2>
|
||||
<p className="text-xl text-purple-100 mb-8">
|
||||
Be part of the next generation of innovators solving global challenges
|
||||
through cutting-edge research and real-world applications.
|
||||
</p>
|
||||
<div className="flex flex-wrap justify-center gap-4">
|
||||
<button className="bg-white text-purple-600 px-8 py-4 rounded-lg font-semibold hover:bg-purple-50 transition-colors text-lg">
|
||||
Research Programs
|
||||
</button>
|
||||
<button className="border-2 border-white text-white px-8 py-4 rounded-lg font-semibold hover:bg-white hover:text-purple-600 transition-colors text-lg">
|
||||
Innovation Partnerships
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InnovationPage;
|
||||
+41
-12
@@ -1,6 +1,13 @@
|
||||
'use client';
|
||||
|
||||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import { AuthProvider } from "@/components/providers/MockAuthProvider";
|
||||
import { LanguageProvider, useLanguage } from "@/components/providers/LanguageProvider";
|
||||
import FloatingChatbot from "@/components/Chat/FloatingChatbot";
|
||||
import { GDPRBanner } from "@/components/GDPRBanner";
|
||||
import SimplifiedNavigation from "@/components/Navigation/SimplifiedNavigation";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
@@ -13,22 +20,44 @@ const geistMono = Geist_Mono({
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
title: "University Portal - Next Generation Student Experience",
|
||||
description: "AI-enabled university portal with personalized content and 24/7 support",
|
||||
keywords: "university, portal, student, AI, education",
|
||||
authors: [{ name: "University IT Department" }],
|
||||
viewport: "width=device-width, initial-scale=1",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
function RootLayoutContent({ children }: { children: React.ReactNode }) {
|
||||
const { language } = useLanguage();
|
||||
|
||||
return (
|
||||
<html lang="en">
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
{children}
|
||||
<html lang={language} dir={language === 'ar' ? 'rtl' : 'ltr'} className="scroll-smooth">
|
||||
<body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
|
||||
<AuthProvider>
|
||||
<SimplifiedNavigation />
|
||||
<main className="min-h-screen bg-gray-50">
|
||||
<div className="page-container">
|
||||
{children}
|
||||
</div>
|
||||
</main>
|
||||
<FloatingChatbot position={language === 'ar' ? 'bottom-left' : 'bottom-right'} theme="blue" />
|
||||
<GDPRBanner />
|
||||
</AuthProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<LanguageProvider>
|
||||
<RootLayoutContent>
|
||||
{children}
|
||||
</RootLayoutContent>
|
||||
</LanguageProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,414 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { useAuth } from '@/components/providers/MockAuthProvider'
|
||||
import { useLanguage } from '@/components/providers/LanguageProvider'
|
||||
import {
|
||||
Globe,
|
||||
BookOpen,
|
||||
Users,
|
||||
MessageSquare,
|
||||
Heart,
|
||||
Shield,
|
||||
BarChart3,
|
||||
Languages,
|
||||
LogIn,
|
||||
GraduationCap,
|
||||
Star,
|
||||
Clock,
|
||||
MapPin
|
||||
} from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
|
||||
export default function HomePage() {
|
||||
const { user, login, loading } = useAuth()
|
||||
const { t, language, setLanguage } = useLanguage()
|
||||
const [loginForm, setLoginForm] = useState({ email: '', password: '' })
|
||||
const [showLogin, setShowLogin] = useState(false)
|
||||
|
||||
const handleLogin = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
try {
|
||||
await login(loginForm.email, loginForm.password)
|
||||
} catch (error) {
|
||||
console.error('Login failed:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const toggleLanguage = () => {
|
||||
setLanguage(language === 'en' ? 'ar' : 'en')
|
||||
}
|
||||
|
||||
if (user) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
|
||||
{/* Header */}
|
||||
<header className="bg-white shadow-sm">
|
||||
<div className="container mx-auto px-4 py-4 flex items-center justify-between">
|
||||
<div className="flex items-center space-x-3">
|
||||
<GraduationCap size={32} className="text-blue-600" />
|
||||
<h1 className="text-2xl font-bold text-gray-900">University Portal</h1>
|
||||
</div>
|
||||
<div className="flex items-center space-x-4">
|
||||
<button
|
||||
onClick={toggleLanguage}
|
||||
className="flex items-center space-x-2 px-3 py-2 rounded-lg bg-gray-100 hover:bg-gray-200 transition-colors"
|
||||
>
|
||||
<Languages size={16} />
|
||||
<span className="text-sm font-medium">{language.toUpperCase()}</span>
|
||||
</button>
|
||||
<Link
|
||||
href="/dashboard"
|
||||
className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
{t('dashboard')}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Welcome Section */}
|
||||
<section className="py-20">
|
||||
<div className="container mx-auto px-4 text-center">
|
||||
<h2 className="text-4xl font-bold text-gray-900 mb-6">
|
||||
{t('welcome')}
|
||||
</h2>
|
||||
<p className="text-xl text-gray-600 mb-8 max-w-2xl mx-auto">
|
||||
Experience the future of university services with AI-powered assistance,
|
||||
personalized content, and comprehensive support.
|
||||
</p>
|
||||
<div className="flex justify-center space-x-4">
|
||||
<Link
|
||||
href="/dashboard"
|
||||
className="bg-blue-600 text-white px-8 py-3 rounded-lg text-lg hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
Go to Dashboard
|
||||
</Link>
|
||||
<Link
|
||||
href="/wellbeing"
|
||||
className="bg-pink-600 text-white px-8 py-3 rounded-lg text-lg hover:bg-pink-700 transition-colors flex items-center space-x-2"
|
||||
>
|
||||
<Heart size={20} />
|
||||
<span>{t('wellbeing')}</span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Features Grid */}
|
||||
<section className="py-16 bg-white">
|
||||
<div className="container mx-auto px-4">
|
||||
<h3 className="text-3xl font-bold text-center text-gray-900 mb-12">
|
||||
Flagship Features
|
||||
</h3>
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
<FeatureCard
|
||||
icon={<MessageSquare size={24} />}
|
||||
title="24/7 AI Assistant"
|
||||
description="Get instant answers to your questions in English or Arabic with our GPT-powered chatbot."
|
||||
/>
|
||||
<FeatureCard
|
||||
icon={<Users size={24} />}
|
||||
title="Personalized Dashboard"
|
||||
description="View your timetable, tuition balance, and advisor contact in one place."
|
||||
/>
|
||||
<FeatureCard
|
||||
icon={<Shield size={24} />}
|
||||
title="Accessibility First"
|
||||
description="Automated WCAG compliance checking with AI-generated alt text suggestions."
|
||||
/>
|
||||
<FeatureCard
|
||||
icon={<Heart size={24} />}
|
||||
title="Mental Health Support"
|
||||
description="Dedicated wellbeing chat with professional counselor referrals."
|
||||
/>
|
||||
<FeatureCard
|
||||
icon={<BarChart3 size={24} />}
|
||||
title="Smart Feedback"
|
||||
description="Real-time satisfaction surveys with live dashboard updates."
|
||||
/>
|
||||
<FeatureCard
|
||||
icon={<Globe size={24} />}
|
||||
title="Multilingual Support"
|
||||
description="Full interface available in English and Arabic with RTL support."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Quick Actions */}
|
||||
<section className="py-16 bg-gray-50">
|
||||
<div className="container mx-auto px-4">
|
||||
<h3 className="text-3xl font-bold text-center text-gray-900 mb-12">
|
||||
Quick Actions
|
||||
</h3>
|
||||
<div className="grid md:grid-cols-4 gap-6">
|
||||
<QuickAction
|
||||
icon={<BookOpen size={20} />}
|
||||
title="Course Registration"
|
||||
description="Register for fall courses"
|
||||
href="/courses"
|
||||
/>
|
||||
<QuickAction
|
||||
icon={<Star size={20} />}
|
||||
title="Scholarships"
|
||||
description="View available scholarships"
|
||||
href="/scholarships"
|
||||
/>
|
||||
<QuickAction
|
||||
icon={<Clock size={20} />}
|
||||
title="Academic Calendar"
|
||||
description="Important dates & deadlines"
|
||||
href="/calendar"
|
||||
/>
|
||||
<QuickAction
|
||||
icon={<MapPin size={20} />}
|
||||
title="Campus Map"
|
||||
description="Find buildings & facilities"
|
||||
href="/map"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
|
||||
{/* Header */}
|
||||
<header className="bg-white shadow-sm">
|
||||
<div className="container mx-auto px-4 py-4 flex items-center justify-between">
|
||||
<div className="flex items-center space-x-3">
|
||||
<GraduationCap size={32} className="text-blue-600" />
|
||||
<h1 className="text-2xl font-bold text-gray-900">University Portal</h1>
|
||||
</div>
|
||||
<div className="flex items-center space-x-4">
|
||||
<button
|
||||
onClick={toggleLanguage}
|
||||
className="flex items-center space-x-2 px-3 py-2 rounded-lg bg-gray-100 hover:bg-gray-200 transition-colors"
|
||||
>
|
||||
<Languages size={16} />
|
||||
<span className="text-sm font-medium">{language.toUpperCase()}</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowLogin(true)}
|
||||
className="flex items-center space-x-2 bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
<LogIn size={16} />
|
||||
<span>{t('login')}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Hero Section */}
|
||||
<section className="py-20">
|
||||
<div className="container mx-auto px-4 text-center">
|
||||
<h2 className="text-5xl font-bold text-gray-900 mb-6">
|
||||
{t('welcome')}
|
||||
</h2>
|
||||
<p className="text-xl text-gray-600 mb-8 max-w-3xl mx-auto">
|
||||
Experience the future of university services with AI-powered assistance,
|
||||
personalized content, comprehensive accessibility features, and 24/7 support.
|
||||
</p>
|
||||
<div className="flex justify-center space-x-4">
|
||||
<button
|
||||
onClick={() => setShowLogin(true)}
|
||||
className="bg-blue-600 text-white px-8 py-3 rounded-lg text-lg hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
{t('login')}
|
||||
</button>
|
||||
<Link
|
||||
href="/wellbeing"
|
||||
className="bg-pink-600 text-white px-8 py-3 rounded-lg text-lg hover:bg-pink-700 transition-colors flex items-center space-x-2"
|
||||
>
|
||||
<Heart size={20} />
|
||||
<span>{t('wellbeing')}</span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Features Section */}
|
||||
<section className="py-16 bg-white">
|
||||
<div className="container mx-auto px-4">
|
||||
<h3 className="text-3xl font-bold text-center text-gray-900 mb-12">
|
||||
Flagship Capabilities
|
||||
</h3>
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
<FeatureCard
|
||||
icon={<MessageSquare size={24} />}
|
||||
title="24/7 Conversational Assistant"
|
||||
description="Multilingual GPT-4 powered chatbot answering 20+ FAQs with human escalation support."
|
||||
/>
|
||||
<FeatureCard
|
||||
icon={<Users size={24} />}
|
||||
title="Personalized Dashboard"
|
||||
description="Login-based content showing timetable, tuition balance, and advisor contact from mock DB."
|
||||
/>
|
||||
<FeatureCard
|
||||
icon={<Shield size={24} />}
|
||||
title="Automated Accessibility"
|
||||
description="WCAG 2.2 compliance checking with AI-generated alt text suggestions via Vision API."
|
||||
/>
|
||||
<FeatureCard
|
||||
icon={<Heart size={24} />}
|
||||
title="Mental Health Triage"
|
||||
description="Empathic chat flow with trigger phrase detection and counselor calendar integration."
|
||||
/>
|
||||
<FeatureCard
|
||||
icon={<BarChart3 size={24} />}
|
||||
title="Smart Feedback Surveys"
|
||||
description="One-click CSAT with real-time dashboard updates in under 5 seconds."
|
||||
/>
|
||||
<FeatureCard
|
||||
icon={<Globe size={24} />}
|
||||
title="Multilingual Interface"
|
||||
description="Full English/Arabic support with RTL layout and cultural adaptations."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Demo Accounts */}
|
||||
<section className="py-16 bg-gray-50">
|
||||
<div className="container mx-auto px-4">
|
||||
<h3 className="text-3xl font-bold text-center text-gray-900 mb-12">
|
||||
Demo Accounts
|
||||
</h3>
|
||||
<div className="grid md:grid-cols-3 gap-6 max-w-4xl mx-auto">
|
||||
<DemoAccount
|
||||
name="Sara Johnson"
|
||||
role="Year 2 Arts Student"
|
||||
email="sara.year2@university.edu"
|
||||
password="sara123"
|
||||
features={['Registration opens Aug 15', 'Tuition balance: $2,500', 'Advisor: Dr. Smith']}
|
||||
/>
|
||||
<DemoAccount
|
||||
name="John Smith"
|
||||
role="Postgraduate Engineering"
|
||||
email="john.postgrad@university.edu"
|
||||
password="john123"
|
||||
features={['Research project status', 'Tuition balance: $1,200', 'Advisor: Dr. Johnson']}
|
||||
/>
|
||||
<DemoAccount
|
||||
name="Admin User"
|
||||
role="System Administrator"
|
||||
email="admin@university.edu"
|
||||
password="admin123"
|
||||
features={['Full system access', 'Analytics dashboard', 'User management']}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Login Modal */}
|
||||
{showLogin && (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
<div className="bg-white rounded-lg p-8 w-full max-w-md">
|
||||
<h3 className="text-2xl font-bold mb-6 text-center">{t('login')}</h3>
|
||||
<form onSubmit={handleLogin} className="space-y-4">
|
||||
<div>
|
||||
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Email
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
value={loginForm.email}
|
||||
onChange={(e) => setLoginForm({ ...loginForm, email: e.target.value })}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="password" className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Password
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
value={loginForm.password}
|
||||
onChange={(e) => setLoginForm({ ...loginForm, password: e.target.value })}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="flex space-x-4">
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="flex-1 bg-blue-600 text-white py-2 rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50"
|
||||
>
|
||||
{loading ? 'Signing in...' : t('login')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowLogin(false)}
|
||||
className="flex-1 bg-gray-300 text-gray-700 py-2 rounded-lg hover:bg-gray-400 transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Helper Components
|
||||
const FeatureCard: React.FC<{ icon: React.ReactNode; title: string; description: string }> = ({
|
||||
icon,
|
||||
title,
|
||||
description,
|
||||
}) => (
|
||||
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
|
||||
<div className="text-blue-600 mb-4">{icon}</div>
|
||||
<h4 className="text-lg font-semibold text-gray-900 mb-2">{title}</h4>
|
||||
<p className="text-gray-600">{description}</p>
|
||||
</div>
|
||||
)
|
||||
|
||||
const QuickAction: React.FC<{
|
||||
icon: React.ReactNode
|
||||
title: string
|
||||
description: string
|
||||
href: string
|
||||
}> = ({ icon, title, description, href }) => (
|
||||
<Link
|
||||
href={href}
|
||||
className="bg-white p-6 rounded-lg shadow-sm border border-gray-200 hover:shadow-md transition-shadow"
|
||||
>
|
||||
<div className="text-blue-600 mb-3">{icon}</div>
|
||||
<h4 className="font-semibold text-gray-900 mb-1">{title}</h4>
|
||||
<p className="text-sm text-gray-600">{description}</p>
|
||||
</Link>
|
||||
)
|
||||
|
||||
const DemoAccount: React.FC<{
|
||||
name: string
|
||||
role: string
|
||||
email: string
|
||||
password: string
|
||||
features: string[]
|
||||
}> = ({ name, role, email, password, features }) => (
|
||||
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
|
||||
<h4 className="font-semibold text-gray-900 mb-1">{name}</h4>
|
||||
<p className="text-sm text-gray-600 mb-4">{role}</p>
|
||||
<div className="space-y-2 mb-4">
|
||||
<p className="text-sm"><span className="font-medium">Email:</span> {email}</p>
|
||||
<p className="text-sm"><span className="font-medium">Password:</span> {password}</p>
|
||||
</div>
|
||||
<ul className="text-sm text-gray-600 space-y-1">
|
||||
{features.map((feature, index) => (
|
||||
<li key={index} className="flex items-center space-x-2">
|
||||
<span className="w-1.5 h-1.5 bg-blue-600 rounded-full"></span>
|
||||
<span>{feature}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,647 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useAuth } from '@/components/providers/MockAuthProvider';
|
||||
import { useLanguage } from '@/components/providers/LanguageProvider';
|
||||
import {
|
||||
Globe,
|
||||
BookOpen,
|
||||
Users,
|
||||
MessageSquare,
|
||||
Heart,
|
||||
Shield,
|
||||
BarChart3,
|
||||
Languages,
|
||||
LogIn,
|
||||
GraduationCap,
|
||||
Star,
|
||||
MapPin,
|
||||
Trophy,
|
||||
Waves,
|
||||
Leaf,
|
||||
ChevronRight,
|
||||
Award,
|
||||
Zap,
|
||||
MapIcon,
|
||||
Phone,
|
||||
Mail,
|
||||
ExternalLink
|
||||
} from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
export default function HomePage() {
|
||||
const { user, login, loading } = useAuth();
|
||||
const { language, setLanguage } = useLanguage();
|
||||
const router = useRouter();
|
||||
const [loginForm, setLoginForm] = useState({ email: '', password: '' });
|
||||
const [showLogin, setShowLogin] = useState(false);
|
||||
const [currentSlide, setCurrentSlide] = useState(0);
|
||||
const [animatedStats, setAnimatedStats] = useState({ students: 0, programs: 0, satisfaction: 0 });
|
||||
|
||||
// Auto-slide for hero banner
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => {
|
||||
setCurrentSlide((prev) => (prev + 1) % 3);
|
||||
}, 5000);
|
||||
return () => clearInterval(timer);
|
||||
}, []);
|
||||
|
||||
// Animate statistics
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
const interval = setInterval(() => {
|
||||
setAnimatedStats(prev => ({
|
||||
students: prev.students < 35000 ? prev.students + 500 : 35000,
|
||||
programs: prev.programs < 350 ? prev.programs + 5 : 350,
|
||||
satisfaction: prev.satisfaction < 92 ? prev.satisfaction + 1 : 92
|
||||
}));
|
||||
}, 50);
|
||||
|
||||
setTimeout(() => clearInterval(interval), 2000);
|
||||
}, 1000);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
const handleLogin = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
const success = await login(loginForm.email, loginForm.password);
|
||||
if (success) {
|
||||
router.push('/dashboard');
|
||||
} else {
|
||||
alert('Invalid credentials. Use: student@university.edu / password123 or admin@university.edu / admin123');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Login failed:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleLanguage = () => {
|
||||
setLanguage(language === 'en' ? 'ar' : 'en');
|
||||
};
|
||||
|
||||
const heroSlides = [
|
||||
{
|
||||
title: language === 'en' ? "#1 in Climate Action Globally" : "#1 عالمياً في العمل المناخي",
|
||||
subtitle: language === 'en' ? "Leading the world in sustainability and environmental innovation" : "رائدة عالمياً في الاستدامة والابتكار البيئي",
|
||||
image: "https://images.unsplash.com/photo-1569163139394-de4e4f43e4e3?w=1200&h=600&fit=crop",
|
||||
cta: language === 'en' ? "Discover Our Impact" : "اكتشف تأثيرنا"
|
||||
},
|
||||
{
|
||||
title: language === 'en' ? "World-Class Marine & Antarctic Science" : "علوم بحرية وقطبية عالمية المستوى",
|
||||
subtitle: language === 'en' ? "Pioneering research in marine ecosystems and climate science" : "أبحاث رائدة في النظم البيئية البحرية وعلوم المناخ",
|
||||
image: "https://images.unsplash.com/photo-1583212292454-1fe6229603b7?w=1200&h=600&fit=crop",
|
||||
cta: language === 'en' ? "Explore Research" : "اكتشف البحوث"
|
||||
},
|
||||
{
|
||||
title: language === 'en' ? "Transform Your Future with UTAS" : "حوّل مستقبلك مع جامعة تاسمانيا",
|
||||
subtitle: language === 'en' ? "Join Australia's most sustainable university" : "انضم إلى أكثر الجامعات الأسترالية استدامة",
|
||||
image: "https://images.unsplash.com/photo-1523050854058-8df90110c9f1?w=1200&h=600&fit=crop",
|
||||
cta: language === 'en' ? "Start Your Journey" : "ابدأ رحلتك"
|
||||
}
|
||||
];
|
||||
|
||||
const studyAreas = [
|
||||
{
|
||||
title: language === 'en' ? "Business and Law" : "الأعمال والقانون",
|
||||
description: language === 'en' ? "Leading programs in commerce, law, and business innovation" : "برامج رائدة في التجارة والقانون وابتكار الأعمال",
|
||||
icon: <Users className="w-8 h-8" />,
|
||||
color: "from-blue-500 to-blue-600"
|
||||
},
|
||||
{
|
||||
title: language === 'en' ? "Earth, Sea, Antarctic & Environment" : "الأرض والبحار والقطب الجنوبي والبيئة",
|
||||
description: language === 'en' ? "World-renowned research in climate and environmental sciences" : "أبحاث مشهورة عالمياً في علوم المناخ والبيئة",
|
||||
icon: <Waves className="w-8 h-8" />,
|
||||
color: "from-teal-500 to-cyan-600"
|
||||
},
|
||||
{
|
||||
title: language === 'en' ? "Health and Medicine" : "الصحة والطب",
|
||||
description: language === 'en' ? "Innovative health programs with real-world impact" : "برامج صحية مبتكرة ذات تأثير حقيقي",
|
||||
icon: <Heart className="w-8 h-8" />,
|
||||
color: "from-red-500 to-pink-600"
|
||||
},
|
||||
{
|
||||
title: language === 'en' ? "Science, Technology & Engineering" : "العلوم والتكنولوجيا والهندسة",
|
||||
description: language === 'en' ? "Cutting-edge programs in STEM fields" : "برامج متطورة في مجالات العلوم والتكنولوجيا",
|
||||
icon: <Zap className="w-8 h-8" />,
|
||||
color: "from-purple-500 to-indigo-600"
|
||||
},
|
||||
{
|
||||
title: language === 'en' ? "Creative Arts and Design" : "الفنون الإبداعية والتصميم",
|
||||
description: language === 'en' ? "Inspiring creativity and artistic excellence" : "إلهام الإبداع والتميز الفني",
|
||||
icon: <Star className="w-8 h-8" />,
|
||||
color: "from-orange-500 to-yellow-600"
|
||||
},
|
||||
{
|
||||
title: language === 'en' ? "Education, Humanities & Social Sciences" : "التعليم والعلوم الإنسانية والاجتماعية",
|
||||
description: language === 'en' ? "Shaping the future of education and society" : "تشكيل مستقبل التعليم والمجتمع",
|
||||
icon: <BookOpen className="w-8 h-8" />,
|
||||
color: "from-green-500 to-emerald-600"
|
||||
}
|
||||
];
|
||||
|
||||
const achievements = [
|
||||
{
|
||||
rank: "#1",
|
||||
title: language === 'en' ? "Climate Action Globally" : "العمل المناخي عالمياً",
|
||||
subtitle: language === 'en' ? "THE Impact Rankings 2025" : "تصنيف التأثير 2025",
|
||||
icon: <Trophy className="w-6 h-6" />
|
||||
},
|
||||
{
|
||||
rank: "5★",
|
||||
title: language === 'en' ? "QS Overall Rating" : "التقييم العام QS",
|
||||
subtitle: language === 'en' ? "Excellence in Education" : "التميز في التعليم",
|
||||
icon: <Award className="w-6 h-6" />
|
||||
},
|
||||
{
|
||||
rank: "Top 1%",
|
||||
title: language === 'en' ? "Global Universities" : "الجامعات العالمية",
|
||||
subtitle: language === 'en' ? "World University Rankings" : "تصنيف الجامعات العالمية",
|
||||
icon: <Globe className="w-6 h-6" />
|
||||
}
|
||||
];
|
||||
|
||||
if (user) {
|
||||
router.push('/dashboard');
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`min-h-screen ${language === 'ar' ? 'rtl' : 'ltr'}`}>
|
||||
{/* Header */}
|
||||
<header className="bg-white/95 backdrop-blur-sm shadow-lg border-b border-gray-200 sticky top-0 z-50">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="flex items-center justify-between h-20">
|
||||
{/* Logo */}
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="bg-gradient-to-br from-blue-600 to-teal-600 p-2 rounded-xl">
|
||||
<GraduationCap size={32} className="text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold bg-gradient-to-r from-blue-600 to-teal-600 bg-clip-text text-transparent">
|
||||
UTAS Portal
|
||||
</h1>
|
||||
<p className="text-xs text-gray-500">University of Tasmania</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Navigation */}
|
||||
<nav className="hidden md:flex items-center space-x-8">
|
||||
<Link href="#study" className="text-gray-700 hover:text-blue-600 font-medium transition-colors">
|
||||
{language === 'en' ? 'Study' : 'الدراسة'}
|
||||
</Link>
|
||||
<Link href="#research" className="text-gray-700 hover:text-blue-600 font-medium transition-colors">
|
||||
{language === 'en' ? 'Research' : 'البحث'}
|
||||
</Link>
|
||||
<Link href="#campus" className="text-gray-700 hover:text-blue-600 font-medium transition-colors">
|
||||
{language === 'en' ? 'Campus Life' : 'الحياة الجامعية'}
|
||||
</Link>
|
||||
<Link href="#about" className="text-gray-700 hover:text-blue-600 font-medium transition-colors">
|
||||
{language === 'en' ? 'About' : 'حول'}
|
||||
</Link>
|
||||
</nav>
|
||||
|
||||
{/* Language & Login */}
|
||||
<div className="flex items-center space-x-4">
|
||||
<button
|
||||
onClick={toggleLanguage}
|
||||
className="flex items-center space-x-2 px-3 py-2 rounded-lg bg-gray-100 hover:bg-gray-200 transition-colors"
|
||||
>
|
||||
<Languages size={18} />
|
||||
<span className="font-medium">{language === 'en' ? 'ع' : 'EN'}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => setShowLogin(true)}
|
||||
className="bg-gradient-to-r from-blue-600 to-teal-600 text-white px-6 py-2 rounded-xl hover:shadow-lg transition-all duration-200 font-medium flex items-center space-x-2"
|
||||
>
|
||||
<LogIn size={18} />
|
||||
<span>{language === 'en' ? 'Portal Login' : 'دخول البوابة'}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Hero Section */}
|
||||
<section className="relative h-screen overflow-hidden">
|
||||
{heroSlides.map((slide, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`absolute inset-0 transition-transform duration-1000 ease-in-out ${
|
||||
index === currentSlide ? 'translate-x-0' :
|
||||
index < currentSlide ? '-translate-x-full' : 'translate-x-full'
|
||||
}`}
|
||||
style={{
|
||||
backgroundImage: `linear-gradient(rgba(0,0,0,0.4), rgba(0,0,0,0.6)), url(${slide.image})`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center'
|
||||
}}
|
||||
>
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div className="text-center text-white max-w-4xl px-4">
|
||||
<h1 className="text-5xl md:text-7xl font-bold mb-6 animate-fade-in-up">
|
||||
{slide.title}
|
||||
</h1>
|
||||
<p className="text-xl md:text-2xl mb-8 opacity-90 animate-fade-in-up-delay">
|
||||
{slide.subtitle}
|
||||
</p>
|
||||
<button className="bg-gradient-to-r from-teal-500 to-blue-600 text-white px-8 py-4 rounded-xl text-lg font-semibold hover:shadow-2xl transition-all duration-300 transform hover:scale-105 animate-fade-in-up-delay-2">
|
||||
{slide.cta} <ChevronRight className="inline ml-2" size={20} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Slide Indicators */}
|
||||
<div className="absolute bottom-8 left-1/2 transform -translate-x-1/2 flex space-x-3">
|
||||
{heroSlides.map((_, index) => (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => setCurrentSlide(index)}
|
||||
className={`w-3 h-3 rounded-full transition-all duration-300 ${
|
||||
index === currentSlide ? 'bg-white scale-125' : 'bg-white/50'
|
||||
}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Scroll Indicator */}
|
||||
<div className="absolute bottom-20 left-1/2 transform -translate-x-1/2 animate-bounce">
|
||||
<div className="w-1 h-16 bg-white/30 rounded-full">
|
||||
<div className="w-1 h-4 bg-white rounded-full animate-pulse"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Statistics Section */}
|
||||
<section className="py-20 bg-gradient-to-r from-blue-600 to-teal-600 text-white">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 text-center">
|
||||
<div className="space-y-4">
|
||||
<div className="text-5xl font-bold">{animatedStats.students.toLocaleString()}+</div>
|
||||
<div className="text-xl opacity-90">{language === 'en' ? 'Students Worldwide' : 'طالب حول العالم'}</div>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div className="text-5xl font-bold">{animatedStats.programs}+</div>
|
||||
<div className="text-xl opacity-90">{language === 'en' ? 'Study Programs' : 'برنامج دراسي'}</div>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div className="text-5xl font-bold">{animatedStats.satisfaction}%</div>
|
||||
<div className="text-xl opacity-90">{language === 'en' ? 'Student Satisfaction' : 'رضا الطلاب'}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Achievements Banner */}
|
||||
<section className="py-16 bg-gradient-to-r from-gray-900 to-blue-900 text-white">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="text-center mb-12">
|
||||
<h2 className="text-4xl font-bold mb-4">
|
||||
{language === 'en' ? 'Globally Recognized Excellence' : 'تميز معترف به عالمياً'}
|
||||
</h2>
|
||||
<p className="text-xl opacity-90">
|
||||
{language === 'en' ? 'Leading the world in sustainability and innovation' : 'رائدة عالمياً في الاستدامة والابتكار'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
{achievements.map((achievement, index) => (
|
||||
<div key={index} className="bg-white/10 backdrop-blur-sm rounded-2xl p-8 text-center hover:bg-white/15 transition-all duration-300">
|
||||
<div className="flex justify-center mb-4 text-teal-400">
|
||||
{achievement.icon}
|
||||
</div>
|
||||
<div className="text-3xl font-bold text-teal-400 mb-2">{achievement.rank}</div>
|
||||
<div className="text-xl font-semibold mb-2">{achievement.title}</div>
|
||||
<div className="text-sm opacity-75">{achievement.subtitle}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Study Areas */}
|
||||
<section id="study" className="py-20 bg-gray-50">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-4xl md:text-5xl font-bold text-gray-900 mb-4">
|
||||
{language === 'en' ? 'Discover Your Pathway' : 'اكتشف مسارك'}
|
||||
</h2>
|
||||
<p className="text-xl text-gray-600 max-w-3xl mx-auto">
|
||||
{language === 'en'
|
||||
? 'Choose from world-class programs designed to prepare you for the future'
|
||||
: 'اختر من بين البرامج عالمية المستوى المصممة لإعدادك للمستقبل'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{studyAreas.map((area, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="group bg-white rounded-2xl shadow-lg hover:shadow-2xl transition-all duration-500 overflow-hidden transform hover:-translate-y-2"
|
||||
>
|
||||
<div className={`h-2 bg-gradient-to-r ${area.color}`}></div>
|
||||
<div className="p-8">
|
||||
<div className={`inline-flex p-4 rounded-2xl bg-gradient-to-r ${area.color} text-white mb-6 group-hover:scale-110 transition-transform duration-300`}>
|
||||
{area.icon}
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-gray-900 mb-4 group-hover:text-blue-600 transition-colors">
|
||||
{area.title}
|
||||
</h3>
|
||||
<p className="text-gray-600 mb-6">
|
||||
{area.description}
|
||||
</p>
|
||||
<button className="text-blue-600 font-semibold hover:text-blue-800 transition-colors flex items-center">
|
||||
{language === 'en' ? 'Learn More' : 'اعرف أكثر'}
|
||||
<ChevronRight size={16} className="ml-1 group-hover:translate-x-1 transition-transform" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* AI Features Showcase */}
|
||||
<section className="py-20 bg-gradient-to-br from-blue-900 via-purple-900 to-teal-900 text-white relative overflow-hidden">
|
||||
<div className="absolute inset-0 opacity-10">
|
||||
<div className="absolute inset-0 bg-repeat" style={{
|
||||
backgroundImage: `url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.4'%3E%3Ccircle cx='30' cy='30' r='1'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E")`,
|
||||
backgroundSize: '60px 60px'
|
||||
}}></div>
|
||||
</div>
|
||||
|
||||
<div className="container mx-auto px-4 relative z-10">
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-4xl md:text-5xl font-bold mb-4">
|
||||
{language === 'en' ? 'AI-Powered Student Experience' : 'تجربة طلابية مدعومة بالذكاء الاصطناعي'}
|
||||
</h2>
|
||||
<p className="text-xl opacity-90 max-w-3xl mx-auto">
|
||||
{language === 'en'
|
||||
? 'Experience the future of education with our intelligent portal features'
|
||||
: 'اختبر مستقبل التعليم مع ميزات البوابة الذكية'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
|
||||
<div className="bg-white/10 backdrop-blur-sm rounded-2xl p-6 hover:bg-white/20 transition-all duration-300">
|
||||
<MessageSquare size={40} className="text-teal-400 mb-4" />
|
||||
<h3 className="text-xl font-bold mb-3">
|
||||
{language === 'en' ? '24/7 AI Assistant' : 'مساعد ذكي 24/7'}
|
||||
</h3>
|
||||
<p className="text-sm opacity-90">
|
||||
{language === 'en'
|
||||
? 'Multilingual support in English and Arabic with smart FAQ responses'
|
||||
: 'دعم متعدد اللغات بالإنجليزية والعربية مع ردود ذكية'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white/10 backdrop-blur-sm rounded-2xl p-6 hover:bg-white/20 transition-all duration-300">
|
||||
<Shield size={40} className="text-blue-400 mb-4" />
|
||||
<h3 className="text-xl font-bold mb-3">
|
||||
{language === 'en' ? 'Smart Accessibility' : 'إمكانية وصول ذكية'}
|
||||
</h3>
|
||||
<p className="text-sm opacity-90">
|
||||
{language === 'en'
|
||||
? 'AI-powered alt-text generation and WCAG compliance checking'
|
||||
: 'توليد نص بديل بالذكاء الاصطناعي وفحص التوافق مع معايير الوصول'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white/10 backdrop-blur-sm rounded-2xl p-6 hover:bg-white/20 transition-all duration-300">
|
||||
<Heart size={40} className="text-pink-400 mb-4" />
|
||||
<h3 className="text-xl font-bold mb-3">
|
||||
{language === 'en' ? 'Mental Health Support' : 'دعم الصحة النفسية'}
|
||||
</h3>
|
||||
<p className="text-sm opacity-90">
|
||||
{language === 'en'
|
||||
? 'AI-powered mental health triage with crisis detection and escalation'
|
||||
: 'فرز الصحة النفسية بالذكاء الاصطناعي مع كشف الأزمات'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white/10 backdrop-blur-sm rounded-2xl p-6 hover:bg-white/20 transition-all duration-300">
|
||||
<BarChart3 size={40} className="text-green-400 mb-4" />
|
||||
<h3 className="text-xl font-bold mb-3">
|
||||
{language === 'en' ? 'Smart Analytics' : 'تحليلات ذكية'}
|
||||
</h3>
|
||||
<p className="text-sm opacity-90">
|
||||
{language === 'en'
|
||||
? 'Real-time dashboards with predictive insights and personalized recommendations'
|
||||
: 'لوحات معلومات فورية مع رؤى تنبؤية وتوصيات شخصية'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Campus Locations */}
|
||||
<section id="campus" className="py-20 bg-white">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-4xl md:text-5xl font-bold text-gray-900 mb-4">
|
||||
{language === 'en' ? 'Our Campuses' : 'حرمنا الجامعي'}
|
||||
</h2>
|
||||
<p className="text-xl text-gray-600 max-w-3xl mx-auto">
|
||||
{language === 'en'
|
||||
? 'Study at world-class facilities across Australia'
|
||||
: 'ادرس في مرافق عالمية المستوى عبر أستراليا'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
|
||||
{[
|
||||
{ name: 'Hobart', location: 'Tasmania, Australia', image: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=400&h=300&fit=crop' },
|
||||
{ name: 'Launceston', location: 'Tasmania, Australia', image: 'https://images.unsplash.com/photo-1523050854058-8df90110c9f1?w=400&h=300&fit=crop' },
|
||||
{ name: 'Burnie', location: 'Tasmania, Australia', image: 'https://images.unsplash.com/photo-1541339907198-e08756dedf3f?w=400&h=300&fit=crop' },
|
||||
{ name: 'Sydney', location: 'NSW, Australia', image: 'https://images.unsplash.com/photo-1506973035872-a4ec16b8e8d9?w=400&h=300&fit=crop' }
|
||||
].map((campus, index) => (
|
||||
<div key={index} className="group relative overflow-hidden rounded-2xl shadow-lg hover:shadow-2xl transition-all duration-500">
|
||||
<Image
|
||||
src={campus.image}
|
||||
alt={campus.name}
|
||||
width={400}
|
||||
height={300}
|
||||
className="w-full h-64 object-cover group-hover:scale-110 transition-transform duration-500"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/20 to-transparent">
|
||||
<div className="absolute bottom-6 left-6 text-white">
|
||||
<h3 className="text-2xl font-bold mb-2">{campus.name}</h3>
|
||||
<p className="flex items-center opacity-90">
|
||||
<MapPin size={16} className="mr-1" />
|
||||
{campus.location}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute top-4 right-4 bg-white/20 backdrop-blur-sm rounded-full p-2 opacity-0 group-hover:opacity-100 transition-opacity duration-300">
|
||||
<ExternalLink size={16} className="text-white" />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="bg-gray-900 text-white py-16">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-12">
|
||||
<div className="col-span-1 md:col-span-2">
|
||||
<div className="flex items-center space-x-4 mb-6">
|
||||
<div className="bg-gradient-to-br from-blue-600 to-teal-600 p-2 rounded-xl">
|
||||
<GraduationCap size={32} className="text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-2xl font-bold">University of Tasmania</h3>
|
||||
<p className="text-gray-400">{language === 'en' ? '#1 in Climate Action Globally' : '#1 عالمياً في العمل المناخي'}</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-gray-400 mb-6 max-w-md">
|
||||
{language === 'en'
|
||||
? 'Leading the world in sustainability research and education. Join us in creating a better future for all.'
|
||||
: 'رائدة عالمياً في أبحاث وتعليم الاستدامة. انضم إلينا في خلق مستقبل أفضل للجميع.'
|
||||
}
|
||||
</p>
|
||||
<div className="flex space-x-4">
|
||||
<div className="bg-blue-600 p-2 rounded-lg">
|
||||
<Award size={20} />
|
||||
</div>
|
||||
<div className="bg-teal-600 p-2 rounded-lg">
|
||||
<Leaf size={20} />
|
||||
</div>
|
||||
<div className="bg-green-600 p-2 rounded-lg">
|
||||
<Globe size={20} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 className="text-lg font-semibold mb-6">{language === 'en' ? 'Quick Links' : 'روابط سريعة'}</h4>
|
||||
<ul className="space-y-3">
|
||||
<li><Link href="#study" className="text-gray-400 hover:text-white transition-colors">{language === 'en' ? 'Study Areas' : 'مجالات الدراسة'}</Link></li>
|
||||
<li><Link href="#research" className="text-gray-400 hover:text-white transition-colors">{language === 'en' ? 'Research' : 'البحث'}</Link></li>
|
||||
<li><Link href="#campus" className="text-gray-400 hover:text-white transition-colors">{language === 'en' ? 'Campus Life' : 'الحياة الجامعية'}</Link></li>
|
||||
<li><Link href="/privacy" className="text-gray-400 hover:text-white transition-colors">{language === 'en' ? 'Privacy Policy' : 'سياسة الخصوصية'}</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 className="text-lg font-semibold mb-6">{language === 'en' ? 'Contact Us' : 'اتصل بنا'}</h4>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center space-x-3">
|
||||
<Phone size={16} className="text-teal-400" />
|
||||
<span className="text-gray-400">+61 3 6226 6200</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3">
|
||||
<Mail size={16} className="text-teal-400" />
|
||||
<span className="text-gray-400">info@utas.edu.au</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3">
|
||||
<MapIcon size={16} className="text-teal-400" />
|
||||
<span className="text-gray-400">Hobart, Tasmania</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-gray-800 mt-12 pt-8 text-center">
|
||||
<p className="text-gray-400">
|
||||
{language === 'en'
|
||||
? '© 2025 University of Tasmania. All rights reserved. CRICOS Provider Code 00586B'
|
||||
: '© 2025 جامعة تاسمانيا. جميع الحقوق محفوظة. رمز مقدم CRICOS 00586B'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
{/* Login Modal */}
|
||||
{showLogin && (
|
||||
<div className="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-white rounded-2xl shadow-2xl max-w-md w-full p-8 transform transition-all duration-300 scale-100">
|
||||
<div className="text-center mb-8">
|
||||
<div className="bg-gradient-to-br from-blue-600 to-teal-600 p-3 rounded-2xl inline-block mb-4">
|
||||
<LogIn size={32} className="text-white" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-2">
|
||||
{language === 'en' ? 'Welcome Back' : 'مرحباً بعودتك'}
|
||||
</h2>
|
||||
<p className="text-gray-600">
|
||||
{language === 'en' ? 'Sign in to access your portal' : 'سجل الدخول للوصول إلى بوابتك'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleLogin} className="space-y-6">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
{language === 'en' ? 'Email Address' : 'عنوان البريد الإلكتروني'}
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
value={loginForm.email}
|
||||
onChange={(e) => setLoginForm({...loginForm, email: e.target.value})}
|
||||
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200"
|
||||
placeholder={language === 'en' ? 'Enter your email' : 'أدخل بريدك الإلكتروني'}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
{language === 'en' ? 'Password' : 'كلمة المرور'}
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
value={loginForm.password}
|
||||
onChange={(e) => setLoginForm({...loginForm, password: e.target.value})}
|
||||
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200"
|
||||
placeholder={language === 'en' ? 'Enter your password' : 'أدخل كلمة المرور'}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="bg-blue-50 border border-blue-200 rounded-xl p-4">
|
||||
<p className="text-sm text-blue-800 mb-2 font-medium">
|
||||
{language === 'en' ? 'Demo Accounts:' : 'حسابات تجريبية:'}
|
||||
</p>
|
||||
<div className="text-xs text-blue-700 space-y-1">
|
||||
<div>Student: student@university.edu / password123</div>
|
||||
<div>Admin: admin@university.edu / admin123</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex space-x-4">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowLogin(false)}
|
||||
className="flex-1 px-6 py-3 border border-gray-300 text-gray-700 rounded-xl hover:bg-gray-50 transition-all duration-200 font-medium"
|
||||
>
|
||||
{language === 'en' ? 'Cancel' : 'إلغاء'}
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="flex-1 bg-gradient-to-r from-blue-600 to-teal-600 text-white px-6 py-3 rounded-xl hover:shadow-lg transition-all duration-200 font-medium disabled:opacity-50"
|
||||
>
|
||||
{loading ? (language === 'en' ? 'Signing in...' : 'جارٍ تسجيل الدخول...') : (language === 'en' ? 'Sign In' : 'تسجيل الدخول')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+1299
-95
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,216 @@
|
||||
'use client';
|
||||
|
||||
import { useLanguage } from '@/components/providers/LanguageProvider';
|
||||
import { ArrowLeftIcon } from '@heroicons/react/24/outline';
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function PrivacyPage() {
|
||||
const { language, t } = useLanguage();
|
||||
|
||||
const content = {
|
||||
en: {
|
||||
title: 'Privacy Policy',
|
||||
lastUpdated: 'Last updated: December 2024',
|
||||
sections: [
|
||||
{
|
||||
title: 'Information We Collect',
|
||||
content: [
|
||||
'Personal identification information (name, email, student ID)',
|
||||
'Academic information (courses, grades, enrollment status)',
|
||||
'Chat conversations with our AI assistant',
|
||||
'Survey responses and feedback',
|
||||
'Accessibility audit data',
|
||||
'Usage analytics and performance data'
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'How We Use Your Information',
|
||||
content: [
|
||||
'Provide personalized educational services',
|
||||
'Improve our AI assistant and chatbot responses',
|
||||
'Generate analytics and insights for better services',
|
||||
'Ensure accessibility compliance',
|
||||
'Communicate important updates and notifications',
|
||||
'Comply with legal and regulatory requirements'
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Data Security',
|
||||
content: [
|
||||
'We implement industry-standard security measures',
|
||||
'All data is encrypted in transit and at rest',
|
||||
'Access to personal data is restricted to authorized personnel',
|
||||
'Regular security audits and assessments',
|
||||
'Incident response procedures for data breaches'
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Your Rights',
|
||||
content: [
|
||||
'Access your personal data',
|
||||
'Correct inaccurate information',
|
||||
'Request deletion of your data',
|
||||
'Object to processing of your data',
|
||||
'Data portability',
|
||||
'Withdraw consent at any time'
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Third-Party Services',
|
||||
content: [
|
||||
'OpenAI for AI chat functionality',
|
||||
'Supabase for authentication and database',
|
||||
'Azure Computer Vision for accessibility features',
|
||||
'Analytics providers for usage insights'
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Contact Us',
|
||||
content: [
|
||||
'For privacy-related questions or concerns:',
|
||||
'Email: privacy@university.edu',
|
||||
'Phone: +1 (555) 123-4567',
|
||||
'Office: Student Services Building, Room 101'
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
ar: {
|
||||
title: 'سياسة الخصوصية',
|
||||
lastUpdated: 'آخر تحديث: ديسمبر 2024',
|
||||
sections: [
|
||||
{
|
||||
title: 'المعلومات التي نجمعها',
|
||||
content: [
|
||||
'معلومات الهوية الشخصية (الاسم، البريد الإلكتروني، رقم الطالب)',
|
||||
'المعلومات الأكاديمية (المقررات، الدرجات، حالة التسجيل)',
|
||||
'محادثات الدردشة مع المساعد الذكي',
|
||||
'ردود الاستبيانات والتعليقات',
|
||||
'بيانات تدقيق إمكانية الوصول',
|
||||
'تحليلات الاستخدام وبيانات الأداء'
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'كيف نستخدم معلوماتك',
|
||||
content: [
|
||||
'تقديم خدمات تعليمية مخصصة',
|
||||
'تحسين المساعد الذكي وردود الدردشة',
|
||||
'إنشاء تحليلات ورؤى لخدمات أفضل',
|
||||
'ضمان امتثال إمكانية الوصول',
|
||||
'التواصل بشأن التحديثات والإشعارات المهمة',
|
||||
'الامتثال للمتطلبات القانونية والتنظيمية'
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'أمان البيانات',
|
||||
content: [
|
||||
'نطبق تدابير أمنية معيارية في الصناعة',
|
||||
'جميع البيانات مشفرة أثناء النقل والتخزين',
|
||||
'الوصول إلى البيانات الشخصية مقيد للموظفين المخولين',
|
||||
'عمليات تدقيق وتقييم أمنية منتظمة',
|
||||
'إجراءات الاستجابة للحوادث لخروق البيانات'
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'حقوقك',
|
||||
content: [
|
||||
'الوصول إلى بياناتك الشخصية',
|
||||
'تصحيح المعلومات غير الدقيقة',
|
||||
'طلب حذف بياناتك',
|
||||
'الاعتراض على معالجة بياناتك',
|
||||
'قابلية نقل البيانات',
|
||||
'سحب الموافقة في أي وقت'
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'خدمات الطرف الثالث',
|
||||
content: [
|
||||
'OpenAI لوظائف الدردشة الذكية',
|
||||
'Supabase للمصادقة وقاعدة البيانات',
|
||||
'Azure Computer Vision لميزات إمكانية الوصول',
|
||||
'موفرو التحليلات لرؤى الاستخدام'
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'اتصل بنا',
|
||||
content: [
|
||||
'للأسئلة أو المخاوف المتعلقة بالخصوصية:',
|
||||
'البريد الإلكتروني: privacy@university.edu',
|
||||
'الهاتف: +1 (555) 123-4567',
|
||||
'المكتب: مبنى خدمات الطلاب، الغرفة 101'
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const currentContent = content[language];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 py-8">
|
||||
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="bg-white rounded-lg shadow-lg p-6 md:p-8">
|
||||
<div className="flex items-center mb-6">
|
||||
<Link
|
||||
href="/"
|
||||
className="flex items-center text-blue-600 hover:text-blue-800 mr-4"
|
||||
>
|
||||
<ArrowLeftIcon className="h-5 w-5 mr-2" />
|
||||
{t('back')}
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">
|
||||
{currentContent.title}
|
||||
</h1>
|
||||
<p className="text-gray-600">{currentContent.lastUpdated}</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-8">
|
||||
{currentContent.sections.map((section, index) => (
|
||||
<div key={index} className="border-l-4 border-blue-500 pl-6">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">
|
||||
{section.title}
|
||||
</h2>
|
||||
<ul className="space-y-2">
|
||||
{section.content.map((item, itemIndex) => (
|
||||
<li key={itemIndex} className="text-gray-700 leading-relaxed">
|
||||
{item.includes('@') || item.includes('+') ? (
|
||||
<span className="font-medium">{item}</span>
|
||||
) : (
|
||||
<>• {item}</>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-12 p-6 bg-blue-50 rounded-lg">
|
||||
<div className="flex items-center">
|
||||
<div className="flex-shrink-0">
|
||||
<div className="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center">
|
||||
<span className="text-white font-bold">i</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="ml-4">
|
||||
<p className="text-blue-800 font-medium">
|
||||
{language === 'en'
|
||||
? 'Questions about this privacy policy?'
|
||||
: 'أسئلة حول سياسة الخصوصية؟'}
|
||||
</p>
|
||||
<p className="text-blue-700 mt-1">
|
||||
{language === 'en'
|
||||
? 'Contact our privacy team at privacy@university.edu'
|
||||
: 'اتصل بفريق الخصوصية على privacy@university.edu'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
'use client';
|
||||
|
||||
import { useLanguage } from '@/components/providers/LanguageProvider';
|
||||
import { Award, Trophy, Globe, Star, BarChart3 } from 'lucide-react';
|
||||
|
||||
const RankingsPage = () => {
|
||||
const { language, t } = useLanguage();
|
||||
const isRTL = language === 'ar';
|
||||
|
||||
const rankings = [
|
||||
{
|
||||
rank: "#1",
|
||||
title: "Climate Action Research",
|
||||
organization: "Times Higher Education Impact Rankings 2024",
|
||||
description: "Leading the world in climate change research and solutions",
|
||||
icon: <Globe className="w-8 h-8" />,
|
||||
color: "text-green-600",
|
||||
bgColor: "bg-green-50"
|
||||
},
|
||||
{
|
||||
rank: "#1",
|
||||
title: "Antarctic & Marine Science",
|
||||
organization: "Shanghai Ranking Global Ranking of Academic Subjects",
|
||||
description: "World's premier institution for polar and marine research",
|
||||
icon: <Award className="w-8 h-8" />,
|
||||
color: "text-blue-600",
|
||||
bgColor: "bg-blue-50"
|
||||
},
|
||||
{
|
||||
rank: "Top 10",
|
||||
title: "Small University Rankings",
|
||||
organization: "Times Higher Education World University Rankings",
|
||||
description: "Excellence in education and research at intimate scale",
|
||||
icon: <Trophy className="w-8 h-8" />,
|
||||
color: "text-yellow-600",
|
||||
bgColor: "bg-yellow-50"
|
||||
},
|
||||
{
|
||||
rank: "Top 2%",
|
||||
title: "Global University Rankings",
|
||||
organization: "QS World University Rankings 2024",
|
||||
description: "Among the world's top universities for quality education",
|
||||
icon: <Star className="w-8 h-8" />,
|
||||
color: "text-purple-600",
|
||||
bgColor: "bg-purple-50"
|
||||
}
|
||||
];
|
||||
|
||||
const achievements = [
|
||||
{
|
||||
metric: "40+",
|
||||
label: "Years in Antarctica",
|
||||
description: "Continuous research presence since 1983"
|
||||
},
|
||||
{
|
||||
metric: "95%",
|
||||
label: "Graduate Employment",
|
||||
description: "Employment rate within 6 months of graduation"
|
||||
},
|
||||
{
|
||||
metric: "300+",
|
||||
label: "Research Partners",
|
||||
description: "Global collaborations across 6 continents"
|
||||
},
|
||||
{
|
||||
metric: "$200M+",
|
||||
label: "Research Income",
|
||||
description: "Annual research funding and grants"
|
||||
}
|
||||
];
|
||||
|
||||
const impacts = [
|
||||
{
|
||||
title: "Antarctic Gateway Strategy",
|
||||
description: "UTAS serves as Australia's primary gateway to Antarctic research, hosting the Australian Antarctic Division headquarters and leading international polar science initiatives.",
|
||||
impact: "50% of Australia's Antarctic research"
|
||||
},
|
||||
{
|
||||
title: "Climate Solutions Hub",
|
||||
description: "Our Climate Futures research centre develops cutting-edge solutions for climate adaptation and mitigation, directly informing global policy.",
|
||||
impact: "Advising 30+ governments worldwide"
|
||||
},
|
||||
{
|
||||
title: "Marine Innovation",
|
||||
description: "IMAS leads breakthrough research in sustainable fisheries, ocean conservation, and marine biotechnology with real-world applications.",
|
||||
impact: "$500M+ industry impact annually"
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={`min-h-screen bg-gradient-to-br from-slate-50 to-blue-50 ${isRTL ? 'rtl' : 'ltr'}`}>
|
||||
{/* Hero Section */}
|
||||
<div className="bg-gradient-to-r from-blue-900 via-blue-800 to-teal-700 text-white">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-20">
|
||||
<div className="text-center">
|
||||
<h1 className="text-4xl md:text-6xl font-bold mb-6">
|
||||
World-Class Excellence
|
||||
</h1>
|
||||
<p className="text-xl md:text-2xl mb-8 text-blue-100 max-w-4xl mx-auto">
|
||||
Discover why UTAS ranks among the world's leading universities for research impact,
|
||||
student satisfaction, and global influence
|
||||
</p>
|
||||
<div className="flex flex-wrap justify-center gap-4">
|
||||
<div className="bg-white/10 backdrop-blur-sm rounded-lg px-6 py-3">
|
||||
<div className="text-3xl font-bold text-yellow-300">#1</div>
|
||||
<div className="text-sm text-blue-100">Climate Research</div>
|
||||
</div>
|
||||
<div className="bg-white/10 backdrop-blur-sm rounded-lg px-6 py-3">
|
||||
<div className="text-3xl font-bold text-yellow-300">#1</div>
|
||||
<div className="text-sm text-blue-100">Antarctic Science</div>
|
||||
</div>
|
||||
<div className="bg-white/10 backdrop-blur-sm rounded-lg px-6 py-3">
|
||||
<div className="text-3xl font-bold text-yellow-300">Top 2%</div>
|
||||
<div className="text-sm text-blue-100">Globally</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Rankings Grid */}
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
||||
<h2 className="text-3xl font-bold text-center mb-12 text-gray-900">
|
||||
International Recognition
|
||||
</h2>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 mb-16">
|
||||
{rankings.map((ranking, index) => (
|
||||
<div key={index} className="bg-white rounded-xl shadow-lg p-6 hover:shadow-xl transition-shadow duration-300">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className={`${ranking.bgColor} ${ranking.color} p-3 rounded-lg`}>
|
||||
{ranking.icon}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<span className={`text-3xl font-bold ${ranking.color}`}>
|
||||
{ranking.rank}
|
||||
</span>
|
||||
<h3 className="text-xl font-semibold text-gray-900">
|
||||
{ranking.title}
|
||||
</h3>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 mb-2 font-medium">
|
||||
{ranking.organization}
|
||||
</p>
|
||||
<p className="text-gray-700">
|
||||
{ranking.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Key Metrics */}
|
||||
<div className="bg-gradient-to-r from-blue-600 to-teal-600 rounded-2xl p-8 mb-16">
|
||||
<h2 className="text-3xl font-bold text-center mb-12 text-white">
|
||||
By the Numbers
|
||||
</h2>
|
||||
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-8">
|
||||
{achievements.map((achievement, index) => (
|
||||
<div key={index} className="text-center">
|
||||
<div className="text-4xl md:text-5xl font-bold text-yellow-300 mb-2">
|
||||
{achievement.metric}
|
||||
</div>
|
||||
<div className="text-lg font-semibold text-white mb-1">
|
||||
{achievement.label}
|
||||
</div>
|
||||
<div className="text-sm text-blue-100">
|
||||
{achievement.description}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Research Impact */}
|
||||
<div className="mb-16">
|
||||
<h2 className="text-3xl font-bold text-center mb-12 text-gray-900">
|
||||
Global Research Impact
|
||||
</h2>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
{impacts.map((impact, index) => (
|
||||
<div key={index} className="bg-white rounded-xl shadow-lg p-6 hover:shadow-xl transition-shadow duration-300">
|
||||
<h3 className="text-xl font-bold mb-4 text-gray-900">
|
||||
{impact.title}
|
||||
</h3>
|
||||
<p className="text-gray-700 mb-4">
|
||||
{impact.description}
|
||||
</p>
|
||||
<div className="bg-gradient-to-r from-blue-500 to-teal-500 text-white px-4 py-2 rounded-lg text-sm font-semibold">
|
||||
<BarChart3 className="w-4 h-4 inline mr-2" />
|
||||
{impact.impact}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Call to Action */}
|
||||
<div className="bg-gradient-to-r from-teal-500 to-blue-600 rounded-2xl p-8 text-center text-white">
|
||||
<h2 className="text-3xl font-bold mb-4">
|
||||
Join the World's Leading Voices
|
||||
</h2>
|
||||
<p className="text-xl mb-6 text-blue-100">
|
||||
Be part of groundbreaking research that shapes our planet's future
|
||||
</p>
|
||||
<div className="flex flex-wrap justify-center gap-4">
|
||||
<button className="bg-white text-blue-600 px-8 py-3 rounded-lg font-semibold hover:bg-blue-50 transition-colors">
|
||||
Explore Programs
|
||||
</button>
|
||||
<button className="border-2 border-white text-white px-8 py-3 rounded-lg font-semibold hover:bg-white hover:text-blue-600 transition-colors">
|
||||
Research Opportunities
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RankingsPage;
|
||||
@@ -0,0 +1,440 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { useLanguage } from '@/components/providers/LanguageProvider';
|
||||
import { Waves, Snowflake, Fish, ThermometerSun, Globe, MapPin, ExternalLink } from 'lucide-react';
|
||||
import { mockResearch } from '@/lib/mockData';
|
||||
|
||||
export default function MarineAntarcticResearchPage() {
|
||||
const { language, dir } = useLanguage();
|
||||
const [activeSection, setActiveSection] = useState('overview');
|
||||
|
||||
// Filter for marine and climate research
|
||||
const marineResearch = mockResearch.filter(research =>
|
||||
research.institute === 'Institute for Marine and Antarctic Studies (IMAS)' ||
|
||||
research.tags.some(tag => tag.toLowerCase().includes('marine') || tag.toLowerCase().includes('climate') || tag.toLowerCase().includes('antarctic'))
|
||||
);
|
||||
|
||||
const researchHighlights = [
|
||||
{
|
||||
id: 'southern-ocean',
|
||||
titleEn: 'Southern Ocean Research',
|
||||
titleAr: 'أبحاث المحيط الجنوبي',
|
||||
descriptionEn: 'Monitoring and understanding the Southern Ocean\'s role in global climate systems.',
|
||||
descriptionAr: 'مراقبة وفهم دور المحيط الجنوبي في أنظمة المناخ العالمية.',
|
||||
icon: <Waves className="text-blue-500" size={48} />,
|
||||
funding: 'A$25M',
|
||||
duration: '2020-2025',
|
||||
keyFindings: {
|
||||
en: ['Ocean warming accelerating', 'Carbon absorption declining', 'Marine ecosystem shifts'],
|
||||
ar: ['تسارع الاحترار البحري', 'انخفاض امتصاص الكربون', 'تغيرات النظام البيئي البحري']
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'ice-sheet-dynamics',
|
||||
titleEn: 'Antarctic Ice Sheet Dynamics',
|
||||
titleAr: 'ديناميكيات الغطاء الجليدي القطبي',
|
||||
descriptionEn: 'Advanced monitoring of ice sheet changes and their global sea level implications.',
|
||||
descriptionAr: 'المراقبة المتقدمة لتغيرات الغطاء الجليدي وآثارها على مستوى سطح البحر العالمي.',
|
||||
icon: <Snowflake className="text-cyan-500" size={48} />,
|
||||
funding: 'A$18M',
|
||||
duration: '2019-2024',
|
||||
keyFindings: {
|
||||
en: ['Accelerated ice loss', 'Unstable ice shelf behavior', 'Sea level rise contributions'],
|
||||
ar: ['تسارع فقدان الجليد', 'سلوك غير مستقر للرف الجليدي', 'مساهمات ارتفاع مستوى سطح البحر']
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'marine-biodiversity',
|
||||
titleEn: 'Marine Biodiversity Conservation',
|
||||
titleAr: 'حفظ التنوع البيولوجي البحري',
|
||||
descriptionEn: 'Protecting marine species and ecosystems in a changing climate.',
|
||||
descriptionAr: 'حماية الأنواع البحرية والنظم البيئية في مناخ متغير.',
|
||||
icon: <Fish className="text-green-500" size={48} />,
|
||||
funding: 'A$12M',
|
||||
duration: '2021-2026',
|
||||
keyFindings: {
|
||||
en: ['Species migration patterns', 'Ecosystem resilience factors', 'Conservation strategies'],
|
||||
ar: ['أنماط هجرة الأنواع', 'عوامل مرونة النظام البيئي', 'استراتيجيات الحفظ']
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const expeditions = [
|
||||
{
|
||||
id: 'aurora-australis',
|
||||
nameEn: 'Aurora Australis Expedition 2024',
|
||||
nameAr: 'رحلة أورورا أوستراليس 2024',
|
||||
descriptionEn: 'Multi-disciplinary research voyage to the Antarctic Peninsula focusing on climate change impacts.',
|
||||
descriptionAr: 'رحلة بحثية متعددة التخصصات إلى شبه الجزيرة القطبية الجنوبية تركز على تأثيرات تغير المناخ.',
|
||||
duration: '45 days',
|
||||
participants: 32,
|
||||
objectives: {
|
||||
en: ['Ice core sampling', 'Marine ecosystem surveys', 'Climate monitoring station deployment'],
|
||||
ar: ['أخذ عينات من النواة الجليدية', 'مسوحات النظام البيئي البحري', 'نشر محطة مراقبة المناخ']
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'deep-sea-exploration',
|
||||
nameEn: 'Deep Sea Exploration Initiative',
|
||||
nameAr: 'مبادرة استكشاف أعماق البحار',
|
||||
descriptionEn: 'Advanced submersible research exploring deep ocean ecosystems and geological features.',
|
||||
descriptionAr: 'بحث متقدم بالغواصات لاستكشاف النظم البيئية في أعماق المحيطات والميزات الجيولوجية.',
|
||||
duration: '6 months',
|
||||
participants: 28,
|
||||
objectives: {
|
||||
en: ['Deep sea biodiversity mapping', 'Hydrothermal vent studies', 'Ocean floor geological surveys'],
|
||||
ar: ['رسم خرائط التنوع البيولوجي في أعماق البحار', 'دراسات الفتحات الحرارية المائية', 'المسوحات الجيولوجية لقاع المحيط']
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const facilities = [
|
||||
{
|
||||
nameEn: 'IMAS Research Vessels',
|
||||
nameAr: 'سفن البحث IMAS',
|
||||
descriptionEn: 'State-of-the-art research vessels equipped for polar and marine research.',
|
||||
descriptionAr: 'سفن بحثية حديثة مجهزة للبحوث القطبية والبحرية.',
|
||||
capabilities: {
|
||||
en: ['Ice-breaking capability', 'Advanced laboratory facilities', 'Remote sensing equipment'],
|
||||
ar: ['قدرة كسر الجليد', 'مرافق مختبرية متقدمة', 'معدات الاستشعار عن بعد']
|
||||
}
|
||||
},
|
||||
{
|
||||
nameEn: 'Antarctic Research Station',
|
||||
nameAr: 'محطة البحوث القطبية',
|
||||
descriptionEn: 'Year-round research facility in Antarctica for continuous climate monitoring.',
|
||||
descriptionAr: 'مرفق بحثي على مدار السنة في القطب الجنوبي للمراقبة المستمرة للمناخ.',
|
||||
capabilities: {
|
||||
en: ['Meteorological monitoring', 'Ice core drilling', 'Wildlife observation'],
|
||||
ar: ['المراقبة الأرصادية', 'حفر النواة الجليدية', 'مراقبة الحياة البرية']
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={`min-h-screen bg-gray-50 ${dir === 'rtl' ? 'font-arabic' : ''}`} dir={dir}>
|
||||
{/* Hero Section */}
|
||||
<div className="bg-gradient-to-r from-blue-600 via-cyan-500 to-blue-800 text-white py-20">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-5xl mx-auto text-center">
|
||||
<h1 className="text-5xl md:text-6xl font-bold mb-6">
|
||||
{language === 'en' ? 'Marine & Antarctic Studies' : 'دراسات البحرية والقطب الجنوبي'}
|
||||
</h1>
|
||||
<p className="text-xl opacity-90 mb-8 max-w-3xl mx-auto">
|
||||
{language === 'en'
|
||||
? 'World-leading research at IMAS, tackling climate change through polar and marine science. From Antarctica to Tasmania\'s waters, we\'re understanding our changing planet.'
|
||||
: 'الأبحاث الرائدة عالمياً في IMAS، مواجهة تغير المناخ من خلال علوم القطبين والبحار. من القطب الجنوبي إلى مياه تسمانيا، نحن نفهم كوكبنا المتغير.'
|
||||
}
|
||||
</p>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-6 max-w-4xl mx-auto">
|
||||
<div className="text-center">
|
||||
<div className="text-3xl font-bold">#1</div>
|
||||
<div className="opacity-80">{language === 'en' ? 'Climate Research' : 'بحوث المناخ'}</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-3xl font-bold">50+</div>
|
||||
<div className="opacity-80">{language === 'en' ? 'Expeditions/Year' : 'رحلة/سنة'}</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-3xl font-bold">200+</div>
|
||||
<div className="opacity-80">{language === 'en' ? 'Researchers' : 'باحث'}</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-3xl font-bold">A$80M</div>
|
||||
<div className="opacity-80">{language === 'en' ? 'Annual Funding' : 'التمويل السنوي'}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="container mx-auto px-4 py-12">
|
||||
{/* Navigation */}
|
||||
<div className="flex justify-center mb-12">
|
||||
<div className="bg-white rounded-xl p-2 shadow-lg">
|
||||
{[
|
||||
{ id: 'overview', labelEn: 'Research Overview', labelAr: 'نظرة عامة على البحث' },
|
||||
{ id: 'expeditions', labelEn: 'Expeditions', labelAr: 'الرحلات الاستكشافية' },
|
||||
{ id: 'facilities', labelEn: 'Facilities', labelAr: 'المرافق' }
|
||||
].map(section => (
|
||||
<button
|
||||
key={section.id}
|
||||
onClick={() => setActiveSection(section.id)}
|
||||
className={`px-6 py-3 mx-1 rounded-lg font-semibold transition-colors ${
|
||||
activeSection === section.id
|
||||
? 'bg-blue-600 text-white'
|
||||
: 'text-gray-600 hover:text-blue-600 hover:bg-blue-50'
|
||||
}`}
|
||||
>
|
||||
{language === 'en' ? section.labelEn : section.labelAr}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Research Overview */}
|
||||
{activeSection === 'overview' && (
|
||||
<div className="space-y-12">
|
||||
{/* Research Highlights */}
|
||||
<div className="grid lg:grid-cols-3 gap-8">
|
||||
{researchHighlights.map(highlight => (
|
||||
<div key={highlight.id} className="bg-white rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 overflow-hidden">
|
||||
<div className="p-8">
|
||||
<div className="flex items-center justify-center mb-6">
|
||||
{highlight.icon}
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-gray-900 mb-4 text-center">
|
||||
{language === 'en' ? highlight.titleEn : highlight.titleAr}
|
||||
</h3>
|
||||
<p className="text-gray-600 mb-6 text-center">
|
||||
{language === 'en' ? highlight.descriptionEn : highlight.descriptionAr}
|
||||
</p>
|
||||
|
||||
<div className="space-y-4 mb-6">
|
||||
<div className="flex justify-between items-center text-sm">
|
||||
<span className="font-medium text-gray-700">{language === 'en' ? 'Funding:' : 'التمويل:'}</span>
|
||||
<span className="text-blue-600 font-semibold">{highlight.funding}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center text-sm">
|
||||
<span className="font-medium text-gray-700">{language === 'en' ? 'Duration:' : 'المدة:'}</span>
|
||||
<span className="text-green-600 font-semibold">{highlight.duration}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<h4 className="font-semibold text-gray-900 mb-3">
|
||||
{language === 'en' ? 'Key Findings' : 'النتائج الرئيسية'}
|
||||
</h4>
|
||||
<ul className="space-y-2">
|
||||
{(language === 'en' ? highlight.keyFindings.en : highlight.keyFindings.ar).map((finding, index) => (
|
||||
<li key={index} className="text-sm text-gray-600 flex items-start">
|
||||
<span className="w-2 h-2 bg-blue-500 rounded-full mt-2 mr-3 flex-shrink-0"></span>
|
||||
{finding}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<button className="w-full bg-blue-600 text-white py-3 px-4 rounded-lg font-semibold hover:bg-blue-700 transition-colors">
|
||||
{language === 'en' ? 'Learn More' : 'تعلم المزيد'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Current Research Projects */}
|
||||
<div className="bg-white rounded-xl shadow-lg p-8">
|
||||
<h2 className="text-3xl font-bold text-gray-900 mb-8 text-center">
|
||||
{language === 'en' ? 'Current Research Projects' : 'مشاريع البحث الحالية'}
|
||||
</h2>
|
||||
<div className="grid lg:grid-cols-2 gap-8">
|
||||
{marineResearch.slice(0, 4).map(research => (
|
||||
<div key={research.id} className="border border-gray-200 rounded-lg p-6 hover:shadow-md transition-shadow">
|
||||
<h3 className="text-lg font-bold text-gray-900 mb-3">
|
||||
{language === 'en' ? research.title : research.titleAr}
|
||||
</h3>
|
||||
<p className="text-gray-600 mb-4 text-sm">
|
||||
{language === 'en' ? research.description : research.descriptionAr}
|
||||
</p>
|
||||
<div className="flex items-center justify-between text-sm text-gray-500 mb-4">
|
||||
<span>{language === 'en' ? research.institute : research.instituteAr}</span>
|
||||
<span>{research.publications} {language === 'en' ? 'publications' : 'منشورات'}</span>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<button className="flex-1 bg-blue-50 text-blue-700 py-2 px-4 rounded-lg font-medium hover:bg-blue-100 transition-colors">
|
||||
{language === 'en' ? 'Details' : 'التفاصيل'}
|
||||
</button>
|
||||
<button className="px-4 py-2 border border-gray-300 text-gray-700 rounded-lg font-medium hover:bg-gray-50 transition-colors">
|
||||
<ExternalLink size={16} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Expeditions */}
|
||||
{activeSection === 'expeditions' && (
|
||||
<div className="space-y-8">
|
||||
<div className="text-center mb-12">
|
||||
<h2 className="text-3xl font-bold text-gray-900 mb-4">
|
||||
{language === 'en' ? 'Research Expeditions' : 'الرحلات الاستكشافية البحثية'}
|
||||
</h2>
|
||||
<p className="text-xl text-gray-600 max-w-3xl mx-auto">
|
||||
{language === 'en'
|
||||
? 'Leading scientific expeditions to the world\'s most remote and challenging environments to advance our understanding of climate change.'
|
||||
: 'قيادة البعثات العلمية إلى البيئات الأكثر نائية وتحدياً في العالم لتطوير فهمنا لتغير المناخ.'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid lg:grid-cols-2 gap-8">
|
||||
{expeditions.map(expedition => (
|
||||
<div key={expedition.id} className="bg-white rounded-xl shadow-lg overflow-hidden">
|
||||
<div className="h-48 bg-gradient-to-br from-cyan-500 to-blue-600 flex items-center justify-center">
|
||||
<div className="text-center text-white">
|
||||
<Globe size={64} className="mx-auto mb-4 opacity-80" />
|
||||
<div className="text-lg font-semibold">
|
||||
{language === 'en' ? 'Scientific Expedition' : 'رحلة علمية'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-8">
|
||||
<h3 className="text-xl font-bold text-gray-900 mb-4">
|
||||
{language === 'en' ? expedition.nameEn : expedition.nameAr}
|
||||
</h3>
|
||||
|
||||
<p className="text-gray-600 mb-6">
|
||||
{language === 'en' ? expedition.descriptionEn : expedition.descriptionAr}
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4 mb-6">
|
||||
<div className="text-center p-3 bg-gray-50 rounded-lg">
|
||||
<div className="text-2xl font-bold text-blue-600">{expedition.duration}</div>
|
||||
<div className="text-sm text-gray-600">{language === 'en' ? 'Duration' : 'المدة'}</div>
|
||||
</div>
|
||||
<div className="text-center p-3 bg-gray-50 rounded-lg">
|
||||
<div className="text-2xl font-bold text-green-600">{expedition.participants}</div>
|
||||
<div className="text-sm text-gray-600">{language === 'en' ? 'Researchers' : 'باحثين'}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<h4 className="font-semibold text-gray-900 mb-3">
|
||||
{language === 'en' ? 'Research Objectives' : 'أهداف البحث'}
|
||||
</h4>
|
||||
<ul className="space-y-2">
|
||||
{(language === 'en' ? expedition.objectives.en : expedition.objectives.ar).map((objective, index) => (
|
||||
<li key={index} className="text-sm text-gray-600 flex items-start">
|
||||
<span className="w-2 h-2 bg-cyan-500 rounded-full mt-2 mr-3 flex-shrink-0"></span>
|
||||
{objective}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<button className="w-full bg-cyan-600 text-white py-3 px-4 rounded-lg font-semibold hover:bg-cyan-700 transition-colors">
|
||||
{language === 'en' ? 'Expedition Details' : 'تفاصيل الرحلة'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Facilities */}
|
||||
{activeSection === 'facilities' && (
|
||||
<div className="space-y-8">
|
||||
<div className="text-center mb-12">
|
||||
<h2 className="text-3xl font-bold text-gray-900 mb-4">
|
||||
{language === 'en' ? 'Research Facilities' : 'مرافق البحث'}
|
||||
</h2>
|
||||
<p className="text-xl text-gray-600 max-w-3xl mx-auto">
|
||||
{language === 'en'
|
||||
? 'World-class facilities supporting cutting-edge marine and Antarctic research.'
|
||||
: 'مرافق عالمية المستوى تدعم البحوث الرائدة في المجالين البحري والقطب الجنوبي.'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid lg:grid-cols-2 gap-8">
|
||||
{facilities.map((facility, index) => (
|
||||
<div key={index} className="bg-white rounded-xl shadow-lg p-8">
|
||||
<div className="flex items-center mb-6">
|
||||
<div className="w-16 h-16 bg-gradient-to-br from-blue-500 to-cyan-500 rounded-full flex items-center justify-center text-white mr-4">
|
||||
<MapPin size={32} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-bold text-gray-900">
|
||||
{language === 'en' ? facility.nameEn : facility.nameAr}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-gray-600 mb-6">
|
||||
{language === 'en' ? facility.descriptionEn : facility.descriptionAr}
|
||||
</p>
|
||||
|
||||
<div className="mb-6">
|
||||
<h4 className="font-semibold text-gray-900 mb-3">
|
||||
{language === 'en' ? 'Capabilities' : 'القدرات'}
|
||||
</h4>
|
||||
<ul className="space-y-2">
|
||||
{(language === 'en' ? facility.capabilities.en : facility.capabilities.ar).map((capability, capIndex) => (
|
||||
<li key={capIndex} className="text-sm text-gray-600 flex items-start">
|
||||
<span className="w-2 h-2 bg-blue-500 rounded-full mt-2 mr-3 flex-shrink-0"></span>
|
||||
{capability}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<button className="w-full bg-blue-600 text-white py-3 px-4 rounded-lg font-semibold hover:bg-blue-700 transition-colors">
|
||||
{language === 'en' ? 'Facility Details' : 'تفاصيل المرفق'}
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Global Impact */}
|
||||
<div className="mt-16 bg-gradient-to-r from-blue-600 to-cyan-600 rounded-xl p-8 text-white">
|
||||
<h2 className="text-3xl font-bold mb-8 text-center">
|
||||
{language === 'en' ? 'Global Climate Impact' : 'التأثير المناخي العالمي'}
|
||||
</h2>
|
||||
<div className="grid md:grid-cols-3 gap-8">
|
||||
<div className="text-center">
|
||||
<div className="bg-white bg-opacity-20 rounded-full p-4 w-16 h-16 mx-auto mb-4 flex items-center justify-center">
|
||||
<ThermometerSun size={32} />
|
||||
</div>
|
||||
<h3 className="font-semibold mb-2">
|
||||
{language === 'en' ? 'Climate Monitoring' : 'مراقبة المناخ'}
|
||||
</h3>
|
||||
<p className="opacity-90 text-sm">
|
||||
{language === 'en'
|
||||
? 'Real-time monitoring of Southern Ocean and Antarctic climate systems.'
|
||||
: 'المراقبة في الوقت الفعلي لأنظمة المناخ في المحيط الجنوبي والقطب الجنوبي.'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="bg-white bg-opacity-20 rounded-full p-4 w-16 h-16 mx-auto mb-4 flex items-center justify-center">
|
||||
<Waves size={32} />
|
||||
</div>
|
||||
<h3 className="font-semibold mb-2">
|
||||
{language === 'en' ? 'Ocean Science' : 'علوم المحيطات'}
|
||||
</h3>
|
||||
<p className="opacity-90 text-sm">
|
||||
{language === 'en'
|
||||
? 'Understanding ocean circulation and its role in global climate regulation.'
|
||||
: 'فهم دوران المحيطات ودورها في تنظيم المناخ العالمي.'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="bg-white bg-opacity-20 rounded-full p-4 w-16 h-16 mx-auto mb-4 flex items-center justify-center">
|
||||
<Fish size={32} />
|
||||
</div>
|
||||
<h3 className="font-semibold mb-2">
|
||||
{language === 'en' ? 'Marine Conservation' : 'حفظ البحرية'}
|
||||
</h3>
|
||||
<p className="opacity-90 text-sm">
|
||||
{language === 'en'
|
||||
? 'Protecting marine biodiversity and sustainable fishing practices.'
|
||||
: 'حماية التنوع البيولوجي البحري وممارسات الصيد المستدامة.'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import { useLanguage } from '@/components/providers/LanguageProvider';
|
||||
import { Award, DollarSign, Users, Globe, CheckCircle, Clock, Search, Filter, ExternalLink, ArrowRight, CalendarDays, GraduationCap } from 'lucide-react';
|
||||
import { mockScholarships, getOpenScholarships, getScholarshipsByCategory, Scholarship } from '@/lib/mockData';
|
||||
|
||||
export default function ScholarshipsPage() {
|
||||
const { language, t, dir } = useLanguage();
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [selectedCategory, setSelectedCategory] = useState('');
|
||||
const [selectedStatus, setSelectedStatus] = useState('');
|
||||
const [showFilters, setShowFilters] = useState(false);
|
||||
|
||||
// Filter scholarships based on search and filters
|
||||
const filteredScholarships = useMemo(() => {
|
||||
let results = mockScholarships;
|
||||
|
||||
// Text search
|
||||
if (searchQuery) {
|
||||
const query = searchQuery.toLowerCase();
|
||||
results = results.filter(scholarship =>
|
||||
(language === 'en' ? scholarship.title : scholarship.titleAr).toLowerCase().includes(query) ||
|
||||
(language === 'en' ? scholarship.description : scholarship.descriptionAr).toLowerCase().includes(query) ||
|
||||
(language === 'en' ? scholarship.value : scholarship.valueAr).toLowerCase().includes(query)
|
||||
);
|
||||
}
|
||||
|
||||
// Category filter
|
||||
if (selectedCategory) {
|
||||
results = results.filter(scholarship => scholarship.category === selectedCategory);
|
||||
}
|
||||
|
||||
// Status filter
|
||||
if (selectedStatus) {
|
||||
results = results.filter(scholarship => scholarship.status === selectedStatus);
|
||||
}
|
||||
|
||||
return results;
|
||||
}, [searchQuery, selectedCategory, selectedStatus, language]);
|
||||
|
||||
const ScholarshipCard = ({ scholarship }: { scholarship: Scholarship }) => (
|
||||
<div className="group bg-white rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 overflow-hidden border border-gray-100">
|
||||
<div className="p-6">
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div className={`p-2 rounded-lg ${
|
||||
scholarship.category === 'merit' ? 'bg-blue-100 text-blue-600' :
|
||||
scholarship.category === 'need' ? 'bg-green-100 text-green-600' :
|
||||
scholarship.category === 'research' ? 'bg-purple-100 text-purple-600' :
|
||||
'bg-orange-100 text-orange-600'
|
||||
}`}>
|
||||
<Award size={20} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-xl font-bold text-gray-900">
|
||||
{language === 'en' ? scholarship.title : scholarship.titleAr}
|
||||
</h3>
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<span className={`px-3 py-1 rounded-full text-xs font-semibold ${
|
||||
scholarship.status === 'open' ? 'bg-green-500 text-white' :
|
||||
scholarship.status === 'closing' ? 'bg-yellow-500 text-white' :
|
||||
'bg-red-500 text-white'
|
||||
}`}>
|
||||
{scholarship.status === 'open' ? (language === 'en' ? 'Open' : 'مفتوح') :
|
||||
scholarship.status === 'closing' ? (language === 'en' ? 'Closing Soon' : 'يغلق قريباً') :
|
||||
(language === 'en' ? 'Closed' : 'مغلق')}
|
||||
</span>
|
||||
<span className="text-sm text-gray-500 capitalize">
|
||||
{scholarship.category === 'merit' ? (language === 'en' ? 'Merit-based' : 'على أساس الجدارة') :
|
||||
scholarship.category === 'need' ? (language === 'en' ? 'Need-based' : 'على أساس الحاجة') :
|
||||
scholarship.category === 'research' ? (language === 'en' ? 'Research' : 'بحثي') :
|
||||
(language === 'en' ? 'International' : 'دولي')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-gray-600 mb-4 leading-relaxed">
|
||||
{language === 'en' ? scholarship.description : scholarship.descriptionAr}
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4 mb-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<DollarSign className="w-4 h-4 text-green-500" />
|
||||
<div>
|
||||
<div className="text-sm text-gray-500">{language === 'en' ? 'Value' : 'القيمة'}</div>
|
||||
<div className="font-semibold text-gray-900">
|
||||
{language === 'en' ? scholarship.value : scholarship.valueAr}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock className="w-4 h-4 text-blue-500" />
|
||||
<div>
|
||||
<div className="text-sm text-gray-500">{language === 'en' ? 'Duration' : 'المدة'}</div>
|
||||
<div className="font-semibold text-gray-900">
|
||||
{language === 'en' ? scholarship.duration : scholarship.durationAr}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<h4 className="font-semibold text-gray-900 mb-3 flex items-center gap-2">
|
||||
<Users className="w-4 h-4" />
|
||||
{language === 'en' ? 'Eligibility Requirements' : 'متطلبات الأهلية'}
|
||||
</h4>
|
||||
<ul className="space-y-2">
|
||||
{(language === 'en' ? scholarship.eligibility : scholarship.eligibilityAr).slice(0, 3).map((requirement, index) => (
|
||||
<li key={index} className="flex items-start gap-2 text-sm text-gray-600">
|
||||
<CheckCircle className="w-4 h-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>{requirement}</span>
|
||||
</li>
|
||||
))}
|
||||
{(language === 'en' ? scholarship.eligibility : scholarship.eligibilityAr).length > 3 && (
|
||||
<li className="text-sm text-gray-500 ml-6">
|
||||
{language === 'en' ?
|
||||
`+${(scholarship.eligibility.length - 3)} more requirements` :
|
||||
`+${(scholarship.eligibilityAr.length - 3)} متطلبات أخرى`
|
||||
}
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-50 rounded-lg p-4 mb-6">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<CalendarDays className="w-4 h-4 text-red-500" />
|
||||
<span className="font-semibold text-gray-900">
|
||||
{language === 'en' ? 'Application Deadline' : 'موعد التقديم النهائي'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-lg font-bold text-red-600">
|
||||
{language === 'en' ? scholarship.deadline : scholarship.deadlineAr}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-3">
|
||||
<button
|
||||
className="flex-1 bg-blue-600 text-white py-3 px-4 rounded-lg font-semibold hover:bg-blue-700 transition-colors duration-200 flex items-center justify-center gap-2"
|
||||
disabled={scholarship.status === 'closed'}
|
||||
>
|
||||
{scholarship.status === 'closed' ?
|
||||
(language === 'en' ? 'Application Closed' : 'التقديم مغلق') :
|
||||
(language === 'en' ? 'Apply Now' : 'قدم الآن')
|
||||
}
|
||||
{scholarship.status !== 'closed' && <ArrowRight size={16} />}
|
||||
</button>
|
||||
<button className="px-4 py-3 border border-gray-300 text-gray-700 rounded-lg font-semibold hover:bg-gray-50 transition-colors duration-200 flex items-center gap-2">
|
||||
<ExternalLink size={16} />
|
||||
{language === 'en' ? 'Details' : 'التفاصيل'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={`min-h-screen bg-gray-50 ${dir === 'rtl' ? 'font-arabic' : ''}`} dir={dir}>
|
||||
{/* Hero Section */}
|
||||
<div className="bg-gradient-to-r from-blue-600 to-purple-600 text-white py-16">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<h1 className="text-4xl md:text-5xl font-bold mb-6">
|
||||
{t('scholarships')} & {language === 'en' ? 'Financial Support' : 'الدعم المالي'}
|
||||
</h1>
|
||||
<p className="text-xl opacity-90 mb-8">
|
||||
{language === 'en'
|
||||
? 'Invest in your future with UTAS scholarships. Up to $15,000 per year available for eligible students.'
|
||||
: 'استثمر في مستقبلك مع منح UTAS الدراسية. حتى 15,000 دولار سنوياً متاح للطلاب المؤهلين.'
|
||||
}
|
||||
</p>
|
||||
<div className="flex justify-center space-x-8 text-center">
|
||||
<div>
|
||||
<div className="text-3xl font-bold">$15,000</div>
|
||||
<div className="opacity-80">{language === 'en' ? 'Max Annual Value' : 'أقصى قيمة سنوية'}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-3xl font-bold">150+</div>
|
||||
<div className="opacity-80">{language === 'en' ? 'Available Scholarships' : 'منحة متاحة'}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-3xl font-bold">85%</div>
|
||||
<div className="opacity-80">{language === 'en' ? 'Students Receive Support' : 'الطلاب يتلقون الدعم'}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
{/* Search and Filters */}
|
||||
<div className="bg-white rounded-xl shadow-lg p-6 mb-8">
|
||||
{/* Search Bar */}
|
||||
<div className="relative mb-6">
|
||||
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400" size={20} />
|
||||
<input
|
||||
type="text"
|
||||
placeholder={language === 'en' ? 'Search scholarships by name, value, or category...' : 'البحث عن المنح بالاسم أو القيمة أو الفئة...'}
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="w-full pl-12 pr-4 py-4 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent text-lg"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Filter Toggle */}
|
||||
<button
|
||||
onClick={() => setShowFilters(!showFilters)}
|
||||
className="flex items-center gap-2 mb-4 px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<Filter size={16} />
|
||||
{t('filter')}
|
||||
</button>
|
||||
|
||||
{/* Filters */}
|
||||
{showFilters && (
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
{language === 'en' ? 'Category' : 'الفئة'}
|
||||
</label>
|
||||
<select
|
||||
value={selectedCategory}
|
||||
onChange={(e) => setSelectedCategory(e.target.value)}
|
||||
className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="">{language === 'en' ? 'All Categories' : 'جميع الفئات'}</option>
|
||||
<option value="merit">{language === 'en' ? 'Merit-based' : 'على أساس الجدارة'}</option>
|
||||
<option value="need">{language === 'en' ? 'Need-based' : 'على أساس الحاجة'}</option>
|
||||
<option value="research">{language === 'en' ? 'Research' : 'بحثي'}</option>
|
||||
<option value="international">{language === 'en' ? 'International' : 'دولي'}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
{language === 'en' ? 'Status' : 'الحالة'}
|
||||
</label>
|
||||
<select
|
||||
value={selectedStatus}
|
||||
onChange={(e) => setSelectedStatus(e.target.value)}
|
||||
className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="">{language === 'en' ? 'All Status' : 'جميع الحالات'}</option>
|
||||
<option value="open">{language === 'en' ? 'Open for Applications' : 'مفتوح للتقديم'}</option>
|
||||
<option value="closing">{language === 'en' ? 'Closing Soon' : 'يغلق قريباً'}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Results Header */}
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h2 className="text-2xl font-bold text-gray-900">
|
||||
{language === 'en' ? `${filteredScholarships.length} Scholarships Available` : `${filteredScholarships.length} منحة متاحة`}
|
||||
</h2>
|
||||
<div className="flex items-center gap-2 text-sm text-gray-600">
|
||||
<GraduationCap size={16} />
|
||||
{language === 'en' ? 'Sorted by deadline' : 'مرتب حسب الموعد النهائي'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Scholarships Grid */}
|
||||
{filteredScholarships.length > 0 ? (
|
||||
<div className="grid lg:grid-cols-2 gap-8">
|
||||
{filteredScholarships.map(scholarship => (
|
||||
<ScholarshipCard key={scholarship.id} scholarship={scholarship} />
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center py-12">
|
||||
<Award size={64} className="mx-auto text-gray-300 mb-4" />
|
||||
<h3 className="text-xl font-semibold text-gray-600 mb-2">{t('no_results')}</h3>
|
||||
<p className="text-gray-500">
|
||||
{language === 'en'
|
||||
? 'Try adjusting your search or filters to find more scholarships.'
|
||||
: 'حاول تعديل البحث أو المرشحات للعثور على المزيد من المنح.'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Application Tips */}
|
||||
<div className="mt-16 bg-gradient-to-r from-purple-600 to-blue-600 rounded-xl p-8 text-white">
|
||||
<h2 className="text-3xl font-bold mb-6 text-center">
|
||||
{language === 'en' ? 'Scholarship Application Tips' : 'نصائح تقديم المنح الدراسية'}
|
||||
</h2>
|
||||
<div className="grid md:grid-cols-3 gap-6">
|
||||
<div className="text-center">
|
||||
<div className="bg-white bg-opacity-20 rounded-full p-4 w-16 h-16 mx-auto mb-4 flex items-center justify-center">
|
||||
<CheckCircle size={32} />
|
||||
</div>
|
||||
<h3 className="font-semibold mb-2">
|
||||
{language === 'en' ? 'Apply Early' : 'قدم مبكراً'}
|
||||
</h3>
|
||||
<p className="opacity-90">
|
||||
{language === 'en'
|
||||
? 'Submit your application as early as possible to avoid deadline rush.'
|
||||
: 'قدم طلبك في أقرب وقت ممكن لتجنب زحمة الموعد النهائي.'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="bg-white bg-opacity-20 rounded-full p-4 w-16 h-16 mx-auto mb-4 flex items-center justify-center">
|
||||
<Users size={32} />
|
||||
</div>
|
||||
<h3 className="font-semibold mb-2">
|
||||
{language === 'en' ? 'Meet Requirements' : 'استوف المتطلبات'}
|
||||
</h3>
|
||||
<p className="opacity-90">
|
||||
{language === 'en'
|
||||
? 'Carefully review and ensure you meet all eligibility criteria.'
|
||||
: 'راجع بعناية وتأكد من استيفائك لجميع معايير الأهلية.'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="bg-white bg-opacity-20 rounded-full p-4 w-16 h-16 mx-auto mb-4 flex items-center justify-center">
|
||||
<ExternalLink size={32} />
|
||||
</div>
|
||||
<h3 className="font-semibold mb-2">
|
||||
{language === 'en' ? 'Prepare Documents' : 'حضر الوثائق'}
|
||||
</h3>
|
||||
<p className="opacity-90">
|
||||
{language === 'en'
|
||||
? 'Gather all required documents including transcripts and references.'
|
||||
: 'اجمع جميع الوثائق المطلوبة بما في ذلك النصوص والمراجع.'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,754 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import { useLanguage } from '@/components/providers/LanguageProvider';
|
||||
import { Award, DollarSign, Users, Globe, CheckCircle, Clock, Search, Filter, ExternalLink, ArrowRight, CalendarDays, GraduationCap } from 'lucide-react';
|
||||
import { mockScholarships, getOpenScholarships, getScholarshipsByCategory, Scholarship } from '@/lib/mockData';
|
||||
|
||||
export default function ScholarshipsPage() {
|
||||
const { language, t, dir } = useLanguage();
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [selectedCategory, setSelectedCategory] = useState('');
|
||||
const [selectedStatus, setSelectedStatus] = useState('');
|
||||
const [showFilters, setShowFilters] = useState(false);
|
||||
|
||||
// Filter scholarships based on search and filters
|
||||
const filteredScholarships = useMemo(() => {
|
||||
let results = mockScholarships;
|
||||
|
||||
// Text search
|
||||
if (searchQuery) {
|
||||
const query = searchQuery.toLowerCase();
|
||||
results = results.filter(scholarship =>
|
||||
(language === 'en' ? scholarship.title : scholarship.titleAr).toLowerCase().includes(query) ||
|
||||
(language === 'en' ? scholarship.description : scholarship.descriptionAr).toLowerCase().includes(query) ||
|
||||
(language === 'en' ? scholarship.value : scholarship.valueAr).toLowerCase().includes(query)
|
||||
);
|
||||
}
|
||||
|
||||
// Category filter
|
||||
if (selectedCategory) {
|
||||
results = results.filter(scholarship => scholarship.category === selectedCategory);
|
||||
}
|
||||
|
||||
// Status filter
|
||||
if (selectedStatus) {
|
||||
results = results.filter(scholarship => scholarship.status === selectedStatus);
|
||||
}
|
||||
|
||||
return results;
|
||||
}, [searchQuery, selectedCategory, selectedStatus, language]);
|
||||
|
||||
const ScholarshipCard = ({ scholarship }: { scholarship: Scholarship }) => (
|
||||
<div className="group bg-white rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 overflow-hidden border border-gray-100">
|
||||
<div className="p-6">
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div className={`p-2 rounded-lg ${
|
||||
scholarship.category === 'merit' ? 'bg-blue-100 text-blue-600' :
|
||||
scholarship.category === 'need' ? 'bg-green-100 text-green-600' :
|
||||
scholarship.category === 'research' ? 'bg-purple-100 text-purple-600' :
|
||||
'bg-orange-100 text-orange-600'
|
||||
}`}>
|
||||
<Award size={20} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-xl font-bold text-gray-900">
|
||||
{language === 'en' ? scholarship.title : scholarship.titleAr}
|
||||
</h3>
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<span className={`px-3 py-1 rounded-full text-xs font-semibold ${
|
||||
scholarship.status === 'open' ? 'bg-green-500 text-white' :
|
||||
scholarship.status === 'closing' ? 'bg-yellow-500 text-white' :
|
||||
'bg-red-500 text-white'
|
||||
}`}>
|
||||
{scholarship.status === 'open' ? (language === 'en' ? 'Open' : 'مفتوح') :
|
||||
scholarship.status === 'closing' ? (language === 'en' ? 'Closing Soon' : 'يغلق قريباً') :
|
||||
(language === 'en' ? 'Closed' : 'مغلق')}
|
||||
</span>
|
||||
<span className="text-sm text-gray-500 capitalize">
|
||||
{scholarship.category === 'merit' ? (language === 'en' ? 'Merit-based' : 'على أساس الجدارة') :
|
||||
scholarship.category === 'need' ? (language === 'en' ? 'Need-based' : 'على أساس الحاجة') :
|
||||
scholarship.category === 'research' ? (language === 'en' ? 'Research' : 'بحثي') :
|
||||
(language === 'en' ? 'International' : 'دولي')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-gray-600 mb-4 leading-relaxed">
|
||||
{language === 'en' ? scholarship.description : scholarship.descriptionAr}
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4 mb-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<DollarSign className="w-4 h-4 text-green-500" />
|
||||
<div>
|
||||
<div className="text-sm text-gray-500">{language === 'en' ? 'Value' : 'القيمة'}</div>
|
||||
<div className="font-semibold text-gray-900">
|
||||
{language === 'en' ? scholarship.value : scholarship.valueAr}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock className="w-4 h-4 text-blue-500" />
|
||||
<div>
|
||||
<div className="text-sm text-gray-500">{language === 'en' ? 'Duration' : 'المدة'}</div>
|
||||
<div className="font-semibold text-gray-900">
|
||||
{language === 'en' ? scholarship.duration : scholarship.durationAr}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<h4 className="font-semibold text-gray-900 mb-3 flex items-center gap-2">
|
||||
<Users className="w-4 h-4" />
|
||||
{language === 'en' ? 'Eligibility Requirements' : 'متطلبات الأهلية'}
|
||||
</h4>
|
||||
<ul className="space-y-2">
|
||||
{(language === 'en' ? scholarship.eligibility : scholarship.eligibilityAr).slice(0, 3).map((requirement, index) => (
|
||||
<li key={index} className="flex items-start gap-2 text-sm text-gray-600">
|
||||
<CheckCircle className="w-4 h-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>{requirement}</span>
|
||||
</li>
|
||||
))}
|
||||
{(language === 'en' ? scholarship.eligibility : scholarship.eligibilityAr).length > 3 && (
|
||||
<li className="text-sm text-gray-500 ml-6">
|
||||
{language === 'en' ?
|
||||
`+${(scholarship.eligibility.length - 3)} more requirements` :
|
||||
`+${(scholarship.eligibilityAr.length - 3)} متطلبات أخرى`
|
||||
}
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-50 rounded-lg p-4 mb-6">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<CalendarDays className="w-4 h-4 text-red-500" />
|
||||
<span className="font-semibold text-gray-900">
|
||||
{language === 'en' ? 'Application Deadline' : 'موعد التقديم النهائي'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-lg font-bold text-red-600">
|
||||
{language === 'en' ? scholarship.deadline : scholarship.deadlineAr}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-3">
|
||||
<button
|
||||
className="flex-1 bg-blue-600 text-white py-3 px-4 rounded-lg font-semibold hover:bg-blue-700 transition-colors duration-200 flex items-center justify-center gap-2"
|
||||
disabled={scholarship.status === 'closed'}
|
||||
>
|
||||
{scholarship.status === 'closed' ?
|
||||
(language === 'en' ? 'Application Closed' : 'التقديم مغلق') :
|
||||
(language === 'en' ? 'Apply Now' : 'قدم الآن')
|
||||
}
|
||||
{scholarship.status !== 'closed' && <ArrowRight size={16} />}
|
||||
</button>
|
||||
<button className="px-4 py-3 border border-gray-300 text-gray-700 rounded-lg font-semibold hover:bg-gray-50 transition-colors duration-200 flex items-center gap-2">
|
||||
<ExternalLink size={16} />
|
||||
{language === 'en' ? 'Details' : 'التفاصيل'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={`min-h-screen bg-gray-50 ${dir === 'rtl' ? 'font-arabic' : ''}`} dir={dir}>
|
||||
{/* Hero Section */}
|
||||
<div className="bg-gradient-to-r from-blue-600 to-purple-600 text-white py-16">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<h1 className="text-4xl md:text-5xl font-bold mb-6">
|
||||
{t('scholarships')} & {language === 'en' ? 'Financial Support' : 'الدعم المالي'}
|
||||
</h1>
|
||||
<p className="text-xl opacity-90 mb-8">
|
||||
{language === 'en'
|
||||
? 'Invest in your future with UTAS scholarships. Up to $15,000 per year available for eligible students.'
|
||||
: 'استثمر في مستقبلك مع منح UTAS الدراسية. حتى 15,000 دولار سنوياً متاح للطلاب المؤهلين.'
|
||||
}
|
||||
</p>
|
||||
<div className="flex justify-center space-x-8 text-center">
|
||||
<div>
|
||||
<div className="text-3xl font-bold">$15,000</div>
|
||||
<div className="opacity-80">{language === 'en' ? 'Max Annual Value' : 'أقصى قيمة سنوية'}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-3xl font-bold">150+</div>
|
||||
<div className="opacity-80">{language === 'en' ? 'Available Scholarships' : 'منحة متاحة'}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-3xl font-bold">85%</div>
|
||||
<div className="opacity-80">{language === 'en' ? 'Students Receive Support' : 'الطلاب يتلقون الدعم'}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
{/* Search and Filters */}
|
||||
<div className="bg-white rounded-xl shadow-lg p-6 mb-8">
|
||||
{/* Search Bar */}
|
||||
<div className="relative mb-6">
|
||||
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400" size={20} />
|
||||
<input
|
||||
type="text"
|
||||
placeholder={language === 'en' ? 'Search scholarships by name, value, or category...' : 'البحث عن المنح بالاسم أو القيمة أو الفئة...'}
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="w-full pl-12 pr-4 py-4 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent text-lg"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Filter Toggle */}
|
||||
<button
|
||||
onClick={() => setShowFilters(!showFilters)}
|
||||
className="flex items-center gap-2 mb-4 px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<Filter size={16} />
|
||||
{t('filter')}
|
||||
</button>
|
||||
|
||||
{/* Filters */}
|
||||
{showFilters && (
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
{language === 'en' ? 'Category' : 'الفئة'}
|
||||
</label>
|
||||
<select
|
||||
value={selectedCategory}
|
||||
onChange={(e) => setSelectedCategory(e.target.value)}
|
||||
className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="">{language === 'en' ? 'All Categories' : 'جميع الفئات'}</option>
|
||||
<option value="merit">{language === 'en' ? 'Merit-based' : 'على أساس الجدارة'}</option>
|
||||
<option value="need">{language === 'en' ? 'Need-based' : 'على أساس الحاجة'}</option>
|
||||
<option value="research">{language === 'en' ? 'Research' : 'بحثي'}</option>
|
||||
<option value="international">{language === 'en' ? 'International' : 'دولي'}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
{language === 'en' ? 'Status' : 'الحالة'}
|
||||
</label>
|
||||
<select
|
||||
value={selectedStatus}
|
||||
onChange={(e) => setSelectedStatus(e.target.value)}
|
||||
className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="">{language === 'en' ? 'All Status' : 'جميع الحالات'}</option>
|
||||
<option value="open">{language === 'en' ? 'Open for Applications' : 'مفتوح للتقديم'}</option>
|
||||
<option value="closing">{language === 'en' ? 'Closing Soon' : 'يغلق قريباً'}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Results Header */}
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h2 className="text-2xl font-bold text-gray-900">
|
||||
{language === 'en' ? `${filteredScholarships.length} Scholarships Available` : `${filteredScholarships.length} منحة متاحة`}
|
||||
</h2>
|
||||
<div className="flex items-center gap-2 text-sm text-gray-600">
|
||||
<GraduationCap size={16} />
|
||||
{language === 'en' ? 'Sorted by deadline' : 'مرتب حسب الموعد النهائي'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Scholarships Grid */}
|
||||
{filteredScholarships.length > 0 ? (
|
||||
<div className="grid lg:grid-cols-2 gap-8">
|
||||
{filteredScholarships.map(scholarship => (
|
||||
<ScholarshipCard key={scholarship.id} scholarship={scholarship} />
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center py-12">
|
||||
<Award size={64} className="mx-auto text-gray-300 mb-4" />
|
||||
<h3 className="text-xl font-semibold text-gray-600 mb-2">{t('no_results')}</h3>
|
||||
<p className="text-gray-500">
|
||||
{language === 'en'
|
||||
? 'Try adjusting your search or filters to find more scholarships.'
|
||||
: 'حاول تعديل البحث أو المرشحات للعثور على المزيد من المنح.'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Application Tips */}
|
||||
<div className="mt-16 bg-gradient-to-r from-purple-600 to-blue-600 rounded-xl p-8 text-white">
|
||||
<h2 className="text-3xl font-bold mb-6 text-center">
|
||||
{language === 'en' ? 'Scholarship Application Tips' : 'نصائح تقديم المنح الدراسية'}
|
||||
</h2>
|
||||
<div className="grid md:grid-cols-3 gap-6">
|
||||
<div className="text-center">
|
||||
<div className="bg-white bg-opacity-20 rounded-full p-4 w-16 h-16 mx-auto mb-4 flex items-center justify-center">
|
||||
<CheckCircle size={32} />
|
||||
</div>
|
||||
<h3 className="font-semibold mb-2">
|
||||
{language === 'en' ? 'Apply Early' : 'قدم مبكراً'}
|
||||
</h3>
|
||||
<p className="opacity-90">
|
||||
{language === 'en'
|
||||
? 'Submit your application as early as possible to avoid deadline rush.'
|
||||
: 'قدم طلبك في أقرب وقت ممكن لتجنب زحمة الموعد النهائي.'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="bg-white bg-opacity-20 rounded-full p-4 w-16 h-16 mx-auto mb-4 flex items-center justify-center">
|
||||
<Users size={32} />
|
||||
</div>
|
||||
<h3 className="font-semibold mb-2">
|
||||
{language === 'en' ? 'Meet Requirements' : 'استوف المتطلبات'}
|
||||
</h3>
|
||||
<p className="opacity-90">
|
||||
{language === 'en'
|
||||
? 'Carefully review and ensure you meet all eligibility criteria.'
|
||||
: 'راجع بعناية وتأكد من استيفائك لجميع معايير الأهلية.'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="bg-white bg-opacity-20 rounded-full p-4 w-16 h-16 mx-auto mb-4 flex items-center justify-center">
|
||||
<ExternalLink size={32} />
|
||||
</div>
|
||||
<h3 className="font-semibold mb-2">
|
||||
{language === 'en' ? 'Prepare Documents' : 'حضر الوثائق'}
|
||||
</h3>
|
||||
<p className="opacity-90">
|
||||
{language === 'en'
|
||||
? 'Gather all required documents including transcripts and references.'
|
||||
: 'اجمع جميع الوثائق المطلوبة بما في ذلك النصوص والمراجع.'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
],
|
||||
description: 'Recognizes academic achievement and helps cover study costs for high-achieving students.',
|
||||
type: 'merit',
|
||||
deadline: 'January 15, 2025',
|
||||
applicationUrl: '/apply/scholarship/merit'
|
||||
},
|
||||
{
|
||||
id: 'equity-scholarship',
|
||||
title: 'UTAS Equity Scholarship',
|
||||
amount: '$5,000 per year',
|
||||
duration: 'Duration of degree',
|
||||
eligibility: [
|
||||
'Financial disadvantage',
|
||||
'First in family to attend university',
|
||||
'Remote or regional background',
|
||||
'Care leavers or refugee background'
|
||||
],
|
||||
description: 'Supporting students from disadvantaged backgrounds to access higher education.',
|
||||
type: 'equity',
|
||||
deadline: 'December 31, 2024',
|
||||
applicationUrl: '/apply/scholarship/equity'
|
||||
},
|
||||
{
|
||||
id: 'international-scholarship',
|
||||
title: 'International Excellence Scholarship',
|
||||
amount: '25% tuition reduction',
|
||||
duration: 'Duration of degree',
|
||||
eligibility: [
|
||||
'International students',
|
||||
'Academic excellence in previous studies',
|
||||
'Commencing undergraduate or postgraduate',
|
||||
'Full fee-paying students'
|
||||
],
|
||||
description: 'Attracts high-achieving international students to study at UTAS with significant tuition reduction.',
|
||||
type: 'international',
|
||||
deadline: 'Rolling applications',
|
||||
applicationUrl: '/apply/scholarship/international'
|
||||
},
|
||||
{
|
||||
id: 'research-scholarship',
|
||||
title: 'Research Training Program (RTP)',
|
||||
amount: '$28,994 per year (2024 rate)',
|
||||
duration: '3-4 years',
|
||||
eligibility: [
|
||||
'PhD or Research Masters students',
|
||||
'Strong academic background',
|
||||
'Research proposal approved',
|
||||
'Full-time enrollment'
|
||||
],
|
||||
description: 'Australian Government funded scholarship for research degree students, covering living costs.',
|
||||
type: 'research',
|
||||
deadline: 'Ongoing throughout year',
|
||||
applicationUrl: '/apply/scholarship/rtp'
|
||||
},
|
||||
{
|
||||
id: 'indigenous-scholarship',
|
||||
title: 'Indigenous Student Success Scholarship',
|
||||
amount: '$5,000 per year + support',
|
||||
duration: 'Duration of degree',
|
||||
eligibility: [
|
||||
'Aboriginal or Torres Strait Islander students',
|
||||
'Australian citizens',
|
||||
'Enrolled in eligible programs',
|
||||
'Maintaining satisfactory progress'
|
||||
],
|
||||
description: 'Comprehensive support including financial assistance, mentoring, and cultural programs.',
|
||||
type: 'indigenous',
|
||||
deadline: 'December 31, 2024',
|
||||
applicationUrl: '/apply/scholarship/indigenous'
|
||||
}
|
||||
];
|
||||
|
||||
const scholarshipTypes = [
|
||||
{ value: 'merit', label: 'Merit Based', icon: <Award className="w-4 h-4" /> },
|
||||
{ value: 'equity', label: 'Equity & Access', icon: <Users className="w-4 h-4" /> },
|
||||
{ value: 'international', label: 'International', icon: <Globe className="w-4 h-4" /> },
|
||||
{ value: 'research', label: 'Research', icon: <CheckCircle className="w-4 h-4" /> },
|
||||
{ value: 'indigenous', label: 'Indigenous', icon: <Users className="w-4 h-4" /> }
|
||||
];
|
||||
|
||||
const filteredScholarships = scholarships.filter(scholarship => {
|
||||
if (searchQuery && !scholarship.title.toLowerCase().includes(searchQuery.toLowerCase()) &&
|
||||
!scholarship.description.toLowerCase().includes(searchQuery.toLowerCase())) {
|
||||
return false;
|
||||
}
|
||||
if (selectedType && scholarship.type !== selectedType) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
const clearFilters = () => {
|
||||
setSearchQuery('');
|
||||
setSelectedType('');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
{/* Hero Section */}
|
||||
<div className="bg-gradient-to-r from-emerald-600 to-teal-600 text-white">
|
||||
<div className="container mx-auto px-4 py-20">
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<Award className="w-16 h-16 mx-auto mb-6 text-emerald-200" />
|
||||
<h1 className="text-4xl md:text-5xl font-bold mb-6">
|
||||
UTAS Scholarships
|
||||
</h1>
|
||||
<p className="text-xl opacity-90 mb-8">
|
||||
Invest in your future with scholarships worth over $20 million annually. From merit-based awards to equity support, we help make education accessible.
|
||||
</p>
|
||||
|
||||
<div className="grid md:grid-cols-3 gap-6 mb-8">
|
||||
<div className="bg-white/10 backdrop-blur-sm rounded-xl p-6">
|
||||
<div className="text-3xl font-bold mb-2">$15,000</div>
|
||||
<div className="text-emerald-200">Maximum annual award</div>
|
||||
</div>
|
||||
<div className="bg-white/10 backdrop-blur-sm rounded-xl p-6">
|
||||
<div className="text-3xl font-bold mb-2">70%</div>
|
||||
<div className="text-emerald-200">Students receive support</div>
|
||||
</div>
|
||||
<div className="bg-white/10 backdrop-blur-sm rounded-xl p-6">
|
||||
<div className="text-3xl font-bold mb-2">$20M+</div>
|
||||
<div className="text-emerald-200">Total scholarships annually</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href="/apply/scholarships"
|
||||
className="bg-white text-emerald-600 px-8 py-4 rounded-xl font-semibold hover:bg-gray-100 transition-all duration-200 inline-flex items-center"
|
||||
>
|
||||
Apply for Scholarships
|
||||
<ArrowRight className="ml-2" size={20} />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Search and Filters */}
|
||||
<div className="bg-white shadow-sm border-b border-gray-200">
|
||||
<div className="container mx-auto px-4 py-6">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<div className="flex flex-col md:flex-row gap-4 items-center">
|
||||
<div className="relative flex-grow">
|
||||
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400" size={20} />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search scholarships by name or description..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="w-full pl-12 pr-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-emerald-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => setShowFilters(!showFilters)}
|
||||
className="flex items-center space-x-2 px-4 py-3 bg-gray-100 text-gray-700 rounded-xl hover:bg-gray-200 transition-colors"
|
||||
>
|
||||
<Filter size={20} />
|
||||
<span>Filters</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{showFilters && (
|
||||
<div className="mt-4 p-4 bg-gray-50 rounded-xl">
|
||||
<div className="flex flex-wrap gap-3 items-center">
|
||||
<span className="text-sm font-medium text-gray-700">Filter by type:</span>
|
||||
{scholarshipTypes.map(type => (
|
||||
<button
|
||||
key={type.value}
|
||||
onClick={() => setSelectedType(selectedType === type.value ? '' : type.value)}
|
||||
className={`flex items-center space-x-2 px-3 py-2 rounded-lg text-sm font-medium transition-colors ${
|
||||
selectedType === type.value
|
||||
? 'bg-emerald-600 text-white'
|
||||
: 'bg-white text-gray-700 hover:bg-gray-100 border border-gray-300'
|
||||
}`}
|
||||
>
|
||||
{type.icon}
|
||||
<span>{type.label}</span>
|
||||
</button>
|
||||
))}
|
||||
|
||||
{(searchQuery || selectedType) && (
|
||||
<button
|
||||
onClick={clearFilters}
|
||||
className="ml-4 text-sm text-gray-600 hover:text-gray-800 underline"
|
||||
>
|
||||
Clear filters
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Scholarships Grid */}
|
||||
<div className="container mx-auto px-4 py-12">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<div className="flex justify-between items-center mb-8">
|
||||
<h2 className="text-2xl font-bold text-gray-900">
|
||||
{filteredScholarships.length} Scholarships Available
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
{filteredScholarships.length === 0 ? (
|
||||
<div className="text-center py-12">
|
||||
<Award size={64} className="mx-auto text-gray-400 mb-4" />
|
||||
<h3 className="text-xl font-semibold text-gray-600 mb-2">No scholarships found</h3>
|
||||
<p className="text-gray-500 mb-4">Try adjusting your search criteria</p>
|
||||
<button
|
||||
onClick={clearFilters}
|
||||
className="bg-emerald-600 text-white px-6 py-2 rounded-lg hover:bg-emerald-700 transition-colors"
|
||||
>
|
||||
Clear Filters
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid gap-8">
|
||||
{filteredScholarships.map((scholarship) => (
|
||||
<div key={scholarship.id} className="bg-white rounded-xl shadow-sm border border-gray-200 hover:shadow-md transition-shadow">
|
||||
<div className="p-8">
|
||||
<div className="flex flex-col lg:flex-row lg:items-start lg:justify-between gap-6">
|
||||
<div className="flex-grow">
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div>
|
||||
<div className="flex items-center space-x-3 mb-2">
|
||||
<span className={`inline-flex items-center px-3 py-1 rounded-full text-xs font-medium ${
|
||||
scholarship.type === 'merit' ? 'bg-blue-100 text-blue-800' :
|
||||
scholarship.type === 'equity' ? 'bg-purple-100 text-purple-800' :
|
||||
scholarship.type === 'international' ? 'bg-green-100 text-green-800' :
|
||||
scholarship.type === 'research' ? 'bg-orange-100 text-orange-800' :
|
||||
'bg-pink-100 text-pink-800'
|
||||
}`}>
|
||||
{scholarshipTypes.find(t => t.value === scholarship.type)?.label}
|
||||
</span>
|
||||
<div className="flex items-center text-gray-500 text-sm">
|
||||
<Clock size={16} className="mr-1" />
|
||||
Deadline: {scholarship.deadline}
|
||||
</div>
|
||||
</div>
|
||||
<h3 className="text-2xl font-bold text-gray-900 mb-3">
|
||||
{scholarship.title}
|
||||
</h3>
|
||||
<p className="text-gray-600 mb-4 leading-relaxed">
|
||||
{scholarship.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-6 mb-6">
|
||||
<div>
|
||||
<h4 className="font-semibold text-gray-900 mb-2 flex items-center">
|
||||
<DollarSign size={18} className="mr-2 text-green-600" />
|
||||
Financial Details
|
||||
</h4>
|
||||
<ul className="text-gray-600 space-y-1">
|
||||
<li><strong>Amount:</strong> {scholarship.amount}</li>
|
||||
<li><strong>Duration:</strong> {scholarship.duration}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 className="font-semibold text-gray-900 mb-2 flex items-center">
|
||||
<CheckCircle size={18} className="mr-2 text-emerald-600" />
|
||||
Eligibility Requirements
|
||||
</h4>
|
||||
<ul className="text-gray-600 space-y-1">
|
||||
{scholarship.eligibility.slice(0, 2).map((req, index) => (
|
||||
<li key={index}>• {req}</li>
|
||||
))}
|
||||
{scholarship.eligibility.length > 2 && (
|
||||
<li className="text-gray-500">+ {scholarship.eligibility.length - 2} more requirements</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-shrink-0 lg:w-64">
|
||||
<div className="bg-gray-50 rounded-xl p-6 text-center">
|
||||
<div className="text-3xl font-bold text-emerald-600 mb-2">
|
||||
{scholarship.amount.split(' ')[0]}
|
||||
</div>
|
||||
<div className="text-gray-600 text-sm mb-4">
|
||||
{scholarship.amount.includes('per year') ? 'per year' : 'total value'}
|
||||
</div>
|
||||
<Link
|
||||
href={scholarship.applicationUrl}
|
||||
className="w-full bg-emerald-600 text-white px-6 py-3 rounded-xl font-semibold hover:bg-emerald-700 transition-colors inline-flex items-center justify-center"
|
||||
>
|
||||
Apply Now
|
||||
<ExternalLink className="ml-2" size={16} />
|
||||
</Link>
|
||||
<Link
|
||||
href={`/scholarships/${scholarship.id}`}
|
||||
className="w-full mt-3 border border-gray-300 text-gray-700 px-6 py-3 rounded-xl font-medium hover:bg-gray-50 transition-colors inline-block"
|
||||
>
|
||||
View Details
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Application Tips */}
|
||||
<div className="bg-white">
|
||||
<div className="container mx-auto px-4 py-16">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<h2 className="text-3xl font-bold text-center text-gray-900 mb-12">
|
||||
Scholarship Application Tips
|
||||
</h2>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-8">
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-start space-x-4">
|
||||
<div className="flex-shrink-0 w-8 h-8 bg-emerald-100 rounded-full flex items-center justify-center">
|
||||
<span className="text-emerald-600 font-bold text-sm">1</span>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-gray-900 mb-2">Apply Early</h3>
|
||||
<p className="text-gray-600">Many scholarships have limited places. Submit your application as early as possible to maximize your chances.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start space-x-4">
|
||||
<div className="flex-shrink-0 w-8 h-8 bg-emerald-100 rounded-full flex items-center justify-center">
|
||||
<span className="text-emerald-600 font-bold text-sm">2</span>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-gray-900 mb-2">Read Requirements Carefully</h3>
|
||||
<p className="text-gray-600">Ensure you meet all eligibility criteria before applying. Some scholarships are automatically considered upon course application.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start space-x-4">
|
||||
<div className="flex-shrink-0 w-8 h-8 bg-emerald-100 rounded-full flex items-center justify-center">
|
||||
<span className="text-emerald-600 font-bold text-sm">3</span>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-gray-900 mb-2">Prepare Strong Supporting Documents</h3>
|
||||
<p className="text-gray-600">Personal statements, academic transcripts, and references should clearly demonstrate your eligibility and motivation.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-start space-x-4">
|
||||
<div className="flex-shrink-0 w-8 h-8 bg-emerald-100 rounded-full flex items-center justify-center">
|
||||
<span className="text-emerald-600 font-bold text-sm">4</span>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-gray-900 mb-2">Apply for Multiple Scholarships</h3>
|
||||
<p className="text-gray-600">You can often apply for multiple scholarships. Don't limit yourself to just one opportunity.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start space-x-4">
|
||||
<div className="flex-shrink-0 w-8 h-8 bg-emerald-100 rounded-full flex items-center justify-center">
|
||||
<span className="text-emerald-600 font-bold text-sm">5</span>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-gray-900 mb-2">Maintain Academic Standards</h3>
|
||||
<p className="text-gray-600">Most scholarships require you to maintain satisfactory academic progress to continue receiving funding.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start space-x-4">
|
||||
<div className="flex-shrink-0 w-8 h-8 bg-emerald-100 rounded-full flex items-center justify-center">
|
||||
<span className="text-emerald-600 font-bold text-sm">6</span>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-gray-900 mb-2">Get Help if Needed</h3>
|
||||
<p className="text-gray-600">Contact our Student Central team for guidance on scholarship applications and eligibility questions.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Call to Action */}
|
||||
<div className="bg-gradient-to-r from-teal-600 to-emerald-600 text-white">
|
||||
<div className="container mx-auto px-4 py-16">
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<h2 className="text-3xl font-bold mb-4">Don't Miss Out on Funding Opportunities</h2>
|
||||
<p className="text-xl opacity-90 mb-8">
|
||||
Start your scholarship applications today and invest in your future success
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<Link
|
||||
href="/apply/scholarships"
|
||||
className="bg-white text-emerald-600 px-8 py-4 rounded-xl font-semibold hover:bg-gray-100 transition-all duration-200"
|
||||
>
|
||||
Apply for Scholarships
|
||||
</Link>
|
||||
<Link
|
||||
href="/contact/scholarships"
|
||||
className="border-2 border-white text-white px-8 py-4 rounded-xl font-semibold hover:bg-white hover:text-emerald-600 transition-all duration-200"
|
||||
>
|
||||
Get Scholarship Advice
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import { useLanguage } from '@/components/providers/LanguageProvider';
|
||||
import { Award, DollarSign, Users, CheckCircle, Clock, Search, Filter, ExternalLink, ArrowRight, CalendarDays, GraduationCap } from 'lucide-react';
|
||||
import { mockScholarships, Scholarship } from '@/lib/mockData';
|
||||
|
||||
export default function ScholarshipsPage() {
|
||||
const { language, t, dir } = useLanguage();
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [selectedCategory, setSelectedCategory] = useState('');
|
||||
const [selectedStatus, setSelectedStatus] = useState('');
|
||||
const [showFilters, setShowFilters] = useState(false);
|
||||
|
||||
// Filter scholarships based on search and filters
|
||||
const filteredScholarships = useMemo(() => {
|
||||
let results = mockScholarships;
|
||||
|
||||
// Text search
|
||||
if (searchQuery) {
|
||||
const query = searchQuery.toLowerCase();
|
||||
results = results.filter(scholarship =>
|
||||
(language === 'en' ? scholarship.title : scholarship.titleAr).toLowerCase().includes(query) ||
|
||||
(language === 'en' ? scholarship.description : scholarship.descriptionAr).toLowerCase().includes(query) ||
|
||||
(language === 'en' ? scholarship.value : scholarship.valueAr).toLowerCase().includes(query)
|
||||
);
|
||||
}
|
||||
|
||||
// Category filter
|
||||
if (selectedCategory) {
|
||||
results = results.filter(scholarship => scholarship.category === selectedCategory);
|
||||
}
|
||||
|
||||
// Status filter
|
||||
if (selectedStatus) {
|
||||
results = results.filter(scholarship => scholarship.status === selectedStatus);
|
||||
}
|
||||
|
||||
return results;
|
||||
}, [searchQuery, selectedCategory, selectedStatus, language]);
|
||||
|
||||
const ScholarshipCard = ({ scholarship }: { scholarship: Scholarship }) => (
|
||||
<div className="group bg-white rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 overflow-hidden border border-gray-100">
|
||||
<div className="p-6">
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div className={`p-2 rounded-lg ${
|
||||
scholarship.category === 'merit' ? 'bg-blue-100 text-blue-600' :
|
||||
scholarship.category === 'need' ? 'bg-green-100 text-green-600' :
|
||||
scholarship.category === 'research' ? 'bg-purple-100 text-purple-600' :
|
||||
'bg-orange-100 text-orange-600'
|
||||
}`}>
|
||||
<Award size={20} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-xl font-bold text-gray-900">
|
||||
{language === 'en' ? scholarship.title : scholarship.titleAr}
|
||||
</h3>
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<span className={`px-3 py-1 rounded-full text-xs font-semibold ${
|
||||
scholarship.status === 'open' ? 'bg-green-500 text-white' :
|
||||
scholarship.status === 'closing' ? 'bg-yellow-500 text-white' :
|
||||
'bg-red-500 text-white'
|
||||
}`}>
|
||||
{scholarship.status === 'open' ? (language === 'en' ? 'Open' : 'مفتوح') :
|
||||
scholarship.status === 'closing' ? (language === 'en' ? 'Closing Soon' : 'يغلق قريباً') :
|
||||
(language === 'en' ? 'Closed' : 'مغلق')}
|
||||
</span>
|
||||
<span className="text-sm text-gray-500 capitalize">
|
||||
{scholarship.category === 'merit' ? (language === 'en' ? 'Merit-based' : 'على أساس الجدارة') :
|
||||
scholarship.category === 'need' ? (language === 'en' ? 'Need-based' : 'على أساس الحاجة') :
|
||||
scholarship.category === 'research' ? (language === 'en' ? 'Research' : 'بحثي') :
|
||||
(language === 'en' ? 'International' : 'دولي')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-gray-600 mb-4 leading-relaxed">
|
||||
{language === 'en' ? scholarship.description : scholarship.descriptionAr}
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4 mb-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<DollarSign className="w-4 h-4 text-green-500" />
|
||||
<div>
|
||||
<div className="text-sm text-gray-500">{language === 'en' ? 'Value' : 'القيمة'}</div>
|
||||
<div className="font-semibold text-gray-900">
|
||||
{language === 'en' ? scholarship.value : scholarship.valueAr}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock className="w-4 h-4 text-blue-500" />
|
||||
<div>
|
||||
<div className="text-sm text-gray-500">{language === 'en' ? 'Duration' : 'المدة'}</div>
|
||||
<div className="font-semibold text-gray-900">
|
||||
{language === 'en' ? scholarship.duration : scholarship.durationAr}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<h4 className="font-semibold text-gray-900 mb-3 flex items-center gap-2">
|
||||
<Users className="w-4 h-4" />
|
||||
{language === 'en' ? 'Eligibility Requirements' : 'متطلبات الأهلية'}
|
||||
</h4>
|
||||
<ul className="space-y-2">
|
||||
{(language === 'en' ? scholarship.eligibility : scholarship.eligibilityAr).slice(0, 3).map((requirement, index) => (
|
||||
<li key={index} className="flex items-start gap-2 text-sm text-gray-600">
|
||||
<CheckCircle className="w-4 h-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>{requirement}</span>
|
||||
</li>
|
||||
))}
|
||||
{(language === 'en' ? scholarship.eligibility : scholarship.eligibilityAr).length > 3 && (
|
||||
<li className="text-sm text-gray-500 ml-6">
|
||||
{language === 'en' ?
|
||||
`+${(scholarship.eligibility.length - 3)} more requirements` :
|
||||
`+${(scholarship.eligibilityAr.length - 3)} متطلبات أخرى`
|
||||
}
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-50 rounded-lg p-4 mb-6">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<CalendarDays className="w-4 h-4 text-red-500" />
|
||||
<span className="font-semibold text-gray-900">
|
||||
{language === 'en' ? 'Application Deadline' : 'موعد التقديم النهائي'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-lg font-bold text-red-600">
|
||||
{language === 'en' ? scholarship.deadline : scholarship.deadlineAr}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-3">
|
||||
<button
|
||||
className="flex-1 bg-blue-600 text-white py-3 px-4 rounded-lg font-semibold hover:bg-blue-700 transition-colors duration-200 flex items-center justify-center gap-2"
|
||||
disabled={scholarship.status === 'closed'}
|
||||
>
|
||||
{scholarship.status === 'closed' ?
|
||||
(language === 'en' ? 'Application Closed' : 'التقديم مغلق') :
|
||||
(language === 'en' ? 'Apply Now' : 'قدم الآن')
|
||||
}
|
||||
{scholarship.status !== 'closed' && <ArrowRight size={16} />}
|
||||
</button>
|
||||
<button className="px-4 py-3 border border-gray-300 text-gray-700 rounded-lg font-semibold hover:bg-gray-50 transition-colors duration-200 flex items-center gap-2">
|
||||
<ExternalLink size={16} />
|
||||
{language === 'en' ? 'Details' : 'التفاصيل'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={`min-h-screen bg-gray-50 ${dir === 'rtl' ? 'font-arabic' : ''}`} dir={dir}>
|
||||
{/* Hero Section */}
|
||||
<div className="bg-gradient-to-r from-blue-600 to-purple-600 text-white py-16">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<h1 className="text-4xl md:text-5xl font-bold mb-6">
|
||||
{t('scholarships')} & {language === 'en' ? 'Financial Support' : 'الدعم المالي'}
|
||||
</h1>
|
||||
<p className="text-xl opacity-90 mb-8">
|
||||
{language === 'en'
|
||||
? 'Invest in your future with UTAS scholarships. Up to $15,000 per year available for eligible students.'
|
||||
: 'استثمر في مستقبلك مع منح UTAS الدراسية. حتى 15,000 دولار سنوياً متاح للطلاب المؤهلين.'
|
||||
}
|
||||
</p>
|
||||
<div className="flex justify-center space-x-8 text-center">
|
||||
<div>
|
||||
<div className="text-3xl font-bold">$15,000</div>
|
||||
<div className="opacity-80">{language === 'en' ? 'Max Annual Value' : 'أقصى قيمة سنوية'}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-3xl font-bold">150+</div>
|
||||
<div className="opacity-80">{language === 'en' ? 'Available Scholarships' : 'منحة متاحة'}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-3xl font-bold">85%</div>
|
||||
<div className="opacity-80">{language === 'en' ? 'Students Receive Support' : 'الطلاب يتلقون الدعم'}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
{/* Search and Filters */}
|
||||
<div className="bg-white rounded-xl shadow-lg p-6 mb-8">
|
||||
{/* Search Bar */}
|
||||
<div className="relative mb-6">
|
||||
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400" size={20} />
|
||||
<input
|
||||
type="text"
|
||||
placeholder={language === 'en' ? 'Search scholarships by name, value, or category...' : 'البحث عن المنح بالاسم أو القيمة أو الفئة...'}
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="w-full pl-12 pr-4 py-4 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent text-lg"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Filter Toggle */}
|
||||
<button
|
||||
onClick={() => setShowFilters(!showFilters)}
|
||||
className="flex items-center gap-2 mb-4 px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<Filter size={16} />
|
||||
{t('filter')}
|
||||
</button>
|
||||
|
||||
{/* Filters */}
|
||||
{showFilters && (
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
{language === 'en' ? 'Category' : 'الفئة'}
|
||||
</label>
|
||||
<select
|
||||
value={selectedCategory}
|
||||
onChange={(e) => setSelectedCategory(e.target.value)}
|
||||
className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="">{language === 'en' ? 'All Categories' : 'جميع الفئات'}</option>
|
||||
<option value="merit">{language === 'en' ? 'Merit-based' : 'على أساس الجدارة'}</option>
|
||||
<option value="need">{language === 'en' ? 'Need-based' : 'على أساس الحاجة'}</option>
|
||||
<option value="research">{language === 'en' ? 'Research' : 'بحثي'}</option>
|
||||
<option value="international">{language === 'en' ? 'International' : 'دولي'}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
{language === 'en' ? 'Status' : 'الحالة'}
|
||||
</label>
|
||||
<select
|
||||
value={selectedStatus}
|
||||
onChange={(e) => setSelectedStatus(e.target.value)}
|
||||
className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="">{language === 'en' ? 'All Status' : 'جميع الحالات'}</option>
|
||||
<option value="open">{language === 'en' ? 'Open for Applications' : 'مفتوح للتقديم'}</option>
|
||||
<option value="closing">{language === 'en' ? 'Closing Soon' : 'يغلق قريباً'}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Results Header */}
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h2 className="text-2xl font-bold text-gray-900">
|
||||
{language === 'en' ? `${filteredScholarships.length} Scholarships Available` : `${filteredScholarships.length} منحة متاحة`}
|
||||
</h2>
|
||||
<div className="flex items-center gap-2 text-sm text-gray-600">
|
||||
<GraduationCap size={16} />
|
||||
{language === 'en' ? 'Sorted by deadline' : 'مرتب حسب الموعد النهائي'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Scholarships Grid */}
|
||||
{filteredScholarships.length > 0 ? (
|
||||
<div className="grid lg:grid-cols-2 gap-8">
|
||||
{filteredScholarships.map(scholarship => (
|
||||
<ScholarshipCard key={scholarship.id} scholarship={scholarship} />
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center py-12">
|
||||
<Award size={64} className="mx-auto text-gray-300 mb-4" />
|
||||
<h3 className="text-xl font-semibold text-gray-600 mb-2">{t('no_results')}</h3>
|
||||
<p className="text-gray-500">
|
||||
{language === 'en'
|
||||
? 'Try adjusting your search or filters to find more scholarships.'
|
||||
: 'حاول تعديل البحث أو المرشحات للعثور على المزيد من المنح.'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Application Tips */}
|
||||
<div className="mt-16 bg-gradient-to-r from-purple-600 to-blue-600 rounded-xl p-8 text-white">
|
||||
<h2 className="text-3xl font-bold mb-6 text-center">
|
||||
{language === 'en' ? 'Scholarship Application Tips' : 'نصائح تقديم المنح الدراسية'}
|
||||
</h2>
|
||||
<div className="grid md:grid-cols-3 gap-6">
|
||||
<div className="text-center">
|
||||
<div className="bg-white bg-opacity-20 rounded-full p-4 w-16 h-16 mx-auto mb-4 flex items-center justify-center">
|
||||
<CheckCircle size={32} />
|
||||
</div>
|
||||
<h3 className="font-semibold mb-2">
|
||||
{language === 'en' ? 'Apply Early' : 'قدم مبكراً'}
|
||||
</h3>
|
||||
<p className="opacity-90">
|
||||
{language === 'en'
|
||||
? 'Submit your application as early as possible to avoid deadline rush.'
|
||||
: 'قدم طلبك في أقرب وقت ممكن لتجنب زحمة الموعد النهائي.'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="bg-white bg-opacity-20 rounded-full p-4 w-16 h-16 mx-auto mb-4 flex items-center justify-center">
|
||||
<Users size={32} />
|
||||
</div>
|
||||
<h3 className="font-semibold mb-2">
|
||||
{language === 'en' ? 'Meet Requirements' : 'استوف المتطلبات'}
|
||||
</h3>
|
||||
<p className="opacity-90">
|
||||
{language === 'en'
|
||||
? 'Carefully review and ensure you meet all eligibility criteria.'
|
||||
: 'راجع بعناية وتأكد من استيفائك لجميع معايير الأهلية.'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="bg-white bg-opacity-20 rounded-full p-4 w-16 h-16 mx-auto mb-4 flex items-center justify-center">
|
||||
<ExternalLink size={32} />
|
||||
</div>
|
||||
<h3 className="font-semibold mb-2">
|
||||
{language === 'en' ? 'Prepare Documents' : 'حضر الوثائق'}
|
||||
</h3>
|
||||
<p className="opacity-90">
|
||||
{language === 'en'
|
||||
? 'Gather all required documents including transcripts and references.'
|
||||
: 'اجمع جميع الوثائق المطلوبة بما في ذلك النصوص والمراجع.'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,435 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { useAuth } from '@/components/providers/MockAuthProvider'
|
||||
import { useLanguage } from '@/components/providers/LanguageProvider'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import {
|
||||
Heart,
|
||||
MessageCircle,
|
||||
Phone,
|
||||
Calendar,
|
||||
LogOut,
|
||||
Languages,
|
||||
ArrowLeft,
|
||||
AlertTriangle,
|
||||
CheckCircle,
|
||||
Clock,
|
||||
Users
|
||||
} from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
|
||||
export default function WellbeingPage() {
|
||||
const { user, userProfile, logout } = useAuth()
|
||||
const { t, language, setLanguage } = useLanguage()
|
||||
const router = useRouter()
|
||||
const [messages, setMessages] = useState<Array<{id: string; text: string; sender: 'user' | 'bot'; timestamp: Date}>>([])
|
||||
const [inputText, setInputText] = useState('')
|
||||
const [isTyping, setIsTyping] = useState(false)
|
||||
const [showEmergencyModal, setShowEmergencyModal] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!user) {
|
||||
router.push('/')
|
||||
}
|
||||
}, [user, router])
|
||||
|
||||
useEffect(() => {
|
||||
// Start with a welcome message
|
||||
setMessages([
|
||||
{
|
||||
id: '1',
|
||||
text: 'Hello! I\'m here to support your mental health and wellbeing. How are you feeling today?',
|
||||
sender: 'bot',
|
||||
timestamp: new Date()
|
||||
}
|
||||
])
|
||||
}, [])
|
||||
|
||||
const handleSendMessage = async () => {
|
||||
if (!inputText.trim()) return
|
||||
|
||||
const userMessage = inputText.trim()
|
||||
setInputText('')
|
||||
|
||||
const newMessage = {
|
||||
id: Date.now().toString(),
|
||||
text: userMessage,
|
||||
sender: 'user' as const,
|
||||
timestamp: new Date()
|
||||
}
|
||||
|
||||
setMessages(prev => [...prev, newMessage])
|
||||
setIsTyping(true)
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/chat', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
message: userMessage,
|
||||
mode: 'mental_health',
|
||||
history: messages,
|
||||
}),
|
||||
})
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
setIsTyping(false)
|
||||
|
||||
const botMessage = {
|
||||
id: (Date.now() + 1).toString(),
|
||||
text: data.response,
|
||||
sender: 'bot' as const,
|
||||
timestamp: new Date()
|
||||
}
|
||||
|
||||
setMessages(prev => [...prev, botMessage])
|
||||
|
||||
// Check for escalation
|
||||
if (data.shouldEscalate) {
|
||||
setShowEmergencyModal(true)
|
||||
}
|
||||
} catch (error) {
|
||||
setIsTyping(false)
|
||||
console.error('Chat error:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleLogout = async () => {
|
||||
await logout()
|
||||
}
|
||||
|
||||
const toggleLanguage = () => {
|
||||
setLanguage(language === 'en' ? 'ar' : 'en')
|
||||
}
|
||||
|
||||
if (!user || !userProfile) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-pink-600 mx-auto mb-4"></div>
|
||||
<p className="text-gray-600">Loading wellbeing support...</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-pink-50 to-purple-50">
|
||||
{/* Header */}
|
||||
<header className="bg-white shadow-sm border-b">
|
||||
<div className="container mx-auto px-4 py-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center space-x-4">
|
||||
<Link href="/dashboard" className="flex items-center space-x-2 text-pink-600 hover:text-pink-800">
|
||||
<ArrowLeft size={20} />
|
||||
<span>Back to Dashboard</span>
|
||||
</Link>
|
||||
<div className="text-gray-300">|</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Heart className="text-pink-600" size={24} />
|
||||
<h1 className="text-xl font-bold text-gray-900">
|
||||
{t('wellbeing')} Support
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-4">
|
||||
<button
|
||||
onClick={toggleLanguage}
|
||||
className="flex items-center space-x-2 px-3 py-2 rounded-lg bg-gray-100 hover:bg-gray-200 transition-colors"
|
||||
>
|
||||
<Languages size={16} />
|
||||
<span className="text-sm font-medium">{language.toUpperCase()}</span>
|
||||
</button>
|
||||
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="w-8 h-8 bg-pink-600 rounded-full flex items-center justify-center">
|
||||
<span className="text-white text-sm font-medium">
|
||||
{userProfile.name.charAt(0)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="hidden md:block">
|
||||
<p className="text-sm font-medium text-gray-900">{userProfile.name}</p>
|
||||
<p className="text-xs text-gray-500">{userProfile.role}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="flex items-center space-x-2 px-3 py-2 rounded-lg bg-red-100 hover:bg-red-200 transition-colors text-red-700"
|
||||
>
|
||||
<LogOut size={16} />
|
||||
<span className="text-sm font-medium">{t('logout')}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Main Content */}
|
||||
<main className="container mx-auto px-4 py-8">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
{/* Welcome Section */}
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6 mb-8">
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-4">
|
||||
Welcome to Mental Health Support
|
||||
</h2>
|
||||
<p className="text-gray-600 mb-6">
|
||||
Your wellbeing is important to us. This is a safe space where you can talk about how you're feeling.
|
||||
Our AI assistant is trained to provide empathetic support and can connect you with professional help when needed.
|
||||
</p>
|
||||
|
||||
{/* Emergency Resources */}
|
||||
<div className="bg-red-50 border border-red-200 rounded-lg p-4 mb-6">
|
||||
<div className="flex items-start space-x-3">
|
||||
<AlertTriangle className="text-red-600 mt-0.5" size={20} />
|
||||
<div>
|
||||
<h3 className="font-medium text-red-900 mb-2">
|
||||
Emergency Resources
|
||||
</h3>
|
||||
<p className="text-sm text-red-800 mb-3">
|
||||
If you're experiencing a mental health emergency, please contact:
|
||||
</p>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Phone className="text-red-600" size={16} />
|
||||
<span className="text-sm text-red-800">Crisis Line: 988</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Phone className="text-red-600" size={16} />
|
||||
<span className="text-sm text-red-800">Campus Security: (555) 123-4567</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
{/* Chat Interface */}
|
||||
<div className="lg:col-span-2">
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 h-[600px] flex flex-col">
|
||||
<div className="p-4 border-b bg-pink-50">
|
||||
<h3 className="font-semibold text-gray-900 flex items-center space-x-2">
|
||||
<MessageCircle className="text-pink-600" size={20} />
|
||||
<span>Wellbeing Chat</span>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
{/* Messages */}
|
||||
<div className="flex-1 overflow-y-auto p-4 space-y-4">
|
||||
{messages.map((message) => (
|
||||
<div
|
||||
key={message.id}
|
||||
className={`flex ${message.sender === 'user' ? 'justify-end' : 'justify-start'}`}
|
||||
>
|
||||
<div
|
||||
className={`max-w-xs px-4 py-2 rounded-lg ${
|
||||
message.sender === 'user'
|
||||
? 'bg-pink-600 text-white'
|
||||
: 'bg-pink-100 text-pink-900'
|
||||
}`}
|
||||
>
|
||||
<p className="text-sm">{message.text}</p>
|
||||
<p className="text-xs opacity-75 mt-1">
|
||||
{message.timestamp.toLocaleTimeString()}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{isTyping && (
|
||||
<div className="flex justify-start">
|
||||
<div className="bg-pink-100 px-4 py-2 rounded-lg text-pink-900">
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="flex space-x-1">
|
||||
<div className="w-2 h-2 bg-pink-600 rounded-full animate-bounce"></div>
|
||||
<div className="w-2 h-2 bg-pink-600 rounded-full animate-bounce" style={{ animationDelay: '0.1s' }}></div>
|
||||
<div className="w-2 h-2 bg-pink-600 rounded-full animate-bounce" style={{ animationDelay: '0.2s' }}></div>
|
||||
</div>
|
||||
<span className="text-xs">Typing...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Input */}
|
||||
<div className="p-4 border-t">
|
||||
<div className="flex space-x-2">
|
||||
<input
|
||||
type="text"
|
||||
value={inputText}
|
||||
onChange={(e) => setInputText(e.target.value)}
|
||||
onKeyPress={(e) => e.key === 'Enter' && handleSendMessage()}
|
||||
placeholder={t('type_message')}
|
||||
className="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-pink-500"
|
||||
/>
|
||||
<button
|
||||
onClick={handleSendMessage}
|
||||
disabled={!inputText.trim()}
|
||||
className="bg-pink-600 text-white px-4 py-2 rounded-lg hover:bg-pink-700 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{t('send')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sidebar */}
|
||||
<div className="space-y-6">
|
||||
{/* Quick Actions */}
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
||||
<h3 className="font-semibold text-gray-900 mb-4">Quick Actions</h3>
|
||||
<div className="space-y-3">
|
||||
<button
|
||||
onClick={() => setShowEmergencyModal(true)}
|
||||
className="w-full flex items-center space-x-3 p-3 bg-red-50 border border-red-200 rounded-lg hover:bg-red-100 transition-colors"
|
||||
>
|
||||
<Phone className="text-red-600" size={20} />
|
||||
<span className="text-red-900 font-medium">Emergency Help</span>
|
||||
</button>
|
||||
|
||||
<a
|
||||
href="https://calendly.com/university-counseling"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="w-full flex items-center space-x-3 p-3 bg-blue-50 border border-blue-200 rounded-lg hover:bg-blue-100 transition-colors"
|
||||
>
|
||||
<Calendar className="text-blue-600" size={20} />
|
||||
<span className="text-blue-900 font-medium">{t('book_counselor')}</span>
|
||||
</a>
|
||||
|
||||
<Link
|
||||
href="/wellbeing/resources"
|
||||
className="w-full flex items-center space-x-3 p-3 bg-green-50 border border-green-200 rounded-lg hover:bg-green-100 transition-colors"
|
||||
>
|
||||
<Heart className="text-green-600" size={20} />
|
||||
<span className="text-green-900 font-medium">Self-Care Resources</span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Counseling Services */}
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
||||
<h3 className="font-semibold text-gray-900 mb-4">Counseling Services</h3>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center space-x-3">
|
||||
<CheckCircle className="text-green-600" size={16} />
|
||||
<span className="text-sm text-gray-700">Individual counseling</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3">
|
||||
<CheckCircle className="text-green-600" size={16} />
|
||||
<span className="text-sm text-gray-700">Group therapy sessions</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3">
|
||||
<CheckCircle className="text-green-600" size={16} />
|
||||
<span className="text-sm text-gray-700">Crisis intervention</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3">
|
||||
<CheckCircle className="text-green-600" size={16} />
|
||||
<span className="text-sm text-gray-700">Stress management workshops</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Office Hours */}
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
||||
<h3 className="font-semibold text-gray-900 mb-4">Office Hours</h3>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-gray-700">Monday - Friday</span>
|
||||
<span className="text-sm font-medium text-gray-900">8:00 AM - 6:00 PM</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-gray-700">Saturday</span>
|
||||
<span className="text-sm font-medium text-gray-900">10:00 AM - 4:00 PM</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-gray-700">Sunday</span>
|
||||
<span className="text-sm font-medium text-gray-900">Closed</span>
|
||||
</div>
|
||||
<div className="border-t pt-3">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Clock className="text-blue-600" size={16} />
|
||||
<span className="text-sm text-blue-900">24/7 Crisis Line Available</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{/* Emergency Modal */}
|
||||
{showEmergencyModal && (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
<div className="bg-white rounded-lg p-8 w-full max-w-md">
|
||||
<div className="flex items-center space-x-3 mb-6">
|
||||
<AlertTriangle className="text-red-600" size={24} />
|
||||
<h3 className="text-xl font-bold text-gray-900">Emergency Support</h3>
|
||||
</div>
|
||||
|
||||
<p className="text-gray-600 mb-6">
|
||||
If you're experiencing a mental health emergency, please contact one of these resources immediately:
|
||||
</p>
|
||||
|
||||
<div className="space-y-4 mb-6">
|
||||
<div className="flex items-center space-x-3 p-3 bg-red-50 border border-red-200 rounded-lg">
|
||||
<Phone className="text-red-600" size={20} />
|
||||
<div>
|
||||
<p className="font-medium text-red-900">Crisis Lifeline</p>
|
||||
<p className="text-sm text-red-800">988</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-3 p-3 bg-blue-50 border border-blue-200 rounded-lg">
|
||||
<Users className="text-blue-600" size={20} />
|
||||
<div>
|
||||
<p className="font-medium text-blue-900">Campus Counseling</p>
|
||||
<p className="text-sm text-blue-800">(555) 123-4567</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-3 p-3 bg-green-50 border border-green-200 rounded-lg">
|
||||
<Calendar className="text-green-600" size={20} />
|
||||
<div>
|
||||
<p className="font-medium text-green-900">Schedule Appointment</p>
|
||||
<a
|
||||
href="https://calendly.com/university-counseling"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm text-green-800 hover:underline"
|
||||
>
|
||||
Book with Counselor
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex space-x-4">
|
||||
<button
|
||||
onClick={() => setShowEmergencyModal(false)}
|
||||
className="flex-1 bg-gray-300 text-gray-700 py-2 rounded-lg hover:bg-gray-400 transition-colors"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
<a
|
||||
href="tel:988"
|
||||
className="flex-1 bg-red-600 text-white py-2 rounded-lg hover:bg-red-700 transition-colors text-center"
|
||||
>
|
||||
Call 988
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user