diff --git a/.env b/.env index 3b2245b..a94444b 100644 --- a/.env +++ b/.env @@ -34,7 +34,7 @@ DEFAULT_URI=http://localhost # DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4" # DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8" ###< doctrine/doctrine-bundle ### -DATABASE_URL="mysql://appcontrib:123abc@127.0.0.1:3306/contribV2?serverVersion=8.0.32&charset=utf8mb4" +DATABASE_URL="mysql://appcontrib:123abc@127.0.0.1:3307/contribV2?serverVersion=8.0.32&charset=utf8mb4" ###> symfony/messenger ### # Choose one of the transports below # MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3610f19 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "editor.tabCompletion": "on", + "diffEditor.codeLens": true, + "MutableAI.upsell": true +} \ No newline at end of file diff --git a/INLINE_EDITING.md b/INLINE_EDITING.md new file mode 100644 index 0000000..d282ccc --- /dev/null +++ b/INLINE_EDITING.md @@ -0,0 +1,205 @@ +# Système d'Édition Inline avec Verrouillage + +## Vue d'ensemble + +Ce système permet l'édition directe des données dans les listes avec un mécanisme de verrouillage pour éviter les modifications concurrentes. + +## Fonctionnalités + +### ✅ Édition Inline +- **Clic pour éditer** : Cliquez sur une cellule éditables pour la modifier +- **Sauvegarde automatique** : Les modifications sont sauvegardées après 2 secondes d'inactivité +- **Validation en temps réel** : Vérification des données avant sauvegarde +- **Raccourcis clavier** : + - `Entrée` : Sauvegarder + - `Échap` : Annuler + +### ✅ Système de Verrouillage +- **Verrous automatiques** : Acquisition automatique lors de l'édition +- **Expiration** : Les verrous expirent après 30 minutes +- **Prolongation** : Les verrous sont automatiquement prolongés +- **Protection concurrente** : Empêche les modifications simultanées + +### ✅ Interface Utilisateur +- **Indicateurs visuels** : Cellules verrouillées avec icône 🔒 +- **Messages d'état** : Notifications de succès/erreur +- **Styles adaptatifs** : Couleurs différentes selon l'état + +## Architecture + +### Entités +- **Lock** : Gestion des verrous avec expiration +- **Membre** : Entité principale avec édition inline + +### Services +- **LockService** : Gestion des verrous (création, suppression, vérification) +- **MembreApiController** : API REST pour l'édition inline + +### JavaScript +- **InlineEditing** : Classe principale pour l'édition inline +- **Gestion des verrous** : Acquisition, prolongation, libération +- **Interface utilisateur** : Création d'inputs, validation, sauvegarde + +## Utilisation + +### 1. Édition d'un Membre +```javascript +// Clic sur une cellule éditables +// → Acquisition automatique du verrou +// → Création d'un input +// → Sauvegarde automatique après 2s +``` + +### 2. Gestion des Verrous +```bash +# Nettoyer les verrous expirés +php bin/console app:cleanup-locks + +# Voir les statistiques +/lock/stats +``` + +### 3. API Endpoints +``` +POST /api/membre/{id}/lock # Acquérir un verrou +POST /api/membre/{id}/unlock # Libérer un verrou +POST /api/membre/{id}/extend-lock # Prolonger un verrou +POST /api/membre/{id}/update-field # Mettre à jour un champ +GET /api/membre/{id}/lock-status # Statut du verrou +``` + +## Configuration + +### Durée des Verrous +```php +// Dans Lock.php +$this->expiresAt = new \DateTime('+30 minutes'); +``` + +### Vérification des Verrous +```javascript +// Dans inline-editing.js +this.lockCheckInterval = setInterval(() => { + this.checkLocks(); +}, 30000); // Toutes les 30 secondes +``` + +### Sauvegarde Automatique +```javascript +// Dans inline-editing.js +input.addEventListener('input', () => { + clearTimeout(saveTimeout); + saveTimeout = setTimeout(() => { + this.saveField(entityType, entityId, field, input.value); + }, 2000); // 2 secondes +}); +``` + +## Sécurité + +### Protection des Données +- **Validation côté serveur** : Vérification des données avant sauvegarde +- **Vérification des verrous** : Contrôle d'accès basé sur les verrous +- **Nettoyage automatique** : Suppression des verrous expirés + +### Gestion des Conflits +- **Détection de verrous** : Vérification avant édition +- **Messages d'erreur** : Notification si élément verrouillé +- **Libération automatique** : Nettoyage à la fermeture de page + +## Monitoring + +### Statistiques +- **Verrous actifs** : Nombre de verrous en cours +- **Verrous expirés** : Éléments à nettoyer +- **Utilisateurs** : Qui modifie quoi + +### Commandes de Maintenance +```bash +# Nettoyer les verrous expirés +php bin/console app:cleanup-locks + +# Voir les statistiques +curl /lock/stats +``` + +## Extension + +### Ajouter l'Édition Inline à d'Autres Entités + +1. **Créer l'API Controller** : +```php +// src/Controller/Api/ProjetApiController.php +#[Route('/api/projet')] +class ProjetApiController extends AbstractController +{ + // Implémenter les mêmes méthodes que MembreApiController +} +``` + +2. **Mettre à jour le Template** : +```html +
| Id | +Nom | +Nombre de contributions | +Moyenne pertinence | +Moyenne temps | +Actions | +
|---|---|---|---|---|---|
| {{ assistant_ia.id }} | +{{ assistant_ia.nom }} | +{{ assistant_ia.nombreContributions }} | ++ {% if assistant_ia.moyennePertinence %} + {{ assistant_ia.moyennePertinence }}/5 + {% else %} + Non évalué + {% endif %} + | ++ {% if assistant_ia.moyenneTemps %} + {{ assistant_ia.moyenneTemps }}/5 + {% else %} + Non évalué + {% endif %} + | ++ + Voir + + + Modifier + + | +
| Aucun assistant IA trouvé | +|||||
ID : {{ assistant_ia.id }}
+Nom : {{ assistant_ia.nom }}
+Nombre de contributions : {{ assistant_ia.nombreContributions }}
+Moyenne pertinence : + {% if assistant_ia.moyennePertinence %} + {{ assistant_ia.moyennePertinence }}/5 + {% else %} + Non évalué + {% endif %} +
+Moyenne temps : + {% if assistant_ia.moyenneTemps %} + {{ assistant_ia.moyenneTemps }}/5 + {% else %} + Non évalué + {% endif %} +
+| Contribution | +Pertinence | +Temps | +Moyenne | +Commentaire | +
|---|---|---|---|---|
| + + {{ contrib_ia.contribution }} + + | ++ {% if contrib_ia.evaluationPertinence %} + {{ contrib_ia.libellePertinence }} + {% else %} + Non évalué + {% endif %} + | ++ {% if contrib_ia.evaluationTemps %} + {{ contrib_ia.libelleTemps }} + {% else %} + Non évalué + {% endif %} + | ++ {% if contrib_ia.moyenneEvaluation %} + {{ contrib_ia.moyenneEvaluation }}/5 + {% else %} + Non évalué + {% endif %} + | +{{ contrib_ia.commentaire|default('') }} | +
| Id | +Assistant IA | +Contribution | +Pertinence | +Temps | +Moyenne | +Actions | +
|---|---|---|---|---|---|---|
| {{ contrib_ia.id }} | +{{ contrib_ia.assistantIa.nom }} | +{{ contrib_ia.contribution }} | ++ {% if contrib_ia.evaluationPertinence %} + {{ contrib_ia.libellePertinence }} + {% else %} + Non évalué + {% endif %} + | ++ {% if contrib_ia.evaluationTemps %} + {{ contrib_ia.libelleTemps }} + {% else %} + Non évalué + {% endif %} + | ++ {% if contrib_ia.moyenneEvaluation %} + {{ contrib_ia.moyenneEvaluation }}/5 + {% else %} + Non évalué + {% endif %} + | ++ + Voir + + + Modifier + + | +
| Aucune contribution IA trouvée | +||||||
ID : {{ contrib_ia.id }}
+Assistant IA : + + {{ contrib_ia.assistantIa.nom }} + +
+Contribution : + + {{ contrib_ia.contribution }} + +
+Pertinence : + {% if contrib_ia.evaluationPertinence %} + {{ contrib_ia.libellePertinence }} ({{ contrib_ia.evaluationPertinence }}/5) + {% else %} + Non évalué + {% endif %} +
+Temps : + {% if contrib_ia.evaluationTemps %} + {{ contrib_ia.libelleTemps }} ({{ contrib_ia.evaluationTemps }}/5) + {% else %} + Non évalué + {% endif %} +
+Moyenne : + {% if contrib_ia.moyenneEvaluation %} + {{ contrib_ia.moyenneEvaluation }}/5 + {% else %} + Non évalué + {% endif %} +
+{{ contrib_ia.commentaire }}
+| Id | +Membre | +Projet | +Date | +Durée | +Commentaire | +Actions | +
|---|---|---|---|---|---|---|
| {{ contribution.id }} | +{{ contribution.membre }} | +{{ contribution.projet.nom }} | +{{ contribution.dateContribution|date('d/m/Y') }} | +{{ contribution.dureeFormatee }} | +{{ contribution.commentaire|default('')|slice(0, 50) }}{% if contribution.commentaire|length > 50 %}...{% endif %} | ++ + Voir + + + Modifier + + | +
| Aucune contribution trouvée | +||||||
ID : {{ contribution.id }}
+Membre : + + {{ contribution.membre }} + +
+Projet : + + {{ contribution.projet.nom }} + +
+Date : {{ contribution.dateContribution|date('d/m/Y') }}
+Durée : {{ contribution.dureeFormatee }}
+{{ contribution.commentaire }}
+ {% else %} +Aucun commentaire
+ {% endif %} +| Assistant IA | +Pertinence | +Temps | +Moyenne | +Commentaire | +
|---|---|---|---|---|
| + + {{ contrib_ia.assistantIa.nom }} + + | ++ {% if contrib_ia.evaluationPertinence %} + {{ contrib_ia.libellePertinence }} + {% else %} + Non évalué + {% endif %} + | ++ {% if contrib_ia.evaluationTemps %} + {{ contrib_ia.libelleTemps }} + {% else %} + Non évalué + {% endif %} + | ++ {% if contrib_ia.moyenneEvaluation %} + {{ contrib_ia.moyenneEvaluation }}/5 + {% else %} + Non évalué + {% endif %} + | +{{ contrib_ia.commentaire|default('') }} | +
Bienvenue dans votre système de gestion de projet avec intelligence artificielle.
+Configurez et gérez vos assistants d'intelligence artificielle.
+ Voir les assistants IA +Verrous actifs : -
+Dernière vérification : -
+Verrous expirés : -
+Chargement...
+| Id | +Nom | +Prénom | +Contributions | +Actions | +|
|---|---|---|---|---|---|
| {{ membre.id }} | ++ {{ membre.nom|default('') }} + | ++ {{ membre.prenom|default('') }} + | ++ {{ membre.email|default('') }} + | +{{ membre.contributions|length }} | ++ + Voir + + + Modifier + + | +
| Aucun membre trouvé | +|||||
ID : {{ membre.id }}
+Nom : {{ membre.nom }}
+Prénom : {{ membre.prenom }}
+Email : {{ membre.email }}
+Nombre de contributions : {{ membre.contributions|length }}
+| Projet | +Date | +Durée | +Commentaire | +Actions | +
|---|---|---|---|---|
| {{ contribution.projet.nom }} | +{{ contribution.dateContribution|date('d/m/Y') }} | +{{ contribution.dureeFormatee }} | +{{ contribution.commentaire|default('')|slice(0, 30) }}{% if contribution.commentaire|length > 30 %}...{% endif %} | ++ + Voir + + | +
| Id | +Nom | +Statut | +Date lancement | +Date clôture | +Commentaire | +Actions | +
|---|---|---|---|---|---|---|
| {{ projet.id }} | +{{ projet.nom }} | ++ {% set statutClass = { + 'en_attente': 'warning', + 'en_cours': 'info', + 'termine': 'success', + 'annule': 'danger' + } %} + + {% for label, value in projet.getStatutChoices() %} + {% if value == projet.statut %}{{ label }}{% endif %} + {% endfor %} + + | +{{ projet.dateLancement ? projet.dateLancement|date('d/m/Y') : '-' }} | +{{ projet.dateCloture ? projet.dateCloture|date('d/m/Y') : '-' }} | +{{ projet.commentaire|default('')|slice(0, 50) }}{% if projet.commentaire|length > 50 %}...{% endif %} | ++ + Voir + + + Modifier + + | +
| Aucun projet trouvé | +||||||
ID : {{ projet.id }}
+Nom : {{ projet.nom }}
+Statut : + {% set statutClass = { + 'en_attente': 'warning', + 'en_cours': 'info', + 'termine': 'success', + 'annule': 'danger' + } %} + + {% for label, value in projet.getStatutChoices() %} + {% if value == projet.statut %}{{ label }}{% endif %} + {% endfor %} + +
+Date de lancement : {{ projet.dateLancement ? projet.dateLancement|date('d/m/Y') : 'Non définie' }}
+Date de clôture : {{ projet.dateCloture ? projet.dateCloture|date('d/m/Y') : 'Non définie' }}
+{{ projet.commentaire }}
+ {% else %} +Aucun commentaire
+ {% endif %} +| Membre | +Date | +Durée | +Commentaire | +Actions | +
|---|---|---|---|---|
| {{ contribution.membre }} | +{{ contribution.dateContribution|date('d/m/Y') }} | +{{ contribution.dureeFormatee }} | +{{ contribution.commentaire|default('')|slice(0, 30) }}{% if contribution.commentaire|length > 30 %}...{% endif %} | ++ + Voir + + | +
| Nom | +Prénom | +|
|---|---|---|
| + Jean + | ++ Dupont + | ++ jean.dupont@example.com + | +