import { Controls, Canvas, Meta, Source } from '@storybook/blocks';
import * as FormValidationStories from './FormValidation.stories';

<Meta of={FormValidationStories} />

<div className="header">
  <h1>Système de validation en cascade</h1>
  <p>Le système de validation en cascade permet de gérer facilement la validation de formulaires complexes et imbriqués dans des applications Vue 3.</p>
</div>

## Aperçu

Le système de validation en cascade de Synapse Design System est composé de trois éléments clés :

1. **SyForm** - Le composant conteneur qui gère la validation globale
2. **useFormValidation** - Le composable qui centralise l'enregistrement des champs et la validation
3. **useValidatable** - Le composable qui permet aux composants de s'auto-enregistrer auprès du formulaire parent

Cette architecture permet de gérer automatiquement la validation de formulaires complexes sans avoir à gérer manuellement les références vers chaque champ.

## Exemple simple

Voici un exemple simple d'utilisation du système de validation :

<Canvas of={FormValidationStories.SimpleForm} />

<Source dark code={'\n<script setup lang="ts">\nimport { ref } from "vue"\nimport SyForm from "@/components/Customs/SyForm/SyForm.vue"\nimport SyTextField from "@/components/Customs/SyTextField/SyTextField.vue"\n\nconst name = ref("")\nconst email = ref("")\n\nconst onSubmit = (event) => {\n  if (event.isValid) {\n    alert("Formulaire valide !")\n  }\n}\n</script>\n\n<template>\n  <SyForm @submit="onSubmit">\n    <div class="d-flex flex-column gap-4">\n      <SyTextField v-model="name" label="Nom" required />\n      <SyTextField v-model="email" label="Email" required :rules="[v => /.+@.+/.test(v) || \'E-mail invalide\']" />\n      <v-btn type="submit" color="primary">Soumettre</v-btn>\n    </div>\n  </SyForm>\n</template>'} />

## Composants mixtes (personnalisés et natifs)

Le système de validation en cascade prend en charge à la fois les composants personnalisés qui utilisent `useValidatable` et les composants natifs Vuetify.

<Canvas of={FormValidationStories.MixedComponentsForm} />

<Source dark code={'\n<script setup lang="ts">\nimport { ref } from "vue"\nimport SyForm from "@/components/Customs/SyForm/SyForm.vue"\nimport SyTextField from "@/components/Customs/SyTextField/SyTextField.vue"\nimport SySelect from "@/components/Customs/Selects/SySelect/SySelect.vue"\nimport SyCheckbox from "@/components/Customs/SyCheckbox/SyCheckbox.vue"\nimport DatePicker from "@/components/DatePicker/CalendarMode/DatePicker.vue"\nimport { VTextField } from "vuetify/components"\n\nconst user = ref({\n  name: "",\n  birthDate: null,\n  country: "",\n  city: "",\n  acceptTerms: false,\n})\n\nconst countries = [\n  { text: "France", value: "fr" },\n  { text: "Allemagne", value: "de" },\n  { text: "Espagne", value: "es" },\n]\n\nconst onSubmit = (event) => {\n  if (event.isValid) {\n    alert("Formulaire valide ! Données : " + JSON.stringify(user.value))\n  }\n}\n</script>\n\n<template>\n  <SyForm @submit="onSubmit" class="form-mixed">\n    <div class="d-flex flex-column gap-4">\n      <!-- Composant personnalisé avec useValidatable -->\n      <SyTextField v-model="user.name" label="Nom" required />\n      \n      <!-- Composant personnalisé avec useValidatable -->\n      <DatePicker v-model="user.birthDate" label="Date de naissance" required />\n      \n      <!-- Composant personnalisé avec useValidatable -->\n      <SySelect v-model="user.country" :items="countries" label="Pays" required />\n      \n      <!-- Composant natif Vuetify -->\n      <v-text-field v-model="user.city" label="Ville" :rules="[v => !!v || \'Ville requise\']"></v-text-field>\n      \n      <!-- Composant personnalisé avec useValidatable -->\n      <SyCheckbox v-model="user.acceptTerms" label="J\'accepte les conditions" required />\n      \n      <v-btn type="submit" color="primary">Soumettre</v-btn>\n    </div>\n  </SyForm>\n</template>'} />

## Formulaires imbriqués

Le système de validation en cascade est particulièrement utile pour les formulaires avec des structures imbriquées complexes.

<Canvas of={FormValidationStories.NestedForm} />

<Source dark code={'\n<script setup lang="ts">\nimport { ref } from "vue"\nimport SyForm from "@/components/Customs/SyForm/SyForm.vue"\nimport SyTextField from "@/components/Customs/SyTextField/SyTextField.vue"\n\nconst user = ref({\n  name: "",\n  address: {\n    street: "",\n    city: "",\n    zipcode: "",\n  },\n})\n\nconst onSubmit = (event) => {\n  if (event.isValid) {\n    alert("Formulaire valide ! Données : " + JSON.stringify(user.value))\n  }\n}\n</script>\n\n<template>\n  <SyForm @submit="onSubmit">\n    <div class="d-flex flex-column gap-4">\n      <h3>Informations personnelles</h3>\n      <SyTextField v-model="user.name" label="Nom" required />\n      \n      <fieldset style="border: 1px solid #ccc; padding: 16px; border-radius: 4px;">\n        <legend>Adresse</legend>\n        <div class="d-flex flex-column gap-4">\n          <SyTextField v-model="user.address.street" label="Rue" required />\n          <SyTextField v-model="user.address.city" label="Ville" required />\n          <SyTextField v-model="user.address.zipcode" label="Code postal" required />\n        </div>\n      </fieldset>\n      \n      <v-btn type="submit" color="primary">Soumettre</v-btn>\n    </div>\n  </SyForm>\n</template>'} />

## Implémentation personnalisée

Vous pouvez facilement personnaliser la validation en fonction de vos besoins spécifiques.

<Canvas of={FormValidationStories.CustomImplementation} />

<Source dark code={'\n<script setup lang="ts">\nimport { ref } from "vue"\nimport SyForm from "@/components/Customs/SyForm/SyForm.vue"\nimport SyTextField from "@/components/Customs/SyTextField/SyTextField.vue"\n\nconst email = ref("")\nconst password = ref("")\nconst confirmPassword = ref("")\n\nconst validatePasswordMatch = () => {\n  return password.value === confirmPassword.value || "Les mots de passe ne correspondent pas"\n}\n\nconst onSubmit = (event) => {\n  if (event.isValid) {\n    alert("Inscription réussie !")\n  }\n}\n</script>\n\n<template>\n  <SyForm @submit="onSubmit">\n    <div class="d-flex flex-column gap-4">\n      <SyTextField v-model="email" label="Email" type="email" required :rules="[v => /.+@.+/.test(v) || \'E-mail invalide\']" />\n      <SyTextField v-model="password" label="Mot de passe" type="password" required :rules="[v => v.length >= 8 || \'Minimum 8 caractères\']" />\n      <SyTextField \n        v-model="confirmPassword" \n        label="Confirmer le mot de passe" \n        type="password" \n        required \n        :rules="[validatePasswordMatch]" \n      />\n      <v-btn type="submit" color="primary">S\'inscrire</v-btn>\n    </div>\n  </SyForm>\n</template>'} />

## Composants compatibles

Les composants suivants intègrent déjà le système de validation en cascade :

- SyTextField
- SyCheckbox
- SySelect
- DatePicker
- ComplexDatePicker
- DateTextInput
- NirField
- PasswordField
- PhoneField

## Guide d'intégration

### Pour les composants existants

Pour intégrer un composant existant au système de validation en cascade :

<Source dark code={'\nimport { useValidatable } from "@/composables/validation/useValidatable";\n\n// Définir une méthode de validation qui sera appelée lors de la soumission du formulaire\nconst validateOnSubmit = () => {\n  // Implémenter votre logique de validation ici\n  return isValid; // Retourner true si valide, false sinon\n};\n\n// Enregistrer le composant auprès du formulaire parent\nuseValidatable(validateOnSubmit);\n\n// Exposer la méthode pour permettre des appels directs\ndefineExpose({\n  validateOnSubmit\n});'} />

### Pour les nouveaux composants

Lors de la création d'un nouveau composant de formulaire, assurez-vous d'implémenter la méthode `validateOnSubmit` et d'utiliser le composable `useValidatable`.

<Source dark code={'\n<script setup lang="ts">\nimport { ref } from "vue";\nimport { useValidatable } from "@/composables/validation/useValidatable";\n\nconst props = defineProps({\n  modelValue: String,\n  label: String,\n  required: Boolean\n});\n\nconst emit = defineEmits(["update:modelValue"]);\n\nconst hasError = ref(false);\nconst errorMessage = ref("");\n\n// Méthode de validation\nconst validateOnSubmit = () => {\n  if (props.required && !props.modelValue) {\n    hasError.value = true;\n    errorMessage.value = "Ce champ est requis";\n    return false;\n  }\n  \n  hasError.value = false;\n  errorMessage.value = "";\n  return true;\n};\n\n// Enregistrer le composant\nuseValidatable(validateOnSubmit);\n\ndefineExpose({\n  validateOnSubmit\n});\n</script>'} />

## API

### SyForm

<Controls of={FormValidationStories.SimpleForm} />

### useValidatable

Le composable `useValidatable` accepte une fonction qui sera appelée lors de la validation du formulaire :

```typescript
function useValidatable(validateMethod: () => Promise<boolean> | boolean): void
```

| Paramètre | Type | Description |
|-----------|------|-------------|
| validateMethod | `() => Promise<boolean> \| boolean` | Fonction de validation qui retourne un booléen ou une promesse de booléen |

### useFormValidation

Le composable `useFormValidation` fournit les méthodes et propriétés suivantes :

```typescript
function useFormValidation() {
  return {
    register: (component: ValidatableComponent) => void,
    unregister: (component: ValidatableComponent) => void,
    validateAll: () => Promise<boolean>
  }
}
```

## La documentation du composable ValidationSystem

Pour une compréhension approfondie du fonctionnement interne de `SyForm`, consultez la [documentation du système de validation en cascade](?path=/docs/composables-validationsystem--docs).

Cette documentation détaille :

- Le fonctionnement interne du système d'enregistrement des champs
- Comment étendre le système pour vos propres composants
- Les hooks disponibles pour l'intégration personnalisée
- Des exemples avancés de validation asynchrone
- Comment implémenter des validations dépendantes entre champs

## Migrer d'un v-form à SyForm

La migration est généralement simple :

1. Remplacez `v-form` par `SyForm` dans vos templates
2. Adaptez la signature de votre gestionnaire d'événement submit pour accepter `event: { isValid: boolean }`
3. Pour les composants personnalisés, implémentez `useValidatable` comme détaillé dans la documentation

<Source dark code={`
<!-- Avant -->
<v-form @submit.prevent="onSubmit">
  <!-- contenu du formulaire -->
</v-form>

<!-- Après -->
<SyForm @submit="onSubmit">
  <!-- même contenu du formulaire -->
</SyForm>
`} />

## Bonnes pratiques

1. **Toujours retourner un booléen** : La méthode `validateOnSubmit` doit toujours retourner un booléen indiquant si la validation a réussi.
2. **Gérer le cas readonly** : Un composant en mode lecture seule doit toujours retourner `true` pour sa validation.
3. **Valider de manière complète** : Vérifier toutes les règles de validation et mettre à jour l'état d'erreur.
4. **Exposer validateOnSubmit** : Cette méthode doit être exposée via `defineExpose` pour permettre des appels manuels si nécessaire.
5. **Validation asynchrone** : Pour les validations asynchrones, retournez une promesse de booléen.
