Aller au contenu

Comment créer un formulaire de demande de devis dans React Native

Un formulaire de demande de devis React Native et Expo avec choix de la prestation, du budget et de l’échéance, qui vous envoie chaque demande par e-mail et la confirme automatiquement au client.

Mis à jour:

La vidéo est en anglais ; le texte ci-dessous est le tutoriel complet.

Ce tutoriel crée un écran de demande de devis dans React Native : le client choisit une prestation et un budget, ajoute une échéance et une description, et vous recevez un e-mail structuré. Une réponse automatique confirme la demande au client.

Il vous faut : un compte Frontmail gratuit, un projet Expo ou React Native et environ quinze minutes.

1. Connectez votre boîte mail et créez les modèles

  1. Services d’envoi → Ajouter un service : connectez votre boîte mail ou votre fournisseur, Définir par défaut, puis Vérifier et enregistrer.
  2. Modèles → Depuis la galerie → Demande de devis (catégorie Entreprise) → Utiliser ce modèle.
  3. Depuis la galerie → Réponse automatique : merci pour votre message → Utiliser ce modèle. Définissez les valeurs par défaut de company_name et response_time, puis choisissez-le dans le modèle de devis sous Paramètres → Envoi → Modèle de réponse automatique.

Paramètres du modèle de devis : name, email, service, details (obligatoires) et phone, company, budget, deadline (facultatifs, échéance au format YYYY-MM-DD). service et budget n’acceptent que les valeurs listées dans Paramètres. Le code ci-dessous utilise les valeurs du modèle anglais ; si vous créez le modèle en français, ce sont Site web, Boutique en ligne, Application mobile, Conseil, Autre et Moins de 5 000 €, 5 000 – 20 000 €, 20 000 – 50 000 €, Plus de 50 000 €. L’application doit envoyer exactement les chaînes qui y figurent.

2. Autorisez les applications mobiles et activez le CAPTCHA

  • Sécurité → Applications mobiles → Autoriser les applications mobiles → Enregistrer.
  • Modèle de devis → Paramètres → Protection → Exiger un CAPTCHA (Turnstile) – l’application utilise le widget partagé de Frontmail, aucune clé nécessaire.

3. Installez

npx expo install @frontmail/react-native @react-native-async-storage/async-storage react-native-webview

4. Créez le formulaire

Pour que l’exemple n’ait pas de dépendances supplémentaires, les choix sont de simples boutons « chips » et l’échéance se saisit au format YYYY-MM-DD. Remplacez-les par vos composants de sélection préférés – seules comptent les valeurs que vous passez à send.

import { useRef, useState } from 'react';
import { Button, Pressable, Text, TextInput, View } from 'react-native';
import { FrontmailProvider, TurnstileWebView, useSendEmail, type TurnstileWebViewHandle } from '@frontmail/react-native';

const options = { publicKey: 'pk_…' };
const SERVICES = ['Website', 'E-shop', 'Mobile app', 'Consulting', 'Other'];
const BUDGETS = ['Under $5k', '$5k–20k', '$20k–50k', 'Over $50k'];

export default function App() {
  return (
    <FrontmailProvider options={options}>
      <QuoteForm />
    </FrontmailProvider>
  );
}

function Choice({ values, value, onChange }: { values: string[]; value?: string; onChange(v?: string): void }) {
  return (
    <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8 }}>
      {values.map((v) => (
        <Pressable key={v} onPress={() => onChange(v === value ? undefined : v)} accessibilityState={{ selected: v === value }}>
          <Text style={{ padding: 8, borderWidth: 1, borderRadius: 16, fontWeight: v === value ? '700' : '400' }}>{v}</Text>
        </Pressable>
      ))}
    </View>
  );
}

function QuoteForm() {
  const { send, status, error } = useSendEmail(null, 'tpl_…');
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [service, setService] = useState<string | undefined>(SERVICES[0]);
  const [budget, setBudget] = useState<string>();
  const [deadline, setDeadline] = useState('');
  const [details, setDetails] = useState('');
  const [token, setToken] = useState<string>();
  const turnstile = useRef<TurnstileWebViewHandle>(null);

  const onSend = async () => {
    await send({ name, email, service, budget, deadline, details }, { turnstileToken: token });
    setToken(undefined);
    turnstile.current?.reset(); // a token works only once
  };

  if (status === 'sent' || status === 'held') return <Text>Thanks! We’ve emailed you a confirmation.</Text>;

  return (
    <View style={{ gap: 12, padding: 16 }}>
      <TextInput placeholder="Name" value={name} onChangeText={setName} />
      <TextInput placeholder="Email" value={email} onChangeText={setEmail} keyboardType="email-address" autoCapitalize="none" />
      <Text>What do you need?</Text>
      <Choice values={SERVICES} value={service} onChange={setService} />
      <Text>Budget (optional)</Text>
      <Choice values={BUDGETS} value={budget} onChange={setBudget} />
      <TextInput placeholder="Deadline (YYYY-MM-DD, optional)" value={deadline} onChangeText={setDeadline} />
      <TextInput placeholder="Project details" value={details} onChangeText={setDetails} multiline />
      <TurnstileWebView ref={turnstile} onToken={setToken} />
      <Button title="Request a quote" disabled={!token || !service || status === 'sending'} onPress={onSend} />
      {error && <Text>{error.message}</Text>}
    </View>
  );
}

Les valeurs facultatives vides (pas de budget, échéance vide) sont automatiquement omises de l’e-mail.

5. Testez

Lancez l’application et envoyez une demande avec votre propre adresse e-mail. Vous recevez la demande et la confirmation ; les deux apparaissent dans l’Historique. Les erreurs de validation reviennent sous la forme invalid_template_params, avec le champ en cause dans error.details.

Une demande et sa confirmation consomment deux crédits. Quand vos crédits sont épuisés, les demandes sont acceptées avec le statut held et envoyées dès que vous rechargez – le hook renvoie status === 'held'.

Étapes suivantes

  • Gardez la liste de toutes les personnes qui vous ont sollicité : activez la Collecte des contacts dans les paramètres du modèle.
  • Passez { locale: 'de' } comme second argument de send pour utiliser une variante linguistique du modèle.
Tous les tutoriels