Save France
Frontend

Pages

Documentation des pages principales et leur gestion de contrat

Vue d'ensemble

Les pages de l'application sont organisées dans le dossier app/pages/ et suivent le système de routing file-based de Nuxt. Chaque fichier ou dossier représente une route accessible.

Pages de gestion de contrat

1. Page de contrat ([reqID]/contract.vue)

Description

Page principale de gestion d'un contrat qui affiche le statut actuel et permet différentes actions selon l'état du contrat.

Route

/{reqID}/contract

Middleware

  • auth : Authentification requise
  • request-id : Validation de l'ID de la requête

Statuts gérés

StatutDescriptionActions disponibles
pendingContrat en attente d'envoiRenvoyer le contrat, Contacter le support
waiting_signatureEn attente de signatureRenvoyer le contrat, Contacter le support
expiredLien de signature expiréDemander un nouveau contrat, Remplir le questionnaire de non-souscription
waiting_paymentSigné, en attente de paiementProcéder au paiement, Télécharger le contrat
completeSigné et payéTélécharger le contrat, Contacter le support
rejectedContrat rejetéDemander un nouveau contrat, Contacter le support

Composants utilisés

  • ContractSummaryCard : Affichage du résumé du contrat
  • ContactSupportModal : Modal de contact du support
  • UAlert : Alertes de statut
  • UButton : Boutons d'action

Fonctionnalités

1. Affichage du statut

Affiche une alerte contextuelle selon le statut du contrat avec les informations pertinentes (date d'expiration, actions possibles, etc.).

2. Résumé du contrat

Affiche les détails du contrat et les informations du client via le composant ContractSummaryCard.

3. Actions disponibles
  • Renvoyer le contrat : Renvoie l'email de signature au client
  • Demander un nouveau contrat : Génère un nouveau contrat lorsque l'ancien a expiré ou a été rejeté
  • Télécharger le contrat signé : Permet de télécharger le PDF du contrat signé
  • Procéder au paiement : Redirige vers la page de paiement Stripe
  • Contacter le support : Ouvre une modal de contact
4. Questionnaire de non-souscription

Pour les contrats expirés (waiting_signature + contractExpired), une section invite l'utilisateur à remplir un questionnaire pour comprendre les raisons de non-souscription.

Exemple de code

<template>
  <UCard class="max-w-6xl mx-auto lg:my-16 shadow-lg">
    <!-- Alerte de statut -->
    <LazyUAlert
      v-if="signatureStatus === 'waiting_signature' && !contractExpired"
      class="mb-8"
      title="Signature en attente"
      color="warning"
    />

    <!-- Résumé du contrat -->
    <ContractSummaryCard :request="request" class="mb-8" />

    <!-- Actions -->
    <div class="flex flex-col sm:flex-row gap-4 justify-center">
      <LazyUButton
        v-if="contractExpired"
        @click="requestNewContract"
        label="Demander un nouveau contrat"
      />
    </div>
  </UCard>
</template>

2. Page de non-souscription ([reqID]/refus-souscription.vue)

Description

Page de questionnaire permettant de recueillir les raisons pour lesquelles un client n'a pas souhaité souscrire au contrat.

Route

/{reqID}/refus-souscription

Middleware

  • auth : Authentification requise

Composants utilisés

  • ContractSummaryCard : Affichage du résumé du contrat concerné
  • UForm : Formulaire avec validation Zod
  • UCheckboxGroup : Sélection multiple des raisons
  • UTextarea : Commentaires additionnels
  • UAlert : Message de confirmation d'envoi

Fonctionnalités

1. Affichage du contrat concerné

Affiche le résumé du contrat pour que l'utilisateur sache de quel contrat il s'agit.

2. Sélection des raisons
  • Liste de raisons prédéfinies chargées depuis l'API
  • Sélection multiple obligatoire (minimum 1 raison)
  • Validation avec Zod
3. Commentaires additionnels

Zone de texte optionnelle pour des détails supplémentaires.

4. Soumission du formulaire
  • Envoie les données à l'API via un PATCH sur /api/requests/{reqID}
  • Gère les erreurs (ex: questionnaire déjà rempli)
  • Affiche un toast de confirmation

Validation

const refusalSchema = z.object({
  reasons: z.array(z.string()).min(1, "Vous devez sélectionner au moins une raison"),
  comment: z.string().optional(),
});

Flux utilisateur

  1. L'utilisateur accède à la page depuis la page de contrat (lien affiché si le contrat a expiré)
  2. Il voit un résumé du contrat concerné
  3. Il sélectionne une ou plusieurs raisons de non-souscription
  4. Il peut ajouter un commentaire optionnel
  5. Il soumet le formulaire
  6. Un toast confirme l'envoi ou affiche une erreur

Gestion des erreurs

CodeDescriptionAction
403Questionnaire déjà rempliRedirection vers la page de contrat
AutreErreur serveurAffichage d'un toast d'erreur

Exemple de code

<template>
  <UCard class="max-w-6xl mx-auto lg:my-16 shadow-lg">
    <div class="text-center mb-6">
      <UIcon name="i-heroicons-document-minus" class="w-12 h-12 text-orange-500 mx-auto mb-4"/>
      <h3 class="text-xl font-semibold">Questionnaire de non-souscription</h3>
    </div>

    <!-- Résumé du contrat -->
    <ContractSummaryCard v-if="request" :request="request" class="mb-8" />

    <!-- Formulaire -->
    <UForm :schema="refusalSchema" :state="form" @submit="handleSubmit">
      <UFormField name="reasons" required label="Pourquoi ne souhaitez-vous pas souscrire ?">
        <UCheckboxGroup v-model="form.reasons" :items="formatedReasons" />
      </UFormField>

      <UFormField label="Commentaires additionnels (optionnel)" name="comment">
        <UTextarea v-model="form.comment" />
      </UFormField>

      <UButton type="submit" :loading="isSubmitting">
        Envoyer le questionnaire
      </UButton>
    </UForm>
  </UCard>
</template>

Depuis la page de contrat

  • Si le contrat a expiré → Lien vers /[reqID]/refus-souscription
  • Si le contrat est en attente de paiement → Redirection vers l'URL de paiement Stripe

Depuis la page de refus de souscription

  • Après soumission avec erreur 403 → Redirection vers /[reqID]/contract

Bonnes pratiques

Gestion de l'état

  • Utiliser useFetch pour les appels API avec clés de cache
  • Initialiser les cookies au montage avec initializeFromCookies()
  • Gérer les états de chargement (isSubmitting, loadResend, etc.)

Sécurité

  • Toujours inclure le token JWT dans les headers
  • Valider les données côté client avec Zod
  • Gérer les erreurs de permissions (403)

UX

  • Afficher des messages clairs pour chaque statut
  • Désactiver les boutons pendant le chargement
  • Afficher des toasts de confirmation/erreur
  • Fournir des alternatives (contacter le support)

Accessibilité

  • Utiliser des icônes avec du texte
  • Assurer la navigation au clavier
  • Fournir des descriptions claires pour les alertes

Exemples complets

Récupération des données d'une requête

const { getToken } = useAuth();
const token = getToken();
const route = useRoute();
const reqID = route.params.reqID as string;

const { data: request } = await useFetch(`/api/requests/${reqID}`, {
  key: "currentRequest",
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

Gestion d'une action avec loading

const loadResend = ref(false);
const toast = useToast();

const resendContract = async () => {
  try {
    loadResend.value = true;
    const response = await useFetch(`/api/requests/${reqID}/send-contract`, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${token}`,
      },
    });

    if (response.data.value) {
      toast.add({
        title: "Votre contrat a été renvoyé",
        description: `Un email a été envoyé à l'adresse ${request.value?.contact.email}`,
        color: "success",
      });
    }
  } catch (error: any) {
    console.error("Erreur lors de l'envoi:", error);
    toast.add({
      title: "Une erreur est survenue",
      description: error.data?.detail,
      color: "error",
    });
  } finally {
    loadResend.value = false;
  }
};

Tests

Tests unitaires recommandés

  1. Page de contrat
    • Affichage correct selon chaque statut
    • Affichage des boutons appropriés selon le statut
    • Gestion des erreurs lors des actions
  2. Page de refus de souscription
    • Validation du formulaire
    • Soumission des données
    • Gestion de l'erreur 403 (déjà soumis)
    • Redirection après erreur

Dépannage

Le token n'est pas disponible

Vérifier que initializeFromCookies() est appelé dans onMounted().

Les données ne se chargent pas

Vérifier que le token est valide et que l'API est accessible.

Le formulaire ne se soumet pas

Vérifier la validation Zod et les messages d'erreur dans la console.

Prochaines étapes

  • Ajouter des tests unitaires pour les pages
  • Améliorer la gestion des erreurs avec Sentry
  • Ajouter des analytics sur les actions utilisateur