testes/src_2/features/rh/hooks/useEmployees.js

335 lines
10 KiB
JavaScript

import { create } from 'zustand';
import { rhService } from '../services/rhService';
import { useRhFeedbackStore } from '../components/RhFeedbackNotification';
const notify = (type, title, message) => useRhFeedbackStore.getState().notify(type, title, message);
/**
* Hook customizado para gestão de colaboradores (Padrão Integra Finance - ZUSTAND).
* Centraliza toda a lógica de negócio e comunicação com a API de colaboradores.
* Agora utiliza Zustand para compartilhamento de estado entre componentes.
*/
export const useEmployees = create((set, get) => ({
employees: [],
loading: false,
error: null,
statistics: [],
alerts: [],
/**
* Busca lista de colaboradores com filtros
*/
fetchEmployees: async (params = {}) => {
set({ loading: true, error: null });
try {
const response = await rhService.getCollaborators(params);
const rawList = Array.isArray(response) ? response : (response?.data || []);
// Mapeamento de campos da API para a UI
const mappedEmployees = rawList.map(emp => {
// Cálculo de status CNH baseado na validade
let cnhStatus = 'valida';
if (emp.validade_cnh) {
const expiry = new Date(emp.validade_cnh);
const now = new Date();
const diffDays = Math.ceil((expiry - now) / (1000 * 60 * 60 * 24));
if (expiry.getFullYear() < 1900) cnhStatus = 'valida'; // Fallback para datas zeradas ou inválidas
else if (diffDays < 0) cnhStatus = 'vencida';
else if (diffDays <= 30) cnhStatus = 'proxima_vencer';
}
return {
id: emp.idcolaborador || emp.id,
idcolaborador: emp.idcolaborador,
name: emp.colaboradores || emp.nome || emp.name,
email: emp.email_corporativo || emp.email,
phone: emp.telefone,
cpf: emp.cpf,
role: emp.cargo || emp.role,
department: emp.empresas || emp.department,
status: emp.status_contrato || emp.status || 'Ativo',
cnh_status: cnhStatus,
admissionDate: emp.data_admissao ? new Date(emp.data_admissao).toLocaleDateString() : '-',
pasta: emp.pasta_rh || emp.pasta,
salary: emp.salario,
manager: emp.responsavel_direto_validado || '-',
// Manter os dados brutos para preenchimento de formulários
...emp
};
});
set({ employees: mappedEmployees });
return mappedEmployees;
} catch (err) {
set({ error: err.message });
notify('error', 'Falha na Busca', 'Erro ao carregar colaboradores: ' + err.message);
return [];
} finally {
set({ loading: false });
}
},
/**
* Busca um colaborador por ID
*/
fetchEmployeeById: async (id) => {
try {
const response = await rhService.getCollaboratorById(id);
if (response?.success && response?.data) {
return response.data;
} else if (response?.data) {
return response.data;
} else if (response && typeof response === 'object') {
return response;
}
return null;
} catch (err) {
notify('error', 'Erro de Detalhes', 'Não foi possível buscar os dados completos do colaborador.');
return null;
}
},
/**
* Busca estatísticas de colaboradores
*/
fetchStatistics: async () => {
set({ loading: true });
try {
const response = await rhService.getStatistics();
let raw = {};
if (response?.success && response?.data) {
raw = response.data;
} else if (response?.data) {
raw = response.data;
} else if (response && typeof response === 'object') {
raw = response;
} else {
set({ statistics: [] });
return null;
}
const mappedStats = [
{
label: 'Total Geral',
value: Number(raw.total || 0),
trend: 'Colaboradores',
color: 'bg-indigo-500/10 text-indigo-500'
},
{
label: 'Colaboradores Ativos',
value: Number(raw.por_status_contrato?.find(i => i.status_contrato === 'Ativo')?.quantidade || 0),
trend: 'Em operação',
color: 'bg-emerald-500/10 text-emerald-500'
},
{
label: 'CNH Vencidas',
value: Number(raw.cnh_status?.cnh_vencida || 0),
trend: 'Ação imediata',
color: 'bg-rose-500/10 text-rose-500',
negative: true
},
{
label: 'CNH a Vencer',
value: Number(raw.cnh_status?.cnh_proxima_vencer || 0),
trend: 'Próximos 30 dias',
color: 'bg-amber-500/10 text-amber-500'
},
{
label: 'Contas Email Corp.',
value: Number(raw.email_corporativo?.com_email || 0),
trend: 'Acesso liberado',
color: 'bg-blue-500/10 text-blue-500'
},
{
label: 'Desktops da Empresa',
value: Number(raw.desktop_empresa?.com_desktop || 0),
trend: 'Patrimônio em uso',
color: 'bg-slate-500/10 text-slate-500'
}
];
set({ statistics: mappedStats });
return {
stats: mappedStats,
charts: {
por_base: raw.por_base || [],
por_cargo: raw.por_cargo || [],
por_cliente: raw.por_cliente || [],
por_status: raw.por_status_contrato || [],
por_empresa: raw.por_empresa || []
}
};
} catch (err) {
set({ statistics: [] });
return null;
} finally {
set({ loading: false });
}
},
/**
* Busca alertas de colaboradores
*/
fetchAlerts: async () => {
try {
const response = await rhService.getAlerts();
let raw = {};
if (response?.success && response?.data) {
raw = response.data;
} else if (response?.data) {
raw = response.data;
} else if (response && typeof response === 'object') {
raw = response;
} else {
set({ alerts: [] });
return null;
}
const mappedAlerts = [
{
label: 'CNH Vencida AGORA',
value: Array.isArray(raw.cnh_vencida) ? raw.cnh_vencida.length : 0,
trend: 'Bloqueio imediato',
color: 'bg-rose-500/10 text-rose-500',
negative: true,
urgent: true
},
{
label: 'CNH a Vencer',
value: Array.isArray(raw.cnh_proxima_vencer) ? raw.cnh_proxima_vencer.length : 0,
trend: 'Próximos 30 dias',
color: 'bg-amber-500/10 text-amber-500'
},
{
label: 'Férias Próximas',
value: Array.isArray(raw.ferias_proximas) ? raw.ferias_proximas.length : 0,
trend: 'Próximos 60 dias',
color: 'bg-blue-500/10 text-blue-500'
},
{
label: 'Falta Telefone',
value: Array.isArray(raw.sem_telefone) ? raw.sem_telefone.length : 0,
trend: 'Contato urgente',
color: 'bg-slate-500/10 text-slate-500',
negative: true
},
{
label: 'Sem Email Corp.',
value: Array.isArray(raw.sem_email_corporativo) ? raw.sem_email_corporativo.length : 0,
trend: 'Pendência TI',
color: 'bg-amber-500/10 text-amber-500'
}
];
set({ alerts: mappedAlerts });
return raw;
} catch (err) {
set({ alerts: [] });
return null;
}
},
/**
* Cria um novo colaborador
*/
createEmployee: async (payload) => {
set({ loading: true });
try {
const response = await rhService.createCollaborator(payload);
if (response?.success || (response && !response.error)) {
notify('success', 'Cadastro Concluído', 'O colaborador foi registrado com sucesso na base de dados.');
await get().fetchEmployees();
return true;
}
throw new Error(response?.error || 'Erro desconhecido');
} catch (err) {
notify('error', 'Falha no Cadastro', err.message);
return false;
} finally {
set({ loading: false });
}
},
/**
* Atualiza dados de um colaborador
*/
updateEmployee: async (id, payload) => {
set({ loading: true });
try {
const response = await rhService.updateCollaborator(id, payload);
if (response?.success || (response && !response.error)) {
notify('success', 'Registro Atualizado', 'As alterações foram salvas com sucesso.');
await get().fetchEmployees();
return true;
}
throw new Error(response?.error || 'Erro desconhecido');
} catch (err) {
notify('error', 'Erro na Atualização', err.message);
return false;
} finally {
set({ loading: false });
}
},
/**
* Inativar colaborador
*/
inactivateEmployee: async (id) => {
set({ loading: true });
try {
const response = await rhService.inactivateCollaborator(id);
if (response?.success) {
notify('success', 'Status Alterado', 'O colaborador foi inativado com sucesso.');
await get().fetchEmployees();
return true;
}
throw new Error(response?.error || 'Falha ao inativar');
} catch (err) {
notify('error', 'Erro de Operação', err.message);
return false;
} finally {
set({ loading: false });
}
},
/**
* Reativar colaborador
*/
reactivateEmployee: async (id) => {
set({ loading: true });
try {
const response = await rhService.reactivateCollaborator(id);
if (response?.success) {
notify('success', 'Status Alterado', 'O colaborador foi reativado com sucesso.');
await get().fetchEmployees();
return true;
}
throw new Error(response?.error || 'Falha ao reativar');
} catch (err) {
notify('error', 'Erro de Operação', err.message);
return false;
} finally {
set({ loading: false });
}
},
/**
* Chat / Busca Inteligente
*/
searchInChat: async (pergunta) => {
try {
const response = await rhService.searchChat(pergunta);
return response;
} catch (err) {
notify('error', 'Erro na IA', 'Não foi possível completar a consulta inteligente.');
return null;
}
},
setEmployees: (employees) => set({ employees })
}));