diff --git a/client/index.html b/client/index.html index 18597a5..4636db0 100644 --- a/client/index.html +++ b/client/index.html @@ -7,8 +7,8 @@ - - + + (null); + // Enable global notifications polling + useNotifications({ poll: true }); + const queryClient = useQueryClient(); const { data: user, isLoading: isLoadingUser } = useQuery({ @@ -381,6 +387,7 @@ function App() { )} + diff --git a/client/src/components/FocusMode.tsx b/client/src/components/FocusMode.tsx index d30956a..d74317c 100644 --- a/client/src/components/FocusMode.tsx +++ b/client/src/components/FocusMode.tsx @@ -4,7 +4,7 @@ import { Task } from '@shared/schema'; import TaskCard from './TaskCard'; import { Card } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; -import { Trophy, Target, Calendar as CalendarIcon, ArrowRight, GripVertical } from 'lucide-react'; +import { Trophy, Target, Calendar as CalendarIcon, ArrowRight, GripVertical, Sun, Moon } from 'lucide-react'; import { motion, AnimatePresence } from 'framer-motion'; import { DndContext, @@ -190,13 +190,35 @@ export default function FocusMode({ initial={{ opacity: 0, scale: 0.95 }} animate={{ opacity: 1, scale: 1 }} transition={{ delay: 0.2 }} + className="grid grid-rows-2 gap-4 h-full" > - - -

{t('focus.viewAll.title')}

-

{t('focus.viewAll.description')}

- + + + + +
+ +

{t('focus.viewAll.title')}

+
+
diff --git a/client/src/components/TaskCard.tsx b/client/src/components/TaskCard.tsx index 0afe667..301861c 100644 --- a/client/src/components/TaskCard.tsx +++ b/client/src/components/TaskCard.tsx @@ -31,6 +31,10 @@ import { triggerConfetti } from "@/lib/confetti"; import { playSuccessSound } from "@/lib/sounds"; import { simulateAIDecomposition } from "@/lib/ai-simulator"; import { Wand2, Loader2 } from "lucide-react"; +import { apiRequest } from "@/lib/queryClient"; +import { useToast } from "@/hooks/use-toast"; + + interface TaskCardProps { task: Task; @@ -120,6 +124,7 @@ function SharedMenuItem({ task, onShare }: { task: Task, onShare: () => void }) export default function TaskCard({ task, onStartTimer, onStopTimer, onEdit, onDelete, onStatusChange, onUpdate, isDragging }: TaskCardProps) { const { t } = useTranslation(); + const { toast } = useToast(); const [isAnalyzing, setIsAnalyzing] = useState(false); const [isShareModalOpen, setIsShareModalOpen] = useState(false); @@ -201,6 +206,56 @@ export default function TaskCard({ task, onStartTimer, onStopTimer, onEdit, onDe return `${hours}h ${mins}m`; }; + /* AI Follow-up Check */ + const checkFollowUp = async () => { + // Fire and forget - don't block UI + try { + const res = await apiRequest("POST", "/api/ai/analyze-completion", { taskId: task.id }); + if (res.ok) { + const data = await res.json(); + if (data.needed && data.title) { + toast({ + title: t('ai.followUpSuggestion', 'Follow-up Suggested'), + description: `${data.title} - ${data.description || ''}`, + action: ( + + ), + duration: 8000, + }); + } + } + } catch (e) { + console.error("Follow-up check failed", e); + } + }; + const handleToggleComplete = (e: React.MouseEvent) => { e.stopPropagation(); if (isBlocked) return; @@ -210,6 +265,8 @@ export default function TaskCard({ task, onStartTimer, onStopTimer, onEdit, onDe triggerConfetti(e.clientX / window.innerWidth, e.clientY / window.innerHeight); playSuccessSound(); onStatusChange?.('done'); + // Trigger AI check + checkFollowUp(); } }; diff --git a/client/src/components/TaskCreationModal.tsx b/client/src/components/TaskCreationModal.tsx index 1e942e4..808699c 100644 --- a/client/src/components/TaskCreationModal.tsx +++ b/client/src/components/TaskCreationModal.tsx @@ -38,6 +38,12 @@ export default function TaskCreationModal({ isOpen, onClose, onSave }: TaskCreat const [isCalendarOpen, setIsCalendarOpen] = useState(false); const [isStartDateOpen, setIsStartDateOpen] = useState(false); + + const [isRecurring, setIsRecurring] = useState(false); + const [recurrenceInterval, setRecurrenceInterval] = useState(); + const [recurrenceIntervalValue, setRecurrenceIntervalValue] = useState(1); + const [recurrenceEnd, setRecurrenceEnd] = useState(); + const [error, setError] = useState(null); // Fetch labels @@ -62,6 +68,11 @@ export default function TaskCreationModal({ isOpen, onClose, onSave }: TaskCreat energyLevel, estimatedDuration, + isRecurring, + recurrenceInterval, + recurrenceIntervalValue, + recurrenceEnd, + startDate, dueDate, labelId, @@ -80,6 +91,10 @@ export default function TaskCreationModal({ isOpen, onClose, onSave }: TaskCreat setPriority('medium'); setDueDate(undefined); setStartDate(undefined); + setIsRecurring(false); + setRecurrenceInterval(undefined); + setRecurrenceIntervalValue(1); + setRecurrenceEnd(undefined); setLabelId(undefined); setDependencies([]); setError(null); @@ -99,6 +114,10 @@ export default function TaskCreationModal({ isOpen, onClose, onSave }: TaskCreat setPriority('medium'); setDueDate(undefined); setStartDate(undefined); + setIsRecurring(false); + setRecurrenceInterval(undefined); + setRecurrenceIntervalValue(1); + setRecurrenceEnd(undefined); setLabelId(undefined); setDependencies([]); setError(null); @@ -283,6 +302,42 @@ export default function TaskCreationModal({ isOpen, onClose, onSave }: TaskCreat + {/* Recurrence Selection */} +
+ + + + {isRecurring && ( +
+ {t('taskCreation.recurrence.every')} + setRecurrenceIntervalValue(parseInt(e.target.value) || 1)} + /> + + {recurrenceInterval && t(`taskCreation.recurrence.units.${recurrenceInterval}`)} + +
+ )} +
+ {/* Dependencies Selector */}
@@ -217,6 +221,21 @@ export function AiSettingsCard() { )} +
+ +