sgmp/templates/base_original.html

514 lines
25 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% 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>
<!-- Favicon -->
<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>">
<!-- FONTES -->
<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">
<style>
:root {
--primary-color: #2563eb;
--primary-hover: #1d4ed8;
--bg-color: #f1f5f9;
--sidebar-bg: #0f172a;
--text-main: #334155;
--text-light: #64748b;
--border-color: #e2e8f0;
--card-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
--success-color: #10b981;
--danger-color: #ef4444;
--warning-color: #f59e0b;
}
* { box-sizing: border-box; }
body {
font-family: "Inter", sans-serif;
background: var(--bg-color);
margin: 0;
padding: 0;
color: var(--text-main);
height: 100vh;
overflow: hidden;
-webkit-font-smoothing: antialiased;
}
/* --- LAYOUT GERAL --- */
.wrapper { display: flex; width: 100%; height: 100vh; }
/* --- SIDEBAR --- */
.sidebar {
background: var(--sidebar-bg); color: #fff; width: 260px; flex-shrink: 0;
display: flex; flex-direction: column; transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative; z-index: 50; border-right: 1px solid #1e293b;
}
.sidebar.collapsed { width: 72px; }
.sidebar-header {
height: 64px; display: flex; align-items: center; justify-content: space-between;
padding: 0 20px; border-bottom: 1px solid #1e293b;
}
.sidebar.collapsed .sidebar-header { justify-content: center; padding: 0; }
.brand { font-size: 1.1rem; font-weight: 700; letter-spacing: -0.5px; color: #fff; white-space: nowrap; overflow: hidden; }
.sidebar.collapsed .brand { display: none; }
.toggle-btn {
background: transparent; border: none; color: #94a3b8; cursor: pointer; font-size: 1.2rem;
display: flex; align-items: center; justify-content: center; padding: 5px; border-radius: 4px; transition: color 0.2s;
}
.toggle-btn:hover { color: #fff; background: #1e293b; }
.nav-links { padding: 20px 12px; display: flex; flex-direction: column; gap: 6px; flex-grow: 1; overflow-y: auto; }
.nav-item {
color: #94a3b8; font-size: 0.9rem; font-weight: 500; text-decoration: none;
display: flex; align-items: center; padding: 10px 12px; border-radius: 6px;
transition: all 0.2s ease; border: none; background: transparent; width: 100%; cursor: pointer; font-family: inherit;
}
.nav-item:hover { color: #fff; background: #1e293b; }
.nav-item.active { color: #fff; background: var(--primary-color); }
.sidebar.collapsed .nav-item { justify-content: center; padding: 10px 0; }
.link-icon { font-size: 1.1rem; min-width: 24px; display: flex; align-items: center; justify-content: center; }
.link-text { margin-left: 12px; white-space: nowrap; overflow: hidden; opacity: 1; transition: opacity 0.2s; }
.sidebar.collapsed .link-text { opacity: 0; width: 0; display: none; }
.user-section { padding: 16px; background: #0b1120; border-top: 1px solid #1e293b; }
.user-profile { display: flex; align-items: center; gap: 12px; color: #fff; text-decoration: none; overflow: hidden; }
.avatar {
width: 32px; height: 32px; background: var(--primary-color); border-radius: 50%;
display: flex; align-items: center; justify-content: center; font-weight: 600; font-size: 0.8rem; flex-shrink: 0;
}
.user-info { display: flex; flex-direction: column; white-space: nowrap; overflow: hidden; }
.user-name { font-size: 0.85rem; font-weight: 600; }
.user-role { font-size: 0.75rem; color: #94a3b8; }
.sidebar.collapsed .user-info { display: none; }
.sidebar.collapsed .user-section { padding: 16px 0; display: flex; justify-content: center; }
/* --- CONTEÚDO PRINCIPAL --- */
.main-content { flex-grow: 1; overflow-y: auto; height: 100vh; background: var(--bg-color); position: relative; }
.container { max-width: 1200px; margin: 0 auto; padding: 2rem; }
/* --- ESTILOS GERAIS DE FORMULÁRIO (PADRÃO DOMÍNIO) ---
Estes estilos serão usados pelos templates filhos para criar o visual segmentado
*/
.page-header-title { font-size: 1.5rem; font-weight: 700; color: #1e293b; margin-bottom: 1.5rem; display: flex; align-items: center; gap: 10px; }
/* Card de Domínio (Seção) */
.domain-section {
background: white;
border: 1px solid #e2e8f0;
border-radius: 10px;
margin-bottom: 24px;
overflow: hidden;
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
}
.domain-header {
background: #f8fafc;
padding: 16px 24px;
border-bottom: 1px solid #e2e8f0;
display: flex;
align-items: center;
gap: 12px;
}
.domain-header h3 { margin: 0; font-size: 0.95rem; font-weight: 600; color: #475569; text-transform: uppercase; letter-spacing: 0.05em; }
.domain-icon { color: var(--primary-color); font-size: 1.1rem; }
.domain-body { padding: 24px; }
/* Grid para Inputs */
.form-grid { display: grid; grid-template-columns: 1fr; gap: 20px; }
@media (min-width: 768px) { .form-grid { grid-template-columns: repeat(2, 1fr); } }
.full-width { grid-column: 1 / -1; }
/* Inputs Padrão */
.form-control-std {
width: 100%; padding: 10px 12px; border: 1px solid #e2e8f0; border-radius: 6px;
font-size: 0.95rem; font-family: inherit; transition: border-color 0.2s; background: #fff;
}
.form-control-std:focus { outline: none; border-color: var(--primary-color); box-shadow: 0 0 0 3px rgba(37,99,235,0.1); }
.label-std { display: block; margin-bottom: 6px; font-size: 0.9rem; font-weight: 500; color: #334155; }
/* Botões de Ação */
.form-actions { margin-top: 30px; display: flex; gap: 15px; border-top: 1px solid #e2e8f0; padding-top: 20px; }
.btn-std { padding: 12px 24px; border-radius: 8px; font-weight: 500; cursor: pointer; text-decoration: none; display: inline-flex; align-items: center; justify-content: center; gap: 8px; font-size: 0.95rem; border: none; transition: background 0.2s; }
.btn-primary { background: var(--primary-color); color: white; }
.btn-primary:hover { background: var(--primary-hover); }
.btn-secondary { background: white; color: #475569; border: 1px solid #cbd5e1; }
.btn-secondary:hover { background: #f1f5f9; color: #1e293b; }
.btn-danger { background: var(--danger-color); color: white; }
.btn-danger:hover { background: #dc2626; }
/* --- MODAL WIZARD --- */
.wizard-overlay {
position: fixed; inset: 0; background: rgba(15, 23, 42, 0.6); z-index: 1000;
display: none; align-items: center; justify-content: center; backdrop-filter: blur(4px);
opacity: 0; transition: opacity 0.2s ease-in-out;
}
.wizard-overlay.show { opacity: 1; }
.wizard-box {
background: white; width: 100%; max-width: 640px; border-radius: 12px;
box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25); overflow: hidden;
display: flex; flex-direction: column; max-height: 85vh;
transform: scale(0.95); transition: transform 0.2s ease-in-out;
}
.wizard-overlay.show .wizard-box { transform: scale(1); }
.wizard-header { padding: 20px 24px; border-bottom: 1px solid var(--border-color); display: flex; justify-content: space-between; align-items: center; background: #fff; }
.wizard-header h3 { margin: 0; font-size: 1.1rem; font-weight: 600; color: var(--text-main); }
.close-wizard { background: transparent; border: none; font-size: 1.5rem; color: var(--text-light); cursor: pointer; width: 32px; height: 32px; border-radius: 6px; display: flex; align-items: center; justify-content: center; transition: background 0.2s; }
.close-wizard:hover { background: #f1f5f9; color: var(--text-main); }
.wizard-body { padding: 24px; overflow-y: auto; background: #fff; }
.type-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 16px; }
.type-card {
background: #fff; border: 1px solid var(--border-color); padding: 20px; border-radius: 10px;
cursor: pointer; transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); text-align: center;
display: flex; flex-direction: column; align-items: center; gap: 12px;
}
.type-card:hover { border-color: var(--primary-color); background: #eff6ff; transform: translateY(-2px); box-shadow: var(--card-shadow); }
.type-icon { font-size: 2rem; }
.type-title { font-weight: 600; color: var(--text-main); font-size: 0.95rem; }
/* Busca no Wizard */
.back-link { color: var(--text-light); cursor: pointer; font-size: 0.85rem; margin-bottom: 20px; display: inline-flex; align-items: center; gap: 4px; font-weight: 500; }
.back-link:hover { color: var(--primary-color); }
.search-container { display: flex; flex-direction: column; gap: 16px; }
.search-input-group { display: flex; gap: 12px; position: relative; }
.search-input { flex: 1; padding: 12px 16px; padding-left: 40px; border: 1px solid var(--border-color); border-radius: 8px; font-size: 0.95rem; font-family: inherit; transition: border-color 0.2s, box-shadow 0.2s; }
.search-input:focus { outline: none; border-color: var(--primary-color); box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1); }
.search-icon-fake { position: absolute; left: 14px; top: 50%; transform: translateY(-50%); color: #94a3b8; pointer-events: none; }
.btn-search { background: var(--primary-color); color: white; border: none; padding: 0 24px; border-radius: 8px; font-weight: 500; cursor: pointer; transition: background 0.2s; }
.btn-search:hover { background: var(--primary-hover); }
.results-list { margin-top: 10px; display: flex; flex-direction: column; gap: 10px; max-height: 400px; overflow-y: auto; }
.result-item { display: flex; justify-content: space-between; align-items: center; padding: 16px; background: #fff; border: 1px solid var(--border-color); border-radius: 8px; transition: all 0.2s; }
.result-item:hover { border-color: var(--primary-color); background: #f8fafc; box-shadow: var(--card-shadow); }
.result-info { display: flex; flex-direction: column; gap: 4px; }
.result-name { font-weight: 600; color: var(--text-main); font-size: 0.95rem; }
.result-meta { font-size: 0.8rem; color: var(--text-light); }
.btn-select { background: white; color: var(--primary-color); border: 1px solid var(--border-color); padding: 8px 16px; border-radius: 6px; font-size: 0.85rem; font-weight: 600; cursor: pointer; text-decoration: none; transition: all 0.2s; }
.btn-select:hover { background: var(--primary-color); color: white; border-color: var(--primary-color); }
.loading-spinner { text-align: center; color: var(--text-light); padding: 30px; display: none; font-size: 0.9rem; }
.wizard-step { display: none; animation: fadeIn 0.3s ease; }
.wizard-step.active { display: block; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: translateY(0); } }
/* --- RESPONSIVIDADE --- */
@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%); } /* Reseta no mobile */
.main-content { padding: 0; }
.container { padding: 1.5rem; }
.mobile-menu-btn { display: block !important; position: absolute; top: 15px; left: 15px; z-index: 40; background: white; border: 1px solid #e2e8f0; border-radius: 6px; padding: 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); }
.sidebar-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.5); z-index: 45; backdrop-filter: blur(2px); }
.sidebar.active + .sidebar-overlay { display: block; }
}
.mobile-menu-btn { display: none; }
</style>
{% block css %}{% endblock %}
</head>
<body>
<div class="wrapper">
<!-- Overlay mobile -->
<button class="mobile-menu-btn" onclick="toggleSidebarMobile()">
<span style="font-size: 1.2rem;"></span>
</button>
<!-- SIDEBAR -->
<nav class="sidebar" id="sidebar">
<div class="sidebar-header">
<div class="brand">SGMP <span>CORP</span></div>
<button class="toggle-btn" onclick="toggleSidebarDesktop()" title="Recolher Menu">
<span>«</span>
</button>
</div>
<div class="nav-links">
<a href="{% url 'solicitacoes:dashboard' %}" class="nav-item" title="Dashboard">
<span class="link-icon">📊</span>
<span class="link-text">Dashboard</span>
</a>
{# Menu de "Todas as Solicitações" - visível para qualquer perfil autenticado, exceto Gestor #}
{% if user.is_authenticated and usuario_sistema and usuario_sistema.perfil != 'GESTOR' %}
<a href="{% url 'solicitacoes:todas_solicitacoes' %}" class="nav-item" title="Todas as solicitações">
<span class="link-icon">📑</span>
<span class="link-text">Todas as solicitações</span>
</a>
{% endif %}
{% if user.is_authenticated and usuario_sistema and usuario_sistema.perfil == 'GESTOR' %}
<button onclick="openWizard()" class="nav-item" title="Criar Solicitação">
<span class="link-icon"></span>
<span class="link-text">Nova Solicitação</span>
</button>
{% endif %}
{% comment %} <a href="{% url 'solicitacoes:gerenciar_permissoes' %}" class="nav-item" title="Gerenciar Permissões">
<span class="link-icon">⚙️</span>
<span class="link-text">Administração</span>
</a> {% endcomment %}
</div>
{% if user.is_authenticated %}
<div class="user-section">
<a href="{% url 'solicitacoes:logout' %}" class="user-profile" title="Sair">
<div class="avatar">
{% if usuario_sistema %}
{{ usuario_sistema.nome|make_list|first }}
{% else %}
{{ user.username|make_list|first }}
{% endif %}
</div>
<div class="user-info">
<span class="user-name">
{% if usuario_sistema %}{{ usuario_sistema.nome }}{% else %}{{ user.username }}{% endif %}
</span>
<span class="user-role">
{% if usuario_sistema %}{{ usuario_sistema.get_perfil_display }}{% else %}Usuário{% endif %}
</span>
</div>
<span class="link-icon" style="margin-left: auto; color: #ef4444;" title="Sair">🚪</span>
</a>
</div>
{% endif %}
</nav>
<div class="sidebar-overlay" onclick="toggleSidebarMobile()"></div>
<!-- CONTEÚDO PRINCIPAL -->
<main class="main-content">
<div class="container">
{% block content %}{% endblock %}
</div>
</main>
</div>
<!-- ======================= -->
<!-- MODAL WIZARD DE CRIAÇÃO -->
<!-- ======================= -->
<div id="createWizard" class="wizard-overlay">
<div class="wizard-box">
<div class="wizard-header">
<h3 id="wizardTitle">Iniciar Novo Processo</h3>
<button class="close-wizard" onclick="closeWizard()">×</button>
</div>
<div class="wizard-body">
<!-- PASSO 1: Selecionar Tipo -->
<div id="step1" class="wizard-step active">
<p class="wizard-intro">Qual tipo de movimentação você deseja realizar hoje?</p>
<div class="type-grid">
<div class="type-card" onclick="goToStep2('desligamento', 'Desligamento')">
<span class="type-icon">🚫</span>
<span class="type-title">Desligamento</span>
</div>
<div class="type-card" onclick="goToStep2('movimentacao', 'Movimentação Interna')">
<span class="type-icon">🔄</span>
<span class="type-title">Movimentação</span>
</div>
<div class="type-card" onclick="goToStep2('aumento-quadro', 'Aumento de Quadro')">
<span class="type-icon">📈</span>
<span class="type-title">Aumento de Quadro</span>
</div>
<div class="type-card" onclick="goToStep2('substituicao', 'Substituição')">
<span class="type-icon">👥</span>
<span class="type-title">Substituição</span>
</div>
</div>
</div>
<!-- PASSO 2: Buscar Colaborador -->
<div id="step2" class="wizard-step">
<div class="back-link" onclick="backToStep1()">
<span></span> Voltar para seleção
</div>
<p style="margin-bottom: 15px; color: var(--text-main);">Selecione o colaborador para o processo de <strong id="selectedTypeLabel"></strong>:</p>
<div class="search-container">
<div class="search-input-group">
<span class="search-icon-fake">🔍</span>
<input type="text" id="wizardSearchInput" class="search-input" placeholder="Busque por Nome ou Matrícula..." onkeydown="if(event.key==='Enter') doSearch()">
<button class="btn-search" onclick="doSearch()">Buscar</button>
</div>
<div id="loading" class="loading-spinner">
Consultando base de dados...
</div>
<div id="searchResults" class="results-list">
<!-- Resultados injetados via JS -->
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// --- LÓGICA DA SIDEBAR ---
function toggleSidebarDesktop() {
const sidebar = document.getElementById('sidebar');
const btn = sidebar.querySelector('.toggle-btn span');
sidebar.classList.toggle('collapsed');
// Troca icone da seta
if(sidebar.classList.contains('collapsed')) {
btn.innerHTML = '»';
} else {
btn.innerHTML = '«';
}
}
function toggleSidebarMobile() {
const sidebar = document.getElementById('sidebar');
sidebar.classList.toggle('active');
}
// --- VARIÁVEIS DE ESTADO ---
let currentTypeSlug = '';
// --- CONTROLE DO MODAL ---
function openWizard() {
const overlay = document.getElementById('createWizard');
overlay.style.display = 'flex';
// Pequeno timeout para ativar a transição de opacidade
setTimeout(() => overlay.classList.add('show'), 10);
backToStep1();
}
function closeWizard() {
const overlay = document.getElementById('createWizard');
overlay.classList.remove('show');
setTimeout(() => {
overlay.style.display = 'none';
}, 200); // Espera animação CSS
}
// Fechar com ESC ou clicando fora
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');
}
// --- LÓGICA DE BUSCA ---
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 htmlText = await response.text();
const parser = new DOMParser();
const doc = parser.parseFromString(htmlText, 'text/html');
const rows = doc.querySelectorAll('table tbody tr');
if (rows.length === 0) {
resultsDiv.innerHTML = '<div style="text-align:center; padding:20px; color:#64748b">Nenhum colaborador encontrado.</div>';
} else {
rows.forEach(row => {
const cells = row.querySelectorAll('td');
if (cells.length < 6) return;
const matricula = cells[0].textContent.trim();
const nome = cells[1].textContent.trim();
const cargo = cells[2].textContent.trim();
let actionLink = null;
const links = row.querySelectorAll('a');
const linkTextMap = {
'desligamento': 'Desligamento',
'movimentacao': 'Movimentação',
'substituicao': 'Substituição'
};
const linkText = linkTextMap[currentTypeSlug];
if (linkText) {
actionLink = Array.from(links).find(l => l.textContent.includes(linkText));
}
if (!actionLink) return;
const href = actionLink.getAttribute('href');
const card = document.createElement('div');
card.className = 'result-item';
card.innerHTML = `
<div class="result-info">
<span class="result-name">${nome}</span>
<span class="result-meta">Matrícula: ${matricula} | ${cargo}</span>
</div>
<a href="${href}" class="btn-select">Selecionar</a>
`;
resultsDiv.appendChild(card);
});
}
} catch (error) {
console.error(error);
resultsDiv.innerHTML = '<div style="color:red; text-align:center">Erro ao buscar. Tente novamente.</div>';
} finally {
loader.style.display = 'none';
}
}
</script>
{% block scripts %}{% endblock %}
</body>
</html>