Ir para o conteúdo

Página principal: mudanças entre as edições

De Normas Regulamentadoras
Sem resumo de edição
 
Sem resumo de edição
Linha 1: Linha 1:
<strong>O MediaWiki foi instalado.</strong>
<?php
// template/page-nr/page-inicialphp
global $selector_nrs, $amokadb;
$atualizado = "14/02/2023 15h10";


Consulte o [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Manual de Usuário] para informações de como usar o software wiki.
$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")
    );
}


== Começando ==
$selector_nrs = $amokadb->select($select, $options, false);
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Lista de opções de configuração]
 
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ FAQ do MediaWiki]
// =====================================================================
* [https://lists.wikimedia.org/postorius/lists/mediawiki-announce.lists.wikimedia.org/ Lista de discussão com avisos de novas versões do MediaWiki]
// CORREÇÃO: Nova query adaptada para a arquitetura fatiada (INNER JOIN)
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Traduza o MediaWiki para seu idioma]
// =====================================================================
$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>
<?php endforeach; ?>
               <?php if ($empresa_id && nr_user_can_audit()): ?>

Painel de sincronização ativo.

               <?php else: ?>
                   <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; ?>
  • Gerais: Aspectos comuns sem prender-se a setores.
  • Especiais: Execução de trabalho, atividades e EPIs.
  • Setoriais: Relativas a setores econômicos específicos.

<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>