feat: wire Footer contact info to live site content

This commit is contained in:
belviskhoremk
2026-08-13 09:14:40 +02:00
parent 36c042bb90
commit 96c8578c2c
2 changed files with 56 additions and 4 deletions

View File

@@ -1,12 +1,44 @@
import { render, screen } from '@testing-library/react'
import { MemoryRouter } from 'react-router-dom'
import { describe, it, expect } from 'vitest'
import { describe, it, expect, vi, beforeEach } from 'vitest'
const singleMock = vi.fn()
const eqMock = vi.fn(() => ({ single: singleMock }))
const selectMock = vi.fn(() => ({ eq: eqMock }))
const fromMock = vi.fn<(table: string) => { select: typeof selectMock }>(() => ({ select: selectMock }))
vi.mock('../lib/supabase', () => ({
supabase: { from: (table: string) => fromMock(table) },
}))
vi.mock('../lib/resolveImageUrl', () => ({
resolveImageUrl: (path: string) => path,
}))
const SITE_CONTENT_ROW = {
id: 'default',
hero_headline: 'Vos cheveux, votre couronne',
hero_tagline: 'Coiffures protectrices par Mira',
about_bio: '',
about_portrait_image: '',
contact_email: 'hello@braidsbymira.com',
contact_hours: 'Mar–Sam, 9h à 18h',
}
import Footer from './Footer'
describe('Footer', () => {
it('renders contact info and quick links', () => {
beforeEach(() => {
singleMock.mockReset().mockResolvedValue({ data: SITE_CONTENT_ROW, error: null })
eqMock.mockClear()
selectMock.mockClear()
fromMock.mockClear()
})
it('renders contact info and quick links from site content', async () => {
render(<MemoryRouter><Footer /></MemoryRouter>)
expect(screen.getAllByText(/braids by mira/i).length).toBeGreaterThan(0)
expect(await screen.findByRole('link', { name: 'hello@braidsbymira.com' })).toHaveAttribute('href', 'mailto:hello@braidsbymira.com')
expect(screen.getByText('Mar–Sam, 9h à 18h')).toBeInTheDocument()
expect(screen.getByRole('link', { name: /boutique/i })).toHaveAttribute('href', '/shop')
expect(screen.getByRole('link', { name: /règlements/i })).toHaveAttribute('href', '/rules')
})

View File

@@ -1,7 +1,23 @@
import { useEffect, useState } from 'react'
import { Link } from 'react-router-dom'
import { fetchSiteContent, type SiteContent } from '../data/siteContent'
import styles from './Footer.module.css'
function Footer() {
const [content, setContent] = useState<SiteContent | null>(null)
useEffect(() => {
async function load() {
try {
setContent(await fetchSiteContent())
} catch {
// Footer content is non-critical; leave contact info blank on failure
// rather than surfacing an error on every single page.
}
}
void load()
}, [])
return (
<footer className={styles.footer}>
<div className={styles.inner}>
@@ -12,8 +28,12 @@ function Footer() {
<div className={styles.column}>
<p className={styles.columnTitle}>Contact</p>
<a href="mailto:hello@braidsbymira.com">hello@braidsbymira.com</a>
<p>Mar–Sam, 9h à 18h</p>
{content && (
<>
<a href={`mailto:${content.contactEmail}`}>{content.contactEmail}</a>
<p>{content.contactHours}</p>
</>
)}
</div>
<div className={styles.column}>