Restrict AI Studio to teachers/admins only + Save to Lesson feature

Access Control:
- AI Studio view checks user role: only SCHOOL_ADMIN, SUPER_ADMIN, TEACHER, VICE_PRINCIPAL
- Students and parents see 'Kein Zugriff' (No Access) message
- AI Studio removed from student navigation
- API routes (/api/ai/snapgen, /api/ai/perchance, /api/ai/lesson-plan, /api/ai/agent) enforce role checks

New Features:
- 'Save to Lesson' dialog in Video and Image generation tabs
  - Pick Subject > Topic > Lesson from existing curriculum
  - Saves AI video/image URL to SubjectLesson record
  - Stores prompt, provider, timestamp, and teacher who generated
  - Students will see this content in homework and lesson materials
- AI Lesson Plan API route (/api/ai/lesson-plan)
- AI Agent Chat API route (/api/ai/agent) with context-aware system prompts
- SubjectLesson model: Added aiVideoUrl, aiImageUrl, aiThumbnailUrl, aiPrompt, aiProvider, aiGeneratedAt, aiGeneratedBy
- Homework model: Added aiLessonId, aiVideoUrl, aiImageUrl for linking AI content

Navigation:
- Removed duplicate AI Studio entry from teacher nav
- Removed AI Studio from student nav (students can't access)
This commit is contained in:
Z User committed 2026-08-10 16:47:05 +00:00
1 parent 763ebef22b
commit fe3eb4a272
6 files changed
+422 -10

No files matched your search

+85
View File
@@ -0,0 +1,85 @@
// @ts-nocheck
import { NextResponse } from 'next/server';
import { db } from '@/lib/db';
import { getSession } from '@/lib/auth';
import ZAI from 'z-ai-web-dev-sdk';
/**
* AI Agent Chat API (Agentic Mode)
* POST /api/ai/agent
* Body: { message, context, language, conversationHistory }
*
* Only teachers and admins can use the agentic AI.
*/
export async function POST(request: Request) {
try {
const session = await getSession();
if (!session?.user) {
return NextResponse.json({ error: 'Not authenticated' }, { status: 401 });
}
// Only admin/teacher can use AI agent
const role = session.user.role;
if (role !== 'SCHOOL_ADMIN' && role !== 'SUPER_ADMIN' && role !== 'TEACHER' && role !== 'VICE_PRINCIPAL') {
return NextResponse.json(
{ error: 'Only teachers and admins can use the AI agent' },
{ status: 403 }
);
}
const { schoolId } = session.user;
if (!schoolId) {
return NextResponse.json({ error: 'No school assigned' }, { status: 403 });
}
const body = await request.json();
const { message, context = 'teacher-assistant', language = 'de', conversationHistory = [] } = body;
if (!message || typeof message !== 'string') {
return NextResponse.json({ error: 'Message is required' }, { status: 400 });
}
// Build system prompt based on context
const systemPrompts: Record<string, string> = {
'teacher-assistant': `Du bist ein KI-Assistent für Lehrkräfte an einer deutschen Schule.
Du hilfst bei Unterrichtsvorbereitung, Bewertungsvorschlägen, Kompetenzanalyse und Differenzierung.
Du kennst das deutsche Schulsystem (Grundschule, Gymnasium) und die kompetenzorientierte Beurteilung.
Antworte auf Deutsch, es sei denn es wird explizit anders verlangt.
Sei konkret, praktisch und praxisnah.`,
'lesson-planner': `Du bist ein Experte für Unterrichtsplanung nach dem kompetenzorientierten Ansatz.
Du erstellst strukturierte Unterrichtsentwürfe mit Phasen, Lernzielen und Differenzierung.`,
'assessment-helper': `Du bist ein Experte für Leistungsbewertung.
Du hilfst bei der Erstellung von Bewertungsrastern, Kompetenzchecklisten und Notenbegründungen.`,
};
const systemPrompt = systemPrompts[context] || systemPrompts['teacher-assistant'];
// Build messages array
const messages = [
{ role: 'system', content: systemPrompt },
...conversationHistory.map((msg: any) => ({
role: msg.role,
content: msg.content,
})),
{ role: 'user', content: message },
];
// Generate using z-ai-web-dev-sdk
const zai = await ZAI.create();
const response = await zai.chat.completions.create({
messages,
thinking: { type: 'disabled' },
});
const responseContent = response.choices?.[0]?.message?.content || 'Ich konnte keine Antwort generieren.';
return NextResponse.json({ response: responseContent });
} catch (error) {
console.error('AI Agent error:', error);
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
}
+96
View File
@@ -0,0 +1,96 @@
// @ts-nocheck
import { NextResponse } from 'next/server';
import { db } from '@/lib/db';
import { getSession } from '@/lib/auth';
import ZAI from 'z-ai-web-dev-sdk';
/**
* AI Lesson Plan Generation API
* POST /api/ai/lesson-plan
* Body: { subject, topic, classLevel, duration, language }
*
* Only teachers and admins can generate lesson plans.
*/
export async function POST(request: Request) {
try {
const session = await getSession();
if (!session?.user) {
return NextResponse.json({ error: 'Not authenticated' }, { status: 401 });
}
// Only admin/teacher can generate lesson plans
const role = session.user.role;
if (role !== 'SCHOOL_ADMIN' && role !== 'SUPER_ADMIN' && role !== 'TEACHER' && role !== 'VICE_PRINCIPAL') {
return NextResponse.json(
{ error: 'Only teachers and admins can generate lesson plans' },
{ status: 403 }
);
}
const { schoolId } = session.user;
if (!schoolId) {
return NextResponse.json({ error: 'No school assigned' }, { status: 403 });
}
// Check AI settings
const aiSettings = await db.aISettings.findUnique({
where: { schoolId },
});
if (!aiSettings) {
return NextResponse.json(
{ error: 'AI settings not configured' },
{ status: 403 }
);
}
const body = await request.json();
const { subject, topic, classLevel, duration = 45, language = 'de' } = body;
if (!topic || typeof topic !== 'string') {
return NextResponse.json({ error: 'Topic is required' }, { status: 400 });
}
// Build the lesson plan prompt
const langText = language === 'de' ? 'Deutsch' : language === 'en' ? 'Englisch' : 'Türkçe';
const prompt = `Erstelle einen detaillierten Unterrichtsentwurf auf ${langText}.
Fach: ${subject || 'Allgemein'}
Thema: ${topic}
Klassenstufe: ${classLevel || '3'}
Dauer: ${duration} Minuten
Struktur:
1. Lernziele (3-5 Ziele)
2. Einstieg/Motivation (5-10 Min.)
3. Erarbeitungsphase (15-20 Min.)
4. Übungsphase (10-15 Min.)
5. Sicherung/Reflexion (5-10 Min.)
6. Differenzierungsmöglichkeiten
7. Materialien/Medien
8. Hausaufgabe
Bitte strukturiere die Antwort mit klaren Überschriften und Bulletpoints.`;
// Generate using z-ai-web-dev-sdk
const zai = await ZAI.create();
const response = await zai.chat.completions.create({
messages: [
{ role: 'system', content: 'Du bist ein erfahrener Lehrer und Unterrichtsplaner. Du erstellst detaillierte, praxisnahe Unterrichtsentwürfe.' },
{ role: 'user', content: prompt },
],
thinking: { type: 'disabled' },
});
const content = response.choices?.[0]?.message?.content || 'Keine Antwort generiert.';
return NextResponse.json({ content });
} catch (error) {
console.error('AI Lesson Plan error:', error);
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
}
@@ -70,6 +70,14 @@ const updateLessonSchema = z.object({
difficulty: z.enum(['easy', 'medium', 'hard']).optional(),
sortOrder: z.number().int().optional(),
estimatedMinutes: z.number().int().optional().nullable(),
// AI-generated content fields
aiVideoUrl: z.string().optional().nullable(),
aiImageUrl: z.string().optional().nullable(),
aiThumbnailUrl: z.string().optional().nullable(),
aiPrompt: z.string().optional().nullable(),
aiProvider: z.string().optional().nullable(),
aiGeneratedAt: z.date().optional().nullable(),
aiGeneratedBy: z.string().optional().nullable(),
});
async function updateLesson(