|
|
import React, { useState } from 'react'; |
|
|
import { X, Send, Image as ImageIcon, Link2, Bold, Italic, List } from 'lucide-react'; |
|
|
import { useModal } from '../context/ModalContext'; |
|
|
import { useAuth } from '../context/AuthContext'; |
|
|
import { forumService } from '../services'; |
|
|
|
|
|
const AskQuestionModal: React.FC = () => { |
|
|
const { isAskQuestionModalOpen, closeAskQuestionModal } = useModal(); |
|
|
const { gainPoints, unlockBadge } = useAuth(); |
|
|
const [title, setTitle] = useState(''); |
|
|
const [category, setCategory] = useState(''); |
|
|
const [level, setLevel] = useState(''); |
|
|
const [tags, setTags] = useState<string[]>([]); |
|
|
const [tagInput, setTagInput] = useState(''); |
|
|
const [content, setContent] = useState(''); |
|
|
const [isSubmitting, setIsSubmitting] = useState(false); |
|
|
const [error, setError] = useState<string | null>(null); |
|
|
|
|
|
if (!isAskQuestionModalOpen) return null; |
|
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => { |
|
|
e.preventDefault(); |
|
|
setIsSubmitting(true); |
|
|
setError(null); |
|
|
|
|
|
try { |
|
|
|
|
|
const allTags = [ |
|
|
...tags, |
|
|
category.toLowerCase(), |
|
|
level.toLowerCase() |
|
|
].filter(Boolean); |
|
|
|
|
|
|
|
|
const newQuestion = await forumService.createQuestion({ |
|
|
title, |
|
|
content, |
|
|
tags: allTags |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
gainPoints(50); |
|
|
unlockBadge('b1'); |
|
|
|
|
|
|
|
|
alert('✅ Votre question a été publiée avec succès !'); |
|
|
|
|
|
|
|
|
closeAskQuestionModal(); |
|
|
|
|
|
|
|
|
setTitle(''); |
|
|
setCategory(''); |
|
|
setLevel(''); |
|
|
setTags([]); |
|
|
setTagInput(''); |
|
|
setContent(''); |
|
|
|
|
|
|
|
|
window.location.reload(); |
|
|
} catch (err: any) { |
|
|
console.error('Erreur lors de la création de la question:', err); |
|
|
setError( |
|
|
err.response?.data?.message || |
|
|
err.response?.data?.detail || |
|
|
'Impossible de publier votre question. Veuillez réessayer.' |
|
|
); |
|
|
} finally { |
|
|
setIsSubmitting(false); |
|
|
} |
|
|
}; |
|
|
|
|
|
const handleTagInputKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => { |
|
|
if (e.key === ' ' || e.key === 'Enter' || e.key === ',') { |
|
|
e.preventDefault(); |
|
|
const newTag = tagInput.trim(); |
|
|
if (newTag && !tags.includes(newTag)) { |
|
|
setTags([...tags, newTag]); |
|
|
} |
|
|
setTagInput(''); |
|
|
} else if (e.key === 'Backspace' && !tagInput && tags.length > 0) { |
|
|
setTags(tags.slice(0, -1)); |
|
|
} |
|
|
}; |
|
|
|
|
|
const removeTag = (tagToRemove: string) => { |
|
|
setTags(tags.filter(tag => tag !== tagToRemove)); |
|
|
}; |
|
|
|
|
|
return ( |
|
|
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm animate-in fade-in duration-200"> |
|
|
<div className="bg-white dark:bg-gray-800 rounded-2xl shadow-2xl w-full max-w-2xl max-h-[90vh] flex flex-col overflow-hidden animate-in zoom-in-95 duration-200 border border-gray-100 dark:border-gray-700"> |
|
|
<div className="flex justify-between items-center p-4 border-b border-gray-100 dark:border-gray-700 bg-gray-50 dark:bg-gray-900 shrink-0"> |
|
|
<h3 className="font-bold text-lg text-edu-primary dark:text-white">Poser une question à la communauté</h3> |
|
|
<button onClick={closeAskQuestionModal} className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 transition-colors p-1 hover:bg-gray-200 dark:hover:bg-gray-700 rounded-full"> |
|
|
<X size={20} /> |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
<form onSubmit={handleSubmit} className="p-6 space-y-5 overflow-y-auto"> |
|
|
{/* Message d'erreur */} |
|
|
{error && ( |
|
|
<div className="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 text-red-600 dark:text-red-400 px-4 py-3 rounded-xl text-sm animate-in slide-in-from-top-2"> |
|
|
<strong className="font-bold">Erreur: </strong> |
|
|
<span>{error}</span> |
|
|
</div> |
|
|
)} |
|
|
|
|
|
<div> |
|
|
<label className="block text-sm font-bold text-gray-700 dark:text-gray-300 mb-1.5">Titre de votre question</label> |
|
|
<input |
|
|
type="text" |
|
|
required |
|
|
value={title} |
|
|
onChange={(e) => setTitle(e.target.value)} |
|
|
disabled={isSubmitting} |
|
|
className="w-full rounded-xl border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-900 px-4 py-2.5 text-sm focus:ring-2 focus:ring-edu-secondary outline-none dark:text-white placeholder-gray-400 transition-shadow disabled:opacity-50 disabled:cursor-not-allowed" |
|
|
placeholder="Ex: Comment implémenter une authentification JWT ?" |
|
|
/> |
|
|
</div> |
|
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> |
|
|
<div> |
|
|
<label className="block text-sm font-bold text-gray-700 dark:text-gray-300 mb-1.5">Matière / Sujet</label> |
|
|
<select |
|
|
required |
|
|
value={category} |
|
|
onChange={(e) => setCategory(e.target.value)} |
|
|
disabled={isSubmitting} |
|
|
className="w-full rounded-xl border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-900 px-4 py-2.5 text-sm focus:ring-2 focus:ring-edu-secondary outline-none dark:text-white cursor-pointer transition-shadow disabled:opacity-50 disabled:cursor-not-allowed" |
|
|
> |
|
|
<option value="">Sélectionner</option> |
|
|
<option value="Mathématiques">Mathématiques</option> |
|
|
<option value="Informatique">Informatique</option> |
|
|
<option value="Physique">Physique</option> |
|
|
<option value="Biologie">Biologie</option> |
|
|
<option value="Économie">Économie</option> |
|
|
<option value="Droit">Droit</option> |
|
|
<option value="Langues">Langues</option> |
|
|
<option value="Autre">Autre</option> |
|
|
</select> |
|
|
</div> |
|
|
|
|
|
<div> |
|
|
<label className="block text-sm font-bold text-gray-700 dark:text-gray-300 mb-1.5">Niveau d'étude</label> |
|
|
<select |
|
|
required |
|
|
value={level} |
|
|
onChange={(e) => setLevel(e.target.value)} |
|
|
disabled={isSubmitting} |
|
|
className="w-full rounded-xl border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-900 px-4 py-2.5 text-sm focus:ring-2 focus:ring-edu-secondary outline-none dark:text-white cursor-pointer transition-shadow disabled:opacity-50 disabled:cursor-not-allowed" |
|
|
> |
|
|
<option value="">Sélectionner</option> |
|
|
<option value="Secondaire">Secondaire / Lycée</option> |
|
|
<option value="Licence">Licence (L1-L3)</option> |
|
|
<option value="Master">Master (M1-M2)</option> |
|
|
<option value="Doctorat">Doctorat</option> |
|
|
<option value="Autre">Autre / Formation Pro</option> |
|
|
</select> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div> |
|
|
<label className="block text-sm font-bold text-gray-700 dark:text-gray-300 mb-1.5">Tags (Mots-clés)</label> |
|
|
<div className="w-full rounded-xl border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-900 px-3 py-2 text-sm focus-within:ring-2 focus-within:ring-edu-secondary transition-shadow flex flex-wrap gap-2 items-center"> |
|
|
{tags.map(tag => ( |
|
|
<span key={tag} className="bg-edu-secondary/10 text-edu-secondary dark:text-blue-300 px-2 py-1 rounded-lg flex items-center gap-1 text-xs font-bold animate-in zoom-in duration-200"> |
|
|
{tag} |
|
|
<button type="button" onClick={() => removeTag(tag)} className="hover:text-red-500 transition-colors" disabled={isSubmitting}> |
|
|
<X size={14} /> |
|
|
</button> |
|
|
</span> |
|
|
))} |
|
|
<input |
|
|
type="text" |
|
|
value={tagInput} |
|
|
onChange={(e) => setTagInput(e.target.value)} |
|
|
onKeyDown={handleTagInputKeyDown} |
|
|
disabled={isSubmitting} |
|
|
className="flex-grow bg-transparent outline-none min-w-[150px] dark:text-white placeholder-gray-400 h-8 disabled:opacity-50" |
|
|
placeholder={tags.length === 0 ? "Ex: algebre, react (Espace pour ajouter)" : ""} |
|
|
/> |
|
|
</div> |
|
|
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1.5">Appuyez sur <kbd className="bg-gray-100 dark:bg-gray-700 px-1 rounded">Espace</kbd> pour créer un tag.</p> |
|
|
</div> |
|
|
|
|
|
<div> |
|
|
<label className="block text-sm font-bold text-gray-700 dark:text-gray-300 mb-1.5">Détails</label> |
|
|
<div className="border border-gray-300 dark:border-gray-600 rounded-xl overflow-hidden focus-within:ring-2 focus-within:ring-edu-secondary transition-shadow bg-white dark:bg-gray-900"> |
|
|
{/* Toolbar simulation */} |
|
|
<div className="bg-gray-50 dark:bg-gray-800/50 border-b border-gray-200 dark:border-gray-600 p-2 flex gap-1"> |
|
|
<button type="button" className="p-1.5 hover:bg-gray-200 dark:hover:bg-gray-700 rounded text-gray-500 dark:text-gray-400" disabled={isSubmitting}><Bold size={16} /></button> |
|
|
<button type="button" className="p-1.5 hover:bg-gray-200 dark:hover:bg-gray-700 rounded text-gray-500 dark:text-gray-400" disabled={isSubmitting}><Italic size={16} /></button> |
|
|
<button type="button" className="p-1.5 hover:bg-gray-200 dark:hover:bg-gray-700 rounded text-gray-500 dark:text-gray-400" disabled={isSubmitting}><List size={16} /></button> |
|
|
<div className="w-px h-6 bg-gray-300 dark:bg-gray-600 mx-1"></div> |
|
|
<button type="button" className="p-1.5 hover:bg-gray-200 dark:hover:bg-gray-700 rounded text-gray-500 dark:text-gray-400" disabled={isSubmitting}><Link2 size={16} /></button> |
|
|
<button type="button" className="p-1.5 hover:bg-gray-200 dark:hover:bg-gray-700 rounded text-gray-500 dark:text-gray-400" disabled={isSubmitting}><ImageIcon size={16} /></button> |
|
|
</div> |
|
|
<textarea |
|
|
required |
|
|
value={content} |
|
|
onChange={(e) => setContent(e.target.value)} |
|
|
disabled={isSubmitting} |
|
|
className="w-full p-4 min-h-[150px] bg-transparent border-none outline-none text-sm dark:text-white resize-none placeholder-gray-400 disabled:opacity-50" |
|
|
placeholder="Expliquez votre problème en détail pour obtenir une meilleure réponse..." |
|
|
></textarea> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div className="pt-2 flex justify-end gap-3 pb-2"> |
|
|
<button |
|
|
type="button" |
|
|
onClick={closeAskQuestionModal} |
|
|
disabled={isSubmitting} |
|
|
className="px-5 py-2.5 text-sm font-medium text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 rounded-xl transition-colors disabled:opacity-50 disabled:cursor-not-allowed" |
|
|
> |
|
|
Annuler |
|
|
</button> |
|
|
<button |
|
|
type="submit" |
|
|
disabled={isSubmitting} |
|
|
className="px-5 py-2.5 text-sm font-bold text-white bg-edu-secondary hover:bg-edu-primary rounded-xl shadow-lg hover:shadow-xl transition-all flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed" |
|
|
> |
|
|
{isSubmitting ? ( |
|
|
<> |
|
|
<div className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin"></div> |
|
|
Publication... |
|
|
</> |
|
|
) : ( |
|
|
<> |
|
|
<Send size={16} /> |
|
|
Publier |
|
|
</> |
|
|
)} |
|
|
</button> |
|
|
</div> |
|
|
</form> |
|
|
</div> |
|
|
</div> |
|
|
); |
|
|
}; |
|
|
|
|
|
export default AskQuestionModal; |