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:
@@ -1,9 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { ChevronDown, Plus, X, AlertCircle, Check } from "lucide-react";
|
||||
import { Plus, X, AlertCircle, Check } from "lucide-react";
|
||||
import { SectionEditor } from "../_components/SectionEditor";
|
||||
import { InputField } from "../_components/FormField";
|
||||
import { CollapsibleSection } from "../_components/CollapsibleSection";
|
||||
import type { ContactInfo } from "@/types/content";
|
||||
|
||||
// --- Phone input with mask ---
|
||||
@@ -160,45 +161,6 @@ function AddressList({ items, onChange }: { items: string[]; onChange: (items: s
|
||||
);
|
||||
}
|
||||
|
||||
// --- Collapsible section ---
|
||||
function CollapsibleSection({
|
||||
title,
|
||||
defaultOpen = true,
|
||||
children,
|
||||
}: {
|
||||
title: string;
|
||||
defaultOpen?: boolean;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const [open, setOpen] = useState(defaultOpen);
|
||||
|
||||
return (
|
||||
<div className="rounded-xl border border-white/10 bg-neutral-900/30 overflow-hidden">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen(!open)}
|
||||
className="flex items-center justify-between w-full px-5 py-3.5 text-left cursor-pointer group hover:bg-white/[0.02] transition-colors"
|
||||
>
|
||||
<h3 className="text-sm font-semibold text-neutral-200 group-hover:text-white transition-colors">{title}</h3>
|
||||
<ChevronDown
|
||||
size={16}
|
||||
className={`text-neutral-500 transition-transform duration-200 ${open ? "rotate-180" : ""}`}
|
||||
/>
|
||||
</button>
|
||||
<div
|
||||
className="grid transition-[grid-template-rows] duration-300 ease-out"
|
||||
style={{ gridTemplateRows: open ? "1fr" : "0fr" }}
|
||||
>
|
||||
<div className="overflow-hidden">
|
||||
<div className="px-5 pb-5 space-y-4">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ContactEditorPage() {
|
||||
return (
|
||||
<SectionEditor<ContactInfo> sectionKey="contact" title="Контакты">
|
||||
|
||||
Reference in New Issue
Block a user