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:
2026-03-26 19:45:37 +03:00
parent ec08f8e8d5
commit 76307e298b
32 changed files with 613 additions and 319 deletions
+27 -24
View File
@@ -12,6 +12,7 @@ import { BackToTop } from "@/components/ui/BackToTop";
import { FloatingContact } from "@/components/ui/FloatingContact";
import { Header } from "@/components/layout/Header";
import { Footer } from "@/components/layout/Footer";
import { ClientShell } from "@/components/layout/ClientShell";
import { getContent } from "@/lib/content";
export const dynamic = "force-dynamic";
@@ -29,30 +30,32 @@ export default function HomePage() {
return (
<>
<Header />
<main>
<Hero data={content.hero} />
<About
data={content.about}
stats={{
trainers: content.team.members.length,
classes: content.classes.items.length,
locations: content.schedule.locations.length,
}}
/>
<Classes data={content.classes} />
<Team data={content.team} schedule={content.schedule.locations} />
{openDayData && <OpenDay data={openDayData} popups={content.popups} teamMembers={content.team.members} />}
<Schedule data={content.schedule} classItems={content.classes.items} teamMembers={content.team.members} />
<Pricing data={content.pricing} />
<MasterClasses data={content.masterClasses} regCounts={mcRegCounts} popups={content.popups} />
<News data={content.news} />
<FAQ data={content.faq} />
<Contact data={content.contact} />
<BackToTop />
<FloatingContact />
</main>
<Footer />
<ClientShell>
<Header />
<main>
<Hero data={content.hero} />
<About
data={content.about}
stats={{
trainers: content.team.members.length,
classes: content.classes.items.length,
locations: content.schedule.locations.length,
}}
/>
<Classes data={content.classes} />
<Team data={content.team} schedule={content.schedule.locations} />
{openDayData && <OpenDay data={openDayData} popups={content.popups} teamMembers={content.team.members} />}
<Schedule data={content.schedule} classItems={content.classes.items} teamMembers={content.team.members} />
<Pricing data={content.pricing} />
<MasterClasses data={content.masterClasses} regCounts={mcRegCounts} popups={content.popups} />
<News data={content.news} />
<FAQ data={content.faq} />
<Contact data={content.contact} />
<BackToTop />
<FloatingContact />
</main>
<Footer />
</ClientShell>
</>
);
}