Initial commit: ExaMetra v1.0.0 — Free & open-source competence & grading platform
Features: - 15+ views: Dashboard, Classes, Progress, Flower, Matrix, Assessments, Grading, Reports, Attendance, Calendar, Lesson Plans, Analytics, Parent Communication, Behavior Tracking, Settings - 34+ API endpoints - 30+ Prisma models - 700+ i18n keys (German + English) - Keyboard shortcuts (Cmd+K, Cmd+1-9, Cmd+/) - Batch deletion, drag-and-drop seating, custom color palette - Demo accounts (admin, teacher, student, parent) - Dark mode, responsive design, print styles - CSV/PDF export, notification system, onboarding tour - Zero emojis — all replaced with Lucide icons - MIT License
This commit is contained in:
commit
0c1f739989
156 files changed
+51090
No files matched your search
@@ -0,0 +1,96 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { db } from '@/lib/db';
|
||||
import { getSession } from '@/lib/auth';
|
||||
|
||||
// PUT /api/demo-accounts/[id] — Toggle a single demo user's active status
|
||||
export async function PUT(
|
||||
request: Request,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
try {
|
||||
const session = await getSession();
|
||||
if (!session?.user) {
|
||||
return NextResponse.json({ error: 'Not authenticated' }, { status: 401 });
|
||||
}
|
||||
|
||||
const role = session.user.role;
|
||||
if (role !== 'SCHOOL_ADMIN' && role !== 'SUPER_ADMIN') {
|
||||
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
|
||||
}
|
||||
|
||||
const { id } = await params;
|
||||
const body = await request.json();
|
||||
const { action } = body; // 'enable' or 'disable'
|
||||
|
||||
const user = await db.user.findUnique({ where: { id } });
|
||||
if (!user || !user.isDemo) {
|
||||
return NextResponse.json({ error: 'Demo account not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
if (action === 'disable') {
|
||||
await db.user.update({
|
||||
where: { id },
|
||||
data: { deletedAt: new Date() },
|
||||
});
|
||||
} else if (action === 'enable') {
|
||||
await db.user.update({
|
||||
where: { id },
|
||||
data: { deletedAt: null },
|
||||
});
|
||||
} else {
|
||||
return NextResponse.json({ error: 'Invalid action. Use "enable" or "disable"' }, { status: 400 });
|
||||
}
|
||||
|
||||
const updated = await db.user.findUnique({
|
||||
where: { id },
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
role: true,
|
||||
isDemo: true,
|
||||
deletedAt: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
});
|
||||
|
||||
return NextResponse.json({ success: true, account: updated });
|
||||
} catch (error) {
|
||||
console.error('Demo account PUT error:', error);
|
||||
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE /api/demo-accounts/[id] — Permanently delete a single demo user
|
||||
export async function DELETE(
|
||||
_request: Request,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
try {
|
||||
const session = await getSession();
|
||||
if (!session?.user) {
|
||||
return NextResponse.json({ error: 'Not authenticated' }, { status: 401 });
|
||||
}
|
||||
|
||||
const role = session.user.role;
|
||||
if (role !== 'SCHOOL_ADMIN' && role !== 'SUPER_ADMIN') {
|
||||
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
|
||||
}
|
||||
|
||||
const { id } = await params;
|
||||
|
||||
const user = await db.user.findUnique({ where: { id } });
|
||||
if (!user || !user.isDemo) {
|
||||
return NextResponse.json({ error: 'Demo account not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
await db.user.delete({ where: { id } });
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Demo account DELETE error:', error);
|
||||
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { db } from '@/lib/db';
|
||||
import { getSession } from '@/lib/auth';
|
||||
|
||||
// GET /api/demo-accounts — List all demo users
|
||||
export async function GET() {
|
||||
try {
|
||||
const session = await getSession();
|
||||
if (!session?.user) {
|
||||
return NextResponse.json({ error: 'Not authenticated' }, { status: 401 });
|
||||
}
|
||||
|
||||
const role = session.user.role;
|
||||
if (role !== 'SCHOOL_ADMIN' && role !== 'SUPER_ADMIN') {
|
||||
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
|
||||
}
|
||||
|
||||
const demoUsers = await db.user.findMany({
|
||||
where: { isDemo: true },
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
role: true,
|
||||
isDemo: true,
|
||||
deletedAt: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
orderBy: { createdAt: 'asc' },
|
||||
});
|
||||
|
||||
return NextResponse.json(demoUsers);
|
||||
} catch (error) {
|
||||
console.error('Demo accounts GET error:', error);
|
||||
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
// PUT /api/demo-accounts — Toggle all demo accounts (enable/disable)
|
||||
export async function PUT(request: Request) {
|
||||
try {
|
||||
const session = await getSession();
|
||||
if (!session?.user) {
|
||||
return NextResponse.json({ error: 'Not authenticated' }, { status: 401 });
|
||||
}
|
||||
|
||||
const role = session.user.role;
|
||||
if (role !== 'SCHOOL_ADMIN' && role !== 'SUPER_ADMIN') {
|
||||
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
|
||||
}
|
||||
|
||||
const body = await request.json();
|
||||
const { action } = body; // 'enable' or 'disable'
|
||||
|
||||
if (action === 'disable') {
|
||||
await db.user.updateMany({
|
||||
where: { isDemo: true, deletedAt: null },
|
||||
data: { deletedAt: new Date() },
|
||||
});
|
||||
} else if (action === 'enable') {
|
||||
await db.user.updateMany({
|
||||
where: { isDemo: true, deletedAt: { not: null } },
|
||||
data: { deletedAt: null },
|
||||
});
|
||||
} else {
|
||||
return NextResponse.json({ error: 'Invalid action. Use "enable" or "disable"' }, { status: 400 });
|
||||
}
|
||||
|
||||
const demoUsers = await db.user.findMany({
|
||||
where: { isDemo: true },
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
role: true,
|
||||
isDemo: true,
|
||||
deletedAt: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
orderBy: { createdAt: 'asc' },
|
||||
});
|
||||
|
||||
return NextResponse.json({ success: true, accounts: demoUsers });
|
||||
} catch (error) {
|
||||
console.error('Demo accounts PUT error:', error);
|
||||
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE /api/demo-accounts — Permanently delete all demo users
|
||||
export async function DELETE() {
|
||||
try {
|
||||
const session = await getSession();
|
||||
if (!session?.user) {
|
||||
return NextResponse.json({ error: 'Not authenticated' }, { status: 401 });
|
||||
}
|
||||
|
||||
const role = session.user.role;
|
||||
if (role !== 'SCHOOL_ADMIN' && role !== 'SUPER_ADMIN') {
|
||||
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
|
||||
}
|
||||
|
||||
const result = await db.user.deleteMany({
|
||||
where: { isDemo: true },
|
||||
});
|
||||
|
||||
return NextResponse.json({ success: true, deleted: result.count });
|
||||
} catch (error) {
|
||||
console.error('Demo accounts DELETE error:', error);
|
||||
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user