main
This commit is contained in:
@@ -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 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user