243 lines
16 KiB
HTML
243 lines
16 KiB
HTML
|
|
{% 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>
|