fix: HIGH priority — scroll debounce, timing-safe auth, a11y, error logging, cleanup dead modals

- Header: throttle scroll handler via requestAnimationFrame (was firing 60+/sec)
- Auth: use crypto.timingSafeEqual for password and token signature comparison
- A11y: add role="dialog", aria-modal, aria-label to all modals (SignupModal, NewsModal, TeamProfile lightbox)
- A11y: add aria-label to close buttons, menu toggle (with aria-expanded), floating CTA
- A11y: add aria-label to MC Instagram buttons
- Error logging: add console.error with route names to all API catch blocks (admin + public)
- Fix open-day-register error leak (was returning raw DB error to client)
- Fix MasterClasses key={index} → key={item.title}
- Delete 3 unused modal components (BookingModal, MasterClassSignupModal, OpenDaySignupModal) — replaced by unified SignupModal

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-19 14:01:21 +03:00
parent 127990e532
commit 66dce3f8f5
19 changed files with 56 additions and 638 deletions

View File

@@ -163,6 +163,7 @@ function MasterClassCard({
onClick={() =>
window.open(item.instagramUrl, "_blank", "noopener,noreferrer")
}
aria-label={`Instagram ${item.trainer}`}
className="flex h-[46px] w-[46px] items-center justify-center rounded-xl border border-white/10 text-white/40 transition-all hover:border-gold/30 hover:text-gold cursor-pointer"
>
<Instagram size={18} />
@@ -225,9 +226,9 @@ export function MasterClasses({ data }: MasterClassesProps) {
) : (
<Reveal>
<div className="mx-auto mt-10 grid max-w-5xl grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
{upcoming.map((item, i) => (
{upcoming.map((item) => (
<MasterClassCard
key={i}
key={item.title}
item={item}
onSignup={() => setSignupTitle(item.title)}
/>

View File

@@ -309,10 +309,14 @@ export function TeamProfile({ member, onBack, schedule }: TeamProfileProps) {
{lightbox && (
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm p-4"
role="dialog"
aria-modal="true"
aria-label="Просмотр изображения"
onClick={() => setLightbox(null)}
>
<button
onClick={() => setLightbox(null)}
aria-label="Закрыть"
className="absolute top-4 right-4 rounded-full bg-white/10 p-2 text-white hover:bg-white/20 transition-colors"
>
<X size={20} />