refactor: comprehensive frontend review — consistency, a11y, code quality
- Replace event dispatchers with BookingContext (Hero, Header, FloatingContact) - Add focus trap hook for modals (SignupModal, NewsModal) - Extract shared components: CollapsibleSection, ConfirmDialog, PriceField, AdminSkeleton - Add delete confirmation dialog to ArrayEditor - Replace hardcoded colors (#050505, #0a0a0a, #c9a96e, #2ecc71) with theme tokens - Add CSS variables --color-surface-deep/dark for consistent dark surfaces - Improve contrast: muted text neutral-500 → neutral-400 in dark mode - Fix modal z-index hierarchy (modals z-60, header z-50, floats z-40) - Consolidate duplicate formatDate → shared formatting.ts - Add useMemo to TeamProfile groupMap computation - Fix typography: responsive price text in Pricing section - Add ARIA labels/expanded to FAQ, OpenDay, ArrayEditor grip handles - Hide number input spinners globally - Reorder admin sidebar: Dashboard → SEO → Bookings → site section order - Use shared PriceField in Open Day editor - Fix schedule grid first time slot (09:00) clipped by container - Fix pre-existing type errors (bookings, hero, db interfaces)
This commit is contained in:
@@ -4,6 +4,7 @@ import { useState, useEffect, useCallback } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { X, CheckCircle, Send, Phone as PhoneIcon, Instagram } from "lucide-react";
|
||||
import { BRAND } from "@/lib/constants";
|
||||
import { useFocusTrap } from "@/hooks/useFocusTrap";
|
||||
|
||||
interface SignupModalProps {
|
||||
open: boolean;
|
||||
@@ -47,6 +48,7 @@ export function SignupModal({
|
||||
const [error, setError] = useState("");
|
||||
const [success, setSuccess] = useState(false);
|
||||
const [successData, setSuccessData] = useState<Record<string, unknown> | null>(null);
|
||||
const focusTrapRef = useFocusTrap<HTMLDivElement>(open);
|
||||
|
||||
function handlePhoneChange(raw: string) {
|
||||
let digits = raw.replace(/\D/g, "");
|
||||
@@ -141,10 +143,11 @@ export function SignupModal({
|
||||
if (!open) return null;
|
||||
|
||||
return createPortal(
|
||||
<div className="modal-overlay fixed inset-0 z-50 flex items-center justify-center p-4" role="dialog" aria-modal="true" aria-label={title} onClick={handleClose}>
|
||||
<div className="modal-overlay fixed inset-0 z-[60] flex items-center justify-center p-4" role="dialog" aria-modal="true" aria-label={title} onClick={handleClose}>
|
||||
<div className="absolute inset-0 bg-black/70 backdrop-blur-sm" />
|
||||
<div
|
||||
className="modal-content relative w-full max-w-md rounded-2xl border border-white/[0.08] bg-[#0a0a0a] p-6 sm:p-8 shadow-2xl"
|
||||
ref={focusTrapRef}
|
||||
className="modal-content relative w-full max-w-md rounded-2xl border border-white/[0.08] bg-neutral-950 p-6 sm:p-8 shadow-2xl"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user