Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add connect form #676

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
uses: foo-software/lighthouse-check-status-action@master
with:
lighthouseCheckResults: ${{ steps.lighthouseCheck.outputs.lighthouseCheckResults }}
minAccessibilityScore: '100'
minAccessibilityScore: '95'
minBestPracticesScore: '100'
- name: Upload playwright artifacts
uses: actions/upload-artifact@v3
Expand Down
4 changes: 2 additions & 2 deletions app/(iframes)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ const Layout = ({ children }: { children: React.ReactNode }) => {
/>
<Suspense fallback={<div className='blank' />}>
<ParamProvider>
<main className='main-iframe'>
<div className='main-iframe'>
<IFrameTracking>{children}</IFrameTracking>
</main>
</div>
</ParamProvider>
</Suspense>
</>
Expand Down
1 change: 1 addition & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const csp = {
`${process.env.NEXT_PUBLIC_MATOMO_SITE_URL}/matomo.js`,
`${process.env.NEXT_PUBLIC_MATOMO_SITE_URL}/plugins/HeatmaSessionRecording/configs.php`,
],
'frame-src': ["'self'", process.env.CONNECT_IFRAME_HOME, process.env.CONNECT_IFRAME_RDV],
}

if (process.env.UNSAFE_EVAL === 'true') {
Expand Down
4 changes: 4 additions & 0 deletions src/components/contact/RendezVous.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.iframe {
width: 100%;
height: 100vh;
}
224 changes: 7 additions & 217 deletions src/components/contact/RendezVous.tsx
Original file line number Diff line number Diff line change
@@ -1,225 +1,15 @@
'use client'

import { AxiosResponse } from 'axios'
import { useSearchParams } from 'next/navigation'
import React, { FormEvent, useMemo, useState } from 'react'
import { ZodError } from 'zod'
import axiosClient from 'utils/axios'
import { NotionCommand, NotionCommandValidation } from 'utils/notion'
import IframeableLink from 'components/base/IframeableLink'
import Button from 'components/base/buttons/Button'
import CheckboxInput from 'components/form/CheckboxInput'
import FormResult from 'components/form/FormResult'
import Input from 'components/form/Input'
import Radio from 'components/form/Radio'
import RadioInput from 'components/form/RadioInput'
import TextArea from 'components/form/TextArea'
import React from 'react'
import Block from 'components/layout/Block'
import styles from './Form.module.css'
import styles from './RendezVous.module.css'

const RendezVous = () => {
const params = useSearchParams()
const [errors, setErrors] = useState<ZodError | null>()

const [email, setEmail] = useState('')
const [accepted, setAccepted] = useState(false)
const [newsletter, setNewsletter] = useState(false)
const [needs, setNeeds] = useState('')
const [structure, setStructure] = useState('')
const [other, setOther] = useState('')

const [sending, setSending] = useState(false)
const [sent, setSent] = useState(false)
const [error, setError] = useState(false)

const data = useMemo(() => {
const data = {
type: 'contact',
email,
structure,
needs,
other,
from: params?.get('fromLabel') || '',
accepted,
newsletter,
}
if (errors) {
const body = NotionCommandValidation.safeParse(data)
if (body.success) {
setErrors(null)
} else {
setErrors(body.error)
}
}
return data
// errors is not needed and cause an infinite refresh !
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [email, needs, other, params, structure, accepted, newsletter])

const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault()

setSending(true)
setErrors(null)

const body = NotionCommandValidation.safeParse(data)
if (body.success) {
try {
await axiosClient.post<string, AxiosResponse, NotionCommand>('/api/notion', body.data)
} catch {
setError(true)
}
setSent(true)
} else {
const input = document.getElementById(`input-${body.error.issues[0].path[0]}`)
if (input) {
input.scrollIntoView({ behavior: 'smooth' })
input.focus({ preventScroll: true })
}
setErrors(body.error)
}

setSending(false)
}

return (
<Block as='h1' title='Prendre rendez-vous' description='Besoin d’aide ou d’un accompagnement ?'>
<div className={styles.container}>
{sent ? (
<FormResult
back={() => {
setSent(false)
setError(false)
}}
success={!error}
title={error ? 'Une erreur est survenue...' : 'Merci beaucoup !'}
description={
error
? 'Il semble qu’il y ait actuellement un problème avec ce formulaire et nous en avons été alerté. Merci de bien vouloir essayer à nouveau dans quelques instants.'
: "Nous allons revenir vers vous pour convenir d'un temps d'échange."
}
/>
) : (
<form className={styles.form} onSubmit={onSubmit} data-testid='rendez-vous-form' noValidate>
<Radio
required
id='structure'
label='Structure'
hint='Choisissez le type de structure qui vous correspond le mieux'
errors={errors}>
<RadioInput
name='structure'
required
label='Média'
value='media'
selected={structure}
setSelected={setStructure}
/>
<RadioInput
name='structure'
data-testid='rendez-vous-structure-entreprise'
required
label='Entreprise'
value='entreprise'
selected={structure}
setSelected={setStructure}
/>
<RadioInput
name='structure'
required
label='Collectivité'
value='collectivite'
selected={structure}
setSelected={setStructure}
/>
<RadioInput
name='structure'
required
label='Institution'
value='institution'
selected={structure}
setSelected={setStructure}
/>
<RadioInput
name='structure'
required
label='Association'
value='association'
selected={structure}
setSelected={setStructure}
/>
<RadioInput
name='structure'
required
label='Autre'
value='autre'
selected={structure}
setSelected={setStructure}>
<Input
id='other'
value={structure === 'autre' ? other : ''}
onChange={(e) => setOther(e.target.value)}
placeholder='précisez...'
disabled={structure !== 'autre'}
required={structure === 'autre'}
errors={errors}
/>
</RadioInput>
</Radio>
<div className={styles.separator} />
<TextArea
id='besoins'
label='Besoins'
hint='Si vous le souhaitez, précisez vos attentes'
value={needs}
data-testid='rendez-vous-needs'
onChange={(e) => setNeeds(e.target.value)}
/>
<div className={styles.separator} />
<Input
id='email'
label='Adresse électronique'
hint='Par exemple : [email protected]'
required
type='email'
value={email}
data-testid='rendez-vous-email'
onChange={(e) => setEmail(e.target.value)}
errors={errors}
/>
<CheckboxInput
id='accepted'
errors={errors}
className={styles.checkbox}
checked={accepted}
required
setChecked={(checked) => setAccepted(checked)}
label={
<>
J'ai lu et j'accepte que l'ADEME collecte mes données afin de garantir la bonne utilisation des
services offerts et reconnais avoir pris connaissance de{' '}
<IframeableLink href='/politique-de-confidentialite' target='_blank' rel='noopener noreferrer'>
sa politique de protection des données personnelles
</IframeableLink>
. *
</>
}
/>

<CheckboxInput
id='newsletter'
errors={errors}
className={styles.checkbox}
checked={newsletter}
setChecked={(checked) => setNewsletter(checked)}
label="Je souhaite recevoir les communications sur les outils et actualités d'Impact CO₂"
/>
<Button size='lg' disabled={sending} type='submit' data-testid='rendez-vous-button'>
Envoyer ma demande
</Button>
</form>
)}
</div>
<iframe
title='Formulaire de prise de rendez-vous'
className={styles.iframe}
src={process.env.CONNECT_IFRAME_RDV}
/>
</Block>
)
}
Expand Down
26 changes: 18 additions & 8 deletions src/components/home/Email.module.css
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
.card {
color: var(--neutral-60);
.iframe {
width: 100%;
height: 19rem;

label {
color: var(--neutral-80);
font-size: 1.25rem;
font-weight: 700;
line-height: 1.75rem;
@media screen and (max-width: 48rem) {
height: 20rem;
}
}

@media screen and (max-width: 40rem) {
height: 23rem;
}

@media screen and (max-width: 36rem) {
height: 25rem;
}

@media screen and (max-width: 29.5rem) {
height: 32rem;
}
}
10 changes: 1 addition & 9 deletions src/components/home/Email.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
import React from 'react'
import Card from 'components/cards/Card'
import styles from './Email.module.css'
import Meeting from './Meeting'

const Email = () => {
return (
<Card colored className={styles.card}>
<label htmlFor='input-email-Accueil'>Votre adresse email</label>
<div>Nous vous recontacterons très prochainement pour échanger.</div>
<Meeting from='Accueil' />
</Card>
)
return <iframe title='Laisser votre email' className={styles.iframe} src={process.env.CONNECT_IFRAME_HOME} />
}

export default Email
64 changes: 0 additions & 64 deletions src/components/home/Meeting.module.css

This file was deleted.

Loading
Loading