335 lines
10 KiB
JavaScript
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 })
|
|
}));
|