diff --git a/src/app/api/admin/team/reorder/route.ts b/src/app/api/admin/team/reorder/route.ts index 5489a22..f9d1368 100644 --- a/src/app/api/admin/team/reorder/route.ts +++ b/src/app/api/admin/team/reorder/route.ts @@ -5,8 +5,8 @@ import { revalidatePath } from "next/cache"; export async function PUT(request: NextRequest) { const { ids } = await request.json() as { ids: number[] }; - if (!Array.isArray(ids) || ids.length === 0 || !ids.every((id) => Number.isInteger(id) && id > 0)) { - return NextResponse.json({ error: "ids must be a non-empty array of positive integers" }, { status: 400 }); + if (!Array.isArray(ids) || ids.length === 0 || ids.length > 100 || !ids.every((id) => Number.isInteger(id) && id > 0)) { + return NextResponse.json({ error: "ids must be a non-empty array of positive integers (max 100)" }, { status: 400 }); } reorderTeamMembers(ids); diff --git a/src/app/api/logout/route.ts b/src/app/api/logout/route.ts index 32bd69b..5b936a5 100644 --- a/src/app/api/logout/route.ts +++ b/src/app/api/logout/route.ts @@ -1,5 +1,5 @@ import { NextResponse } from "next/server"; -import { COOKIE_NAME } from "@/lib/auth"; +import { COOKIE_NAME, CSRF_COOKIE_NAME } from "@/lib/auth"; export async function POST() { const response = NextResponse.json({ ok: true }); @@ -8,5 +8,9 @@ export async function POST() { path: "/", maxAge: 0, }); + response.cookies.set(CSRF_COOKIE_NAME, "", { + path: "/", + maxAge: 0, + }); return response; } diff --git a/src/app/api/open-day-register/route.ts b/src/app/api/open-day-register/route.ts index 0e889c6..81cfea1 100644 --- a/src/app/api/open-day-register/route.ts +++ b/src/app/api/open-day-register/route.ts @@ -1,7 +1,6 @@ import { NextRequest, NextResponse } from "next/server"; import { addOpenDayBooking, - isOpenDayClassBookedByPhone, getPersonOpenDayBookings, getOpenDayEvent, } from "@/lib/db"; @@ -35,11 +34,6 @@ export async function POST(request: NextRequest) { return NextResponse.json({ error: "Телефон обязателен" }, { status: 400 }); } - // Check not already booked - if (isOpenDayClassBookedByPhone(classId, cleanPhone)) { - return NextResponse.json({ error: "Вы уже записаны на это занятие" }, { status: 409 }); - } - const id = addOpenDayBooking(classId, eventId, { name: cleanName, phone: cleanPhone, diff --git a/src/app/styles/animations.css b/src/app/styles/animations.css index b0902a2..26c2e38 100644 --- a/src/app/styles/animations.css +++ b/src/app/styles/animations.css @@ -128,6 +128,7 @@ filter: blur(80px); animation: pulse-glow 6s ease-in-out infinite; pointer-events: none; + will-change: filter, transform; } /* ===== Gradient Text ===== */ @@ -322,6 +323,7 @@ mask-composite: exclude; pointer-events: none; z-index: 1; + will-change: background-position; } /* ===== Notification Pulse ===== */ diff --git a/src/components/sections/Schedule.tsx b/src/components/sections/Schedule.tsx index b90cade..560ea3a 100644 --- a/src/components/sections/Schedule.tsx +++ b/src/components/sections/Schedule.tsx @@ -222,6 +222,20 @@ export function Schedule({ data: schedule, classItems }: ScheduleProps) { dispatch({ type: "SET_LOCATION", mode }); } + const gridLayout = useMemo(() => { + const len = filteredDays.length; + const cls = len >= 7 ? "sm:grid-cols-2 lg:grid-cols-4 xl:grid-cols-7" + : len >= 6 ? "sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6" + : len >= 4 ? "sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5" + : len === 3 ? "sm:grid-cols-2 lg:grid-cols-3" + : len === 2 ? "sm:grid-cols-2" + : "justify-items-center"; + const style = len === 1 ? undefined + : len <= 3 && len > 0 ? { maxWidth: len * 340 + (len - 1) * 12, marginInline: "auto" as const } + : undefined; + return { cls, style }; + }, [filteredDays.length]); + const activeTabClass = "bg-gold text-black shadow-[0_0_20px_rgba(201,169,110,0.3)]"; const inactiveTabClass = "border border-neutral-300 text-neutral-500 hover:border-neutral-400 hover:text-neutral-700 dark:border-white/10 dark:text-neutral-400 dark:hover:text-white dark:hover:border-white/20"; @@ -349,8 +363,8 @@ export function Schedule({ data: schedule, classItems }: ScheduleProps) { {/* Desktop: grid layout */}