import React, { useEffect, useRef, useState, useCallback } from "react"; import { MemberDetailModal } from "../components/MemberDetailModal"; import { motion } from "framer-motion"; import { BarChart3, Calendar, Clock, CheckCircle2, Trophy, Users, Briefcase, Filter, AlertCircle, Loader2, Medal, Timer, } from "lucide-react"; import { format, startOfMonth, endOfMonth } from "date-fns"; import { useAuth } from "../contexts/AuthContext"; import { orgDashboardService, type OrgDashboardData, type OrgProjectStats, } from "../services/orgDashboardService"; import { cn } from "../lib/utils"; import { PieChart as SharedPieChart } from "../components/PieChart"; import { CategoryIcon } from "../components/CategoryIcon"; import { Tag } from "lucide-react"; const CHART_COLORS = [ "#7C3AED", "#06B6D4", "#10B981", "#F59E0B", "#EF4444", "#8B5CF6", "#3B82F6", "#EC4899", ]; interface TooltipState { x: number; y: number; content: string } function PieChart({ data }: { data: OrgProjectStats[] }) { const [tooltip, setTooltip] = useState(null); const svgRef = useRef(null); const total = data.reduce((sum, d) => sum + d.totalMinutes, 0); if (total === 0 || data.length === 0) { return (
Sem dados no período
); } const cx = 100; const cy = 100; const r = 70; const innerR = 42; let startAngle = -Math.PI / 2; const slices = data.map((d, i) => { const fraction = d.totalMinutes / total; const angle = fraction * 2 * Math.PI; const endAngle = startAngle + angle; const x1 = cx + r * Math.cos(startAngle); const y1 = cy + r * Math.sin(startAngle); const x2 = cx + r * Math.cos(endAngle); const y2 = cy + r * Math.sin(endAngle); const ix1 = cx + innerR * Math.cos(endAngle); const iy1 = cy + innerR * Math.sin(endAngle); const ix2 = cx + innerR * Math.cos(startAngle); const iy2 = cy + innerR * Math.sin(startAngle); const largeArc = angle > Math.PI ? 1 : 0; const path = [ `M ${x1} ${y1}`, `A ${r} ${r} 0 ${largeArc} 1 ${x2} ${y2}`, `L ${ix1} ${iy1}`, `A ${innerR} ${innerR} 0 ${largeArc} 0 ${ix2} ${iy2}`, "Z", ].join(" "); const pct = ((fraction) * 100).toFixed(1); const result = { path, color: CHART_COLORS[i % CHART_COLORS.length], fraction, label: `${d.projectName}: ${formatMinutes(d.totalMinutes)} (${pct}%)`, }; startAngle = endAngle; return result; }); const handleMouseMove = (e: React.MouseEvent, content: string) => { const rect = svgRef.current?.getBoundingClientRect(); if (!rect) return; setTooltip({ x: e.clientX - rect.left, y: e.clientY - rect.top, content }); }; return (
setTooltip(null)} > {slices.map((slice, i) => ( handleMouseMove(e, slice.label)} /> ))} projetos {data.length} {tooltip && (
{tooltip.content}
)}
); } function formatMinutes(minutes: number) { const h = Math.floor(minutes / 60); const m = minutes % 60; if (h === 0) return `${m}m`; return `${h}h ${m}m`; } function Avatar({ name, photo, size = "md" }: { name: string; photo?: string | null; size?: "sm" | "md" | "lg" }) { const sizeClass = size === "lg" ? "w-16 h-16 text-xl" : size === "md" ? "w-12 h-12 text-base" : "w-9 h-9 text-sm"; if (photo) { return {name}; } const initials = name .split(" ") .slice(0, 2) .map((n) => n[0]) .join("") .toUpperCase(); return (
{initials}
); } export const OrgDashboardPage: React.FC = () => { const { activeOrg } = useAuth(); const [isLoading, setIsLoading] = useState(true); const [data, setData] = useState(null); const [startDate, setStartDate] = useState(startOfMonth(new Date())); const [endDate, setEndDate] = useState(endOfMonth(new Date())); const [error, setError] = useState(null); const [selectedMemberId, setSelectedMemberId] = useState(null); const fetchData = useCallback(async () => { if (!activeOrg) return; setIsLoading(true); setError(null); try { const result = await orgDashboardService.getDashboard( activeOrg.id, format(startDate, "yyyy-MM-dd'T'HH:mm:ss"), format(endDate, "yyyy-MM-dd'T'HH:mm:ss"), ); setData(result); } catch { setError("Falha ao carregar os dados do dashboard da organização."); } finally { setIsLoading(false); } }, [activeOrg, startDate, endDate]); useEffect(() => { fetchData(); }, [fetchData]); const totalMinutes = data?.projectStats.reduce((sum, p) => sum + p.totalMinutes, 0) ?? 0; return (
{/* Header & Filters */}
Visão da Organização

Dashboard da Org

{activeOrg?.name} — desempenho e produtividade do time.

setStartDate(new Date(e.target.value + "T00:00:00"))} className="bg-transparent border-none text-sm font-bold text-text-main focus:ring-0 outline-none" />
setEndDate(new Date(e.target.value + "T23:59:59"))} className="bg-transparent border-none text-sm font-bold text-text-main focus:ring-0 outline-none" />
{isLoading ? (

Carregando dados da organização...

) : error ? (

Ocorreu um erro

{error}

) : data ? ( <> {/* Pie chart + Top Tasks */}
{/* Pie Chart — Projects */}

Tempo por Projeto

{data.projectStats.length > 0 ? ( data.projectStats .slice() .sort((a, b) => b.totalMinutes - a.totalMinutes) .map((stat, i) => { const pct = totalMinutes > 0 ? ((stat.totalMinutes / totalMinutes) * 100).toFixed(1) : "0"; return (
{stat.projectName} {formatMinutes(stat.totalMinutes)} ({pct}%)
); }) ) : (

Nenhum projeto com atividade no período.

)}
{/* Top 5 Tasks */}

Top 3 Tarefas

por tempo
{data.topTasks.length > 0 ? ( data.topTasks.map((task, idx) => (
{idx + 1}
{task.title}
{task.projectName}
{formatMinutes(task.totalMinutes)}
)) ) : (

Nenhuma tarefa com atividade no período.

)}
{/* Category Pie */} {data.categoryStats.length > 0 && (

Tarefas por Categoria

({ key: c.categoryId, label: c.categoryName, value: c.totalMinutes, color: c.color, }))} formatTooltip={(s, pct) => `${s.label}: ${formatMinutes(s.value)} (${pct.toFixed(1)}%)`} />
{data.categoryStats .slice() .sort((a, b) => b.totalMinutes - a.totalMinutes) .map((c) => { const totalCat = data.categoryStats.reduce((s, x) => s + x.totalMinutes, 0); const pct = totalCat > 0 ? ((c.totalMinutes / totalCat) * 100).toFixed(1) : "0"; return (
{c.categoryName} {formatMinutes(c.totalMinutes)} ({pct}%)
); })}
)} {/* Members Section */}

Membros do Time

ordenado por tempo
{data.memberStats.length === 0 ? (

Nenhum membro encontrado.

) : (
{data.memberStats.map((member, idx) => { const isFirst = idx === 0; return ( setSelectedMemberId(member.userId)} className={cn( "relative rounded-[1.5rem] p-6 border transition-all flex flex-col items-center text-center gap-3 cursor-pointer", isFirst ? "border-amber-400/50 bg-gradient-to-b from-amber-500/10 to-amber-500/5 shadow-xl shadow-amber-500/10 hover:shadow-2xl" : "border-border-soft bg-app-bg hover:border-brand/30 hover:shadow-md", )} > {isFirst && (
Top 1
)}
{member.userName}
{formatMinutes(member.totalMinutesWorked)}
Trabalhado
{member.finishedTasksCount}
Concluídas
); })}
)}
) : null} {activeOrg && ( setSelectedMemberId(null)} /> )}
); };