feat: add fetchSiteContent data function

This commit is contained in:
belviskhoremk
2026-08-13 09:01:18 +02:00
parent 728fc31e83
commit c5a2b9dfae
2 changed files with 93 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
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 }))
const resolveImageUrlMock = vi.fn((path: string) => `https://cdn.example/${path}`)
vi.mock('../lib/supabase', () => ({
supabase: { from: (table: string) => fromMock(table) },
}))
vi.mock('../lib/resolveImageUrl', () => ({
resolveImageUrl: (path: string) => resolveImageUrlMock(path),
}))
const ROW = {
id: 'default',
hero_headline: 'Vos cheveux, votre couronne',
hero_tagline: 'Coiffures protectrices par Mira',
about_bio: 'Bio text.',
about_portrait_image: 'about/mira-portrait.jpg',
contact_email: 'hello@braidsbymira.com',
contact_hours: 'Mar–Sam, 9h à 18h',
}
import { fetchSiteContent, type SiteContent } from './siteContent'
describe('fetchSiteContent', () => {
beforeEach(() => {
singleMock.mockReset()
eqMock.mockClear()
selectMock.mockClear()
fromMock.mockClear()
resolveImageUrlMock.mockClear()
})
it('fetches the default row and maps snake_case columns to the SiteContent shape', async () => {
singleMock.mockResolvedValue({ data: ROW, error: null })
const content = await fetchSiteContent()
expect(fromMock).toHaveBeenCalledWith('site_content')
expect(eqMock).toHaveBeenCalledWith('id', 'default')
expect(resolveImageUrlMock).toHaveBeenCalledWith('about/mira-portrait.jpg')
expect(content).toEqual<SiteContent>({
heroHeadline: 'Vos cheveux, votre couronne',
heroTagline: 'Coiffures protectrices par Mira',
aboutBio: 'Bio text.',
aboutPortraitImage: 'https://cdn.example/about/mira-portrait.jpg',
contactEmail: 'hello@braidsbymira.com',
contactHours: 'Mar–Sam, 9h à 18h',
})
})
it('throws when the fetch fails', async () => {
singleMock.mockResolvedValue({ data: null, error: { message: 'network error' } })
await expect(fetchSiteContent()).rejects.toEqual({ message: 'network error' })
})
})

34
src/data/siteContent.ts Normal file
View File

@@ -0,0 +1,34 @@
import { supabase } from '../lib/supabase'
import { resolveImageUrl } from '../lib/resolveImageUrl'
export interface SiteContent {
heroHeadline: string
heroTagline: string
aboutBio: string
aboutPortraitImage: string
contactEmail: string
contactHours: string
}
interface SiteContentRow {
hero_headline: string
hero_tagline: string
about_bio: string
about_portrait_image: string
contact_email: string
contact_hours: string
}
export async function fetchSiteContent(): Promise<SiteContent> {
const { data, error } = await supabase.from('site_content').select('*').eq('id', 'default').single()
if (error) throw error
const row = data as SiteContentRow
return {
heroHeadline: row.hero_headline,
heroTagline: row.hero_tagline,
aboutBio: row.about_bio,
aboutPortraitImage: resolveImageUrl(row.about_portrait_image),
contactEmail: row.contact_email,
contactHours: row.contact_hours,
}
}