fix: security hardening, UI fixes, and validation improvements
- Fix header nav overflow by switching to lg: breakpoint with tighter gaps - Fix file upload path traversal by whitelisting allowed folders and extensions - Fix BookingModal using hardcoded content instead of DB-backed data - Add input length validation on public master-class registration API - Add ID validation on team member and reorder API routes - Fix BookingModal useCallback missing groupInfo/contact dependencies - Improve admin news date field to use native date picker - Add missing Мастер-классы and Новости cards to admin dashboard Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -100,14 +100,14 @@ export function Header() {
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
<nav className="hidden items-center gap-8 md:flex">
|
||||
<nav className="hidden items-center gap-3 lg:gap-5 xl:gap-6 lg:flex">
|
||||
{visibleLinks.map((link) => {
|
||||
const isActive = activeSection === link.href.replace("#", "");
|
||||
return (
|
||||
<a
|
||||
key={link.href}
|
||||
href={link.href}
|
||||
className={`relative py-1 text-sm font-medium transition-all duration-300 after:absolute after:bottom-0 after:left-0 after:h-[2px] after:bg-gold after:transition-all after:duration-300 ${
|
||||
className={`relative whitespace-nowrap py-1 text-xs lg:text-sm font-medium transition-all duration-300 after:absolute after:bottom-0 after:left-0 after:h-[2px] after:bg-gold after:transition-all after:duration-300 ${
|
||||
isActive
|
||||
? "text-gold-light after:w-full"
|
||||
: "text-neutral-400 after:w-0 hover:text-white hover:after:w-full"
|
||||
@@ -125,7 +125,7 @@ export function Header() {
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<div className="flex items-center gap-2 md:hidden">
|
||||
<div className="flex items-center gap-2 lg:hidden">
|
||||
<button
|
||||
onClick={() => setMenuOpen(!menuOpen)}
|
||||
aria-label="Меню"
|
||||
@@ -138,7 +138,7 @@ export function Header() {
|
||||
|
||||
{/* Mobile menu */}
|
||||
<div
|
||||
className={`overflow-hidden transition-all duration-300 md:hidden ${
|
||||
className={`overflow-hidden transition-all duration-300 lg:hidden ${
|
||||
menuOpen ? "max-h-80 opacity-100" : "max-h-0 opacity-0"
|
||||
}`}
|
||||
>
|
||||
@@ -175,7 +175,7 @@ export function Header() {
|
||||
{/* Floating booking button — visible on scroll, mobile */}
|
||||
<button
|
||||
onClick={() => setBookingOpen(true)}
|
||||
className={`fixed bottom-6 right-6 z-40 flex items-center gap-2 rounded-full bg-gold px-5 py-3 text-sm font-semibold text-black shadow-lg shadow-gold/25 transition-all duration-500 hover:bg-gold-light hover:shadow-xl hover:shadow-gold/30 cursor-pointer md:hidden ${
|
||||
className={`fixed bottom-6 right-6 z-40 flex items-center gap-2 rounded-full bg-gold px-5 py-3 text-sm font-semibold text-black shadow-lg shadow-gold/25 transition-all duration-500 hover:bg-gold-light hover:shadow-xl hover:shadow-gold/30 cursor-pointer lg:hidden ${
|
||||
scrolled ? "translate-y-0 opacity-100" : "translate-y-16 opacity-0 pointer-events-none"
|
||||
}`}
|
||||
>
|
||||
|
||||
@@ -3,16 +3,22 @@
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { X, Instagram, Send, CheckCircle, Phone } from "lucide-react";
|
||||
import { siteContent } from "@/data/content";
|
||||
import { BRAND } from "@/lib/constants";
|
||||
|
||||
interface BookingModalProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
groupInfo?: string;
|
||||
contact?: { instagram: string; phone: string };
|
||||
}
|
||||
|
||||
export function BookingModal({ open, onClose, groupInfo }: BookingModalProps) {
|
||||
const { contact } = siteContent;
|
||||
const DEFAULT_CONTACT = {
|
||||
instagram: BRAND.instagram,
|
||||
phone: "+375 29 389-70-01",
|
||||
};
|
||||
|
||||
export function BookingModal({ open, onClose, groupInfo, contact: contactProp }: BookingModalProps) {
|
||||
const contact = contactProp ?? DEFAULT_CONTACT;
|
||||
const [name, setName] = useState("");
|
||||
const [phone, setPhone] = useState("+375 ");
|
||||
|
||||
@@ -72,7 +78,7 @@ export function BookingModal({ open, onClose, groupInfo }: BookingModalProps) {
|
||||
window.open(instagramUrl, "_blank");
|
||||
setSubmitted(true);
|
||||
},
|
||||
[name, phone]
|
||||
[name, phone, groupInfo, contact]
|
||||
);
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
|
||||
Reference in New Issue
Block a user