diff --git a/README.md b/README.md index 19696f5..6a9f136 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ Une application web moderne pour gérer des campagnes de budgets participatifs, #### 🛠️ **Administration complète** - **Gestion des campagnes** : Création, modification, suppression +- **Support Markdown** : Éditeur avec prévisualisation pour les descriptions de campagnes - **États de campagne** : Dépôt de propositions, vote, terminé - **Statistiques en temps réel** : Nombre de propositions, participants, taux de participation - **Recherche** : Filtrage des campagnes par titre ou description @@ -42,6 +43,7 @@ Une application web moderne pour gérer des campagnes de budgets participatifs, #### 📝 **Gestion des propositions** - **Page dédiée** : Interface complète pour gérer les propositions par campagne - **CRUD complet** : Création, lecture, modification, suppression +- **Support Markdown** : Éditeur avec prévisualisation pour les descriptions - **Informations détaillées** : Auteur, email, date de création - **Interface moderne** : Cartes avec avatars et badges @@ -55,10 +57,12 @@ Une application web moderne pour gérer des campagnes de budgets participatifs, - **Dépôt de propositions** : Interface publique pour soumettre des propositions - URL unique et partageable - Formulaire avec validation + - Support Markdown pour les descriptions - Informations d'auteur obligatoires - **Vote public** : Interface de vote pour les participants - Slider interactif pour les choix de budget - Validation du budget total + - Affichage des descriptions avec support Markdown - Sauvegarde des votes #### 📧 **Système d'email** @@ -75,6 +79,12 @@ Une application web moderne pour gérer des campagnes de budgets participatifs, - **Icônes Lucide** : Icônes modernes et cohérentes ### 🔄 Fonctionnalités avancées +- **Support Markdown** : Éditeur avec prévisualisation pour les descriptions + - **Formatage de texte** : Gras, italique, souligné, barré + - **Titres** : H1, H2, H3 pour structurer le contenu + - **Listes** : Listes à puces et numérotées + - **Liens** : URLs externes avec validation de sécurité + - **Validation** : Contrôle de la longueur et des contenus dangereux - **URLs publiques** : Liens partageables pour le dépôt et le vote - **Copie de liens** : Boutons pour copier les URLs dans le presse-papiers - **Validation en temps réel** : Vérification des budgets lors du vote diff --git a/package-lock.json b/package-lock.json index 0b2bd13..5798fa8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,10 +19,12 @@ "@radix-ui/react-switch": "^1.2.6", "@radix-ui/react-tabs": "^1.1.13", "@supabase/supabase-js": "^2.56.0", + "@types/dompurify": "^3.0.5", "@types/nodemailer": "^7.0.1", "@types/xlsx": "^0.0.35", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "dompurify": "^3.2.6", "dotenv": "^17.2.1", "lucide-react": "^0.541.0", "next": "15.5.0", @@ -3560,6 +3562,15 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/dompurify": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz", + "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==", + "license": "MIT", + "dependencies": { + "@types/trusted-types": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -3626,6 +3637,12 @@ "@types/react": "^19.0.0" } }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT" + }, "node_modules/@types/uuid": { "version": "9.0.8", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", @@ -4941,6 +4958,15 @@ "node": ">=0.10.0" } }, + "node_modules/dompurify": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz", + "integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/dotenv": { "version": "17.2.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz", diff --git a/package.json b/package.json index 7956b18..c7842e1 100644 --- a/package.json +++ b/package.json @@ -22,10 +22,12 @@ "@radix-ui/react-switch": "^1.2.6", "@radix-ui/react-tabs": "^1.1.13", "@supabase/supabase-js": "^2.56.0", + "@types/dompurify": "^3.0.5", "@types/nodemailer": "^7.0.1", "@types/xlsx": "^0.0.35", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "dompurify": "^3.2.6", "dotenv": "^17.2.1", "lucide-react": "^0.541.0", "next": "15.5.0", diff --git a/src/app/campaigns/[id]/propose/page.tsx b/src/app/campaigns/[id]/propose/page.tsx index dfac3bc..d61235c 100644 --- a/src/app/campaigns/[id]/propose/page.tsx +++ b/src/app/campaigns/[id]/propose/page.tsx @@ -7,10 +7,11 @@ import { Campaign } from '@/types'; import { campaignService, propositionService } from '@/lib/services'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; -import { Textarea } from '@/components/ui/textarea'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { ArrowLeft, FileText, User, Mail, CheckCircle, AlertCircle } from 'lucide-react'; +import { MarkdownContent } from '@/components/MarkdownContent'; +import { MarkdownEditor } from '@/components/MarkdownEditor'; export const dynamic = 'force-dynamic'; @@ -192,9 +193,10 @@ export default function PublicProposePage() {

Description

-
- {campaign?.description} -
+
@@ -231,20 +233,13 @@ export default function PublicProposePage() { /> -
- -