- cmdk 1.1.1: Cmd/Ctrl+K command menu in the admin panel (students, courses, lessons, admin sections) plus a "Search" button in the sidebar. New read-only endpoint GET /api/admin/search with the same admin role check as /api/admin/export-users; query length 2-100 chars. - vaul 1.1.2: ResponsiveDialog wrapper (Base UI Dialog on desktop, bottom drawer below 768px) styled per DS-2. Used for the storefront checkout on the dashboard and for course navigation on phones (tablet and desktop unchanged). - @number-flow/react 0.6.2: animated "N of M lessons" and completion percent in course navigation; respects prefers-reduced-motion. No migrations. Not deployed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
62 lines
1.8 KiB
TypeScript
62 lines
1.8 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { headers } from "next/headers";
|
|
import { auth } from "@/lib/auth";
|
|
import { prisma } from "@/lib/prisma";
|
|
|
|
// Read-only lookup for the admin command menu (Ctrl/⌘+K).
|
|
const MIN_QUERY = 2;
|
|
const MAX_QUERY = 100;
|
|
|
|
export async function GET(request: NextRequest) {
|
|
const session = await auth.api.getSession({ headers: await headers() });
|
|
if (!session || session.user.role !== "admin") {
|
|
return NextResponse.json({ error: "Нет доступа" }, { status: 403 });
|
|
}
|
|
|
|
const q = (request.nextUrl.searchParams.get("q") ?? "").trim().slice(0, MAX_QUERY);
|
|
if (q.length < MIN_QUERY) {
|
|
return NextResponse.json({ users: [], courses: [], lessons: [] });
|
|
}
|
|
|
|
const contains = { contains: q, mode: "insensitive" as const };
|
|
|
|
const [users, courses, lessons] = await Promise.all([
|
|
prisma.user.findMany({
|
|
where: { OR: [{ name: contains }, { email: contains }] },
|
|
select: { id: true, name: true, email: true, role: true },
|
|
orderBy: { createdAt: "desc" },
|
|
take: 8,
|
|
}),
|
|
prisma.course.findMany({
|
|
where: { OR: [{ title: contains }, { slug: contains }] },
|
|
select: { id: true, title: true, slug: true, published: true },
|
|
orderBy: { order: "asc" },
|
|
take: 6,
|
|
}),
|
|
prisma.lesson.findMany({
|
|
where: { title: contains },
|
|
select: {
|
|
id: true,
|
|
title: true,
|
|
published: true,
|
|
module: { select: { id: true, courseId: true, course: { select: { title: true } } } },
|
|
},
|
|
orderBy: { updatedAt: "desc" },
|
|
take: 8,
|
|
}),
|
|
]);
|
|
|
|
return NextResponse.json({
|
|
users,
|
|
courses,
|
|
lessons: lessons.map((l) => ({
|
|
id: l.id,
|
|
title: l.title,
|
|
published: l.published,
|
|
moduleId: l.module.id,
|
|
courseId: l.module.courseId,
|
|
courseTitle: l.module.course.title,
|
|
})),
|
|
});
|
|
}
|