sgmp/templates/base.html

243 lines
16 KiB
HTML
Raw Normal View History

2026-03-09 18:46:01 +00:00
{% load static %}
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}SGMP - Gestão de Pessoas{% endblock %}</title>
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>📊</text></svg>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#2563eb',
'primary-hover': '#1d4ed8',
'sidebar-bg': '#0f172a',
'sidebar-border': '#1e293b',
},
fontFamily: { sans: ['Inter', 'sans-serif'] },
}
}
}
</script>
<style>
.sidebar.collapsed { width: 72px; }
.sidebar.collapsed .brand { display: none; }
.sidebar.collapsed .sidebar-header { justify-content: center; padding: 0; }
.sidebar.collapsed .nav-item { justify-content: center; padding: 10px 0; }
.sidebar.collapsed .link-text { opacity: 0; width: 0; display: none; overflow: hidden; }
.sidebar.collapsed .user-info { display: none; }
.sidebar.collapsed .user-section { padding: 16px 0; justify-content: center; }
.wizard-overlay { display: none; }
.wizard-overlay.show { opacity: 1; }
.wizard-overlay.show .wizard-box { transform: scale(1); }
.wizard-step { display: none; }
.wizard-step.active { display: block; }
@media (max-width: 768px) {
.sidebar { position: absolute; height: 100%; width: 260px; transform: translateX(-100%); }
.sidebar.active { transform: translateX(0); }
.sidebar.collapsed { width: 260px; transform: translateX(-100%); }
.sidebar-overlay { display: none; }
.sidebar.active + .sidebar-overlay { display: block; }
}
</style>
{% block css %}{% endblock %}
</head>
<body class="font-sans bg-slate-100 text-slate-700 m-0 p-0 h-screen overflow-hidden antialiased">
<div class="flex w-full h-screen">
<button type="button" class="mobile-menu-btn block md:hidden fixed top-4 left-4 z-40 bg-white border border-slate-200 rounded-md p-2 shadow md:hidden" onclick="toggleSidebarMobile()">
<span class="text-lg"></span>
</button>
<nav id="sidebar" class="sidebar flex flex-col w-[260px] flex-shrink-0 bg-slate-900 text-white border-r border-slate-800 relative z-50 transition-[width] duration-300 ease-out">
<div class="sidebar-header h-16 flex items-center justify-between px-5 border-b border-slate-800">
<div class="brand text-lg font-bold tracking-tight text-white whitespace-nowrap overflow-hidden">SGMP <span>CORP</span></div>
<button type="button" class="toggle-btn bg-transparent border-none text-slate-400 text-xl flex items-center justify-center p-1 rounded hover:text-white hover:bg-slate-800 transition-colors" onclick="toggleSidebarDesktop()" title="Recolher Menu">
<span>«</span>
</button>
</div>
<div class="nav-links py-5 px-3 flex flex-col gap-1.5 flex-grow overflow-y-auto">
<a href="{% url 'solicitacoes:dashboard' %}" class="nav-item flex items-center py-2.5 px-3 rounded-md text-slate-400 text-sm font-medium no-underline border-none bg-transparent w-full cursor-pointer transition-all hover:text-white hover:bg-slate-800" title="Dashboard">
<span class="link-icon text-base min-w-[24px] flex items-center justify-center">📊</span>
<span class="link-text ml-3 whitespace-nowrap overflow-hidden">Dashboard</span>
</a>
{% if user.is_authenticated and usuario_sistema and usuario_sistema.perfil != 'GESTOR' %}
<a href="{% url 'solicitacoes:todas_solicitacoes' %}" class="nav-item flex items-center py-2.5 px-3 rounded-md text-slate-400 text-sm font-medium no-underline border-none bg-transparent w-full cursor-pointer transition-all hover:text-white hover:bg-slate-800" title="Todas as solicitações">
<span class="link-icon text-base min-w-[24px] flex items-center justify-center">📑</span>
<span class="link-text ml-3 whitespace-nowrap overflow-hidden">Todas as solicitações</span>
</a>
{% endif %}
{% if user.is_authenticated and usuario_sistema and usuario_sistema.perfil == 'GESTOR' %}
<button type="button" onclick="openWizard()" class="nav-item flex items-center py-2.5 px-3 rounded-md text-slate-400 text-sm font-medium border-none bg-transparent w-full cursor-pointer transition-all hover:text-white hover:bg-slate-800 text-left" title="Criar Solicitação">
<span class="link-icon text-base min-w-[24px] flex items-center justify-center"></span>
<span class="link-text ml-3 whitespace-nowrap overflow-hidden">Nova Solicitação</span>
</button>
{% endif %}
</div>
{% if user.is_authenticated %}
<div class="user-section py-4 px-4 bg-slate-950 border-t border-slate-800">
<a href="{% url 'solicitacoes:logout' %}" class="user-profile flex items-center gap-3 text-white no-underline overflow-hidden" title="Sair">
<div class="avatar w-8 h-8 rounded-full bg-primary flex items-center justify-center font-semibold text-sm flex-shrink-0">
{% if usuario_sistema %}{{ usuario_sistema.nome|make_list|first }}{% else %}{{ user.username|make_list|first }}{% endif %}
</div>
<div class="user-info flex flex-col whitespace-nowrap overflow-hidden">
<span class="user-name text-sm font-semibold">{% if usuario_sistema %}{{ usuario_sistema.nome }}{% else %}{{ user.username }}{% endif %}</span>
<span class="user-role text-xs text-slate-400">{% if usuario_sistema %}{{ usuario_sistema.get_perfil_display }}{% else %}Usuário{% endif %}</span>
</div>
<span class="link-icon ml-auto text-red-500" title="Sair">🚪</span>
</a>
</div>
{% endif %}
</nav>
<div class="sidebar-overlay fixed inset-0 bg-black/50 z-[45] backdrop-blur-sm md:hidden" onclick="toggleSidebarMobile()"></div>
<main class="main-content flex-grow overflow-y-auto h-screen bg-slate-100 relative">
<div class="container max-w-[1200px] mx-auto py-8 px-8">
{% block content %}{% endblock %}
</div>
</main>
</div>
<div id="createWizard" class="wizard-overlay fixed inset-0 bg-slate-900/60 z-[1000] flex items-center justify-center backdrop-blur-sm opacity-0 transition-opacity duration-200">
<div class="wizard-box bg-white w-full max-w-2xl rounded-xl shadow-2xl overflow-hidden flex flex-col max-h-[85vh] scale-95 transition-transform duration-200">
<div class="wizard-header py-5 px-6 border-b border-slate-200 flex justify-between items-center bg-white">
<h3 id="wizardTitle" class="m-0 text-lg font-semibold text-slate-800">Iniciar Novo Processo</h3>
<button type="button" class="close-wizard bg-transparent border-none text-xl text-slate-500 w-8 h-8 rounded-md flex items-center justify-center cursor-pointer hover:bg-slate-100 hover:text-slate-800 transition-colors" onclick="closeWizard()">×</button>
</div>
<div class="wizard-body p-6 overflow-y-auto bg-white">
<div id="step1" class="wizard-step active">
<p class="text-slate-600 mb-5">Qual tipo de movimentação você deseja realizar hoje?</p>
<div class="type-grid grid grid-cols-1 sm:grid-cols-2 gap-4">
<div class="type-card bg-white border border-slate-200 p-5 rounded-lg cursor-pointer transition-all text-center flex flex-col items-center gap-3 hover:border-primary hover:bg-blue-50 hover:-translate-y-0.5 hover:shadow" onclick="goToStep2('desligamento', 'Desligamento')">
<span class="type-icon text-3xl">🚫</span>
<span class="type-title font-semibold text-slate-800 text-sm">Desligamento</span>
</div>
<div class="type-card bg-white border border-slate-200 p-5 rounded-lg cursor-pointer transition-all text-center flex flex-col items-center gap-3 hover:border-primary hover:bg-blue-50 hover:-translate-y-0.5 hover:shadow" onclick="goToStep2('movimentacao', 'Movimentação Interna')">
<span class="type-icon text-3xl">🔄</span>
<span class="type-title font-semibold text-slate-800 text-sm">Movimentação</span>
</div>
<div class="type-card bg-white border border-slate-200 p-5 rounded-lg cursor-pointer transition-all text-center flex flex-col items-center gap-3 hover:border-primary hover:bg-blue-50 hover:-translate-y-0.5 hover:shadow" onclick="goToStep2('aumento-quadro', 'Aumento de Quadro')">
<span class="type-icon text-3xl">📈</span>
<span class="type-title font-semibold text-slate-800 text-sm">Aumento de Quadro</span>
</div>
<div class="type-card bg-white border border-slate-200 p-5 rounded-lg cursor-pointer transition-all text-center flex flex-col items-center gap-3 hover:border-primary hover:bg-blue-50 hover:-translate-y-0.5 hover:shadow" onclick="goToStep2('substituicao', 'Substituição')">
<span class="type-icon text-3xl">👥</span>
<span class="type-title font-semibold text-slate-800 text-sm">Substituição</span>
</div>
</div>
</div>
<div id="step2" class="wizard-step">
<div class="back-link text-slate-500 cursor-pointer text-sm mb-5 inline-flex items-center gap-1 font-medium hover:text-primary" onclick="backToStep1()">
<span></span> Voltar para seleção
</div>
<p class="mb-4 text-slate-800">Selecione o colaborador para o processo de <strong id="selectedTypeLabel"></strong>:</p>
<div class="search-container flex flex-col gap-4">
<div class="search-input-group flex gap-3 relative">
<span class="search-icon-fake absolute left-3.5 top-1/2 -translate-y-1/2 text-slate-400 pointer-events-none">🔍</span>
<input type="text" id="wizardSearchInput" class="search-input flex-1 py-3 pl-10 pr-4 border border-slate-200 rounded-lg text-sm font-sans transition-colors focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20" placeholder="Busque por Nome ou Matrícula..." onkeydown="if(event.key==='Enter') doSearch()">
<button type="button" class="btn-search bg-primary text-white border-none py-0 px-6 rounded-lg font-medium cursor-pointer transition-colors hover:bg-primary-hover" onclick="doSearch()">Buscar</button>
</div>
<div id="loading" class="loading-spinner text-center text-slate-500 py-8 hidden text-sm">
Consultando base de dados...
</div>
<div id="searchResults" class="results-list flex flex-col gap-2.5 mt-2 max-h-[400px] overflow-y-auto">
</div>
</div>
</div>
</div>
</div>
</div>
<script>
function toggleSidebarDesktop() {
const sidebar = document.getElementById('sidebar');
const btn = sidebar.querySelector('.toggle-btn span');
sidebar.classList.toggle('collapsed');
btn.innerHTML = sidebar.classList.contains('collapsed') ? '»' : '«';
}
function toggleSidebarMobile() {
document.getElementById('sidebar').classList.toggle('active');
}
let currentTypeSlug = '';
function openWizard() {
const overlay = document.getElementById('createWizard');
overlay.style.display = 'flex';
setTimeout(() => overlay.classList.add('show'), 10);
backToStep1();
}
function closeWizard() {
const overlay = document.getElementById('createWizard');
overlay.classList.remove('show');
setTimeout(() => { overlay.style.display = 'none'; }, 200);
}
document.getElementById('createWizard').addEventListener('click', function(e) { if (e.target === this) closeWizard(); });
document.addEventListener('keydown', function(e) { if (e.key === 'Escape') closeWizard(); });
function goToStep2(slug, label) {
if (slug === 'aumento-quadro') {
window.location.href = "{% url 'solicitacoes:criar_admissao_aumento_quadro' %}";
return;
}
currentTypeSlug = slug;
document.getElementById('selectedTypeLabel').textContent = label;
document.getElementById('step1').classList.remove('active');
document.getElementById('step2').classList.add('active');
document.getElementById('wizardSearchInput').value = '';
document.getElementById('searchResults').innerHTML = '';
setTimeout(() => document.getElementById('wizardSearchInput').focus(), 100);
}
function backToStep1() {
document.getElementById('step2').classList.remove('active');
document.getElementById('step1').classList.add('active');
}
async function doSearch() {
const term = document.getElementById('wizardSearchInput').value.trim();
const resultsDiv = document.getElementById('searchResults');
const loader = document.getElementById('loading');
if (!term) return;
resultsDiv.innerHTML = '';
loader.style.display = 'block';
try {
let url = "{% url 'solicitacoes:listar_colaboradores' %}?q=" + encodeURIComponent(term);
if (currentTypeSlug === 'substituicao') url += "&tipo=substituicao";
const response = await fetch(url);
const doc = new DOMParser().parseFromString(await response.text(), 'text/html');
const rows = doc.querySelectorAll('table tbody tr');
if (rows.length === 0) {
resultsDiv.innerHTML = '<div class="text-center py-5 text-slate-500">Nenhum colaborador encontrado.</div>';
} else {
const linkTextMap = { 'desligamento': 'Desligamento', 'movimentacao': 'Movimentação', 'substituicao': 'Substituição' };
rows.forEach(row => {
const cells = row.querySelectorAll('td');
if (cells.length < 6) return;
const matricula = cells[0].textContent.trim(), nome = cells[1].textContent.trim(), cargo = cells[2].textContent.trim();
const linkText = linkTextMap[currentTypeSlug];
const actionLink = linkText ? Array.from(row.querySelectorAll('a')).find(l => l.textContent.includes(linkText)) : null;
if (!actionLink) return;
const href = actionLink.getAttribute('href');
const card = document.createElement('div');
card.className = 'result-item flex justify-between items-center p-4 bg-white border border-slate-200 rounded-lg transition-all hover:border-primary hover:bg-slate-50 hover:shadow';
card.innerHTML = '<div class="result-info flex flex-col gap-1"><span class="result-name font-semibold text-slate-800 text-sm">' + nome + '</span><span class="result-meta text-xs text-slate-500">Matrícula: ' + matricula + ' | ' + cargo + '</span></div><a href="' + href + '" class="btn-select bg-white text-primary border border-slate-200 py-2 px-4 rounded-md text-sm font-semibold no-underline transition-all hover:bg-primary hover:text-white hover:border-primary">Selecionar</a>';
resultsDiv.appendChild(card);
});
}
} catch (err) {
console.error(err);
resultsDiv.innerHTML = '<div class="text-red-600 text-center">Erro ao buscar. Tente novamente.</div>';
} finally {
loader.style.display = 'none';
}
}
</script>
{% block scripts %}{% endblock %}
</body>
</html>