# 📝 Field Renderer Component - Documentation

## 🎯 Vue d'ensemble

Le composant `FieldRendererComponent` est un composant réutilisable qui gère **l'affichage et la validation de 26 types de champs** différents. Il permet d'éliminer la duplication de code dans les formulaires en centralisant toute la logique de rendu.

### ✨ Nouvelles fonctionnalités

- 🌍 **i18n** : Support multilingue (français, anglais, espagnol)
- 🎨 **Thèmes** : Personnalisation complète des couleurs et styles
- 👁️ **Read-only** : Mode lecture seule global ou par champ
- 🆕 **Nouveaux types** : color, range, time, month, week

---

## 🚀 Installation

Le composant est déjà installé dans le projet. Pour l'utiliser dans un nouveau module :

```typescript
import { FieldRendererModule } from 'src/app/components/dynamic-form/field-renderer/field-renderer.module';

@NgModule({
  imports: [
    FieldRendererModule,
    // ... autres imports
  ]
})
export class VotreModule { }
```

---

## 📦 Types de Champs Supportés

### 🔹 Champs Texte (8 types)

| Type | Description | Icône | Validation |
|------|-------------|-------|------------|
| `text` | Champ texte simple | - | minlength, maxlength |
| `string` | Équivalent text (projects.component) | - | - |
| `email` | Email avec validation | 📧 | email format |
| `tel` | Téléphone | 📞 | - |
| `url` | URL | 🔗 | url format |
| `password` | Mot de passe masqué | 🔒 | - |
| `textarea` | Zone de texte multiligne | - | - |

### 🔹 Champs Numériques (4 types)

| Type | Description | Exemple |
|------|-------------|---------|
| `number` | Nombre entier ou décimal | 42, 3.14 |
| `integer` | Nombre entier uniquement | 42 |
| `double` | Nombre décimal (step 0.01) | 3.14 |
| `currency` | Montant avec symbole € | 99.99€ |

### 🔹 Champs de Sélection (4 types)

| Type | Description | Multi-sélection |
|------|-------------|-----------------|
| `select` | Liste déroulante simple | ❌ |
| `multiselect` | Liste déroulante multiple | ✅ |
| `picklist` | Liste de sélection (projects) | ❌ |
| `radio` | Boutons radio | ❌ |

### 🔹 Champs Temporels (7 types)

| Type | Description | Format |
|------|-------------|--------|
| `date` | Sélecteur de date | yyyy-MM-dd |
| `datetime` | Date + heure | yyyy-MM-ddTHH:mm |
| `time` ⭐ **NOUVEAU** | Sélecteur d'heure | HH:mm |
| `month` ⭐ **NOUVEAU** | Sélecteur de mois | yyyy-MM |
| `week` ⭐ **NOUVEAU** | Sélecteur de semaine | yyyy-Www |

### 🔹 Champs Visuels (2 types) ⭐ **NOUVEAU**

| Type | Description | Affichage |
|------|-------------|-----------|
| `color` | Sélecteur de couleur | Palette + aperçu |
| `range` | Slider de valeur | Min/Max + valeur actuelle |

### 🔹 Champs Spéciaux (4 types)

| Type | Description | Événement |
|------|-------------|-----------|
| `checkbox` | Case à cocher | - |
| `boolean` | Équivalent checkbox | - |
| `file` | Upload de fichier | `fileSelected` |
| `reference` | Autocomplétion serveur | `autocompleteInput` |

---

## 💡 Utilisation de Base

### Exemple Simple (Harmonized-Create-View)

```html
<!-- Dans votre template -->
<form [formGroup]="form">
  <app-field-renderer
    [field]="fieldConfig"
    [form]="form">
  </app-field-renderer>
</form>
```

```typescript
// Dans votre component
fieldConfig = {
  controlName: 'email',
  controlType: 'email',
  placeholder: 'Email',
  validators: { required: true }
};

form = this.fb.group({
  email: ['', [Validators.required, Validators.email]]
});
```

### Exemple avec Autocomplétion (Projects)

```html
<app-field-renderer
  [field]="metadata"
  [form]="form"
  [submitted]="submitted"
  [autocompleteData]="searchResults"
  [isLoadingAutocomplete]="isSearching"
  [displayFn]="displayAutocomplete"
  (autocompleteInput)="onSearch($event.value, $event.module)"
  (autocompleteSelected)="onSelect($event.event, $event.fieldName)">
</app-field-renderer>
```

```typescript
// Métadonnées du champ
metadata = {
  name: 'account',
  type: 'reference',
  displayName: 'Compte associé',
  module: 'accounts',
  mandatory: 1
};

// Gestion de la recherche
onSearch(query: string, module: string) {
  this.isSearching = true;
  this.dataService.search(module, query).subscribe(results => {
    this.searchResults = results;
    this.isSearching = false;
  });
}

displayAutocomplete(item: any): string {
  return item?.name || '';
}
```

### Exemple File Upload

```html
<app-field-renderer
  [field]="fileField"
  [form]="form"
  [acceptedFileTypes]="'image/*,.pdf'"
  [maxFileSize]="5242880"
  (fileSelected)="onFileUpload($event.file, $event.fieldName)">
</app-field-renderer>
```

```typescript
fileField = {
  controlName: 'document',
  controlType: 'file',
  placeholder: 'Télécharger un document',
  validators: { required: true }
};

onFileUpload(file: File, fieldName: string) {
  console.log(`File selected for ${fieldName}:`, file.name);
  // Upload le fichier vers le serveur
  this.uploadService.upload(file).subscribe(response => {
    console.log('Upload success:', response);
  });
}
```

---

## 📋 Configuration des Champs

### Format Harmonized-Create-View

```typescript
{
  controlName: string,        // Nom du FormControl
  controlType: string,        // Type de champ (voir liste ci-dessus)
  placeholder: string,        // Label du champ
  valueType?: string,         // Type HTML (pour 'text': email, tel, etc.)
  currentValue?: any,         // Valeur par défaut
  validators?: {
    required?: boolean,
    minlength?: number,
    maxlength?: number
  },
  options?: Array<{           // Pour select/multiselect/radio
    value: any,
    optionName: string
  }>
}
```

### Format Projects.Component

```typescript
{
  name: string,               // Nom du FormControl
  type: string,               // Type de champ
  displayName: string,        // Label du champ
  mandatory?: 0 | 1,          // Requis (1) ou non (0)
  value?: any,                // Valeur par défaut
  module?: string,            // Pour type 'reference'
  acceptedTypes?: string,     // Pour type 'file'
  options?: Array<{           // Pour picklist
    value: any
  }>
}
```

---

## 🎨 Inputs & Outputs

### Inputs Principaux

| Input | Type | Description | Requis |
|-------|------|-------------|--------|
| `field` | `any` | Configuration du champ | ✅ |
| `form` | `FormGroup` | FormGroup parent | ✅ |
| `submitted` | `boolean` | Afficher les erreurs | ❌ |
| `isEditMultiple` | `boolean` | Mode édition multiple | ❌ |

### Inputs pour Autocomplétion

| Input | Type | Description |
|-------|------|-------------|
| `autocompleteData` | `any[]` | Résultats de recherche |
| `isLoadingAutocomplete` | `boolean` | État de chargement |
| `displayFn` | `(item) => string` | Fonction d'affichage |

### Inputs pour File Upload

| Input | Type | Description | Exemple |
|-------|------|-------------|---------|
| `acceptedFileTypes` | `string` | Types acceptés | `"image/*,.pdf"` |
| `maxFileSize` | `number` | Taille max (bytes) | `5242880` (5MB) |

### Inputs pour i18n (Nouveau ✨)

| Input | Type | Description | Défaut |
|-------|------|-------------|--------|
| `locale` | `string` | Langue (fr, en, es) | `'fr'` |
| `errorMessages` | `ErrorMessages` | Messages personnalisés | Messages par défaut |

```typescript
// Exemple d'utilisation i18n
<app-field-renderer
  [field]="field"
  [form]="form"
  [locale]="'en'"
  [errorMessages]="{
    required: 'This field is mandatory',
    email: 'Invalid email address'
  }">
</app-field-renderer>
```

### Input pour Thème (Nouveau ✨)

| Input | Type | Description |
|-------|------|-------------|
| `theme` | `FieldRendererTheme` | Thème personnalisé |

```typescript
// Exemple de thème personnalisé
theme: FieldRendererTheme = {
  primaryColor: '#6366f1',
  accentColor: '#8b5cf6',
  errorColor: '#ef4444',
  backgroundColor: '#f9fafb',
  borderColor: '#d1d5db',
  borderRadius: '0.5rem',
  fontSize: '1rem',
  fontFamily: 'Inter, sans-serif'
};
```

```html
<app-field-renderer
  [field]="field"
  [form]="form"
  [theme]="theme">
</app-field-renderer>
```

### Input pour Read-Only (Nouveau ✨)

| Input | Type | Description | Défaut |
|-------|------|-------------|--------|
| `readonly` | `boolean` | Mode lecture seule | `false` |

```html
<!-- Mode read-only global -->
<app-field-renderer
  [field]="field"
  [form]="form"
  [readonly]="true">
</app-field-renderer>

<!-- Mode read-only par champ -->
<app-field-renderer
  [field]="{ ...field, readonly: true }"
  [form]="form">
</app-field-renderer>
```

### Outputs

| Output | Payload | Description |
|--------|---------|-------------|
| `autocompleteInput` | `{value, module}` | Input recherche autocomplétion |
| `autocompleteSelected` | `{event, fieldName}` | Sélection autocomplétion |
| `autocompleteFocused` | `{event, displayName}` | Focus autocomplétion |
| `autocompleteCleared` | `void` | Clear autocomplétion |
| `fileSelected` | `{file, fieldName}` | Fichier sélectionné |

---

## 🔧 Méthodes Utiles

### Dans le Component

```typescript
// Vérifier si le champ a des erreurs
hasError(): boolean

// Vérifier une erreur spécifique
hasSpecificError(errorType: string): boolean

// Récupérer le FormControl
getControl(): AbstractControl | null

// Récupérer le nom du champ (compatible multi-formats)
getControlName(): string

// Vérifier si le champ est requis
isRequired(): boolean

// Récupérer le label
getLabel(): string

// Récupérer la valeur actuelle
getCurrentValue(): any
```

---

## 🎯 Exemples Complets

### Formulaire de Contact

```typescript
// contact-form.component.ts
export class ContactFormComponent {
  form = this.fb.group({
    name: ['', Validators.required],
    email: ['', [Validators.required, Validators.email]],
    phone: [''],
    message: ['', [Validators.required, Validators.minLength(10)]],
    acceptTerms: [false, Validators.requiredTrue]
  });

  fields = [
    {
      controlName: 'name',
      controlType: 'text',
      placeholder: 'Nom complet',
      validators: { required: true }
    },
    {
      controlName: 'email',
      controlType: 'email',
      placeholder: 'Email',
      validators: { required: true }
    },
    {
      controlName: 'phone',
      controlType: 'tel',
      placeholder: 'Téléphone'
    },
    {
      controlName: 'message',
      controlType: 'textarea',
      placeholder: 'Message',
      validators: { required: true, minlength: 10 }
    },
    {
      controlName: 'acceptTerms',
      controlType: 'checkbox',
      placeholder: 'J\'accepte les conditions',
      validators: { required: true }
    }
  ];

  submitted = false;

  onSubmit() {
    this.submitted = true;
    if (this.form.valid) {
      console.log(this.form.value);
    }
  }
}
```

```html
<!-- contact-form.component.html -->
<form [formGroup]="form" (ngSubmit)="onSubmit()">
  <div *ngFor="let field of fields">
    <app-field-renderer
      [field]="field"
      [form]="form"
      [submitted]="submitted">
    </app-field-renderer>
  </div>

  <button type="submit">Envoyer</button>
</form>
```

### Formulaire E-commerce

```typescript
fields = [
  {
    controlName: 'productName',
    controlType: 'text',
    placeholder: 'Nom du produit',
    validators: { required: true }
  },
  {
    controlName: 'price',
    controlType: 'currency',
    placeholder: 'Prix',
    validators: { required: true }
  },
  {
    controlName: 'category',
    controlType: 'select',
    placeholder: 'Catégorie',
    options: [
      { value: 'electronics', optionName: 'Électronique' },
      { value: 'clothing', optionName: 'Vêtements' },
      { value: 'food', optionName: 'Alimentation' }
    ],
    validators: { required: true }
  },
  {
    controlName: 'availableFrom',
    controlType: 'date',
    placeholder: 'Disponible à partir du',
    validators: { required: true }
  },
  {
    controlName: 'image',
    controlType: 'file',
    placeholder: 'Image du produit',
    acceptedTypes: 'image/*'
  }
];
```

---

## ⚡ Performance & Optimisation

### Lazy Loading

Le `FieldRendererModule` peut être importé uniquement dans les modules qui en ont besoin :

```typescript
// Au lieu de l'importer dans AppModule
// Importez-le dans vos modules de fonctionnalités
@NgModule({
  imports: [FieldRendererModule]
})
export class FormsFeatureModule { }
```

### OnPush Change Detection (Futur)

Pour optimiser les performances, vous pourrez ajouter :

```typescript
@Component({
  selector: 'app-field-renderer',
  changeDetection: ChangeDetectionStrategy.OnPush,
  // ...
})
```

---

## 🐛 Troubleshooting

### Le champ ne s'affiche pas

✅ **Vérifier** : Le `field.controlType` ou `field.type` correspond à un type supporté
✅ **Vérifier** : Le FormControl existe dans le FormGroup
✅ **Vérifier** : `FieldRendererModule` est importé dans votre module

### Les erreurs ne s'affichent pas

✅ **Utiliser** : `[submitted]="true"` pour forcer l'affichage
✅ **Vérifier** : Le FormControl a bien `Validators` configurés

### L'autocomplétion ne fonctionne pas

✅ **Vérifier** : `[autocompleteData]` contient des objets avec une propriété `name`
✅ **Vérifier** : Les événements `(autocompleteInput)` sont bien écoutés
✅ **Fournir** : Une fonction `[displayFn]` si format différent

### Le file upload ne fonctionne pas

✅ **Écouter** : L'événement `(fileSelected)` pour récupérer le fichier
✅ **Vérifier** : `[acceptedFileTypes]` correspond aux types souhaités

---

## 🔮 Évolutions Futures

### Types à Ajouter (sur demande)

- `color` - Sélecteur de couleur
- `range` - Slider de valeurs
- `time` - Sélecteur d'heure
- `month` - Sélecteur de mois
- `week` - Sélecteur de semaine

### Améliorations Prévues

- Validation custom avec messages personnalisés
- Support i18n pour les messages d'erreur
- Thèmes personnalisables
- Mode read-only global

---

## 📞 Support

Pour toute question ou suggestion :

- **Fichier** : [src/app/components/dynamic-form/field-renderer/](.)
- **Exemples** : [EXAMPLES.md](./EXAMPLES.md)
- **Tests** : Voir `harmonized-create-view` et `projects.component`

---

**Créé avec ❤️ pour CHAPS-DESIGN** - Documentation générée avec Claude Code
