import React, { useEffect, useState, useMemo } from 'react'; import { useParams, Link } from 'react-router-dom'; import { getAttendances, getUserById, getFunnels, getOrigins } from '../services/dataService'; import { Attendance, User, FunnelStage, DashboardFilter, FunnelStageDef, OriginItemDef } from '../types'; import { ArrowLeft, Mail, Phone, Clock, MessageSquare, ChevronLeft, ChevronRight, Eye, Filter } from 'lucide-react'; import { DateRangePicker } from '../components/DateRangePicker'; const ITEMS_PER_PAGE = 10; export const UserDetail: React.FC = () => { const { id } = useParams<{ id: string }>(); const [user, setUser] = useState(); const [attendances, setAttendances] = useState([]); const [funnelDefs, setFunnelDefs] = useState([]); const [originDefs, setOriginDefs] = useState([]); const [loading, setLoading] = useState(true); const [currentPage, setCurrentPage] = useState(1); const [filters, setFilters] = useState({ dateRange: { start: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000), end: new Date() }, userId: id, funnelStage: 'all', origin: 'all' }); useEffect(() => { if (id) { setLoading(true); const loadData = async () => { try { const tenantId = localStorage.getItem('ctms_tenant_id'); const u = await getUserById(id); setUser(u); if (u && tenantId) { const [data, fetchedFunnels, fetchedOrigins] = await Promise.all([ getAttendances(tenantId, { ...filters, userId: id }), getFunnels(tenantId), getOrigins(tenantId) ]); setAttendances(data); const targetTeamId = u.team_id || null; let activeFunnel = fetchedFunnels[0]; if (targetTeamId) { const matchedFunnel = fetchedFunnels.find(f => f.teamIds?.includes(targetTeamId)); if (matchedFunnel) activeFunnel = matchedFunnel; } setFunnelDefs(activeFunnel && activeFunnel.stages ? activeFunnel.stages.sort((a: any, b: any) => a.order_index - b.order_index) : []); let activeOriginGroup = fetchedOrigins[0]; if (targetTeamId) { const matchedOrigin = fetchedOrigins.find(o => o.teamIds?.includes(targetTeamId)); if (matchedOrigin) activeOriginGroup = matchedOrigin; } setOriginDefs(activeOriginGroup && activeOriginGroup.items ? activeOriginGroup.items : []); } } catch (error) { console.error("Error loading user details", error); } finally { setLoading(false); } }; loadData(); } }, [id, filters]); const handleFilterChange = (key: keyof DashboardFilter, value: any) => { setFilters(prev => ({ ...prev, [key]: value })); setCurrentPage(1); // Reset pagination on filter change }; const totalPages = Math.ceil(attendances.length / ITEMS_PER_PAGE); const currentData = useMemo(() => { const start = (currentPage - 1) * ITEMS_PER_PAGE; return attendances.slice(start, start + ITEMS_PER_PAGE); }, [attendances, currentPage]); const handlePageChange = (newPage: number) => { if (newPage >= 1 && newPage <= totalPages) { setCurrentPage(newPage); } }; const getStageBadgeColor = (stage: string) => { const def = funnelDefs.find(f => f.name === stage); if (def) return def.color_class; switch (stage) { case FunnelStage.WON: return 'bg-green-100 text-green-700 border-green-200 dark:bg-green-900/30 dark:text-green-400 dark:border-green-800'; case FunnelStage.LOST: return 'bg-red-100 text-red-700 border-red-200 dark:bg-red-900/30 dark:text-red-400 dark:border-red-800'; case FunnelStage.NEGOTIATION: return 'bg-blue-100 text-blue-700 border-blue-200 dark:bg-blue-900/30 dark:text-blue-400 dark:border-blue-800'; case FunnelStage.IDENTIFICATION: return 'bg-yellow-100 text-yellow-700 border-yellow-200 dark:bg-yellow-900/30 dark:text-yellow-400 dark:border-yellow-800'; default: return 'bg-zinc-100 text-zinc-700 border-zinc-200 dark:bg-zinc-800 dark:text-zinc-300 dark:border-zinc-700'; } }; const getScoreColor = (score: number) => { if (score >= 80) return 'text-green-600 bg-green-50 dark:bg-green-900/20 dark:text-green-400'; if (score >= 60) return 'text-yellow-600 bg-yellow-50 dark:bg-yellow-900/20 dark:text-yellow-400'; return 'text-red-600 bg-red-50 dark:bg-red-900/20 dark:text-red-400'; }; if (!loading && !user) return
Usuário não encontrado
; const backendUrl = import.meta.env.PROD ? '' : 'http://localhost:3001'; return (
{/* Header Section */}
{user && (
{user.name}

{user.name}

{user.email} {user.role}
)}
{/* Filters Section */}
Filtros:
handleFilterChange('dateRange', range)} />
{/* KPI Cards */}
Total de Interações
{attendances.length}
Taxa de Conversão
{attendances.length ? ((attendances.filter(a => a.converted).length / attendances.length) * 100).toFixed(1) : 0}%
Nota Média
{attendances.length ? (attendances.reduce((acc, c) => acc + c.score, 0) / attendances.length).toFixed(1) : 0}
{/* Attendance Table */}

Histórico de Atendimentos

Página {currentPage} de {totalPages || 1}
{loading ? (
Carregando registros...
) : (
{currentData.map(att => ( ))}
Data / Hora Resumo Etapa Nota T. Resp. Ação
{new Date(att.created_at).toLocaleDateString('pt-BR')}
{new Date(att.created_at).toLocaleTimeString('pt-BR', {hour: '2-digit', minute:'2-digit', hour12: false})}
{att.title}
{att.origin}
{att.funnel_stage} {att.score}
{att.first_response_time_min}m
)} {/* Pagination Footer */} {totalPages > 1 && (
Mostrando {((currentPage - 1) * ITEMS_PER_PAGE) + 1} a {Math.min(currentPage * ITEMS_PER_PAGE, attendances.length)} de {attendances.length}
)}
); };