354 lines
12 KiB
TypeScript
354 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?.toLowerCase() === 'admin') {
|
||
// Admin ve dados globais
|
||
stats = await this.obterEstatisticasGlobais();
|
||
} else if (userRole?.toLowerCase() === 'owner' && empresaId) {
|
||
// Owner ve dados da propria empresa
|
||
stats = await this.obterEstatisticasEmpresa(empresaId);
|
||
} else if (userRole?.toLowerCase() === 'employee' && empresaId) {
|
||
// Employee ve 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 normalizedRole = userRole?.toLowerCase()
|
||
const pedidosResponse = normalizedRole === 'admin'
|
||
? 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 = normalizedRole === 'admin'
|
||
? 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 = normalizedRole === 'admin'
|
||
? 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 normalizedRole = userRole?.toLowerCase()
|
||
const pedidosResponse = normalizedRole === 'admin'
|
||
? await pedidoService.listar()
|
||
: await pedidoService.listar();
|
||
|
||
const todosPedidos = pedidosResponse.documents || [];
|
||
|
||
// Filtrar pedidos da empresa se não for SUPERADMIN
|
||
const pedidos = normalizedRole === 'admin'
|
||
? 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 [];
|
||
}
|
||
}
|
||
}
|
||
|
||
const dashboardService = new DashboardService();
|
||
|
||
export default dashboardService;
|
||
|
||
|
||
|
||
|