import React, { useState, useEffect, useCallback, useMemo } from 'react' import { Clock, CheckCircle2, TrendingUp, Folder, Tag, Users, RefreshCw, Sparkles, Activity, AlertCircle, } from 'lucide-react' import { useAuth } from '../contexts/AuthContext' import { taskService } from '../services/taskService' import { projectService } from '../services/projectService' import { categoryService } from '../services/categoryService' import { organizationService, type OrganizationMember } from '../services/organizationService' import type { Task, Project, Category } from '../types/task' import { calculateTaskTotalSeconds, formatSecondsToHuman, formatSecondsToTimer } from '../lib/timeUtils' export const AnalyticsPage: React.FC = () => { const { activeOrg } = useAuth() const [tasks, setTasks] = useState([]) const [projects, setProjects] = useState([]) const [categories, setCategories] = useState([]) const [members, setMembers] = useState([]) const [loading, setLoading] = useState(true) const [refreshing, setRefreshing] = useState(false) const [error, setError] = useState(null) const activeOrgId = activeOrg?.id const loadData = useCallback(async () => { if (!activeOrgId) return try { setRefreshing(true) const [tasksRes, projsRes, catsRes, memsRes] = await Promise.allSettled([ taskService.listTasks({ organizationId: activeOrgId }), projectService.listProjects(activeOrgId), categoryService.listCategories(activeOrgId), organizationService.listMembers(activeOrgId), ]) if (tasksRes.status === 'fulfilled') setTasks(tasksRes.value || []) if (projsRes.status === 'fulfilled') setProjects(projsRes.value || []) if (catsRes.status === 'fulfilled') setCategories(catsRes.value || []) if (memsRes.status === 'fulfilled') setMembers(memsRes.value || []) } catch (err: unknown) { console.error(err) setError('Falha ao calcular métricas do workspace.') } finally { setLoading(false) setRefreshing(false) } }, [activeOrgId]) useEffect(() => { loadData() }, [loadData]) // Aggregate high-level KPIs const kpis = useMemo(() => { const totalTasks = tasks.length const completedTasks = tasks.filter((t) => t.status === 'COMPLETED').length const inProgressTasks = tasks.filter((t) => t.status === 'IN_PROGRESS').length const notStartedTasks = tasks.filter((t) => t.status === 'NOT_STARTED').length const canceledTasks = tasks.filter((t) => t.status === 'CANCELED').length const totalSecondsTracked = tasks.reduce( (acc, t) => acc + calculateTaskTotalSeconds(t), 0 ) const completionRate = totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0 const avgSecondsPerCompletedTask = completedTasks > 0 ? Math.round( tasks .filter((t) => t.status === 'COMPLETED') .reduce((acc, t) => acc + calculateTaskTotalSeconds(t), 0) / completedTasks ) : 0 return { totalTasks, completedTasks, inProgressTasks, notStartedTasks, canceledTasks, totalSecondsTracked, completionRate, avgSecondsPerCompletedTask, } }, [tasks]) // Project breakdown const projectBreakdown = useMemo(() => { const map = new Map() projects.forEach((p) => { map.set(p.id, { project: p, seconds: 0, taskCount: 0 }) }) let unassignedSeconds = 0 let unassignedTasks = 0 tasks.forEach((t) => { const sec = calculateTaskTotalSeconds(t) if (t.project?.id && map.has(t.project.id)) { const item = map.get(t.project.id)! item.seconds += sec item.taskCount += 1 } else { unassignedSeconds += sec unassignedTasks += 1 } }) const list = Array.from(map.values()) if (unassignedTasks > 0) { list.push({ project: { id: 0, name: 'Sem Projeto', color: '#64748b' }, seconds: unassignedSeconds, taskCount: unassignedTasks, }) } return list.sort((a, b) => b.seconds - a.seconds) }, [projects, tasks]) // Category breakdown const categoryBreakdown = useMemo(() => { const map = new Map() categories.forEach((c) => { map.set(c.id, { category: c, seconds: 0, taskCount: 0 }) }) let unassignedSeconds = 0 let unassignedTasks = 0 tasks.forEach((t) => { const sec = calculateTaskTotalSeconds(t) if (t.category?.id && map.has(t.category.id)) { const item = map.get(t.category.id)! item.seconds += sec item.taskCount += 1 } else { unassignedSeconds += sec unassignedTasks += 1 } }) const list = Array.from(map.values()) if (unassignedTasks > 0) { list.push({ category: { id: 0, name: 'Sem Categoria', color: '#64748b' }, seconds: unassignedSeconds, taskCount: unassignedTasks, }) } return list.sort((a, b) => b.seconds - a.seconds) }, [categories, tasks]) // Member breakdown const memberBreakdown = useMemo(() => { const map = new Map< string, { name: string; email: string; seconds: number; completedCount: number; totalCount: number } >() members.forEach((m) => { map.set(m.userEmail, { name: m.userName, email: m.userEmail, seconds: 0, completedCount: 0, totalCount: 0, }) }) tasks.forEach((t) => { const sec = calculateTaskTotalSeconds(t) const email = t.user?.email || 'unassigned@bee.internal' if (!map.has(email)) { map.set(email, { name: t.user?.name || 'Não atribuído', email, seconds: 0, completedCount: 0, totalCount: 0, }) } const item = map.get(email)! item.seconds += sec item.totalCount += 1 if (t.status === 'COMPLETED') item.completedCount += 1 }) return Array.from(map.values()).sort((a, b) => b.seconds - a.seconds) }, [members, tasks]) return (
{/* Header */}

Analytics & Produtividade

{activeOrg?.name || 'Workspace'}

Auditoria de tempo investido, distribuição por iniciativas e acompanhamento da vazão da equipe.

{error && (
{error}
)} {loading ? (
{[1, 2, 3, 4].map((i) => (
))}
) : ( <> {/* Top 4 KPI Cards */}
{/* Total Time */}
Tempo Total Dedicado

{formatSecondsToTimer(kpis.totalSecondsTracked)}

Equivalente a {formatSecondsToHuman(kpis.totalSecondsTracked)}

{/* Completion Rate */}
Taxa de Conclusão

{kpis.completionRate}%

{kpis.completedTasks} de {kpis.totalTasks} demandas finalizadas

{/* In Progress vs Backlog */}
Em Execução Agora

{kpis.inProgressTasks} tarefas

{kpis.notStartedTasks} no backlog aguardando início

{/* Avg Time per Task */}
Média por Entrega

{formatSecondsToHuman(kpis.avgSecondsPerCompletedTask)}

Tempo médio gasto em cada entrega concluída

{/* Main Grid: Projects & Categories Breakdown */}
{/* Project Time Distribution */}

Tempo por Projeto

{projectBreakdown.length} projetos
{projectBreakdown.length === 0 ? (

Nenhum dado de projeto disponível.

) : (
{projectBreakdown.map((item) => { const pct = kpis.totalSecondsTracked > 0 ? Math.round((item.seconds / kpis.totalSecondsTracked) * 100) : 0 return (
{item.project.name} ({item.taskCount} {item.taskCount === 1 ? 'tarefa' : 'tarefas'})
{formatSecondsToHuman(item.seconds)} {pct}%
) })}
)}
{/* Category Time Distribution */}

Tempo por Categoria / Tag

{categoryBreakdown.length} categorias
{categoryBreakdown.length === 0 ? (

Nenhum dado de categoria disponível.

) : (
{categoryBreakdown.map((item) => { const pct = kpis.totalSecondsTracked > 0 ? Math.round((item.seconds / kpis.totalSecondsTracked) * 100) : 0 return (
{item.category.name} ({item.taskCount} {item.taskCount === 1 ? 'tarefa' : 'tarefas'})
{formatSecondsToHuman(item.seconds)} {pct}%
) })}
)}
{/* Team Productivity Table */}

Dedicação por Membro da Equipe

{memberBreakdown.length} colaboradores
{memberBreakdown.map((member, idx) => ( ))}
Membro Tarefas Atribuídas Entregas Concluídas Tempo Total Registrado
{member.name.charAt(0)}

{member.name}

{member.email}

{member.totalCount} {member.completedCount} {formatSecondsToTimer(member.seconds)}
)}
) }