346 lines
12 KiB
TypeScript
346 lines
12 KiB
TypeScript
import { produtoService } from './produtoService';
|
|
import { pedidoService } from './pedidoService';
|
|
import { empresaService } from './empresaService';
|
|
|
|
export interface DashboardStats {
|
|
medicamentosDisponiveis: number;
|
|
medicamentosVencimento: number;
|
|
pedidosRecebidos: number;
|
|
pedidosPendentes: number;
|
|
vendasMes: number;
|
|
crescimentoVendas: number; // Porcentagem
|
|
economiaGerada: number;
|
|
}
|
|
|
|
export interface AtividadeRecente {
|
|
id: string;
|
|
tipo: 'pedido' | 'produto' | 'usuario';
|
|
titulo: string;
|
|
descricao: string;
|
|
data: string;
|
|
status: 'novo' | 'atualizado' | 'pendente' | 'concluido';
|
|
}
|
|
|
|
export interface PedidoPendente {
|
|
id: string;
|
|
numero: string;
|
|
status: string;
|
|
valor: number;
|
|
comprador: string;
|
|
data: string;
|
|
}
|
|
|
|
// Tipo para representar documentos do banco
|
|
interface Documento {
|
|
$id: string;
|
|
$createdAt: string;
|
|
$updatedAt: string;
|
|
[key: string]: any;
|
|
}
|
|
|
|
class DashboardService {
|
|
/**
|
|
* Obtém estatísticas do dashboard baseadas no nível do usuário
|
|
*/
|
|
async obterEstatisticas(userRole: string, empresaId?: string): Promise<DashboardStats> {
|
|
try {
|
|
console.log(`📊 [DashboardService] Obtendo estatísticas para role: ${userRole}, empresa: ${empresaId}`);
|
|
|
|
let stats: DashboardStats = {
|
|
medicamentosDisponiveis: 0,
|
|
medicamentosVencimento: 0,
|
|
pedidosRecebidos: 0,
|
|
pedidosPendentes: 0,
|
|
vendasMes: 0,
|
|
crescimentoVendas: 0,
|
|
economiaGerada: 0,
|
|
};
|
|
|
|
if (userRole === 'SUPERADMIN') {
|
|
// Super Admin vê dados globais
|
|
stats = await this.obterEstatisticasGlobais();
|
|
} else if (userRole === 'ADMIN' && empresaId) {
|
|
// Admin vê dados da sua empresa
|
|
stats = await this.obterEstatisticasEmpresa(empresaId);
|
|
} else if (userRole === 'COLABORADOR' && empresaId) {
|
|
// Colaborador vê dados limitados da empresa
|
|
stats = await this.obterEstatisticasColaborador(empresaId);
|
|
}
|
|
|
|
console.log(`✅ [DashboardService] Estatísticas obtidas:`, stats);
|
|
return stats;
|
|
} catch (error) {
|
|
console.error('❌ [DashboardService] Erro ao obter estatísticas:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Estatísticas globais para Super Admin
|
|
*/
|
|
private async obterEstatisticasGlobais(): Promise<DashboardStats> {
|
|
try {
|
|
// Obter produtos de todas as empresas
|
|
const produtosResponse = await produtoService.listar();
|
|
const produtos = produtosResponse.documents || [];
|
|
|
|
// Obter pedidos de todas as empresas
|
|
const pedidosResponse = await pedidoService.listar();
|
|
const pedidos = pedidosResponse.documents || [];
|
|
|
|
// Calcular medicamentos próximos ao vencimento (próximos 30 dias)
|
|
const dataLimite = new Date();
|
|
dataLimite.setDate(dataLimite.getDate() + 30);
|
|
|
|
const medicamentosVencimento = produtos.filter((produto: Documento) => {
|
|
if (produto['data-vencimento']) {
|
|
const dataVencimento = new Date(produto['data-vencimento']);
|
|
return dataVencimento <= dataLimite;
|
|
}
|
|
return false;
|
|
}).length;
|
|
|
|
// Calcular pedidos pendentes
|
|
const pedidosPendentes = pedidos.filter((pedido: Documento) =>
|
|
['pendente', 'aguardando_aprovacao'].includes(pedido.status?.toLowerCase())
|
|
).length;
|
|
|
|
// Calcular vendas do mês atual
|
|
const inicioMes = new Date();
|
|
inicioMes.setDate(1);
|
|
inicioMes.setHours(0, 0, 0, 0);
|
|
|
|
const vendasMes = pedidos.filter((pedido: Documento) => {
|
|
const dataPedido = new Date(pedido.$createdAt);
|
|
return dataPedido >= inicioMes && pedido.status === 'concluido';
|
|
}).length;
|
|
|
|
// Calcular economia gerada (valor total dos produtos vendidos)
|
|
const economiaGerada = pedidos
|
|
.filter((pedido: Documento) => pedido.status === 'concluido')
|
|
.reduce((total: number, pedido: Documento) => total + (pedido['valor-total'] || 0), 0);
|
|
|
|
return {
|
|
medicamentosDisponiveis: produtos.length,
|
|
medicamentosVencimento,
|
|
pedidosRecebidos: pedidos.length,
|
|
pedidosPendentes,
|
|
vendasMes,
|
|
crescimentoVendas: 25, // Calcular baseado no mês anterior
|
|
economiaGerada,
|
|
};
|
|
} catch (error) {
|
|
console.error('❌ [DashboardService] Erro nas estatísticas globais:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Estatísticas da empresa para Admin
|
|
*/
|
|
private async obterEstatisticasEmpresa(empresaId: string): Promise<DashboardStats> {
|
|
try {
|
|
// Obter produtos da empresa
|
|
const produtosResponse = await produtoService.listarPorEmpresa(empresaId);
|
|
const produtos = produtosResponse.documents || [];
|
|
|
|
// Obter pedidos da empresa (vendas e compras)
|
|
const pedidosResponse = await pedidoService.listar();
|
|
const todosPedidos = pedidosResponse.documents || [];
|
|
|
|
// Filtrar pedidos relacionados à empresa
|
|
const pedidos = todosPedidos.filter((pedido: Documento) =>
|
|
pedido.vendedor?.includes(empresaId) || pedido.comprador === empresaId
|
|
);
|
|
|
|
// Calcular medicamentos próximos ao vencimento
|
|
const dataLimite = new Date();
|
|
dataLimite.setDate(dataLimite.getDate() + 30);
|
|
|
|
const medicamentosVencimento = produtos.filter((produto: Documento) => {
|
|
if (produto['data-vencimento']) {
|
|
const dataVencimento = new Date(produto['data-vencimento']);
|
|
return dataVencimento <= dataLimite;
|
|
}
|
|
return false;
|
|
}).length;
|
|
|
|
// Filtrar pedidos recebidos (onde a empresa é vendedora)
|
|
const pedidosRecebidos = pedidos.filter((pedido: Documento) =>
|
|
pedido.vendedor && pedido.vendedor.includes(empresaId)
|
|
);
|
|
|
|
const pedidosPendentes = pedidosRecebidos.filter((pedido: Documento) =>
|
|
['pendente', 'aguardando_aprovacao'].includes(pedido.status?.toLowerCase())
|
|
).length;
|
|
|
|
// Calcular vendas do mês
|
|
const inicioMes = new Date();
|
|
inicioMes.setDate(1);
|
|
inicioMes.setHours(0, 0, 0, 0);
|
|
|
|
const vendasMes = pedidosRecebidos.filter((pedido: Documento) => {
|
|
const dataPedido = new Date(pedido.$createdAt);
|
|
return dataPedido >= inicioMes && pedido.status === 'concluido';
|
|
}).length;
|
|
|
|
// Calcular economia gerada pela empresa
|
|
const economiaGerada = pedidosRecebidos
|
|
.filter((pedido: Documento) => pedido.status === 'concluido')
|
|
.reduce((total: number, pedido: Documento) => total + (pedido['valor-total'] || 0), 0);
|
|
|
|
return {
|
|
medicamentosDisponiveis: produtos.length,
|
|
medicamentosVencimento,
|
|
pedidosRecebidos: pedidosRecebidos.length,
|
|
pedidosPendentes,
|
|
vendasMes,
|
|
crescimentoVendas: 15, // Calcular baseado no mês anterior
|
|
economiaGerada,
|
|
};
|
|
} catch (error) {
|
|
console.error('❌ [DashboardService] Erro nas estatísticas da empresa:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Estatísticas limitadas para Colaborador
|
|
*/
|
|
private async obterEstatisticasColaborador(empresaId: string): Promise<DashboardStats> {
|
|
try {
|
|
// Colaboradores veem apenas estatísticas básicas da empresa
|
|
const produtosResponse = await produtoService.listarPorEmpresa(empresaId);
|
|
const produtos = produtosResponse.documents || [];
|
|
|
|
// Medicamentos próximos ao vencimento
|
|
const dataLimite = new Date();
|
|
dataLimite.setDate(dataLimite.getDate() + 30);
|
|
|
|
const medicamentosVencimento = produtos.filter((produto: Documento) => {
|
|
if (produto['data-vencimento']) {
|
|
const dataVencimento = new Date(produto['data-vencimento']);
|
|
return dataVencimento <= dataLimite;
|
|
}
|
|
return false;
|
|
}).length;
|
|
|
|
return {
|
|
medicamentosDisponiveis: produtos.length,
|
|
medicamentosVencimento,
|
|
pedidosRecebidos: 0, // Colaboradores não veem pedidos
|
|
pedidosPendentes: 0,
|
|
vendasMes: 0,
|
|
crescimentoVendas: 0,
|
|
economiaGerada: 0,
|
|
};
|
|
} catch (error) {
|
|
console.error('❌ [DashboardService] Erro nas estatísticas do colaborador:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Obtém atividades recentes
|
|
*/
|
|
async obterAtividadesRecentes(userRole: string, empresaId?: string, limit: number = 5): Promise<AtividadeRecente[]> {
|
|
try {
|
|
console.log(`📋 [DashboardService] Obtendo atividades recentes para role: ${userRole}`);
|
|
|
|
const atividades: AtividadeRecente[] = [];
|
|
|
|
// Obter pedidos recentes
|
|
const pedidosResponse = userRole === 'SUPERADMIN'
|
|
? await pedidoService.listar(1, limit)
|
|
: await pedidoService.listar(1, limit);
|
|
|
|
const todosPedidos = pedidosResponse.documents || [];
|
|
|
|
// Filtrar pedidos da empresa se não for SUPERADMIN
|
|
const pedidos = userRole === 'SUPERADMIN'
|
|
? todosPedidos
|
|
: todosPedidos.filter((pedido: any) =>
|
|
pedido.vendedor?.includes(empresaId!) || pedido.comprador === empresaId!
|
|
);
|
|
|
|
// Converter pedidos em atividades
|
|
pedidos.forEach((pedido: Documento) => {
|
|
atividades.push({
|
|
id: pedido.$id,
|
|
tipo: 'pedido',
|
|
titulo: `Novo pedido recebido`,
|
|
descricao: `Pedido #${pedido.$id.substring(0, 8)} - R$ ${(pedido['valor-total'] || 0).toFixed(2)}`,
|
|
data: pedido.$createdAt,
|
|
status: pedido.status === 'pendente' ? 'novo' : 'atualizado',
|
|
});
|
|
});
|
|
|
|
// Obter produtos recentemente atualizados
|
|
const produtosResponse = userRole === 'SUPERADMIN'
|
|
? await produtoService.listar(1, limit)
|
|
: await produtoService.listarPorEmpresa(empresaId!, 1, limit);
|
|
|
|
const produtos = produtosResponse.documents || []; // Converter produtos em atividades
|
|
produtos.slice(0, 2).forEach((produto: Documento) => {
|
|
atividades.push({
|
|
id: produto.$id,
|
|
tipo: 'produto',
|
|
titulo: `Produto atualizado`,
|
|
descricao: produto.nome || `Produto ${produto.$id.substring(0, 8)}`,
|
|
data: produto.$updatedAt,
|
|
status: 'atualizado',
|
|
});
|
|
});
|
|
|
|
// Ordenar por data mais recente
|
|
atividades.sort((a, b) => new Date(b.data).getTime() - new Date(a.data).getTime());
|
|
|
|
return atividades.slice(0, limit);
|
|
} catch (error) {
|
|
console.error('❌ [DashboardService] Erro ao obter atividades recentes:', error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Obtém pedidos pendentes
|
|
*/
|
|
async obterPedidosPendentes(userRole: string, empresaId?: string, limit: number = 5): Promise<PedidoPendente[]> {
|
|
try {
|
|
console.log(`📦 [DashboardService] Obtendo pedidos pendentes para role: ${userRole}`);
|
|
|
|
const pedidosResponse = userRole === 'SUPERADMIN'
|
|
? await pedidoService.listar()
|
|
: await pedidoService.listar();
|
|
|
|
const todosPedidos = pedidosResponse.documents || [];
|
|
|
|
// Filtrar pedidos da empresa se não for SUPERADMIN
|
|
const pedidos = userRole === 'SUPERADMIN'
|
|
? todosPedidos
|
|
: todosPedidos.filter((pedido: any) =>
|
|
pedido.vendedor?.includes(empresaId!) || pedido.comprador === empresaId!
|
|
);
|
|
|
|
// Filtrar apenas pedidos pendentes
|
|
const pedidosPendentes = pedidos
|
|
.filter((pedido: Documento) => ['pendente', 'aguardando_aprovacao'].includes(pedido.status?.toLowerCase()))
|
|
.slice(0, limit)
|
|
.map((pedido: Documento) => ({
|
|
id: pedido.$id,
|
|
numero: `#${pedido.$id.substring(0, 8)}`,
|
|
status: pedido.status,
|
|
valor: pedido['valor-total'] || 0,
|
|
comprador: pedido.comprador || 'N/A',
|
|
data: pedido.$createdAt,
|
|
}));
|
|
|
|
return pedidosPendentes;
|
|
} catch (error) {
|
|
console.error('❌ [DashboardService] Erro ao obter pedidos pendentes:', error);
|
|
return [];
|
|
}
|
|
}
|
|
}
|
|
|
|
export default new DashboardService();
|