feat: persist real inquiries to Supabase
This commit is contained in:
@@ -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} />)
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user