feat: persist real inquiries to Supabase

This commit is contained in:
belviskhoremk
2026-08-12 22:49:21 +02:00
parent 4ba6f3e178
commit 5a4581c137
2 changed files with 61 additions and 8 deletions

View File

@@ -1,6 +1,15 @@
import { render, screen } from '@testing-library/react'
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { describe, it, expect, vi } from 'vitest'
import { describe, it, expect, vi, beforeEach } from 'vitest'
const insertMock = vi.fn()
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const fromMock = vi.fn((_: string) => ({ insert: insertMock }))
vi.mock('../lib/supabase', () => ({
supabase: { from: (table: string) => fromMock(table) },
}))
import InquiryModal from './InquiryModal'
import type { Product } from '../data/products'
@@ -10,22 +19,48 @@ const product: Product = {
}
describe('InquiryModal', () => {
it('shows a validation error when submitting without a name or valid email', async () => {
beforeEach(() => {
insertMock.mockReset().mockResolvedValue({ error: null })
fromMock.mockClear()
})
it('shows a validation error when submitting without a name or valid email, without calling Supabase', async () => {
render(<InquiryModal product={product} onClose={vi.fn()} />)
await userEvent.click(screen.getByRole('button', { name: /envoyer la demande/i }))
expect(screen.getByText(/nom est requis/i)).toBeInTheDocument()
expect(screen.getByText(/adresse courriel valide est requise/i)).toBeInTheDocument()
expect(insertMock).not.toHaveBeenCalled()
})
it('shows a confirmation after a valid submit', async () => {
it('inserts the inquiry and shows a confirmation after a valid submit', async () => {
render(<InquiryModal product={product} onClose={vi.fn()} />)
await userEvent.type(screen.getByLabelText(/nom/i), 'Jane Doe')
await userEvent.type(screen.getByLabelText(/courriel/i), 'jane@example.com')
await userEvent.click(screen.getByRole('button', { name: /envoyer la demande/i }))
expect(screen.getByText(/silk edge control/i)).toBeInTheDocument()
await waitFor(() =>
expect(insertMock).toHaveBeenCalledWith({
product_id: 'edge-control',
contact_name: 'Jane Doe',
contact_email: 'jane@example.com',
contact_notes: '',
}),
)
expect(await screen.findByText(/silk edge control/i)).toBeInTheDocument()
expect(screen.getByText(/mira vous recontactera/i)).toBeInTheDocument()
})
it('shows an inline error and keeps the form open when the insert fails', async () => {
insertMock.mockResolvedValue({ error: { message: 'network error' } })
render(<InquiryModal product={product} onClose={vi.fn()} />)
await userEvent.type(screen.getByLabelText(/nom/i), 'Jane Doe')
await userEvent.type(screen.getByLabelText(/courriel/i), 'jane@example.com')
await userEvent.click(screen.getByRole('button', { name: /envoyer la demande/i }))
expect(await screen.findByText(/network error/i)).toBeInTheDocument()
expect(screen.getByRole('button', { name: /envoyer la demande/i })).toBeInTheDocument()
})
it('calls onClose when Close is clicked', async () => {
const onClose = vi.fn()
render(<InquiryModal product={product} onClose={onClose} />)

View File

@@ -1,6 +1,7 @@
import { useEffect, useRef, useState, type FormEvent } from 'react'
import type { Product } from '../data/products'
import { isValidEmail } from '../lib/validation'
import { supabase } from '../lib/supabase'
import styles from './InquiryModal.module.css'
interface InquiryModalProps {
@@ -20,6 +21,7 @@ function InquiryModal({ product, onClose }: InquiryModalProps) {
const [email, setEmail] = useState('')
const [notes, setNotes] = useState('')
const [errors, setErrors] = useState<Errors>({})
const [submitError, setSubmitError] = useState('')
const [submitted, setSubmitted] = useState(false)
const nameInputRef = useRef<HTMLInputElement>(null)
@@ -35,15 +37,30 @@ function InquiryModal({ product, onClose }: InquiryModalProps) {
return () => document.removeEventListener('keydown', handleKeyDown)
}, [onClose])
function handleSubmit(e: FormEvent) {
async function handleSubmit(e: FormEvent) {
e.preventDefault()
const nextErrors: Errors = {}
if (!name.trim()) nextErrors.name = 'Le nom est requis.'
if (!isValidEmail(email)) nextErrors.email = 'Une adresse courriel valide est requise.'
setErrors(nextErrors)
if (Object.keys(nextErrors).length === 0) {
setSubmitted(true)
if (Object.keys(nextErrors).length > 0) {
return
}
setSubmitError('')
const { error } = await supabase.from('inquiries').insert({
product_id: product.id,
contact_name: name,
contact_email: email,
contact_notes: notes,
})
if (error) {
setSubmitError(error.message)
return
}
setSubmitted(true)
}
return (
@@ -86,6 +103,7 @@ function InquiryModal({ product, onClose }: InquiryModalProps) {
<label htmlFor="inquiry-notes">Notes</label>
<textarea id="inquiry-notes" value={notes} onChange={(e) => setNotes(e.target.value)} />
</div>
{submitError && <p className={styles.error}>{submitError}</p>}
<button type="submit">Envoyer la demande</button>
<button type="button" onClick={onClose}>Fermer</button>
</form>