'use client'; import { useEffect, useRef, useState, useCallback } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { apiFetch } from '../../../lib/api'; import { useSession } from '../../../components/session-provider'; interface Task { id: string; title: string; status: string; priority: string | null; tags: string[]; } const PRESETS = [ { label: '25 min', work: 25, break: 5 }, { label: '50 min', work: 50, break: 10 }, { label: '90 min', work: 90, break: 15 }, ]; export default function FocusSessionPage() { const { activeTenant } = useSession(); const tenantId = activeTenant?.tenantId ?? ''; const qc = useQueryClient(); const [preset, setPreset] = useState(PRESETS[0]); const [mode, setMode] = useState<'idle' | 'work' | 'break'>('idle'); const [secsLeft, setSecsLeft] = useState(preset.work * 60); const [selectedTask, setSelectedTask] = useState(''); const [completedSessions, setCompletedSessions] = useState(0); const intervalRef = useRef | null>(null); const { data: tasks = [] } = useQuery({ queryKey: ['focus-tasks', tenantId], queryFn: () => apiFetch('/v1/tasks?limit=200', { tenantId }), enabled: Boolean(tenantId), staleTime: 60_000, }); const activeTasks = tasks.filter((t) => !['completed', 'cancelled'].includes(t.status)); const urgentTasks = activeTasks.filter((t) => t.priority === 'urgent' || t.priority === 'high'); const logMut = useMutation({ mutationFn: (taskTitle: string) => apiFetch('/v1/observations', { tenantId, method: 'POST', body: { metric: 'focus-session', value: `${preset.work}`, unit: 'min', subjectType: 'productivity', confidence: 1, source: taskTitle || undefined, observedAt: new Date().toISOString(), }}), onSuccess: () => qc.invalidateQueries({ queryKey: ['focus-obs', tenantId] }), }); const start = useCallback(() => { setMode('work'); setSecsLeft(preset.work * 60); }, [preset]); const stop = useCallback(() => { if (intervalRef.current) clearInterval(intervalRef.current); setMode('idle'); setSecsLeft(preset.work * 60); }, []); useEffect(() => { if (mode === 'idle') { if (intervalRef.current) clearInterval(intervalRef.current); return; } intervalRef.current = setInterval(() => { setSecsLeft((s) => { if (s <= 1) { if (mode === 'work') { setCompletedSessions((c) => c + 1); logMut.mutate(selectedTask); setMode('break'); return preset.break * 60; } else { setMode('idle'); return preset.work * 60; } } return s - 1; }); }, 1000); return () => { if (intervalRef.current) clearInterval(intervalRef.current); }; }, [mode, preset, selectedTask]); useEffect(() => { if (mode === 'idle') setSecsLeft(preset.work * 60); }, [preset]); const mins = Math.floor(secsLeft / 60).toString().padStart(2, '0'); const secs = (secsLeft % 60).toString().padStart(2, '0'); const totalSecs = (mode === 'work' ? preset.work : preset.break) * 60; const progressPct = mode === 'idle' ? 0 : ((totalSecs - secsLeft) / totalSecs) * 100; const r = 90; const circ = 2 * Math.PI * r; const dash = circ * (1 - progressPct / 100); return (

Focus Session

Timer de lucru focalizat cu logare automată.

{/* Preset selection */}
{PRESETS.map((p) => ( ))}
{/* Task picker */}
{/* Timer circle */}

{mins}:{secs}

{mode === 'idle' ? 'Ready' : mode === 'work' ? 'Focus' : 'Pauză ☕'}

{mode === 'idle' ? ( ) : ( )}
{completedSessions > 0 && (
{Array.from({ length: completedSessions }, (_, i) => ( 🍅 ))}

{completedSessions} sesiune{completedSessions > 1 ? 'i' : ''} azi

)}

Sesiunile completate se loghează automat ca observații (focus-session) în CEO OS.

); }