From cbcefbc5596d2132cf32c024d662705e721ad1d2 Mon Sep 17 00:00:00 2001 From: belviskhoremk Date: Mon, 3 Aug 2026 20:20:22 +0000 Subject: [PATCH] feat: add Home landing page assembling all sections --- src/App.test.tsx | 6 +-- src/App.tsx | 3 +- src/pages/Home.module.css | 53 ++++++++++++++++++++++ src/pages/Home.test.tsx | 40 +++++++++++++++++ src/pages/Home.tsx | 95 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 192 insertions(+), 5 deletions(-) create mode 100644 src/pages/Home.module.css create mode 100644 src/pages/Home.test.tsx create mode 100644 src/pages/Home.tsx diff --git a/src/App.test.tsx b/src/App.test.tsx index 65e7b30..94e4238 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -3,11 +3,9 @@ import { describe, it, expect } from 'vitest' import App from './App' describe('App routing', () => { - it('renders the home placeholder at / with nav and footer', () => { + it('renders the Home page at /', () => { render() - expect(screen.getByTestId('page-home')).toBeInTheDocument() - expect(screen.getByRole('navigation')).toBeInTheDocument() - expect(screen.getByText(/hello@braidsbymira.com/)).toBeInTheDocument() + expect(screen.getByText(/your hair, your crown/i)).toBeInTheDocument() }) it('renders the Shop page at /shop', () => { diff --git a/src/App.tsx b/src/App.tsx index 61356c9..5ace58c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,6 @@ import { BrowserRouter, MemoryRouter, Routes, Route } from 'react-router-dom' import Layout from './components/Layout' +import Home from './pages/Home' import Shop from './pages/Shop' import Book from './pages/Book' import Rules from './pages/Rules' @@ -12,7 +13,7 @@ function AppRoutes() { return ( }> - } /> + } /> } /> } /> } /> diff --git a/src/pages/Home.module.css b/src/pages/Home.module.css new file mode 100644 index 0000000..2cc3bce --- /dev/null +++ b/src/pages/Home.module.css @@ -0,0 +1,53 @@ +.hero { + text-align: center; + padding: var(--space-xl) var(--space-lg); +} + +.headline { + font-family: var(--font-script); + font-size: 3rem; + margin: var(--space-md) 0 0; +} + +.tagline { + font-size: 0.75rem; + letter-spacing: 3px; + text-transform: uppercase; + color: #6b6259; +} + +.ctas { + margin-top: var(--space-lg); + display: flex; + gap: var(--space-md); + justify-content: center; +} + +.section { + padding: var(--space-xl) var(--space-lg); + text-align: center; +} + +.altSection { + composes: section; + background: var(--color-cream-alt); +} + +.row { + display: flex; + gap: var(--space-md); + justify-content: center; + flex-wrap: wrap; + margin-top: var(--space-lg); +} + +.banner { + background: var(--color-charcoal); + color: var(--color-cream); + padding: var(--space-xl) var(--space-lg); + text-align: center; +} + +.banner a { + color: var(--color-gold); +} diff --git a/src/pages/Home.test.tsx b/src/pages/Home.test.tsx new file mode 100644 index 0000000..6f28a22 --- /dev/null +++ b/src/pages/Home.test.tsx @@ -0,0 +1,40 @@ +import { render, screen } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { MemoryRouter, Routes, Route } from 'react-router-dom' +import { describe, it, expect } from 'vitest' +import Home from './Home' +import Book from './Book' +import { services } from '../data/services' + +function renderHome() { + return render( + + + } /> + } /> + + , + ) +} + +describe('Home page', () => { + it('renders the hero headline and all four signature styles', () => { + renderHome() + expect(screen.getByText(/your hair, your crown/i)).toBeInTheDocument() + for (const service of services) { + expect(screen.getByText(service.name)).toBeInTheDocument() + } + }) + + it('renders shop highlights and a link to the rules page', () => { + renderHome() + expect(screen.getByRole('link', { name: /shop all/i })).toHaveAttribute('href', '/shop') + expect(screen.getByRole('link', { name: /read the rules|salon rules/i })).toHaveAttribute('href', '/rules') + }) + + it('navigates to /book with the chosen service preselected when a signature style is clicked', async () => { + renderHome() + await userEvent.click(screen.getByRole('button', { name: new RegExp(services[0].name) })) + expect(screen.getByLabelText(/choose a date/i)).toBeInTheDocument() + }) +}) diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx new file mode 100644 index 0000000..fea9a47 --- /dev/null +++ b/src/pages/Home.tsx @@ -0,0 +1,95 @@ +import { Link, useNavigate } from 'react-router-dom' +import Hero3D from '../components/Hero3D' +import ScrollReveal from '../components/ScrollReveal' +import ServiceCard from '../components/ServiceCard' +import { services } from '../data/services' +import { products } from '../data/products' +import styles from './Home.module.css' + +const TESTIMONIALS = [ + { quote: 'Mira made me feel like royalty in her chair. My braids lasted 8 weeks!', author: 'Amara T.' }, + { quote: 'The most gentle, precise braider I have ever been to.', author: 'Keisha R.' }, + { quote: 'Booking was so easy and the results speak for themselves.', author: 'Nadia F.' }, +] + +function Home() { + const navigate = useNavigate() + + return ( +
+
+ +

Your hair, your crown

+

Protective Styles by Mira

+
+ Book Now + Shop Products +
+
+ + +
+

About Mira

+

+ With over a decade behind the chair, Mira treats every install as an art form — + gentle on the scalp, precise at the root, and finished with a crown you'll want + to show off. +

+
+
+ + +
+

Signature Styles

+
+ {services.map((service) => ( + navigate('/book', { state: { serviceId } })} + /> + ))} +
+
+
+ + +
+

Shop Highlights

+
+ {products.filter((p) => p.inStock).slice(0, 3).map((product) => ( +
+

{product.name}

+

${product.price}

+
+ ))} +
+

Shop All

+
+
+ + +
+

Client Love

+
+ {TESTIMONIALS.map((t) => ( +
+

“{t.quote}”

+ {t.author} +
+ ))} +
+
+
+ +
+

Before You Book

+

+ Please take a moment to read the salon rules before booking your appointment. +

+
+
+ ) +} + +export default Home