Path: C:\
<!DOCTYPE html>
<html lang="pt-br" data-bs-theme="dark">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Trans War - Central de Crachás</title>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/@phosphor-icons/web@2.0.3/src/regular/style.css" rel="stylesheet">
<link rel="icon" type="image/png" href="img/favicon.ico">
<style>
:root {
--bg-color: #0b0f19;
--glass-bg: rgba(18, 25, 38, 0.65);
--glass-border: rgba(255, 255, 255, 0.08);
--primary: #3b82f6;
--primary-hover: #2563eb;
--text-main: #f8fafc;
--text-muted: #94a3b8;
}
body {
font-family: 'Outfit', sans-serif;
background: var(--bg-color);
background-image:
radial-gradient(circle at 15% 50%, rgba(59, 130, 246, 0.15), transparent 25%),
radial-gradient(circle at 85% 30%, rgba(245, 158, 11, 0.08), transparent 25%);
background-attachment: fixed;
color: var(--text-main);
min-height: 100vh;
}
.glass-card {
background: var(--glass-bg);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid var(--glass-border);
border-radius: 16px;
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.2);
}
.navbar-brand img {
max-height: 50px;
}
/* Nav Pills */
.nav-pills .nav-link {
color: var(--text-muted);
border-radius: 8px;
padding: 12px 24px;
font-weight: 500;
transition: all 0.3s ease;
margin-right: 10px;
}
.nav-pills .nav-link:hover {
background: rgba(255, 255, 255, 0.05);
}
.nav-pills .nav-link.active {
background: var(--primary);
color: #fff;
box-shadow: 0 4px 15px rgba(59, 130, 246, 0.4);
}
.btn-primary {
background: var(--primary);
border: none;
box-shadow: 0 4px 15px rgba(59, 130, 246, 0.3);
transition: all 0.3s;
}
.btn-primary:hover {
background: var(--primary-hover);
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(59, 130, 246, 0.5);
}
.form-control,
.form-select {
background-color: rgba(0, 0, 0, 0.2) !important;
border: 1px solid var(--glass-border) !important;
color: var(--text-main) !important;
}
.form-control:focus,
.form-select:focus {
border-color: var(--primary) !important;
box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.15) !important;
}
/* Corrigir styling das opções nos selects */
.form-select option {
background-color: #1e293b;
color: var(--text-main);
}
/* Select2 Dark/Premium Integration */
.select2-container--default .select2-selection--single {
background-color: rgba(0, 0, 0, 0.2);
border: 1px solid var(--glass-border);
border-radius: 8px;
height: 52px;
display: flex;
align-items: center;
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
color: var(--text-main);
padding-left: 15px;
font-size: 1.05rem;
}
.select2-container--default .select2-selection--single .select2-selection__arrow {
height: 50px;
right: 15px;
}
.select2-dropdown {
background-color: #1e293b;
border: 1px solid var(--glass-border);
border-radius: 8px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.4);
}
.select2-container--default .select2-search--dropdown .select2-search__field {
background-color: rgba(0, 0, 0, 0.3);
border: 1px solid var(--glass-border);
color: #fff;
border-radius: 6px;
padding: 8px 12px;
}
.select2-container--default .select2-results__option--highlighted.select2-results__option--selectable {
background-color: var(--primary);
border-radius: 6px;
margin: 0 5px;
}
.select2-results__option {
color: #fff;
padding: 10px 15px;
}
/* Table Aesthetics */
.table {
color: var(--text-main);
--bs-table-bg: transparent;
}
.table-hover tbody tr {
transition: all 0.2s;
}
.table-hover tbody tr:hover {
background-color: rgba(255, 255, 255, 0.03);
color: #fff;
transform: scale(1.005);
box-shadow: inset 2px 0 0 var(--primary);
}
.table td,
.table th {
border-bottom-color: var(--glass-border);
vertical-align: middle;
padding: 15px;
}
#aqui {
display: flex;
justify-content: center;
align-items: center;
}
.empty-state {
text-align: center;
padding: 40px;
color: var(--text-muted);
}
</style>
</head>
<body>
<div class="container py-5">
<!-- HEADER NAVBAR -->
<div class="d-flex flex-column flex-md-row justify-content-between align-items-center mb-5 pb-4 border-bottom"
style="border-color: var(--glass-border) !important;">
<div class="navbar-brand mb-3 mb-md-0">
<img src="img/logo.png" alt="Trans War Logo">
</div>
<h3 class="fw-bold mb-0"
style="background: linear-gradient(90deg, #fff, #94a3b8); -webkit-background-clip: text; -webkit-text-fill-color: transparent;">
Central de Gestão e Crachás Premium
</h3>
</div>
<!-- TABS NAVIGATION -->
<ul class="nav nav-pills mb-4 gap-2" id="pills-tab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active d-flex align-items-center" id="pills-gerar-tab" data-bs-toggle="pill"
data-bs-target="#pills-gerar" type="button" role="tab">
<i class="ph ph-qr-code fs-4 me-2"></i>Gerar Crachá (Estação)
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link d-flex align-items-center" id="pills-gerenciar-tab" data-bs-toggle="pill"
data-bs-target="#pills-gerenciar" type="button" role="tab">
<i class="ph ph-users fs-4 me-2"></i>Gestão de Colaboradores
</button>
</li>
</ul>
<!-- TABS CONTENT -->
<div class="tab-content" id="pills-tabContent">
<!-- GERAR CRACHÁ --->
<div class="tab-pane fade show active" id="pills-gerar" role="tabpanel">
<div class="row align-items-stretch">
<!-- COLUNA 1: DADOS -->
<div class="col-lg-5 mb-4 mb-lg-0">
<div class="glass-card p-4 h-100 position-relative">
<div class="position-absolute top-0 start-0 translate-middle"
style="width: 200px; height: 200px; background: rgba(59, 130, 246, 0.15); filter: blur(80px); border-radius: 50%;">
</div>
<h4 class="fw-bold mb-4 position-relative"><i
class="ph ph-identification-card fs-3 text-primary me-2 align-middle"></i>Dados do
Crachá</h4>
<div class="mb-4 position-relative">
<label class="form-label text-muted fw-medium mb-3">Pesquisar Banco Local</label>
<select id="select-funcionario" class="form-control">
<option value="">Pesquise por nome...</option>
</select>
</div>
<div class="d-flex align-items-center mb-4 position-relative">
<hr class="flex-grow-1" style="border-color: var(--glass-border)">
<span class="px-3 text-muted" style="font-size:0.75rem;">CONTEÚDO DO CARTÃO</span>
<hr class="flex-grow-1" style="border-color: var(--glass-border)">
</div>
<div class="mb-3 position-relative">
<label class="form-label text-muted fw-medium mb-1 small">Nome Formatado (QR Code) <span
class="text-primary">*</span></label>
<input type="text" id="g-nome"
class="form-control fw-bold fs-5 px-3 py-2 bg-dark bg-opacity-25 border-primary"
style="height: 48px;" placeholder="Ex: SILVIO SANTOS">
<input type="hidden" id="g-valor-oculto">
<input type="hidden" id="g-id-oculto">
</div>
<div class="row g-3 mb-3 position-relative">
<div class="col-sm-6">
<label class="form-label text-muted fw-medium mb-1 small">Tipo / Vínculo</label>
<select id="g-vinculo" class="form-select bg-dark bg-opacity-25 border-secondary"
style="font-size:0.9em;">
<option value="">Não Especificado</option>
<option value="Colaborador (Trans War)">Trans War ("Da Casa")</option>
<option value="Motorista Terceiro">Motorista Terceiro</option>
<option value="Agregado">Agregado</option>
</select>
</div>
<div class="col-sm-6">
<label class="form-label text-muted fw-medium mb-1 small">Função</label>
<input type="text" id="g-funcao"
class="form-control bg-dark bg-opacity-25 border-secondary"
placeholder="Ex: Motorista" style="font-size:0.9em;">
</div>
</div>
<div class="row g-3 mb-4 position-relative">
<div class="col-sm-4">
<label class="form-label text-muted fw-medium mb-1 small">RG</label>
<input type="text" id="g-rg"
class="form-control bg-dark bg-opacity-25 border-secondary"
placeholder="...00-0" style="font-size:0.85em;">
</div>
<div class="col-sm-4">
<label class="form-label text-muted fw-medium mb-1 small">CPF</label>
<input type="text" id="g-cpf"
class="form-control bg-dark bg-opacity-25 border-secondary"
placeholder="...000-00" style="font-size:0.85em;">
</div>
<div class="col-sm-4">
<label class="form-label text-muted fw-medium mb-1 small">PIS</label>
<input type="text" id="g-pis"
class="form-control bg-dark bg-opacity-25 border-secondary"
placeholder="0000000" style="font-size:0.85em;">
</div>
</div>
<button id="btn-gerar" class="btn btn-primary w-100 py-3 fw-bold mt-auto position-relative"
style="font-size: 1.1rem; letter-spacing: 1px;">
<i class="ph ph-magic-wand fs-4 me-2 align-middle"></i>GERAR EM TEMPO REAL
</button>
</div>
</div>
<!-- COLUNA 2: PREVIEW -->
<div class="col-lg-7">
<div class="glass-card p-4 h-100 d-flex flex-column" style="background: rgba(18, 25, 38, 0.4);">
<h5 class="text-muted mb-4 opacity-50 fw-normal border-bottom border-secondary pb-3"><i
class="ph ph-cube me-2"></i>Virtualizador de Arte Final</h5>
<div id="aqui" class="flex-grow-1 w-100 position-relative">
<div class="text-center opacity-25" style="margin-top:20%;">
<i class="ph ph-identification-badge" style="font-size: 6rem;"></i>
<p class="mt-2 text-uppercase tracking-widest small">Pronto para Renderização</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- GERENCIAR COLABORADORES --->
<div class="tab-pane fade" id="pills-gerenciar" role="tabpanel">
<div class="glass-card p-4 p-md-5 mt-3">
<div class="d-flex flex-column flex-md-row justify-content-between align-items-center mb-4 pb-3 border-bottom"
style="border-color: var(--glass-border) !important;">
<div class="d-flex align-items-center mb-3 mb-md-0">
<div class="bg-primary bg-opacity-10 p-3 rounded-circle me-3">
<i class="ph ph-users-three fs-3 text-primary"></i>
</div>
<div>
<h4 class="fw-bold mb-1">Base de Dados Integrada</h4>
<p class="text-muted small mb-0">Gestão centralizada do corpo de funcionários</p>
</div>
</div>
<div class="d-flex flex-column flex-md-row align-items-center gap-3 w-100"
style="max-width: 450px;">
<span class="text-muted small fw-medium text-nowrap"><i
class="ph ph-funnel me-1"></i>Filtro:</span>
<select id="filtro-tabela-vinculo"
class="form-select bg-dark bg-opacity-50 border-secondary w-50 w-md-auto">
<option value="">Todos os Registros</option>
<option value="Colaborador (Trans War)">Apenas Trans War ("Da Casa")</option>
<option value="Motorista Terceiro">Apenas Terceiros</option>
<option value="Agregado">Apenas Agregados</option>
</select>
<button
class="btn btn-primary px-4 py-2 d-flex align-items-center fw-medium flex-shrink-0 w-md-auto"
onclick="abrirModalAdd()">
<i class="ph ph-plus-circle fs-4 me-2"></i>Inclusão
</button>
</div>
</div>
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead style="background: rgba(0,0,0,0.15);">
<tr>
<th class="text-muted fw-medium py-3 rounded-start px-4"># ID</th>
<th class="text-muted fw-medium py-3">COLABORADOR / PROPRIEDADES</th>
<th class="text-muted fw-medium py-3">INFORMAÇÃO EMBARCADA</th>
<th class="text-muted fw-medium py-3 rounded-end text-end px-4"
style="width: 200px;">AÇÕES MASTER</th>
</tr>
</thead>
<tbody id="tabela-funcionarios">
<tr>
<td colspan="4" class="empty-state">Gerando Listagem...</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- MODAL CADASTRO / EDIÇÃO -->
<div class="modal fade" id="funcModal" tabindex="-1" aria-hidden="true" data-bs-backdrop="static">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content glass-card" style="background: #1e293b; border: 1px solid rgba(255,255,255,0.1);">
<div class="modal-header border-bottom border-secondary border-opacity-25 pb-3">
<h5 class="modal-title fw-bold" id="funcModalTitle"><i
class="ph ph-user-circle-plus me-2 text-primary fs-4 align-middle"></i>Incluir Colaborador
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"
aria-label="Close"></button>
</div>
<div class="modal-body p-4">
<input type="hidden" id="f-id">
<div class="row">
<div class="col-md-5 mb-4">
<label class="form-label fw-medium mb-2 opacity-75">Tipo / Vínculo</label>
<select id="f-vinculo" class="form-select p-3 bg-dark bg-opacity-50 border-secondary">
<option value="">Não Classificado</option>
<option value="Colaborador (Trans War)">Trans War ("Da Casa")</option>
<option value="Motorista Terceiro">Motorista Terceiro</option>
<option value="Agregado">Agregado</option>
</select>
</div>
<div class="col-md-7 mb-4">
<label class="form-label fw-medium mb-2 opacity-75">Nome Curto / Display</label>
<input type="text" id="f-nome"
class="form-control p-3 bg-dark bg-opacity-50 border-secondary"
placeholder="Ex: JOÃO SILVA">
</div>
</div>
<div class="row">
<div class="col-md-12 mb-4 mt-n2">
<label class="form-label fw-medium mb-2 opacity-75">Função Ocupacional</label>
<input type="text" id="f-funcao"
class="form-control p-3 bg-dark bg-opacity-50 border-secondary"
placeholder="Ex: Motorista M1">
</div>
</div>
<div class="row">
<div class="col-md-4 mb-4">
<label class="form-label fw-medium mb-2 opacity-75">RG</label>
<input type="text" id="f-rg" class="form-control p-3 bg-dark bg-opacity-50 border-secondary"
placeholder="00.000.000-0">
</div>
<div class="col-md-4 mb-4">
<label class="form-label fw-medium mb-2 opacity-75">CPF</label>
<input type="text" id="f-cpf"
class="form-control p-3 bg-dark bg-opacity-50 border-secondary"
placeholder="000.000.000-00">
</div>
<div class="col-md-4 mb-4">
<label class="form-label fw-medium mb-2 opacity-75">PIS</label>
<input type="text" id="f-pis"
class="form-control p-3 bg-dark bg-opacity-50 border-secondary" placeholder="00000000">
</div>
</div>
<!-- Bloco manual removido: Geração da matriz do QR Code (Tripa com separadores) ocorre de modo hidden/automático on-submit -->
<input type="hidden" id="f-valor">
<button id="btn-save-func" class="btn btn-primary w-100 py-3 fw-bold fs-5 shadow-lg mt-2">
Sincronizar Arquivos no Banco Local
</button>
</div>
</div>
</div>
</div>
<!-- SCRIPTS E LIBS -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script>
let funcionariosData = [];
let modalInstancia = null;
let filtroVinculoAtual = "";
// Monta a string legada do leitor a partir dos campos atuais.
// Usada tanto pelo modal de cadastro quanto pela aba de geração.
function montarValorCracha(nome, funcao, rg, cpf, pis) {
const partes = [nome];
if (funcao) partes.push(funcao);
if (rg) partes.push(rg);
if (cpf) partes.push(cpf);
if (pis) partes.push(pis);
return nome + " | " + partes.join(" ; ");
}
// Grava (ou atualiza, se vier id) um colaborador. Devolve o registro salvo.
function salvarFuncionario(payload) {
return fetch('api.php?action=save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
}).then(res => {
if (!res.ok) throw new Error('HTTP ' + res.status);
return res.json();
}).then(res => {
if (!res.success) throw new Error(res.error || 'Erro desconhecido');
return res;
});
}
$.fn.select2.defaults.set("theme", "default");
function getModalInstancia() {
if (!modalInstancia) {
modalInstancia = new bootstrap.Modal(document.getElementById('funcModal'));
}
return modalInstancia;
}
// Listener Filter Bar
$('#filtro-tabela-vinculo').on('change', function () {
filtroVinculoAtual = $(this).val();
renderTabela();
});
// preservarFormulario = true quando a recarga vem da própria aba de geração:
// nesse caso não disparamos o change do select2, que zeraria os campos.
function carregarDados(preservarFormulario) {
return fetch('api.php?action=list')
.then(res => res.json())
.then(data => {
funcionariosData = data;
// Popula sempre o Select2 completo indepentente do filtro de Tabela
$('#select-funcionario').empty().append('<option value="">Selecione para auto-completação...</option>');
data.forEach(f => {
let badgeText = (f.vinculo && f.vinculo !== '') ? `[${f.vinculo.split(' ')[0]}] ` : '';
const option = new Option(badgeText + f.nome + (f.funcao ? " - " + f.funcao : ""), f.valor_cracha, false, false);
$(option).attr('data-id', f.id);
$('#select-funcionario').append(option);
});
if (!preservarFormulario) {
$('#select-funcionario').trigger('change');
}
renderTabela();
});
}
function renderTabela() {
const tbody = document.getElementById('tabela-funcionarios');
tbody.innerHTML = '';
let dadosParaTabela = funcionariosData;
if (filtroVinculoAtual !== "") {
dadosParaTabela = funcionariosData.filter(x => x.vinculo === filtroVinculoAtual);
}
if (dadosParaTabela.length === 0) {
tbody.innerHTML = '<tr><td colspan="4" class="empty-state"><i class="ph ph-folder-open mb-3 fs-1 text-primary"></i><br>Nenhum colaborador preenchendo este requisito matriculado.</td></tr>';
return;
}
dadosParaTabela.forEach(f => {
let smallTags = '';
if (f.funcao) smallTags += `<span class="badge bg-primary bg-opacity-25 border border-primary border-opacity-50 text-primary me-1 fw-normal">${f.funcao}</span>`;
if (f.cpf) smallTags += `<span class="badge bg-secondary bg-opacity-25 border border-secondary border-opacity-50 text-secondary me-1 fw-normal">CPF ${f.cpf}</span>`;
if (f.rg) smallTags += `<span class="badge bg-secondary bg-opacity-25 border border-secondary border-opacity-50 text-secondary fw-normal">RG ${f.rg}</span>`;
let vinculoBadge = '';
if (f.vinculo && f.vinculo !== '') {
let colorClass = 'bg-secondary border-secondary text-secondary';
if (f.vinculo.includes('Trans War')) colorClass = 'bg-success border-success text-success';
if (f.vinculo.includes('Terceiro')) colorClass = 'bg-warning border-warning text-warning';
if (f.vinculo.includes('Agregado')) colorClass = 'bg-info border-info text-info';
vinculoBadge = `<span class="badge ${colorClass} bg-opacity-25 border mb-2 px-2 py-1 fw-medium" style="font-size:0.65em; letter-spacing:1px; text-transform:uppercase;"><i class="ph ph-hard-drives me-1"></i>${f.vinculo}</span><br>`;
}
tbody.innerHTML += `
<tr>
<td class="text-secondary px-4">#${f.id}</td>
<td>
${vinculoBadge}
<div class="fw-bold text-white fs-6 mb-1">${f.nome}</div>
<div>${smallTags}</div>
</td>
<td class="font-monospace opacity-75" style="font-size:0.85em; color:#a7f3d0;">${f.valor_cracha}</td>
<td class="px-4">
<div class="d-flex gap-2 justify-content-end">
<button class="btn btn-sm btn-dark border border-secondary border-opacity-25" onclick="editarFunc(${f.id})" title="Editar"><i class="ph ph-pencil-simple text-warning"></i> Editar</button>
<button class="btn btn-sm btn-dark border border-secondary border-opacity-25" onclick="excluirFunc(${f.id})" title="Remover"><i class="ph ph-trash text-danger"></i> Del</button>
</div>
</td>
</tr>
`;
});
}
// Init Libs
$(document).ready(function () {
// Formatador dinâmico global: Trim automático em todos inputs de texto do painel
$('input[type="text"]').on('blur', function() {
$(this).val($(this).val().trim());
});
$('#select-funcionario').select2({
placeholder: "Pesquise usando nome ou cargo...",
allowClear: true,
width: '100%'
});
carregarDados();
// Auto-populamento interativo inteligente
$('#select-funcionario').on('change', function () {
const id = $(this).find(':selected').attr('data-id');
if (id) {
const func = funcionariosData.find(x => x.id == id);
if (func) {
let parsedFuncao = func.funcao || '';
let parsedRg = func.rg || '';
let parsedCpf = func.cpf || '';
let parsedPis = func.pis || '';
// Extrair dados do valor_cracha legado caso os campos estejam vazios
if (func.valor_cracha && !parsedFuncao && !parsedRg && !parsedCpf && !parsedPis) {
let separator = func.valor_cracha.includes(';') ? ';' : (func.valor_cracha.includes(' | ') ? ' | ' : null);
if (separator) {
let parts = func.valor_cracha.split(separator);
// O split no legacy é quase sempre [Nome, Função, RG, CPF, PIS]
if (parts.length > 1 && parts[1].trim() !== '') parsedFuncao = parts[1].trim();
if (parts.length > 2 && parts[2].trim() !== '') parsedRg = parts[2].trim();
if (parts.length > 3 && parts[3].trim() !== '') parsedCpf = parts[3].trim();
if (parts.length > 4 && parts[4].trim() !== '') parsedPis = parts[4].trim();
}
}
$('#g-nome').val(func.nome);
$('#g-valor-oculto').val(func.valor_cracha);
$('#g-id-oculto').val(func.id);
$('#g-vinculo').val(func.vinculo || '');
$('#g-funcao').val(parsedFuncao);
$('#g-rg').val(parsedRg);
$('#g-cpf').val(parsedCpf);
$('#g-pis').val(parsedPis);
}
} else {
$('#g-nome').val('');
$('#g-valor-oculto').val('');
$('#g-id-oculto').val('');
$('#g-vinculo').val('');
$('#g-funcao').val('');
$('#g-rg').val('');
$('#g-cpf').val('');
$('#g-pis').val('');
}
});
});
// FUNÇÕES CRUD
function abrirModalAdd() {
document.getElementById('funcModalTitle').innerHTML = '<i class="ph ph-user-circle-plus me-2 text-primary fs-4 align-middle"></i>Inclusão de Funcionário';
document.getElementById('f-id').value = '';
document.getElementById('f-vinculo').value = '';
document.getElementById('f-nome').value = '';
document.getElementById('f-funcao').value = '';
document.getElementById('f-rg').value = '';
document.getElementById('f-cpf').value = '';
document.getElementById('f-pis').value = '';
document.getElementById('f-valor').value = '';
getModalInstancia().show();
}
function editarFunc(id) {
const f = funcionariosData.find(x => x.id === id);
if (f) {
document.getElementById('funcModalTitle').innerHTML = '<i class="ph ph-pencil-line me-2 text-warning fs-4 align-middle"></i>Alteração de Registro';
document.getElementById('f-id').value = f.id;
document.getElementById('f-vinculo').value = f.vinculo || '';
document.getElementById('f-nome').value = f.nome || '';
document.getElementById('f-valor').value = f.valor_cracha || '';
let parsedFuncao = f.funcao || '';
let parsedRg = f.rg || '';
let parsedCpf = f.cpf || '';
let parsedPis = f.pis || '';
// Extrai dados da string caso os campos isolados não existam na base (legado)
if (f.valor_cracha && !parsedFuncao && !parsedRg && !parsedCpf && !parsedPis) {
let separator = f.valor_cracha.includes(';') ? ';' : (f.valor_cracha.includes(' | ') ? ' | ' : null);
if (separator) {
let parts = f.valor_cracha.split(separator);
if (parts.length > 1 && parts[1].trim() !== '') parsedFuncao = parts[1].trim();
if (parts.length > 2 && parts[2].trim() !== '') parsedRg = parts[2].trim();
if (parts.length > 3 && parts[3].trim() !== '') parsedCpf = parts[3].trim();
if (parts.length > 4 && parts[4].trim() !== '') parsedPis = parts[4].trim();
}
}
document.getElementById('f-funcao').value = parsedFuncao;
document.getElementById('f-rg').value = parsedRg;
document.getElementById('f-cpf').value = parsedCpf;
document.getElementById('f-pis').value = parsedPis;
getModalInstancia().show();
}
}
document.getElementById('btn-save-func').addEventListener('click', function (e) {
let btnOrig = this.innerHTML;
this.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Processando...';
const id = document.getElementById('f-id').value;
const nome = document.getElementById('f-nome').value.trim();
const f_vinculo = document.getElementById('f-vinculo').value.trim();
const f_funcao = document.getElementById('f-funcao').value.trim();
const f_rg = document.getElementById('f-rg').value.trim();
const f_cpf = document.getElementById('f-cpf').value.trim();
const f_pis = document.getElementById('f-pis').value.trim();
if (!nome) {
alert("Preencha ao menos o Nome Curto/Display para gerar o crachá.");
this.innerHTML = btnOrig;
return;
}
// Geração da matriz do leitor silenciosa (montagem de legacy string com ;)
const valorAutoGerado = montarValorCracha(nome, f_funcao, f_rg, f_cpf, f_pis);
const payload = {
nome: nome,
valor_cracha: valorAutoGerado,
vinculo: f_vinculo,
funcao: f_funcao,
rg: f_rg,
cpf: f_cpf,
pis: f_pis
};
if (id) payload.id = id;
salvarFuncionario(payload).then(() => {
this.innerHTML = btnOrig;
getModalInstancia().hide();
carregarDados();
}).catch(err => {
this.innerHTML = btnOrig;
alert("Operação Abortada: " + err.message);
});
});
function excluirFunc(id) {
if (confirm("Confirmar exclusão definitiva?")) {
fetch('api.php?action=delete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id: id })
}).then(res => res.json()).then(res => {
if (res.success) carregarDados();
});
}
}
// MOTOR DO CRACHA - Geração Via JS -> AJAX PHP Render
$('#btn-gerar').click(function (e) {
e.preventDefault();
const pNome = $('#g-nome').val().trim();
const pId = $('#g-id-oculto').val().trim();
const pVinculo = $('#g-vinculo').val().trim();
const pFuncao = $('#g-funcao').val().trim();
const pRg = $('#g-rg').val().trim();
const pCpf = $('#g-cpf').val().trim();
const pPis = $('#g-pis').val().trim();
if (!pNome) {
alert("O Nome Master do Crachá é obrigatório.");
return;
}
// Sempre remonta a string a partir do que está na tela, senão uma
// correção feita aqui (RG, CPF...) ficaria de fora do registro.
const urlPayload = montarValorCracha(pNome, pFuncao, pRg, pCpf, pPis);
$('#aqui').html(`
<div class="text-center py-5">
<div class="spinner-border text-primary" style="width: 3rem; height: 3rem;" role="status"></div>
<p class="mt-4 fw-bold text-primary tracking-widest">Injetando Matriz Gráfica...</p>
</div>
`);
function renderizarCracha() {
$.ajax({
type: 'POST',
url: "geraQRcode.php",
data: {
texto: urlPayload,
arq: pNome,
vinculo: pVinculo,
funcao: pFuncao,
rg: pRg,
cpf: pCpf,
pis: pPis
},
success: function (data) {
$('#aqui').html(data);
},
error: function () {
$('#aqui').html('<div class="alert alert-danger shadow"><i class="ph ph-warning-circle fs-4 me-2 align-middle"></i>Falha no renderizador BaconQrCode.</div>');
}
});
}
// Gerar também grava: registro selecionado é atualizado, nome novo
// vira um cadastro novo. Antes essa aba não persistia nada.
const payload = {
nome: pNome,
valor_cracha: urlPayload,
vinculo: pVinculo,
funcao: pFuncao,
rg: pRg,
cpf: pCpf,
pis: pPis
};
let idAlvo = pId;
if (!idAlvo) {
// Evita duplicar quem já existe com o mesmo nome
const existente = funcionariosData.find(x => (x.nome || '').trim().toUpperCase() === pNome.toUpperCase());
if (existente) idAlvo = existente.id;
}
if (idAlvo) payload.id = idAlvo;
salvarFuncionario(payload).then(res => {
if (res.id) $('#g-id-oculto').val(res.id);
carregarDados(true);
renderizarCracha();
}).catch(err => {
$('#aqui').html('<div class="alert alert-danger shadow"><i class="ph ph-warning-circle fs-4 me-2 align-middle"></i>Não foi possível gravar no banco: ' + err.message + '</div>');
});
});
</script>
</body>
</html>