first commit
This commit is contained in:
commit
c9fb67e751
423 files changed
+53287
No files matched your search
@@ -0,0 +1,50 @@
|
||||
import React from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { format, differenceInDays } from 'date-fns';
|
||||
import { ptBR } from 'date-fns/locale';
|
||||
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline';
|
||||
import api from '../../services/api';
|
||||
|
||||
export const AlertasPrazos: React.FC = () => {
|
||||
const { data: prazos = [] } = useQuery('prazosFuturos', async () => {
|
||||
const response = await api.get('/dashboard/prazos/proximos');
|
||||
return response.data;
|
||||
});
|
||||
|
||||
const getPrioridadeStyle = (dias: number) => {
|
||||
if (dias <= 3) return 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200';
|
||||
if (dias <= 7) return 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200';
|
||||
return 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200';
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
Prazos Próximos
|
||||
</h2>
|
||||
<ExclamationTriangleIcon className="h-5 w-5 text-yellow-500" />
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
{prazos.map((prazo: any) => {
|
||||
const dias = differenceInDays(new Date(prazo.dataLimite), new Date());
|
||||
return (
|
||||
<div key={prazo.id} className="flex items-center justify-between">
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-medium text-gray-900 dark:text-white">
|
||||
{prazo.descricao}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
Processo: {prazo.processo.numero}
|
||||
</p>
|
||||
</div>
|
||||
<span className={`px-2 py-1 rounded-full text-xs ${getPrioridadeStyle(dias)}`}>
|
||||
{format(new Date(prazo.dataLimite), 'dd/MM/yyyy', { locale: ptBR })}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
import React from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { format } from 'date-fns';
|
||||
import { ptBR } from 'date-fns/locale';
|
||||
import api from '../../services/api';
|
||||
|
||||
export const AtividadesRecentes: React.FC = () => {
|
||||
const { data: atividades = [] } = useQuery('atividades', async () => {
|
||||
const response = await api.get('/dashboard/atividades');
|
||||
return response.data;
|
||||
});
|
||||
|
||||
const formatData = (data: string | Date | null) => {
|
||||
if (!data) return '';
|
||||
try {
|
||||
return format(new Date(data), "dd 'de' MMMM 'às' HH:mm", { locale: ptBR });
|
||||
} catch (error) {
|
||||
console.error('Erro ao formatar data:', error);
|
||||
return 'Data inválida';
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
||||
Atividades Recentes
|
||||
</h2>
|
||||
<div className="space-y-4">
|
||||
{atividades.map((atividade: any) => (
|
||||
<div key={atividade.id} className="flex items-start space-x-3">
|
||||
<span className="text-2xl">{atividade.tipo === 'PROCESSO' ? '⚖️' : '📋'}</span>
|
||||
<div className="flex-1">
|
||||
<p className="text-sm text-gray-900 dark:text-white">
|
||||
{atividade.descricao}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{formatData(atividade.data)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,92 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
ArrowUpIcon,
|
||||
ArrowDownIcon,
|
||||
} from '@heroicons/react/24/outline';
|
||||
|
||||
interface CardProps {
|
||||
title: string;
|
||||
value: string | number;
|
||||
trend?: number;
|
||||
trendLabel?: string;
|
||||
icon?: string;
|
||||
color?: 'blue' | 'green' | 'red' | 'yellow' | 'purple';
|
||||
items?: Array<{ label: string; value: string | number }>;
|
||||
type?: 'default' | 'list';
|
||||
}
|
||||
|
||||
export const Card: React.FC<CardProps> = ({
|
||||
title,
|
||||
value,
|
||||
trend,
|
||||
trendLabel,
|
||||
icon,
|
||||
color = 'blue',
|
||||
items,
|
||||
type = 'default'
|
||||
}) => {
|
||||
const colorClasses = {
|
||||
blue: 'text-blue-600',
|
||||
green: 'text-green-600',
|
||||
red: 'text-red-600',
|
||||
yellow: 'text-yellow-600',
|
||||
purple: 'text-purple-600'
|
||||
};
|
||||
|
||||
const bgColorClasses = {
|
||||
blue: 'bg-blue-50',
|
||||
green: 'bg-green-50',
|
||||
red: 'bg-red-50',
|
||||
yellow: 'bg-yellow-50',
|
||||
purple: 'bg-purple-50'
|
||||
};
|
||||
|
||||
if (type === 'list' && items) {
|
||||
return (
|
||||
<div className="bg-white rounded-lg shadow p-4">
|
||||
<h3 className="text-sm font-medium text-gray-900 mb-4">{title}</h3>
|
||||
<div className="space-y-2">
|
||||
{items.map((item, index) => (
|
||||
<div key={index} className="flex justify-between items-center">
|
||||
<span className="text-sm text-gray-500">{item.label}</span>
|
||||
<span className="text-sm font-medium text-gray-900">{item.value}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-lg shadow p-4">
|
||||
<div className="flex justify-between items-start">
|
||||
<h3 className="text-sm font-medium text-gray-900">{title}</h3>
|
||||
{icon && (
|
||||
<div className={`p-2 rounded-lg ${bgColorClasses[color]}`}>
|
||||
{/* Renderizar ícone dinamicamente */}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-2 flex items-baseline">
|
||||
<p className={`text-2xl font-semibold ${colorClasses[color]}`}>
|
||||
{value}
|
||||
</p>
|
||||
{trend !== undefined && (
|
||||
<div className={`ml-2 flex items-center text-sm ${trend >= 0 ? 'text-green-600' : 'text-red-600'}`}>
|
||||
{trend >= 0 ? (
|
||||
<ArrowUpIcon className="h-4 w-4" />
|
||||
) : (
|
||||
<ArrowDownIcon className="h-4 w-4" />
|
||||
)}
|
||||
<span className="ml-1">{Math.abs(trend)}%</span>
|
||||
{trendLabel && (
|
||||
<span className="ml-1 text-gray-500">
|
||||
{trendLabel}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
import React from 'react';
|
||||
import { useAuth } from '../../contexts/AuthContext';
|
||||
import { useDashboardStats } from '../../hooks/useDashboardStats';
|
||||
import { ProcessosStats } from './stats/ProcessosStats';
|
||||
import { TarefasStats } from './stats/TarefasStats';
|
||||
import { AtendimentosStats } from './stats/AtendimentosStats';
|
||||
import { FinanceiroStats } from './stats/FinanceiroStats';
|
||||
import { AlertasPrazos } from './AlertasPrazos';
|
||||
import { AtividadesRecentes } from './AtividadesRecentes';
|
||||
import { TaxaSucessoCard } from './TaxaSucessoCard';
|
||||
import { KPIsFinanceiros } from './stats/KPIsFinanceiros';
|
||||
|
||||
export const DashboardLayout: React.FC = () => {
|
||||
const { user } = useAuth();
|
||||
const {
|
||||
processosStats,
|
||||
tarefasStats,
|
||||
atendimentosStats,
|
||||
financeiroStats,
|
||||
proximosPrazos,
|
||||
atividades
|
||||
} = useDashboardStats();
|
||||
|
||||
const isAdminOrAdvogado = ['ADVOGADO_ADMIN', 'ADVOGADO'].includes(user?.perfil || '');
|
||||
|
||||
return (
|
||||
<div className="space-y-6 p-6">
|
||||
{/* Stats Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{processosStats.data && <ProcessosStats data={processosStats.data} />}
|
||||
{tarefasStats.data && <TarefasStats data={tarefasStats.data} />}
|
||||
{atendimentosStats.data && <AtendimentosStats data={atendimentosStats.data} />}
|
||||
{isAdminOrAdvogado && financeiroStats.data && (
|
||||
<FinanceiroStats data={financeiroStats.data} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
{/* Coluna da esquerda */}
|
||||
<div className="lg:col-span-2 space-y-6">
|
||||
<TaxaSucessoCard />
|
||||
{isAdminOrAdvogado && <KPIsFinanceiros />}
|
||||
</div>
|
||||
|
||||
{/* Coluna da direita */}
|
||||
<div className="space-y-6">
|
||||
<AlertasPrazos />
|
||||
<AtividadesRecentes />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
import React from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import api from '../../services/api';
|
||||
|
||||
export const KPIsAdvogados: React.FC = () => {
|
||||
const { data: kpis } = useQuery('advogadosKpis', async () => {
|
||||
const response = await api.get('/dashboard/advogados/kpis');
|
||||
return response.data;
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow overflow-hidden">
|
||||
<div className="p-6">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
||||
Produtividade da Equipe
|
||||
</h2>
|
||||
</div>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead className="bg-gray-50 dark:bg-gray-900">
|
||||
<tr>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Advogado
|
||||
</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Tarefas Concluídas
|
||||
</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Processos Concluídos
|
||||
</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Produtividade
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
{kpis?.map((kpi: any) => (
|
||||
<tr key={kpi.id}>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">
|
||||
{kpi.nome}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
||||
{kpi.tarefasConcluidas} / {kpi.tarefasCriadas}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
||||
{kpi.processosConcluidos}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
<div className="w-full bg-gray-200 rounded-full h-2.5 dark:bg-gray-700">
|
||||
<div
|
||||
className="bg-blue-600 h-2.5 rounded-full"
|
||||
style={{ width: `${kpi.produtividade}%` }}
|
||||
/>
|
||||
</div>
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{kpi.produtividade}%
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import api from '../../services/api';
|
||||
|
||||
export const TaxaSucessoCard: React.FC = () => {
|
||||
const { data: stats } = useQuery('taxa-sucesso', async () => {
|
||||
const response = await api.get('/dashboard/processos/taxa-sucesso');
|
||||
return response.data;
|
||||
});
|
||||
|
||||
if (!stats) return null;
|
||||
|
||||
const estatisticas = stats || [];
|
||||
const total = estatisticas.reduce((acc: number, curr: any) => acc + (curr._count || 0), 0);
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-lg shadow p-6">
|
||||
<h3 className="text-lg font-semibold mb-4">Taxa de Sucesso</h3>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="text-3xl font-bold text-green-600">
|
||||
{estatisticas.find((s: any) => s.resultado === 'PROCEDENTE')?._count
|
||||
? Math.round((estatisticas.find((s: any) => s.resultado === 'PROCEDENTE')._count / total) * 100)
|
||||
: 0}%
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">
|
||||
Total julgados: {total}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4 space-y-2">
|
||||
{estatisticas.map((item: any) => (
|
||||
<div key={item.resultado} className="flex justify-between">
|
||||
<span className="text-sm text-gray-600">{item.resultado}</span>
|
||||
<span className="text-sm font-medium">{Math.round((item._count / total) * 100)}%</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
import React from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { format } from 'date-fns';
|
||||
import { ptBR } from 'date-fns/locale';
|
||||
import api from '../../../services/api';
|
||||
|
||||
export const MapaCalor: React.FC = () => {
|
||||
const { data: atividadesDiarias } = useQuery('atividadesDiarias', async () => {
|
||||
const response = await api.get('/dashboard/atividades/diarias');
|
||||
return response.data;
|
||||
});
|
||||
|
||||
const intensidadeCor = (quantidade: number) => {
|
||||
if (quantidade === 0) return 'bg-gray-100 dark:bg-gray-800';
|
||||
if (quantidade <= 3) return 'bg-blue-100 dark:bg-blue-900';
|
||||
if (quantidade <= 6) return 'bg-blue-300 dark:bg-blue-700';
|
||||
return 'bg-blue-500 dark:bg-blue-500';
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
||||
Mapa de Atividades
|
||||
</h2>
|
||||
<div className="grid grid-cols-7 gap-2">
|
||||
{atividadesDiarias?.map((dia: any) => (
|
||||
<div
|
||||
key={dia.data}
|
||||
className={`aspect-square rounded-sm ${intensidadeCor(dia.quantidade)}`}
|
||||
title={`${format(new Date(dia.data), "dd 'de' MMMM", { locale: ptBR })}: ${dia.quantidade} atividades`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
|
||||
import api from '../../../services/api';
|
||||
import { useAuth } from '../../../contexts/AuthContext';
|
||||
import { formatCurrency } from '../../../utils/formatters';
|
||||
|
||||
export const PrevisaoReceitasChart: React.FC = () => {
|
||||
const { user } = useAuth();
|
||||
|
||||
if (!['ADVOGADO_ADMIN', 'ADVOGADO'].includes(user?.perfil || '')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { data: previsao } = useQuery('previsaoReceitas', async () => {
|
||||
const response = await api.get('/dashboard/financeiro/previsao');
|
||||
return response.data;
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
||||
Previsão de Receitas
|
||||
</h2>
|
||||
<div className="h-80">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<BarChart data={previsao}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="mes" />
|
||||
<YAxis tickFormatter={(value) => formatCurrency(value)} />
|
||||
<Tooltip formatter={(value) => formatCurrency(value)} />
|
||||
<Bar dataKey="honorarios" name="Honorários" fill="#10B981" />
|
||||
<Bar dataKey="custas" name="Custas" fill="#6366F1" />
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import { useAuth } from '../../../contexts/AuthContext';
|
||||
import { ProcessosEvolucao } from './ProcessosEvolucao';
|
||||
import { TaxaSucessoChart } from './TaxaSucessoChart';
|
||||
import { PrevisaoReceitasChart } from './PrevisaoReceitasChart';
|
||||
|
||||
export const ProcessosCharts: React.FC = () => {
|
||||
const { user } = useAuth();
|
||||
const isAdminOrAdvogado = user?.perfil === 'ADVOGADO_ADMIN' || user?.perfil === 'ADVOGADO';
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProcessosEvolucao />
|
||||
<TaxaSucessoChart />
|
||||
{isAdminOrAdvogado && <PrevisaoReceitasChart />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
import React from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
|
||||
import api from '../../../services/api';
|
||||
|
||||
export const ProcessosEvolucao: React.FC = () => {
|
||||
const { data: evolucao } = useQuery('processosEvolucao', async () => {
|
||||
const response = await api.get('/dashboard/processos/evolucao');
|
||||
return response.data;
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
||||
Evolução de Processos
|
||||
</h2>
|
||||
<div className="h-80">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<LineChart data={evolucao}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="mes" />
|
||||
<YAxis />
|
||||
<Tooltip />
|
||||
<Line type="monotone" dataKey="novos" stroke="#3B82F6" name="Novos" />
|
||||
<Line type="monotone" dataKey="concluidos" stroke="#10B981" name="Concluídos" />
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
import React from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { PieChart, Pie, Cell, ResponsiveContainer, Legend, Tooltip } from 'recharts';
|
||||
import api from '../../../services/api';
|
||||
|
||||
export const TaxaSucessoChart: React.FC = () => {
|
||||
const { data: stats } = useQuery('taxaSucesso', async () => {
|
||||
const response = await api.get('/dashboard/processos/taxa-sucesso');
|
||||
return response.data;
|
||||
});
|
||||
|
||||
const COLORS = ['#10B981', '#EF4444', '#F59E0B'];
|
||||
|
||||
return (
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
||||
Taxa de Sucesso em Processos
|
||||
</h2>
|
||||
<div className="h-80">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<PieChart>
|
||||
<Pie
|
||||
data={stats}
|
||||
dataKey="valor"
|
||||
nameKey="categoria"
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
outerRadius={80}
|
||||
label
|
||||
>
|
||||
{stats?.map((entry: any, index: number) => (
|
||||
<Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
|
||||
))}
|
||||
</Pie>
|
||||
<Tooltip />
|
||||
<Legend />
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { Card } from '../Card';
|
||||
import api from '../../../services/api';
|
||||
|
||||
export const AtendimentosStats: React.FC = () => {
|
||||
const { data: stats } = useQuery('atendimentosStats', async () => {
|
||||
const response = await api.get('/dashboard/atendimentos/stats');
|
||||
return response.data;
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="grid gap-4">
|
||||
<Card
|
||||
title="Atendimentos Hoje"
|
||||
value={stats?.hoje || 0}
|
||||
icon="Calendar"
|
||||
color="blue"
|
||||
/>
|
||||
<Card
|
||||
title="Próximos 7 dias"
|
||||
value={stats?.proximaSemana || 0}
|
||||
icon="CalendarDays"
|
||||
color="purple"
|
||||
/>
|
||||
<Card
|
||||
title="Total no Mês"
|
||||
value={stats?.totalMes || 0}
|
||||
trend={stats?.crescimentoMes}
|
||||
icon="ChartBar"
|
||||
color="green"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
import React from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { Card } from '../Card';
|
||||
import api from '../../../services/api';
|
||||
import { formatCurrency } from '../../../utils/formatters';
|
||||
|
||||
export const FinanceiroStats: React.FC = () => {
|
||||
const { data: stats } = useQuery('financeiroStats', async () => {
|
||||
const response = await api.get('/dashboard/financeiro/stats');
|
||||
return response.data;
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="grid gap-4">
|
||||
<Card
|
||||
title="Honorários a Receber"
|
||||
value={formatCurrency(stats?.honorariosReceber || 0)}
|
||||
icon="BanknotesIcon"
|
||||
color="green"
|
||||
/>
|
||||
<Card
|
||||
title="Custas do Mês"
|
||||
value={formatCurrency(stats?.custasMes || 0)}
|
||||
trend={stats?.variacaoCustas}
|
||||
icon="ReceiptPercentIcon"
|
||||
color="red"
|
||||
/>
|
||||
<Card
|
||||
title="Receitas (Mês)"
|
||||
value={formatCurrency(stats?.receitasMes || 0)}
|
||||
trend={stats?.crescimentoReceitas}
|
||||
icon="CurrencyDollarIcon"
|
||||
color="blue"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { useAuth } from '../../../contexts/AuthContext';
|
||||
import { Card } from '../../dashboard/Card';
|
||||
import api from '../../../services/api';
|
||||
import { formatCurrency } from '../../../utils/formatters';
|
||||
|
||||
export const KPIsFinanceiros: React.FC = () => {
|
||||
const { user } = useAuth();
|
||||
|
||||
if (!['ADVOGADO_ADMIN', 'ADVOGADO'].includes(user?.perfil || '')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { data: stats } = useQuery('financeiroKPIs', async () => {
|
||||
const response = await api.get('/dashboard/financeiro/stats');
|
||||
return response.data;
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="grid gap-4">
|
||||
<Card
|
||||
title="Faturamento Previsto"
|
||||
value={formatCurrency(stats?.previsaoFaturamento || 0)}
|
||||
trend={stats?.crescimentoFaturamento}
|
||||
icon="BanknotesIcon"
|
||||
color="green"
|
||||
/>
|
||||
<Card
|
||||
title="Taxa de Sucesso"
|
||||
value={`${stats?.taxaSucesso || 0}%`}
|
||||
trend={stats?.crescimentoTaxaSucesso}
|
||||
icon="ChartBarIcon"
|
||||
color="blue"
|
||||
/>
|
||||
<Card
|
||||
title="Custas em Aberto"
|
||||
value={formatCurrency(stats?.custasEmAberto || 0)}
|
||||
icon="ExclamationTriangleIcon"
|
||||
color="yellow"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
import React from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import api from '../../../services/api';
|
||||
import { Card } from '../Card';
|
||||
|
||||
export const ProcessosStats: React.FC = () => {
|
||||
const { data: stats } = useQuery('processosStats', async () => {
|
||||
const response = await api.get('/dashboard/processos/stats');
|
||||
return response.data;
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="grid gap-4">
|
||||
<Card
|
||||
title="Total de Processos"
|
||||
value={stats?.total || 0}
|
||||
trend={stats?.crescimento || 0}
|
||||
icon="DocumentText"
|
||||
/>
|
||||
<Card
|
||||
title="Por Tribunal"
|
||||
items={stats?.porTribunal || []}
|
||||
type="list"
|
||||
/>
|
||||
<Card
|
||||
title="Por Status"
|
||||
items={stats?.porStatus || []}
|
||||
type="list"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { Card } from '../Card';
|
||||
import api from '../../../services/api';
|
||||
|
||||
export const TarefasStats: React.FC = () => {
|
||||
const { data: stats } = useQuery('tarefasStats', async () => {
|
||||
try {
|
||||
const response = await api.get('/dashboard/tarefas/stats');
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Erro ao buscar estatísticas:', error);
|
||||
return {
|
||||
pendentes: 0,
|
||||
atrasadas: 0,
|
||||
concluidas: 0,
|
||||
total: 0,
|
||||
taxaConclusao: 0,
|
||||
porStatus: { PENDENTE: 0 }
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const defaultStats = {
|
||||
pendentes: 0,
|
||||
atrasadas: 0,
|
||||
concluidas: 0,
|
||||
total: 0,
|
||||
taxaConclusao: 0,
|
||||
porStatus: { PENDENTE: 0 }
|
||||
};
|
||||
|
||||
const safeStats = stats || defaultStats;
|
||||
|
||||
return (
|
||||
<div className="grid gap-4">
|
||||
<Card
|
||||
title="Tarefas Pendentes"
|
||||
value={safeStats.porStatus?.PENDENTE || 0}
|
||||
trend={safeStats.atrasadas}
|
||||
trendLabel="atrasadas"
|
||||
icon="ClipboardDocumentList"
|
||||
color="yellow"
|
||||
/>
|
||||
<Card
|
||||
title="Para Hoje"
|
||||
value={safeStats.pendentes || 0}
|
||||
icon="Clock"
|
||||
color="blue"
|
||||
/>
|
||||
<Card
|
||||
title="Concluídas (Mês)"
|
||||
value={safeStats.concluidas || 0}
|
||||
trend={safeStats.taxaConclusao}
|
||||
trendLabel="taxa de conclusão"
|
||||
icon="CheckCircle"
|
||||
color="green"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in new issue
Block a user