diff --git a/src/components/PomodoroTimer.tsx b/src/components/PomodoroTimer.tsx index 0aaa4a8..da420d9 100644 --- a/src/components/PomodoroTimer.tsx +++ b/src/components/PomodoroTimer.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef } from "react"; +import { useState, useEffect, useRef, useCallback } from "react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; @@ -105,27 +105,7 @@ export function PomodoroTimer({ onStateChange }: PomodoroTimerProps = {}) { } }, [isRunning, timeLeft, mode, onStateChange]); - useEffect(() => { - if (isRunning && timeLeft > 0) { - intervalRef.current = setInterval(() => { - setTimeLeft(prev => { - if (prev <= 1) { - handleTimerComplete(); - return 0; - } - return prev - 1; - }); - }, 1000); - } else if (intervalRef.current) { - clearInterval(intervalRef.current); - } - - return () => { - if (intervalRef.current) clearInterval(intervalRef.current); - }; - }, [isRunning, timeLeft]); - - const handleTimerComplete = () => { + const handleTimerComplete = useCallback(() => { setIsRunning(false); // Play notification sound @@ -151,7 +131,34 @@ export function PomodoroTimer({ onStateChange }: PomodoroTimerProps = {}) { setTimeLeft(TIMER_DURATIONS.work); showSuccess('Pause terminée ! Prêt pour une nouvelle session ?'); } - }; + }, [mode, sessionsCompleted]); + + // Manage timer interval + useEffect(() => { + if (isRunning) { + intervalRef.current = setInterval(() => { + setTimeLeft(prev => { + if (prev <= 1) { + handleTimerComplete(); + return 0; + } + return prev - 1; + }); + }, 1000); + } else { + if (intervalRef.current) { + clearInterval(intervalRef.current); + intervalRef.current = null; + } + } + + return () => { + if (intervalRef.current) { + clearInterval(intervalRef.current); + intervalRef.current = null; + } + }; + }, [isRunning, handleTimerComplete]); const handleStart = () => { setIsRunning(true); diff --git a/src/components/QuickCaptureWidget.tsx b/src/components/QuickCaptureWidget.tsx index eb7457f..75a17d1 100644 --- a/src/components/QuickCaptureWidget.tsx +++ b/src/components/QuickCaptureWidget.tsx @@ -68,8 +68,7 @@ export function QuickCaptureWidget({ onNoteCaptured }: QuickCaptureWidgetProps) <> {/* Floating Action Button */} ) : ( - +
@@ -2995,11 +2997,23 @@ const Index = () => { Assistant IA
+ @@ -3007,6 +3021,7 @@ const Index = () => { variant="ghost" size="sm" onClick={() => setChatOpen(false)} + title="Fermer" > @@ -3175,6 +3190,154 @@ const Index = () => { )} + {/* Expanded Chat Modal */} + + + +
+ + + Assistant IA + +
+ +
+
+
+ + + + + + + + Aucun modèle trouvé. + + + {openRouterModels.map((model) => ( + { + setSelectedModel(currentValue); + setModelSelectorOpen(false); + }} + > + +
+ {model.name} + {model.id} +
+
+ ))} +
+
+
+
+
+
+
+ +
+ {chatMessages.length === 0 ? ( +
+ +

Bienvenue dans l'Assistant IA

+

+ Posez vos questions et obtenez des réponses intelligentes grâce à l'IA. + {openRouterModels.length === 0 && user?.is_admin && ( + Configurez une clé API OpenRouter dans les paramètres. + )} +

+
+ ) : ( + chatMessages.map((msg, idx) => ( +
+
+
{msg.content}
+
+
+ )) + )} + {chatLoading && ( +
+
+
+ ● + ● + ● +
+
+
+ )} +
+
+ +
+
{ + e.preventDefault(); + handleSendMessage(); + }} + className="flex gap-2" + > + setChatInput(e.target.value)} + placeholder="Posez une question..." + disabled={chatLoading || !selectedModel} + className="flex-1" + /> + +
+
+ +
+ {/* Image modal for fullscreen view */} {selectedImage && (