import React, { useState, useEffect, useCallback, useMemo } from 'react' import { Link, useNavigate, useSearchParams } from 'react-router-dom' import { Clock, CheckCircle2, FolderKanban, Play, Square, TrendingUp, Plus, Sparkles, ArrowRight, Flame, RefreshCw, Folder, Tag, Activity, AlertCircle, Zap, Target, Settings2, BarChart3, ExternalLink, User, Users, } from 'lucide-react' import { useAuth } from '../contexts/AuthContext' import { useTimer } from '../contexts/TimerContext' import { taskService } from '../services/taskService' import { projectService } from '../services/projectService' import type { Task, Project } from '../types/task' import { calculateTaskTotalSeconds, formatSecondsToHuman, formatSecondsToTimer } from '../lib/timeUtils' import { CreateTaskModal } from '../components/tasks/CreateTaskModal' import { getUserWorkGoal, calculateUserPeriodWorkedSeconds, type WorkGoal, } from '../lib/userGoals' export const DashboardPage: React.FC = () => { const { user, activeOrg } = useAuth() const { activeTask, activeSeconds, startTimer, stopTimer, syncWithTasks } = useTimer() const navigate = useNavigate() // Check if active task belongs to current user (as assignee or creator) const isMyActiveTask = Boolean( activeTask && user?.email && ((activeTask.user?.email && activeTask.user.email.toLowerCase() === user.email.toLowerCase()) || (activeTask.createdBy?.email && activeTask.createdBy.email.toLowerCase() === user.email.toLowerCase())) ) const currentActiveTask = isMyActiveTask ? activeTask : null const [tasks, setTasks] = useState([]) const [projects, setProjects] = useState([]) const [loading, setLoading] = useState(true) const [refreshing, setRefreshing] = useState(false) const [error, setError] = useState(null) // Work Goal State (Global) const [workGoal, setWorkGoal] = useState(() => getUserWorkGoal(user?.email)) // Listen to goal changes in localStorage or custom event useEffect(() => { if (user?.email) { setWorkGoal(getUserWorkGoal(user.email)) } const handleGoalChange = (e: Event) => { const custom = e as CustomEvent if (custom.detail) { setWorkGoal(custom.detail) } else if (user?.email) { setWorkGoal(getUserWorkGoal(user.email)) } } window.addEventListener('beetask_work_goal_changed', handleGoalChange) return () => { window.removeEventListener('beetask_work_goal_changed', handleGoalChange) } }, [user?.email]) // Modal const [isModalOpen, setIsModalOpen] = useState(false) // Scope Toggle State: 'mine' (Individual) vs 'team' (Equipe) const [searchParams, setSearchParams] = useSearchParams() const initialScope = (searchParams.get('scope') as 'mine' | 'team') || (localStorage.getItem('beetask_dashboard_scope') as 'mine' | 'team') || 'mine' const [viewScope, setViewScope] = useState<'mine' | 'team'>(initialScope) const handleScopeChange = (newScope: 'mine' | 'team') => { setViewScope(newScope) try { localStorage.setItem('beetask_dashboard_scope', newScope) } catch (_) {} setSearchParams( (prev) => { const next = new URLSearchParams(prev) next.set('scope', newScope) return next }, { replace: true } ) } const activeOrgId = activeOrg?.id const loadData = useCallback(async () => { if (!activeOrgId) return try { setRefreshing(true) const [tasksRes, projsRes] = await Promise.allSettled([ taskService.listTasks({ organizationId: activeOrgId }), projectService.listProjects(activeOrgId), ]) const loadedTasks = tasksRes.status === 'fulfilled' ? tasksRes.value || [] : [] setTasks(loadedTasks) syncWithTasks(loadedTasks) if (projsRes.status === 'fulfilled') { setProjects(projsRes.value || []) } } catch (err: unknown) { console.error(err) setError('Falha ao atualizar dados do dashboard.') } finally { setLoading(false) setRefreshing(false) } }, [activeOrgId, syncWithTasks]) useEffect(() => { loadData() }, [loadData]) // Filter user's own tasks const myTasks = useMemo(() => { return tasks.filter((t) => { if (!user?.email) return false return Boolean( t.user?.email && user?.email && t.user.email.toLowerCase() === user.email.toLowerCase() ) }) }, [tasks, user]) // Scope tasks based on viewScope const scopeTasks = viewScope === 'mine' ? myTasks : tasks // KPIs dynamically calculated based on active scope const stats = useMemo(() => { const inProgress = scopeTasks.filter((t) => t.status === 'IN_PROGRESS').length const completed = scopeTasks.filter((t) => t.status === 'COMPLETED').length const notStarted = scopeTasks.filter((t) => t.status === 'NOT_STARTED').length const totalSeconds = scopeTasks.reduce((acc, t) => acc + calculateTaskTotalSeconds(t), 0) const uniqueProjectIds = new Set(scopeTasks.map((t) => t.project?.id).filter(Boolean)) const projectsCount = viewScope === 'mine' ? uniqueProjectIds.size : projects.length return { inProgress, completed, notStarted, totalSeconds, projectsCount, } }, [scopeTasks, projects, viewScope]) // Work Goal Progress in Current Period const goalProgress = useMemo(() => { const targetSeconds = (workGoal?.targetHours || 8) * 3600 const workedSeconds = calculateUserPeriodWorkedSeconds( tasks, workGoal?.period || 'DAILY', currentActiveTask?.id, activeSeconds ) const percent = Math.min(100, Math.round((workedSeconds / targetSeconds) * 100)) const remainingSeconds = Math.max(0, targetSeconds - workedSeconds) const periodLabels: Record = { DAILY: 'Meta Diária', WEEKLY: 'Meta Semanal', MONTHLY: 'Meta Mensal', } const periodSubtitles: Record = { DAILY: 'Progresso de hoje', WEEKLY: 'Progresso desta semana', MONTHLY: 'Progresso deste mês', } return { title: periodLabels[workGoal?.period || 'DAILY'], subtitle: periodSubtitles[workGoal?.period || 'DAILY'], targetSeconds, workedSeconds, percent, remainingSeconds, targetHours: workGoal?.targetHours || 8, isCompleted: workedSeconds >= targetSeconds, } }, [tasks, workGoal, currentActiveTask?.id, activeSeconds]) // Recent 5 tasks based on active scope const recentTasks = useMemo(() => { return [...scopeTasks].slice(0, 5) }, [scopeTasks]) // Toggle timer on a task const handleToggleTimer = async (task: Task) => { try { if (currentActiveTask?.id === task.id) { const stopped = await stopTimer(task) if (stopped) { setTasks((prev) => prev.map((t) => (t.id === stopped.id ? stopped : t))) } } else { const started = await startTimer(task) setTasks((prev) => prev.map((t) => (t.id === started.id ? started : t))) } } catch (err: unknown) { console.error(err) setError('Erro ao acionar cronômetro.') } } const handleModalSubmit = async (taskData: { title: string description: string status: 'NOT_STARTED' | 'IN_PROGRESS' | 'COMPLETED' | 'CANCELED' projectId?: number categoryId?: number assigneeUserId?: number history?: import('../types/task').TaskHistoryItemInput[] externalUrl?: string }) => { if (!activeOrgId) return const created = await taskService.createTask({ title: taskData.title, description: taskData.description, status: taskData.status, projectId: taskData.projectId, categoryId: taskData.categoryId, assigneeUserId: taskData.assigneeUserId, organizationId: activeOrgId, externalUrl: taskData.externalUrl, }) setTasks((prev) => [created, ...prev]) } // Priority task to focus on if none running (only user's own tasks) const suggestedTask = useMemo(() => { const myTasks = tasks.filter((t) => { if (!user?.email || !t.user?.email) return false return t.user.email.toLowerCase() === user.email.toLowerCase() }) return ( myTasks.find((t) => t.status === 'IN_PROGRESS') || myTasks.find((t) => t.status === 'NOT_STARTED') || myTasks[0] || null ) }, [tasks, user]) const firstName = user?.name ? user.name.split(' ')[0] : 'Desenvolvedor' return (
{/* Top Banner with Landing Page aesthetic */}
{/* Top pill badge with View Switcher */}
Workspace: {activeOrg?.name || 'BeeTask'}
Experimentar BI v2

Olá, {firstName}!{' '} Bom trabalho hoje.

Acompanhe o ritmo de entregas da sua equipe, audite suas horas e mantenha o foco na próxima entrega.

{error && (
{error}
)} {/* Scope Selector: Individual (Minhas Tarefas) vs Equipe (Workspace) */}
Modo de Visualização:
{viewScope === 'mine' ? 'KPIs e métricas filtrados estritamente para demandas atribuídas a você' : 'KPIs e métricas consolidados de todos os integrantes do workspace'}
{/* 4 Premium KPI Cards with shadow-panel & backdrop-blur */} {loading ? (
{[1, 2, 3, 4].map((i) => (
))}
) : (
{/* In Progress */}
{viewScope === 'mine' ? 'Suas Tarefas em Andamento' : 'Tarefas da Equipe em Andamento'}

{stats.inProgress}

{viewScope === 'mine' ? ( ) : ( )}

{viewScope === 'mine' ? `${stats.notStarted} na sua fila aguardando` : `${stats.notStarted} tarefas aguardando no backlog`}

{/* Completed */}
{viewScope === 'mine' ? 'Suas Entregas Concluídas' : 'Entregas Concluídas da Equipe'}

{stats.completed}

{viewScope === 'mine' ? ( ) : ( )}

{viewScope === 'mine' ? `de ${myTasks.length} demandas suas` : `de ${tasks.length} demandas cadastradas`}

{/* Time Tracked */}
{viewScope === 'mine' ? 'Seu Tempo Dedicado' : 'Tempo Total da Equipe'}

{formatSecondsToTimer(stats.totalSeconds)}

{viewScope === 'mine' ? ( ) : ( )}

{viewScope === 'mine' ? `${formatSecondsToHuman(stats.totalSeconds)} auditadas por você` : `${formatSecondsToHuman(stats.totalSeconds)} auditadas no time`}

{/* Projects */}
{viewScope === 'mine' ? 'Seus Projetos Ativos' : 'Projetos da Organização'}

{stats.projectsCount}

{viewScope === 'mine' ? ( ) : ( )}

{viewScope === 'mine' ? 'Iniciativas com tarefas suas' : 'Iniciativas ativas no workspace'}

)} {/* Main Grid: Focus Session (5 cols) + Recent Sprint Tasks (7 cols) */}
{/* Focus Session Hero Card (Left 5 cols) */}
{/* Header with macOS style dots */}
Sessão em Foco
{currentActiveTask ? 'CRONOMETRANDO' : 'EM ESPERA'}
{/* Active or Suggested Task Info Box */} {currentActiveTask ? (
{currentActiveTask.project && ( {currentActiveTask.project.name} )} {currentActiveTask.category && ( {currentActiveTask.category.name} )}

{currentActiveTask.title}

) : suggestedTask ? (
Sugerida para Focar {suggestedTask.project && ( {suggestedTask.project.name} )}

{suggestedTask.title}

) : (
Nenhuma tarefa ativa. Crie uma demanda para iniciar sua sessão produtiva!
)} {/* Big Bold Timer Display */}
{currentActiveTask ? 'Tempo Decorrido na Tarefa' : 'Pronto para Iniciar'}

{currentActiveTask ? formatSecondsToTimer(activeSeconds) : '00:00:00'}

{/* Timer Actions */} {currentActiveTask ? ( ) : suggestedTask ? ( ) : ( )}
Usuário conectado {user?.name || user?.email}
{/* Workload Goal Progress Card */}

{goalProgress.title} ({goalProgress.subtitle})

Planejado: {goalProgress.targetHours}h {workGoal.period === 'DAILY' ? '/ dia' : workGoal.period === 'WEEKLY' ? '/ semana' : '/ mês'}

Ajustar
{/* Time progress values */}
{formatSecondsToTimer(goalProgress.workedSeconds)} / {goalProgress.targetHours}h
{goalProgress.percent}%
{/* Progress Bar */}
{goalProgress.isCompleted ? '🎉 Meta da jornada concluída!' : `Faltam ${formatSecondsToHuman(goalProgress.remainingSeconds)}`} {goalProgress.percent}% da meta
{/* Quick Shortcuts Card */}
Acesso Rápido ao Workspace
Ver Projetos Analytics da Org
{/* Recent Sprint Tasks (Right 7 cols) */}

{viewScope === 'mine' ? 'Minhas Demandas Recentes' : 'Demandas Recentes da Equipe'}

Ver todas
{loading ? (
{[1, 2, 3].map((i) => (
))}
) : recentTasks.length === 0 ? (

{viewScope === 'mine' ? 'Você não possui nenhuma demanda atribuída no momento.' : 'Nenhuma tarefa encontrada neste workspace.'}

) : (
{recentTasks.map((task) => { const isTracking = currentActiveTask?.id === task.id const taskSec = isTracking ? activeSeconds : calculateTaskTotalSeconds(task) return (
{/* Assignee Indicator */} {task.user?.name ? ( {user?.email && task.user.email && task.user.email.toLowerCase() === user.email.toLowerCase() ? 'Você' : task.user.name.split(' ')[0]} ) : ( Sem responsável )} {task.project && ( {task.project.name} )} {task.category && ( {task.category.name} )} {task.status === 'COMPLETED' ? 'Concluído' : task.status === 'IN_PROGRESS' ? 'Em Andamento' : task.status === 'CANCELED' ? 'Cancelado' : 'A Fazer'}

navigate('/tasks')} className="text-sm font-bold text-foreground hover:text-brand cursor-pointer truncate transition-colors" > {task.title}

{task.externalUrl && ( e.stopPropagation()} title={`Link externo: ${task.externalUrl}`} className="inline-flex items-center text-muted hover:text-brand transition-colors shrink-0 cursor-pointer" > )}
{formatSecondsToTimer(taskSec)}
) })}
)}
{/* Task Creation Modal */} {activeOrgId && ( setIsModalOpen(false)} onSubmit={handleModalSubmit} organizationId={activeOrgId} initialStatus="NOT_STARTED" /> )}
) }