diff --git a/manager/components/Exams.tsx b/manager/components/Exams.tsx index 3577449..4221f82 100644 --- a/manager/components/Exams.tsx +++ b/manager/components/Exams.tsx @@ -25,20 +25,28 @@ const Exams: React.FC = ({ data, updateData }) => { activeProvider: data.aiConfig?.activeProvider || 'gemini', geminiKey: data.aiConfig?.geminiKey ? '********' : '', openaiKey: data.aiConfig?.openaiKey ? '********' : '', - claudeKey: data.aiConfig?.claudeKey ? '********' : '' + claudeKey: data.aiConfig?.claudeKey ? '********' : '', + geminiModel: data.aiConfig?.geminiModel || 'gemini-1.5-flash', + openaiModel: data.aiConfig?.openaiModel || 'gpt-4o-mini', + claudeModel: data.aiConfig?.claudeModel || 'claude-3-5-sonnet-20241022' }); const [isGeneratingAI, setIsGeneratingAI] = useState(false); const [aiTopic, setAiTopic] = useState(''); const [aiQuantity, setAiQuantity] = useState(5); const [aiDifficulty, setAiDifficulty] = useState('medium'); + const [aiContextText, setAiContextText] = useState(''); + const [isGeneratingImage, setIsGeneratingImage] = useState<{[key: string]: boolean}>({}); const handleOpenAIConfig = () => { setAiForm({ activeProvider: data.aiConfig?.activeProvider || 'gemini', geminiKey: data.aiConfig?.geminiKey ? '********' : '', openaiKey: data.aiConfig?.openaiKey ? '********' : '', - claudeKey: data.aiConfig?.claudeKey ? '********' : '' + claudeKey: data.aiConfig?.claudeKey ? '********' : '', + geminiModel: data.aiConfig?.geminiModel || 'gemini-1.5-flash', + openaiModel: data.aiConfig?.openaiModel || 'gpt-4o-mini', + claudeModel: data.aiConfig?.claudeModel || 'claude-3-5-sonnet-20241022' }); setShowAIConfigModal(true); }; @@ -48,7 +56,10 @@ const Exams: React.FC = ({ data, updateData }) => { activeProvider: aiForm.activeProvider, geminiKey: aiForm.geminiKey === '********' ? (data.aiConfig?.geminiKey || '') : aiForm.geminiKey, openaiKey: aiForm.openaiKey === '********' ? (data.aiConfig?.openaiKey || '') : aiForm.openaiKey, - claudeKey: aiForm.claudeKey === '********' ? (data.aiConfig?.claudeKey || '') : aiForm.claudeKey + claudeKey: aiForm.claudeKey === '********' ? (data.aiConfig?.claudeKey || '') : aiForm.claudeKey, + geminiModel: aiForm.geminiModel, + openaiModel: aiForm.openaiModel, + claudeModel: aiForm.claudeModel }; try { @@ -295,7 +306,8 @@ const Exams: React.FC = ({ data, updateData }) => { body: JSON.stringify({ topic: aiTopic, quantity: qty, - difficulty: aiDifficulty + difficulty: aiDifficulty, + contextText: aiContextText }) }); @@ -356,7 +368,8 @@ const Exams: React.FC = ({ data, updateData }) => { body: JSON.stringify({ topic: topic, quantity: 1, - difficulty: aiDifficulty + difficulty: aiDifficulty, + contextText: aiContextText }) }); @@ -396,6 +409,57 @@ const Exams: React.FC = ({ data, updateData }) => { } }; + const handleGenerateImageAI = async (qIndex: number) => { + if (!editingExam) return; + const question = editingExam.questions[qIndex]; + if (!question.text.trim()) { + showAlert('Atenção', 'Digite o enunciado da questão para gerar a imagem.', 'warning'); + return; + } + + const activeProvider = data.aiConfig?.activeProvider || 'gemini'; + const hasKey = !!( + (activeProvider === 'gemini' && data.aiConfig?.geminiKey) || + (activeProvider === 'openai' && data.aiConfig?.openaiKey) || + (activeProvider === 'claude' && data.aiConfig?.claudeKey) + ); + + if (!hasKey) { + showAlert('IA não configurada', 'Por favor, configure sua chave de API clicando no botão "Configurar IA" no cabeçalho.', 'warning'); + return; + } + + setIsGeneratingImage(prev => ({ ...prev, [question.id]: true })); + try { + const response = await fetch('/api/ai/generate-image', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ prompt: question.text }) + }); + + if (!response.ok) { + const errData = await response.json(); + throw new Error(errData.error || 'Erro ao gerar imagem por IA.'); + } + + const { url } = await response.json(); + if (!url) { + throw new Error('URL da imagem não retornada.'); + } + + const newQuestions = [...editingExam.questions]; + newQuestions[qIndex] = { ...newQuestions[qIndex], imageUrl: url }; + setEditingExam({ ...editingExam, questions: newQuestions }); + + showAlert('Sucesso', 'Imagem gerada por IA e anexada com sucesso!', 'success'); + } catch (e: any) { + console.error(e); + showAlert('Erro ao gerar imagem', e.message || 'Houve uma falha de comunicação com o servidor para gerar a imagem.', 'error'); + } finally { + setIsGeneratingImage(prev => ({ ...prev, [question.id]: false })); + } + }; + const handleAddQuestion = () => { if (!editingExam) return; setEditingExam({ @@ -723,6 +787,41 @@ const Exams: React.FC = ({ data, updateData }) => { +
+
+ + +
+