Página principal: mudanças entre as edições
Sem resumo de edição |
Sem resumo de edição |
||
| Linha 1: | Linha 1: | ||
< | <?php | ||
// template/page-nr/page-inicialphp | |||
global $selector_nrs, $amokadb; | |||
$atualizado = "14/02/2023 15h10"; | |||
$empresa_id = nr_user_get_current_empresa(); | |||
if ($empresa_id && nr_user_can_audit()) { | |||
$select_join = "nr_empresa AS e ON (n.nr_ordem = e.nr_ordem AND nr_empresa_id = " . intval($empresa_id) . ")"; | |||
$select = array("nr_nome AS n", array("join" => "LEFT", "table" => $select_join)); | |||
$options = array( | |||
"select" => "n.nr_ordem, nr_nomes, nr_apelido, nr_norma, nr_dou_revisao, nr_tipo, nr_obs, nr_objetivo, n.nr_icon, e.nr_grav, e.nr_amp, e.nr_peso, e.nr_sit, e.nr_perc_aud, e.nr_perc_imp", | |||
"order" => array("n.nr_ordem" => "ASC") | |||
); | |||
} else { | |||
$select = array("nr_nome AS n"); | |||
$options = array( | |||
"select" => "n.nr_ordem, nr_nomes, nr_apelido, nr_norma, nr_dou_revisao, nr_tipo, nr_obs, nr_objetivo, n.nr_icon", | |||
"order" => array("n.nr_ordem" => "ASC") | |||
); | |||
} | |||
== | $selector_nrs = $amokadb->select($select, $options, false); | ||
* | // ===================================================================== | ||
* | // CORREÇÃO: Nova query adaptada para a arquitetura fatiada (INNER JOIN) | ||
// ===================================================================== | |||
$select_portarias = array("nr_itens_portarias_nrs AS rel"); | |||
// Fazemos o JOIN com a tabela mestre para buscar os dados físicos do DOU | |||
$select_join_portaria = "nr_itens_portarias AS p ON (rel.portaria_historico = p.portaria_historico)"; | |||
$select_portarias[] = array("join" => "INNER", "table" => $select_join_portaria); | |||
$options_portarias = array( | |||
// Agora buscamos o código da NR vindo da tabela de relacionamento técnico | |||
"select" => "rel.normas_codigo, p.portaria_normas_titulo, p.portaria_normas_dou, rel.portaria_normas_tipo, p.portaria_normas_link, rel.portaria_normas_ativo", | |||
"order" => array("p.portaria_normas_dou" => "DESC") | |||
); | |||
$todas_portarias = $amokadb->select($select_portarias, $options_portarias, false); | |||
// Agrupar as portarias por código da norma para facilitar o acesso no loop | |||
$portarias_por_nr = array(); | |||
if (!empty($todas_portarias)) { | |||
foreach ($todas_portarias as $portaria) { | |||
$codigo = $portaria['normas_codigo']; | |||
$portarias_por_nr[$codigo][] = $portaria; | |||
} | |||
} | |||
?> | |||
<div class="container main-wiki-container"> | |||
<div class="row header-hero"> | |||
<div class="col s12 center-align"> | |||
<h2 style="color: var(--primary-color); font-weight: 700; margin-bottom: 5px;">NR.WIKI.BR</h2> | |||
<p class="flow-text grey-text text-darken-1">A enciclopédia interativa das Normas Regulamentadoras</p> | |||
</div> | |||
</div> | |||
<div class="row"> | |||
<div class="col s12 l9"> | |||
<div class="search-wrapper card-panel z-depth-1"> | |||
<form id="form-busca-global" method="GET" action=""> | |||
<input type="hidden" name="tp" value="resul"> | |||
<div class="input-field" style="margin: 0;"> | |||
<i class="material-icons prefix" style="color: var(--primary-color);">search</i> | |||
<input type="text" id="search-nr" name="search" placeholder="Pesquisar por número ou palavra-chave (pressione Enter para busca profunda)..." aria-label="Pesquisar Normas Regulamentadoras" autocomplete="off"> | |||
</div> | |||
</form> | |||
</div> | |||
<ul class="collapsible z-depth-1"> | |||
<?php foreach ($selector_nrs as $index => $selector_nr): ?> | |||
<?php | |||
$status = (empty($selector_nr["nr_tipo"]) || $selector_nr["nr_obs"] == "REVOGADA") ? 'revoked' : 'active'; | |||
$quarta = ''; | |||
$quinta = ''; | |||
if (nr_user_can_audit() && $empresa_id) { | |||
if (empty($selector_nr["nr_tipo"])) { | |||
$quarta = $selector_nr["nr_obs"]; | |||
$quinta = "NORMA REVOGADA <br> Nada a fazer"; | |||
} elseif ($selector_nr["nr_sit"] == 1 || $selector_nr["nr_sit"] == null) { | |||
$quarta = $selector_nr["nr_obs"]; | |||
$quinta = "INICIAR"; | |||
} elseif ($selector_nr["nr_sit"] == 2) { | |||
$quarta = " "; | |||
$quinta = "Não Aplicável"; | |||
} elseif ($selector_nr["nr_peso"] == null || $selector_nr["nr_peso"] == 0) { | |||
$quarta = " "; | |||
$quinta = "FALTA Definir Amplitude e Gravidade"; | |||
} elseif ($selector_nr["nr_sit"] == 3 && $selector_nr["nr_perc_aud"] == null) { | |||
$quarta = "Peso: " . $selector_nr["nr_peso"]; | |||
$quinta = "Comece a AUDITAR os itens"; | |||
} elseif ($selector_nr["nr_sit"] == 3 && $selector_nr["nr_perc_aud"] < 100) { | |||
$quarta = "Peso: " . $selector_nr["nr_peso"]; | |||
$quinta = (round($selector_nr["nr_perc_aud"], 2)) . "% Auditado<br>" . (round($selector_nr["nr_perc_imp"], 2)) . "% Implantado"; | |||
} elseif ($selector_nr["nr_sit"] == 3 && $selector_nr["nr_perc_aud"] == 100) { | |||
$quarta = "Peso: " . $selector_nr["nr_peso"]; | |||
$quinta = (round($selector_nr["nr_perc_imp"], 2)) . "% Implantado"; | |||
} | |||
} else { | |||
$quarta = $selector_nr["nr_obs"]; | |||
$quinta = 'Mostrando texto das NR\'s.<a href="/wp-login.php" class="login-link">Entre para personalizar!</a>'; | |||
} | |||
$tipo_obs = $selector_nr["nr_tipo"] ? $selector_nr["nr_tipo"] : $selector_nr["nr_obs"]; | |||
$status_line = "Atualizada: " . htmlspecialchars($selector_nr["nr_dou_revisao"]); | |||
if (nr_user_can_audit() && $empresa_id && $quarta && $status != 'revoked') { | |||
$status_line .= " | " . htmlspecialchars($quarta); | |||
} | |||
if ($quinta) { | |||
$status_line .= " | " . $quinta; | |||
} | |||
// Formata o número da ordem atual para o padrão '01.00', '02.00', etc. | |||
$codigo_busca = str_pad($selector_nr["nr_ordem"], 2, "0", STR_PAD_LEFT) . ".00"; | |||
$portarias_da_nr = isset($portarias_por_nr[$codigo_busca]) ? $portarias_por_nr[$codigo_busca] : array(); | |||
?> | |||
<li | |||
data-ordem="<?php echo htmlspecialchars($selector_nr["nr_ordem"]); ?>" | |||
data-apelido="<?php echo htmlspecialchars($selector_nr["nr_apelido"]); ?>" | |||
data-status-line="<?php echo htmlspecialchars(strip_tags($status_line)); ?>" | |||
data-objetivo="<?php echo htmlspecialchars(strip_tags($selector_nr["nr_objetivo"])); ?>" | |||
data-tipo="<?php echo htmlspecialchars($selector_nr["nr_tipo"]); ?>" | |||
data-portarias="<?php echo htmlspecialchars(json_encode($portarias_da_nr), ENT_QUOTES, 'UTF-8'); ?>" | |||
> | |||
<div class="collapsible-header <?php echo $status; ?>"> | |||
<div class="header-title-wrapper"> | |||
<i class="material-icons icon-<?php echo intval($selector_nr["nr_ordem"]); ?>"></i> | |||
<strong>NR-<?php echo htmlspecialchars($selector_nr["nr_ordem"]); ?></strong>: <?php echo htmlspecialchars($selector_nr["nr_apelido"]); ?> | |||
</div> | |||
<span class="badge-tipo right"><?php echo $tipo_obs ? htmlspecialchars($tipo_obs) : ''; ?></span> | |||
</div> | |||
<div class="collapsible-body"> | |||
<h6><strong>Objetivo:</strong></h6> | |||
<p><?php echo $selector_nr["nr_objetivo"]; ?></p> | |||
<div class="status-divider"></div> | |||
<p class="grey-text text-darken-2" style="font-size: 0.9rem;"><?php echo $status_line; ?></p> | |||
<div class="right-align" style="margin-top: 15px;"> | |||
<a href="/nr/?tp=nr&id=<?php echo urlencode($selector_nr['nr_ordem']); ?>" target="_blank" rel="noopener noreferrer" class="btn waves-effect waves-light primary-btn btn-small"> | |||
<i class="material-icons left">menu_book</i> Abrir Artigos da NR-<?php echo htmlspecialchars($selector_nr["nr_ordem"]); ?> | |||
</a> | |||
</div> | |||
</div> | |||
</li> | |||
<?php endforeach; ?> | |||
</ul> | |||
</div> | |||
<div class="col s12 l3 side-panel"> | |||
<div id="side-panel-nr-detalhes" class="card card-panel-sidebar z-depth-1" style="display: none; border-left: 4px solid var(--primary-color);"> | |||
<h5 class="sidebar-title" style="margin-bottom: 15px;"> | |||
<i class="material-icons left">history</i> Histórico - <span id="side-nr-numero"></span> | |||
</h5> | |||
<div id="side-nr-portarias" style="max-height: 400px; overflow-y: auto; padding-right: 5px;"></div> | |||
</div> | |||
<div class="card card-panel-sidebar z-depth-1"> | |||
<h5 class="sidebar-title"><i class="material-icons left">business</i> Auditoria</h5> | |||
<?php if ($empresa_id && nr_user_can_audit()): ?> | |||
<p class="sidebar-p">Você está auditando para a empresa ID: <strong><?php echo $empresa_id; ?></strong></p> | |||
<div class="divider"></div> | |||
<p style="font-size: 0.85rem; margin-top: 10px;" class="green-text text-darken-2"><strong>Painel de sincronização ativo.</strong></p> | |||
<?php else: ?> | |||
<p class="sidebar-p">Monitore a conformidade legal da sua empresa diretamente na Wiki.</p> | |||
<a href="/wp-login.php" class="btn btn-block waves-effect waves-light accent-btn" style="width: 100%; margin-top: 10px;">Acessar Painel</a> | |||
<?php endif; ?> | |||
</div> | |||
<div class="card card-panel-sidebar z-depth-1 grey lighten-4"> | |||
<h5 class="sidebar-title" style="color: #555;"><i class="material-icons left">gavel</i> Tipos de Normas</h5> | |||
<ul class="collection categorization-list" style="border: none; margin: 0;"> | |||
<li class="collection-item"><strong>Gerais:</strong> Aspectos comuns sem prender-se a setores.</li> | |||
<li class="collection-item"><strong>Especiais:</strong> Execução de trabalho, atividades e EPIs.</li> | |||
<li class="collection-item"><strong>Setoriais:</strong> Relativas a setores econômicos específicos.</li> | |||
</ul> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<style> | |||
<?php | |||
foreach ($selector_nrs as $selector_nr): | |||
$status = (empty($selector_nr["nr_tipo"]) || $selector_nr["nr_obs"] == "REVOGADA") ? 'revoked' : 'active'; | |||
$icon = !empty($selector_nr["nr_icon"]) ? $selector_nr["nr_icon"] : ($status == 'revoked' ? '\f30e' : '\e88e'); | |||
?> | |||
.icon-<?php echo intval($selector_nr["nr_ordem"]); ?>::before { | |||
content: '<?php echo $icon; ?>'; | |||
font-family: 'Material Icons'; | |||
} | |||
<?php endforeach; ?> | |||
</style> | |||
<style> | |||
.main-wiki-container { | |||
margin-top: 20px; | |||
} | |||
.header-hero { | |||
margin-bottom: 30px; | |||
} | |||
.search-wrapper { | |||
padding: 5px 15px !important; | |||
border-radius: 8px; | |||
margin-bottom: 20px; | |||
} | |||
.header-title-wrapper { | |||
display: flex; | |||
align-items: center; | |||
} | |||
.collapsible { | |||
border: none; | |||
border-radius: 8px; | |||
overflow: hidden; | |||
} | |||
.collapsible-header { | |||
font-size: 1.05rem; | |||
padding: 14px 18px; | |||
border-bottom: 1px solid rgba(255,255,255,0.1); | |||
} | |||
.collapsible-header .material-icons { | |||
margin-right: 12px; | |||
font-size: 22px; | |||
} | |||
.badge-tipo { | |||
font-size: 0.8rem; | |||
padding: 2px 8px; | |||
background: rgba(0,0,0,0.15); | |||
border-radius: 4px; | |||
color: #fff; | |||
} | |||
.collapsible-body { | |||
border-bottom: 1px solid #ddd; | |||
padding: 20px !important; | |||
} | |||
.status-divider { | |||
height: 1px; | |||
background: #eee; | |||
margin: 15px 0; | |||
} | |||
.login-link { | |||
color: var(--secondary-color); | |||
font-weight: bold; | |||
text-decoration: underline; | |||
} | |||
.card-panel-sidebar { | |||
padding: 18px !important; | |||
border-radius: 8px; | |||
} | |||
.sidebar-title { | |||
font-size: 1.15rem; | |||
margin: 0 0 12px 0; | |||
font-weight: bold; | |||
color: var(--primary-color); | |||
display: flex; | |||
align-items: center; | |||
} | |||
.sidebar-p { | |||
font-size: 0.9rem; | |||
color: #444; | |||
line-height: 1.4; | |||
} | |||
.categorization-list .collection-item { | |||
background: transparent; | |||
padding: 8px 0; | |||
font-size: 0.85rem; | |||
border-bottom: 1px solid #e0e0e0; | |||
} | |||
.categorization-list .collection-item:last-child { | |||
border: none; | |||
} | |||
.btn-small i { | |||
font-size: 1.2rem !important; | |||
} | |||
/* Container da Linha do Tempo */ | |||
.custom-timeline { | |||
position: relative; | |||
padding: 5px 0; | |||
margin: 5px 0; | |||
} | |||
/* A linha vertical cinza de fundo */ | |||
.custom-timeline::before { | |||
content: ''; | |||
position: absolute; | |||
top: 0; | |||
bottom: 0; | |||
left: 50%; | |||
width: 2px; | |||
background: #e0e0e0; | |||
transform: translateX(-50%); | |||
} | |||
/* Estrutura de cada item */ | |||
.timeline-item { | |||
position: relative; | |||
width: 100%; | |||
margin-bottom: 1px; | |||
display: flex; | |||
} | |||
/* O ponto/bolinha em cima da linha */ | |||
.timeline-badge { | |||
position: absolute; | |||
top: 8px; | |||
left: 50%; | |||
width: 10px; | |||
height: 10px; | |||
border-radius: 50%; | |||
transform: translateX(-50%); | |||
z-index: 2; | |||
border: 2px solid #fff; | |||
box-shadow: 0 0 0 2px rgba(0,0,0,0.05); | |||
} | |||
/* O Card clicável */ | |||
.timeline-content { | |||
display: block; | |||
width: 44%; | |||
padding: 4px; | |||
background: #fff; | |||
border-radius: 6px; | |||
border: 1px solid #e0e0e0; | |||
box-shadow: 0 1px 3px rgba(0,0,0,0.05); | |||
transition: all 0.2s ease; | |||
text-decoration: none !important; | |||
} | |||
.timeline-content:hover { | |||
box-shadow: 0 3px 6px rgba(0,0,0,0.12); | |||
transform: translateY(-1px); | |||
background: #fafafa; | |||
} | |||
/* Posicionamento à Esquerda (Iniciais / Novas Redações) */ | |||
.timeline-item.left-side { | |||
justify-content: flex-start; | |||
} | |||
.timeline-item.left-side .timeline-content { | |||
text-align: right; | |||
} | |||
/* Posicionamento à Direita (Atualizações / Prazos) */ | |||
.timeline-item.right-side { | |||
justify-content: flex-end; | |||
} | |||
.timeline-item.right-side .timeline-content { | |||
text-align: left; | |||
} | |||
/* Textos internos da Timeline */ | |||
.timeline-year { | |||
display: block; | |||
font-weight: bold; | |||
font-size: 0.8rem; | |||
color: #333; | |||
} | |||
.timeline-type { | |||
display: block; | |||
font-size: 0.5rem; | |||
font-weight: 600; | |||
color: #777; | |||
margin-bottom: 3px; | |||
text-transform: uppercase; | |||
} | |||
.timeline-title { | |||
font-size: 0.72rem; | |||
color: #555; | |||
margin: 0; | |||
line-height: 1.3; | |||
} | |||
/* Destaque para a Portaria Vigente */ | |||
.timeline-item.is-vigente .timeline-content { | |||
border-color: #4caf50; | |||
background: #f1f8e9; | |||
} | |||
.timeline-item.is-vigente .timeline-badge { | |||
box-shadow: 0 0 0 1px rgba(76, 175, 80, 0.3); | |||
} | |||
.badge-vigente { | |||
display: inline-block; | |||
font-size: 0.45rem; | |||
background: #4caf50; | |||
color: #fff; | |||
padding: 1px; | |||
border-radius: 3px; | |||
margin-top: 1px; | |||
font-weight: bold; | |||
} | |||
/* Estilos para a Base Centralizada (Ocupa ambos os lados) */ | |||
.timeline-item.center-base { | |||
justify-content: center; | |||
width: 100%; | |||
} | |||
.timeline-item.center-base .timeline-content { | |||
width: 70%; /* Fica mais largo, distribuindo o peso nos dois lados da linha */ | |||
text-align: center; | |||
border: 2px solid #4caf50; | |||
background: #f1f8e9; | |||
} | |||
/* Garante que a bolinha da linha do tempo fique perfeitamente centralizada sobre o bloco da base se necessário */ | |||
.timeline-item.center-base .timeline-badge { | |||
top: -5px; /* Desloca ligeiramente para marcar o início absoluto da linha */ | |||
} | |||
/* Estilos para o Teto da Árvore (Revogação passa dos dois lados) */ | |||
.timeline-item.center-top { | |||
justify-content: center; | |||
width: 100%; | |||
margin-top: 10px; | |||
} | |||
.timeline-item.center-top .timeline-content { | |||
width: 75%; /* Garante estabilidade cruzando o eixo central */ | |||
text-align: center; | |||
border: 2px dashed #d32f2f; /* Borda tracejada vermelha indicando interrupção */ | |||
background: #ffebee; /* Fundo avermelhado suave */ | |||
} | |||
/* Badge estilizada de Revogada */ | |||
.badge-revogada { | |||
display: inline-block; | |||
font-size: 0.65rem; | |||
background: #d32f2f; | |||
color: #fff; | |||
padding: 1px 6px; | |||
border-radius: 3px; | |||
margin-top: 4px; | |||
font-weight: bold; | |||
text-transform: uppercase; | |||
} | |||
/* Ajuste na bolinha da linha do tempo para a revogação */ | |||
.timeline-item.is-revogada .timeline-badge { | |||
box-shadow: 0 0 0 4px rgba(211, 47, 47, 0.3); | |||
width: 12px; | |||
height: 12px; | |||
} | |||
/* Estilos para o Teto da Árvore (Revogação passa dos dois lados) */ | |||
.timeline-item.center-top { | |||
justify-content: center; | |||
width: 100%; | |||
margin-top: 10px; | |||
} | |||
.timeline-item.center-top .timeline-content { | |||
width: 75%; /* Garante estabilidade cruzando o eixo central */ | |||
text-align: center; | |||
border: 2px dashed #d32f2f; /* Borda tracejada vermelha indicando interrupção */ | |||
background: #ffebee; /* Fundo avermelhado suave */ | |||
} | |||
/* Badge estilizada de Revogada */ | |||
.badge-revogada { | |||
display: inline-block; | |||
font-size: 0.65rem; | |||
background: #d32f2f; | |||
color: #fff; | |||
padding: 1px 6px; | |||
border-radius: 3px; | |||
margin-top: 4px; | |||
font-weight: bold; | |||
text-transform: uppercase; | |||
} | |||
/* Ajuste na bolinha da linha do tempo para a revogação */ | |||
.timeline-item.is-revogada .timeline-badge { | |||
box-shadow: 0 0 0 4px rgba(211, 47, 47, 0.3); | |||
width: 12px; | |||
height: 12px; | |||
} | |||
/* Remove a bolinha central das portarias que ocupam os dois lados (Raiz e Teto) */ | |||
.timeline-item.center-base .timeline-badge, | |||
.timeline-item.center-top .timeline-badge { | |||
display: none; | |||
} | |||
</style> | |||
<script> | |||
document.addEventListener('DOMContentLoaded', function () { | |||
var elems = document.querySelectorAll('.collapsible'); | |||
// CORREÇÃO DEFINITIVA: Sincronização inteligente do painel lateral no modo Accordion | |||
M.Collapsible.init(elems, { | |||
accordion: true, | |||
onOpenStart: function(el) { | |||
const sidePanel = document.getElementById('side-panel-nr-detalhes'); | |||
const ordem = el.getAttribute('data-ordem'); | |||
const portariasRaw = el.getAttribute('data-portarias'); | |||
let portarias = []; | |||
try { | |||
portarias = JSON.parse(portariasRaw) || []; | |||
} catch(e) { | |||
console.error("Erro ao processar portarias", e); | |||
} | |||
// 1. Atualiza o número da NR no título lateral | |||
document.getElementById('side-nr-numero').textContent = 'NR-' + ordem; | |||
// 2. Reconstrói a Árvore da Linha do Tempo | |||
const containerPortarias = document.getElementById('side-nr-portarias'); | |||
containerPortarias.innerHTML = ''; | |||
if (portarias.length === 0) { | |||
containerPortarias.innerHTML = '<p class="grey-text" style="font-size: 0.85rem; padding: 0 5px;">Nenhuma portaria registrada para esta norma.</p>'; | |||
} else { | |||
let timelineHtml = '<div class="custom-timeline">'; | |||
portarias.forEach(p => { | |||
let ano = '---'; | |||
if (p.portaria_normas_dou) { | |||
ano = p.portaria_normas_dou.split('-')[0]; | |||
} | |||
let tipoTraduzido = 'Atualização'; | |||
let classePosicao = 'right-side'; | |||
let badgeColor = '#2196f3'; | |||
let ehRevogacao = false; | |||
if (p.portaria_normas_tipo === 'Inicial') { | |||
tipoTraduzido = 'Publicação Original'; | |||
classePosicao = 'center-base'; | |||
badgeColor = '#4caf50'; | |||
} else if (p.portaria_normas_tipo === 'Revoga') { | |||
tipoTraduzido = 'Norma Revogada'; | |||
classePosicao = 'center-top'; | |||
badgeColor = '#d32f2f'; | |||
ehRevogacao = true; | |||
} else if (p.portaria_normas_tipo === 'Nova' || p.portaria_normas_tipo === 'Criar') { | |||
tipoTraduzido = 'Nova Redação'; | |||
classePosicao = 'left-side'; | |||
badgeColor = '#9c27b0'; | |||
} else if (p.portaria_normas_tipo === 'Prazo') { | |||
tipoTraduzido = 'Novo Prazo'; | |||
badgeColor = '#ff9800'; | |||
} | |||
let classeVigente = ''; | |||
if (p.portaria_normas_ativo == 1) { | |||
classeVigente = ehRevogacao ? 'is-revogada' : 'is-vigente'; | |||
} | |||
timelineHtml += ` | |||
<div class="timeline-item ${classePosicao} ${classeVigente}"> | |||
<div class="timeline-badge" style="background-color: ${badgeColor};"></div> | |||
<a href="https://criacoesbutzke.com.br/nr/?tp=port&id=${parseInt(ordem)}" target="_blank" rel="noopener" class="timeline-content"> | |||
<span class="timeline-year">${ano}</span> | |||
<span class="timeline-type">${tipoTraduzido}</span> | |||
${p.portaria_normas_ativo == 1 && !ehRevogacao ? '<span class="badge-vigente">Vigente</span>' : ''} | |||
${ehRevogacao ? '<span class="badge-revogada">Revogada</span>' : ''} | |||
</a> | |||
</div> | |||
`; | |||
}); | |||
timelineHtml += '</div>'; | |||
containerPortarias.innerHTML = timelineHtml; | |||
} | |||
// 3. Força o painel a aparecer imediatamente com os novos dados da NR clicada | |||
if (sidePanel) { | |||
sidePanel.style.display = 'block'; | |||
sidePanel.style.opacity = '1'; | |||
} | |||
}, | |||
onCloseStart: function(el) { | |||
const sidePanel = document.getElementById('side-panel-nr-detalhes'); | |||
// O SEGREDO AQUI: Damos um tempo mínimo (10 milissegundos) para ver se outra NR assumiu a tela. | |||
// Se o usuário clicou em outra NR, o onOpenStart dela já mudou o título para a nova ordem. | |||
// Se o título ainda corresponder à NR que está fechando, significa que ele fechou e NÃO abriu outra. | |||
setTimeout(() => { | |||
if (sidePanel) { | |||
const ordemFechando = 'NR-' + el.getAttribute('data-ordem'); | |||
const ordemAtualNoPainel = document.getElementById('side-nr-numero').textContent; | |||
// Só fecha o painel lateral de verdade se a tela estiver inteiramente limpa (sem nenhuma outra NR aberta) | |||
if (ordemFechando === ordemAtualNoPainel) { | |||
sidePanel.style.opacity = '0'; | |||
setTimeout(() => { | |||
// Confere se continua zero para não quebrar cliques rápidos intermediários | |||
if(sidePanel.style.opacity === '0') { | |||
sidePanel.style.display = 'none'; | |||
} | |||
}, 150); | |||
} | |||
} | |||
}, 10); | |||
} | |||
}); | |||
// Código de busca sequencial abaixo | |||
const searchInput = document.getElementById('search-nr'); | |||
const collapsibleItems = document.querySelectorAll('.collapsible li'); | |||
const genericTerms = ['atividade', 'atividades', 'trabalho', 'segurança', 'saúde', 'desenvolvidas', 'devidamente']; | |||
searchInput.addEventListener('input', function () { | |||
const query = this.value.toLowerCase().trim(); | |||
collapsibleItems.forEach(item => { | |||
const title = item.querySelector('.collapsible-header').textContent.toLowerCase(); | |||
const objetivo = item.getAttribute('data-objetivo').toLowerCase(); | |||
if (query.length === 0) { | |||
item.style.display = ''; | |||
return; | |||
} | |||
if (genericTerms.includes(query) && query.length < 5) { | |||
item.style.display = 'none'; | |||
return; | |||
} | |||
const matches = title.includes(query) || objetivo.includes(query); | |||
item.style.display = matches ? '' : 'none'; | |||
}); | |||
}); | |||
document.getElementById('form-busca-global').addEventListener('submit', function (e) { | |||
const query = searchInput.value.toLowerCase().trim(); | |||
if (query.length > 0 && !query.startsWith('nr')) { | |||
// Permite busca nativa externa | |||
} else { | |||
e.preventDefault(); | |||
} | |||
}); | |||
}); | |||
</script> | |||
Edição das 20h53min de 28 de junho de 2026
<?php // template/page-nr/page-inicialphp global $selector_nrs, $amokadb; $atualizado = "14/02/2023 15h10";
$empresa_id = nr_user_get_current_empresa(); if ($empresa_id && nr_user_can_audit()) {
$select_join = "nr_empresa AS e ON (n.nr_ordem = e.nr_ordem AND nr_empresa_id = " . intval($empresa_id) . ")";
$select = array("nr_nome AS n", array("join" => "LEFT", "table" => $select_join));
$options = array(
"select" => "n.nr_ordem, nr_nomes, nr_apelido, nr_norma, nr_dou_revisao, nr_tipo, nr_obs, nr_objetivo, n.nr_icon, e.nr_grav, e.nr_amp, e.nr_peso, e.nr_sit, e.nr_perc_aud, e.nr_perc_imp",
"order" => array("n.nr_ordem" => "ASC")
);
} else {
$select = array("nr_nome AS n");
$options = array(
"select" => "n.nr_ordem, nr_nomes, nr_apelido, nr_norma, nr_dou_revisao, nr_tipo, nr_obs, nr_objetivo, n.nr_icon",
"order" => array("n.nr_ordem" => "ASC")
);
}
$selector_nrs = $amokadb->select($select, $options, false);
// ===================================================================== // CORREÇÃO: Nova query adaptada para a arquitetura fatiada (INNER JOIN) // ===================================================================== $select_portarias = array("nr_itens_portarias_nrs AS rel");
// Fazemos o JOIN com a tabela mestre para buscar os dados físicos do DOU $select_join_portaria = "nr_itens_portarias AS p ON (rel.portaria_historico = p.portaria_historico)"; $select_portarias[] = array("join" => "INNER", "table" => $select_join_portaria);
$options_portarias = array(
// Agora buscamos o código da NR vindo da tabela de relacionamento técnico
"select" => "rel.normas_codigo, p.portaria_normas_titulo, p.portaria_normas_dou, rel.portaria_normas_tipo, p.portaria_normas_link, rel.portaria_normas_ativo",
"order" => array("p.portaria_normas_dou" => "DESC")
);
$todas_portarias = $amokadb->select($select_portarias, $options_portarias, false);
// Agrupar as portarias por código da norma para facilitar o acesso no loop $portarias_por_nr = array(); if (!empty($todas_portarias)) {
foreach ($todas_portarias as $portaria) {
$codigo = $portaria['normas_codigo'];
$portarias_por_nr[$codigo][] = $portaria;
}
} ?>
NR.WIKI.BR
A enciclopédia interativa das Normas Regulamentadoras
<form id="form-busca-global" method="GET" action="">
<input type="hidden" name="tp" value="resul">
search
<input type="text" id="search-nr" name="search" placeholder="Pesquisar por número ou palavra-chave (pressione Enter para busca profunda)..." aria-label="Pesquisar Normas Regulamentadoras" autocomplete="off">
</form>
-
<?php foreach ($selector_nrs as $index => $selector_nr): ?>
<?php
$status = (empty($selector_nr["nr_tipo"]) || $selector_nr["nr_obs"] == "REVOGADA") ? 'revoked' : 'active';
$quarta = ;
$quinta = ;
if (nr_user_can_audit() && $empresa_id) {
if (empty($selector_nr["nr_tipo"])) {
$quarta = $selector_nr["nr_obs"];
$quinta = "NORMA REVOGADA
Nada a fazer"; } elseif ($selector_nr["nr_sit"] == 1 || $selector_nr["nr_sit"] == null) { $quarta = $selector_nr["nr_obs"]; $quinta = "INICIAR"; } elseif ($selector_nr["nr_sit"] == 2) { $quarta = " "; $quinta = "Não Aplicável"; } elseif ($selector_nr["nr_peso"] == null || $selector_nr["nr_peso"] == 0) { $quarta = " "; $quinta = "FALTA Definir Amplitude e Gravidade"; } elseif ($selector_nr["nr_sit"] == 3 && $selector_nr["nr_perc_aud"] == null) { $quarta = "Peso: " . $selector_nr["nr_peso"]; $quinta = "Comece a AUDITAR os itens"; } elseif ($selector_nr["nr_sit"] == 3 && $selector_nr["nr_perc_aud"] < 100) { $quarta = "Peso: " . $selector_nr["nr_peso"]; $quinta = (round($selector_nr["nr_perc_aud"], 2)) . "% Auditado
" . (round($selector_nr["nr_perc_imp"], 2)) . "% Implantado"; } elseif ($selector_nr["nr_sit"] == 3 && $selector_nr["nr_perc_aud"] == 100) { $quarta = "Peso: " . $selector_nr["nr_peso"]; $quinta = (round($selector_nr["nr_perc_imp"], 2)) . "% Implantado"; } } else { $quarta = $selector_nr["nr_obs"]; $quinta = 'Mostrando texto das NR\'s.<a href="/wp-login.php" class="login-link">Entre para personalizar!</a>'; } $tipo_obs = $selector_nr["nr_tipo"] ? $selector_nr["nr_tipo"] : $selector_nr["nr_obs"]; $status_line = "Atualizada: " . htmlspecialchars($selector_nr["nr_dou_revisao"]); if (nr_user_can_audit() && $empresa_id && $quarta && $status != 'revoked') { $status_line .= " | " . htmlspecialchars($quarta); } if ($quinta) { $status_line .= " | " . $quinta; } // Formata o número da ordem atual para o padrão '01.00', '02.00', etc. $codigo_busca = str_pad($selector_nr["nr_ordem"], 2, "0", STR_PAD_LEFT) . ".00"; $portarias_da_nr = isset($portarias_por_nr[$codigo_busca]) ? $portarias_por_nr[$codigo_busca] : array(); ?> <li data-ordem="<?php echo htmlspecialchars($selector_nr["nr_ordem"]); ?>" data-apelido="<?php echo htmlspecialchars($selector_nr["nr_apelido"]); ?>" data-status-line="<?php echo htmlspecialchars(strip_tags($status_line)); ?>" data-objetivo="<?php echo htmlspecialchars(strip_tags($selector_nr["nr_objetivo"])); ?>" data-tipo="<?php echo htmlspecialchars($selector_nr["nr_tipo"]); ?>" data-portarias="<?php echo htmlspecialchars(json_encode($portarias_da_nr), ENT_QUOTES, 'UTF-8'); ?>" > <div class="collapsible-header <?php echo $status; ?>">
<i class="material-icons icon-<?php echo intval($selector_nr["nr_ordem"]); ?>">
NR-<?php echo htmlspecialchars($selector_nr["nr_ordem"]); ?>: <?php echo htmlspecialchars($selector_nr["nr_apelido"]); ?>
<?php echo $tipo_obs ? htmlspecialchars($tipo_obs) : ; ?>
Objetivo:
<?php echo $selector_nr["nr_objetivo"]; ?>
<?php echo $status_line; ?>
<a href="/nr/?tp=nr&id=<?php echo urlencode($selector_nr['nr_ordem']); ?>" target="_blank" rel="noopener noreferrer" class="btn waves-effect waves-light primary-btn btn-small">
menu_book Abrir Artigos da NR-<?php echo htmlspecialchars($selector_nr["nr_ordem"]); ?>
</a>
<style> <?php foreach ($selector_nrs as $selector_nr):
$status = (empty($selector_nr["nr_tipo"]) || $selector_nr["nr_obs"] == "REVOGADA") ? 'revoked' : 'active';
$icon = !empty($selector_nr["nr_icon"]) ? $selector_nr["nr_icon"] : ($status == 'revoked' ? '\f30e' : '\e88e');
?>
.icon-<?php echo intval($selector_nr["nr_ordem"]); ?>::before {
content: '<?php echo $icon; ?>';
font-family: 'Material Icons';
}
<?php endforeach; ?> </style>
<style>
.main-wiki-container {
margin-top: 20px;
}
.header-hero {
margin-bottom: 30px;
}
.search-wrapper {
padding: 5px 15px !important;
border-radius: 8px;
margin-bottom: 20px;
}
.header-title-wrapper {
display: flex;
align-items: center;
}
.collapsible {
border: none;
border-radius: 8px;
overflow: hidden;
}
.collapsible-header {
font-size: 1.05rem;
padding: 14px 18px;
border-bottom: 1px solid rgba(255,255,255,0.1);
}
.collapsible-header .material-icons {
margin-right: 12px;
font-size: 22px;
}
.badge-tipo {
font-size: 0.8rem;
padding: 2px 8px;
background: rgba(0,0,0,0.15);
border-radius: 4px;
color: #fff;
}
.collapsible-body {
border-bottom: 1px solid #ddd;
padding: 20px !important;
}
.status-divider {
height: 1px;
background: #eee;
margin: 15px 0;
}
.login-link {
color: var(--secondary-color);
font-weight: bold;
text-decoration: underline;
}
.card-panel-sidebar {
padding: 18px !important;
border-radius: 8px;
}
.sidebar-title {
font-size: 1.15rem;
margin: 0 0 12px 0;
font-weight: bold;
color: var(--primary-color);
display: flex;
align-items: center;
}
.sidebar-p {
font-size: 0.9rem;
color: #444;
line-height: 1.4;
}
.categorization-list .collection-item {
background: transparent;
padding: 8px 0;
font-size: 0.85rem;
border-bottom: 1px solid #e0e0e0;
}
.categorization-list .collection-item:last-child {
border: none;
}
.btn-small i {
font-size: 1.2rem !important;
}
/* Container da Linha do Tempo */
.custom-timeline {
position: relative; padding: 5px 0; margin: 5px 0;
}
/* A linha vertical cinza de fundo */ .custom-timeline::before {
content: ; position: absolute; top: 0; bottom: 0; left: 50%; width: 2px; background: #e0e0e0; transform: translateX(-50%);
}
/* Estrutura de cada item */ .timeline-item {
position: relative; width: 100%; margin-bottom: 1px; display: flex;
}
/* O ponto/bolinha em cima da linha */ .timeline-badge {
position: absolute; top: 8px; left: 50%; width: 10px; height: 10px; border-radius: 50%; transform: translateX(-50%); z-index: 2; border: 2px solid #fff; box-shadow: 0 0 0 2px rgba(0,0,0,0.05);
}
/* O Card clicável */ .timeline-content {
display: block; width: 44%; padding: 4px; background: #fff; border-radius: 6px; border: 1px solid #e0e0e0; box-shadow: 0 1px 3px rgba(0,0,0,0.05); transition: all 0.2s ease; text-decoration: none !important;
}
.timeline-content:hover {
box-shadow: 0 3px 6px rgba(0,0,0,0.12); transform: translateY(-1px); background: #fafafa;
}
/* Posicionamento à Esquerda (Iniciais / Novas Redações) */ .timeline-item.left-side {
justify-content: flex-start;
} .timeline-item.left-side .timeline-content {
text-align: right;
}
/* Posicionamento à Direita (Atualizações / Prazos) */ .timeline-item.right-side {
justify-content: flex-end;
} .timeline-item.right-side .timeline-content {
text-align: left;
}
/* Textos internos da Timeline */ .timeline-year {
display: block; font-weight: bold; font-size: 0.8rem; color: #333;
} .timeline-type {
display: block; font-size: 0.5rem; font-weight: 600; color: #777; margin-bottom: 3px; text-transform: uppercase;
} .timeline-title {
font-size: 0.72rem; color: #555; margin: 0; line-height: 1.3;
}
/* Destaque para a Portaria Vigente */ .timeline-item.is-vigente .timeline-content {
border-color: #4caf50; background: #f1f8e9;
} .timeline-item.is-vigente .timeline-badge {
box-shadow: 0 0 0 1px rgba(76, 175, 80, 0.3);
} .badge-vigente {
display: inline-block; font-size: 0.45rem; background: #4caf50; color: #fff; padding: 1px; border-radius: 3px; margin-top: 1px; font-weight: bold;
} /* Estilos para a Base Centralizada (Ocupa ambos os lados) */ .timeline-item.center-base {
justify-content: center; width: 100%;
}
.timeline-item.center-base .timeline-content {
width: 70%; /* Fica mais largo, distribuindo o peso nos dois lados da linha */ text-align: center; border: 2px solid #4caf50; background: #f1f8e9;
}
/* Garante que a bolinha da linha do tempo fique perfeitamente centralizada sobre o bloco da base se necessário */ .timeline-item.center-base .timeline-badge {
top: -5px; /* Desloca ligeiramente para marcar o início absoluto da linha */
} /* Estilos para o Teto da Árvore (Revogação passa dos dois lados) */ .timeline-item.center-top {
justify-content: center; width: 100%; margin-top: 10px;
}
.timeline-item.center-top .timeline-content {
width: 75%; /* Garante estabilidade cruzando o eixo central */ text-align: center; border: 2px dashed #d32f2f; /* Borda tracejada vermelha indicando interrupção */ background: #ffebee; /* Fundo avermelhado suave */
}
/* Badge estilizada de Revogada */ .badge-revogada {
display: inline-block; font-size: 0.65rem; background: #d32f2f; color: #fff; padding: 1px 6px; border-radius: 3px; margin-top: 4px; font-weight: bold; text-transform: uppercase;
}
/* Ajuste na bolinha da linha do tempo para a revogação */ .timeline-item.is-revogada .timeline-badge {
box-shadow: 0 0 0 4px rgba(211, 47, 47, 0.3); width: 12px; height: 12px;
} /* Estilos para o Teto da Árvore (Revogação passa dos dois lados) */ .timeline-item.center-top {
justify-content: center; width: 100%; margin-top: 10px;
}
.timeline-item.center-top .timeline-content {
width: 75%; /* Garante estabilidade cruzando o eixo central */ text-align: center; border: 2px dashed #d32f2f; /* Borda tracejada vermelha indicando interrupção */ background: #ffebee; /* Fundo avermelhado suave */
}
/* Badge estilizada de Revogada */ .badge-revogada {
display: inline-block; font-size: 0.65rem; background: #d32f2f; color: #fff; padding: 1px 6px; border-radius: 3px; margin-top: 4px; font-weight: bold; text-transform: uppercase;
}
/* Ajuste na bolinha da linha do tempo para a revogação */ .timeline-item.is-revogada .timeline-badge {
box-shadow: 0 0 0 4px rgba(211, 47, 47, 0.3); width: 12px; height: 12px;
} /* Remove a bolinha central das portarias que ocupam os dois lados (Raiz e Teto) */ .timeline-item.center-base .timeline-badge, .timeline-item.center-top .timeline-badge {
display: none;
} </style>
<script>
document.addEventListener('DOMContentLoaded', function () {
var elems = document.querySelectorAll('.collapsible');
// CORREÇÃO DEFINITIVA: Sincronização inteligente do painel lateral no modo Accordion
M.Collapsible.init(elems, {
accordion: true,
onOpenStart: function(el) {
const sidePanel = document.getElementById('side-panel-nr-detalhes');
const ordem = el.getAttribute('data-ordem');
const portariasRaw = el.getAttribute('data-portarias');
let portarias = [];
try {
portarias = JSON.parse(portariasRaw) || [];
} catch(e) {
console.error("Erro ao processar portarias", e);
}
// 1. Atualiza o número da NR no título lateral
document.getElementById('side-nr-numero').textContent = 'NR-' + ordem;
// 2. Reconstrói a Árvore da Linha do Tempo
const containerPortarias = document.getElementById('side-nr-portarias');
containerPortarias.innerHTML = ;
if (portarias.length === 0) {
containerPortarias.innerHTML = '
Nenhuma portaria registrada para esta norma.
';
} else {
let timelineHtml = '
portarias.forEach(p => {
let ano = '---';
if (p.portaria_normas_dou) {
ano = p.portaria_normas_dou.split('-')[0];
}
let tipoTraduzido = 'Atualização';
let classePosicao = 'right-side';
let badgeColor = '#2196f3';
let ehRevogacao = false;
if (p.portaria_normas_tipo === 'Inicial') {
tipoTraduzido = 'Publicação Original';
classePosicao = 'center-base';
badgeColor = '#4caf50';
} else if (p.portaria_normas_tipo === 'Revoga') {
tipoTraduzido = 'Norma Revogada';
classePosicao = 'center-top';
badgeColor = '#d32f2f';
ehRevogacao = true;
} else if (p.portaria_normas_tipo === 'Nova' || p.portaria_normas_tipo === 'Criar') {
tipoTraduzido = 'Nova Redação';
classePosicao = 'left-side';
badgeColor = '#9c27b0';
} else if (p.portaria_normas_tipo === 'Prazo') {
tipoTraduzido = 'Novo Prazo';
badgeColor = '#ff9800';
}
let classeVigente = ;
if (p.portaria_normas_ativo == 1) {
classeVigente = ehRevogacao ? 'is-revogada' : 'is-vigente';
}
timelineHtml += `
<a href="https://criacoesbutzke.com.br/nr/?tp=port&id=${parseInt(ordem)}" target="_blank" rel="noopener" class="timeline-content"> ${ano} ${tipoTraduzido} ${p.portaria_normas_ativo == 1 && !ehRevogacao ? 'Vigente' : } ${ehRevogacao ? 'Revogada' : } </a>
`;
});
timelineHtml += '';
containerPortarias.innerHTML = timelineHtml;
}
// 3. Força o painel a aparecer imediatamente com os novos dados da NR clicada
if (sidePanel) {
sidePanel.style.display = 'block';
sidePanel.style.opacity = '1';
}
},
onCloseStart: function(el) {
const sidePanel = document.getElementById('side-panel-nr-detalhes');
// O SEGREDO AQUI: Damos um tempo mínimo (10 milissegundos) para ver se outra NR assumiu a tela.
// Se o usuário clicou em outra NR, o onOpenStart dela já mudou o título para a nova ordem.
// Se o título ainda corresponder à NR que está fechando, significa que ele fechou e NÃO abriu outra.
setTimeout(() => {
if (sidePanel) {
const ordemFechando = 'NR-' + el.getAttribute('data-ordem');
const ordemAtualNoPainel = document.getElementById('side-nr-numero').textContent;
// Só fecha o painel lateral de verdade se a tela estiver inteiramente limpa (sem nenhuma outra NR aberta)
if (ordemFechando === ordemAtualNoPainel) {
sidePanel.style.opacity = '0';
setTimeout(() => {
// Confere se continua zero para não quebrar cliques rápidos intermediários
if(sidePanel.style.opacity === '0') {
sidePanel.style.display = 'none';
}
}, 150);
}
}
}, 10);
}
});
// Código de busca sequencial abaixo
const searchInput = document.getElementById('search-nr');
const collapsibleItems = document.querySelectorAll('.collapsible li');
const genericTerms = ['atividade', 'atividades', 'trabalho', 'segurança', 'saúde', 'desenvolvidas', 'devidamente'];
searchInput.addEventListener('input', function () {
const query = this.value.toLowerCase().trim();
collapsibleItems.forEach(item => {
const title = item.querySelector('.collapsible-header').textContent.toLowerCase();
const objetivo = item.getAttribute('data-objetivo').toLowerCase();
if (query.length === 0) {
item.style.display = ;
return;
}
if (genericTerms.includes(query) && query.length < 5) {
item.style.display = 'none';
return;
}
const matches = title.includes(query) || objetivo.includes(query);
item.style.display = matches ? : 'none';
});
});
document.getElementById('form-busca-global').addEventListener('submit', function (e) {
const query = searchInput.value.toLowerCase().trim();
if (query.length > 0 && !query.startsWith('nr')) {
// Permite busca nativa externa
} else {
e.preventDefault();
}
});
});
</script>