sgmp/templates/dashboard_original.html

1333 lines
70 KiB
HTML
Raw Permalink Normal View History

2026-03-09 18:46:01 +00:00
{% extends "base.html" %}
{% block title %}Dashboard - SGMP{% endblock %}
{% block css %}
<style>
/* --- Header --- */
.dashboard-header {
margin-bottom: 2rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.dashboard-header h1 {
font-size: 1.85rem;
font-weight: 700;
color: #1e293b;
margin: 0;
letter-spacing: -0.025em;
}
.dashboard-header p {
color: #64748b;
font-size: 0.95rem;
line-height: 1.5;
}
/* --- Cards de Métricas --- */
.metrics-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 1.5rem;
margin-bottom: 2.5rem;
}
.metric-card {
background: white;
padding: 1.5rem;
border-radius: 12px;
border: 1px solid #e2e8f0;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
border-left: 5px solid transparent;
transition: all 0.2s ease-in-out;
cursor: pointer;
position: relative;
overflow: hidden;
}
.metric-card:hover {
transform: translateY(-4px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}
.metric-card.active {
background-color: #f8fafc;
border-color: #cbd5e1;
transform: scale(1.02);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
/* Cores dos Cards */
.card-total { border-left-color: #3b82f6; }
.card-total .metric-value { color: #3b82f6; }
.card-pending { border-left-color: #f59e0b; }
.card-pending .metric-value { color: #d97706; }
.metric-label {
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.05em;
color: #64748b;
font-weight: 700;
margin-bottom: 0.5rem;
}
.metric-value {
font-size: 2.25rem;
font-weight: 800;
line-height: 1;
}
/* --- Seções e Tabelas --- */
.section { margin-bottom: 30px; }
.section h3 {
color: #1e293b;
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 1rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
/* Container responsivo para tabela */
.table-responsive {
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
background: white;
border-radius: 12px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
border: 1px solid #e2e8f0;
}
table {
width: 100%;
border-collapse: collapse;
white-space: nowrap; /* Evita quebra de linha indesejada no mobile */
}
th, td {
padding: 16px;
text-align: left;
border-bottom: 1px solid #e2e8f0;
}
th {
background-color: #f8fafc;
font-weight: 600;
color: #475569;
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 0.025em;
}
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background-color: #f8fafc; }
/* Badges de Status */
.status-badge {
display: inline-flex;
align-items: center;
padding: 4px 10px;
border-radius: 9999px;
font-size: 0.75rem;
font-weight: 700;
line-height: 1;
}
.status-RASCUNHO { background: #fffbeb; color: #b45309; border: 1px solid #fcd34d; }
.status-AGUARDANDO_HEAD { background: #fef3c7; color: #b45309; border: 1px solid #fcd34d; }
.status-ENVIADA { background: #eff6ff; color: #1d4ed8; border: 1px solid #bfdbfe; }
.status-APROVADA_GG,
.status-APROVADA_CONTROLADORIA,
.status-APROVADA_DIRETORIA { background: #ecfdf5; color: #047857; border: 1px solid #6ee7b7; }
.status-FINALIZADA { background: #f3f4f6; color: #374151; border: 1px solid #d1d5db; }
.status-REPROVADA { background: #fef2f2; color: #b91c1c; border: 1px solid #fecaca; }
/* --- Paginação --- */
.pagination {
margin-top: 24px;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
flex-wrap: wrap;
}
.pagination a {
padding: 8px 14px;
text-decoration: none;
border: 1px solid #e2e8f0;
border-radius: 6px;
color: #475569;
background: white;
font-size: 0.9rem;
transition: all 0.2s;
}
.pagination a:hover {
background: #f1f5f9;
border-color: #cbd5e1;
color: #1e293b;
}
.pagination .current {
padding: 8px 14px;
background: #2563eb;
color: white;
border-radius: 6px;
font-weight: 500;
border: 1px solid #2563eb;
}
/* --- Mensagens --- */
.messages ul { list-style: none; padding: 0; margin: 0 0 20px 0; }
.messages li {
padding: 14px 18px;
margin-bottom: 12px;
border-radius: 8px;
border: 1px solid transparent;
font-size: 0.95rem;
display: flex;
align-items: center;
}
.messages .error { background: #fef2f2; color: #991b1b; border-color: #fecaca; }
.messages .success { background: #ecfdf5; color: #065f46; border-color: #a7f3d0; }
.messages .info { background: #eff6ff; color: #1e40af; border-color: #bfdbfe; }
/* --- Empty State --- */
.empty-state {
text-align: center;
padding: 60px 20px;
color: #64748b;
background: white;
border-radius: 12px;
border: 1px dashed #cbd5e1;
}
.empty-state p { margin: 0; font-size: 1.1rem; }
/* --- Detalhes Expansíveis --- */
.details-row { background-color: #f8fafc; box-shadow: inset 0 2px 4px rgba(0,0,0,0.02); }
.details-row td { padding: 0 !important; border-bottom: none; }
.details-wrapper { padding: 2rem; animation: slideDown 0.3s cubic-bezier(0.16, 1, 0.3, 1); }
@keyframes slideDown {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
/* Abas */
.tab-nav {
display: flex;
border-bottom: 1px solid #e2e8f0;
margin-bottom: 1.5rem;
gap: 2rem;
overflow-x: auto; /* Scroll nas abas em mobile */
}
.tab-btn {
background: none;
border: none;
padding: 0.75rem 0;
cursor: pointer;
color: #64748b;
border-bottom: 2px solid transparent;
font-size: 0.95rem;
font-weight: 500;
transition: all 0.2s ease;
white-space: nowrap;
}
.tab-btn:hover { color: #2563eb; }
.tab-btn.active { color: #2563eb; border-bottom-color: #2563eb; font-weight: 600; }
.tab-pane { display: none; }
.tab-pane.active { display: block; animation: fadeIn 0.3s ease; }
/* Seções Internas */
.details-section {
margin-bottom: 1.5rem;
background: white;
padding: 1.5rem;
border-radius: 12px;
border: 1px solid #e2e8f0;
}
.details-section h5 {
color: #94a3b8;
font-size: 0.75rem;
text-transform: uppercase;
margin: 1.5rem 0 1rem 0;
font-weight: 700;
letter-spacing: 0.05em;
border-bottom: 1px solid #f1f5f9;
padding-bottom: 8px;
}
.details-section h5:first-child { margin-top: 0; }
/* Grid de Informações */
.info-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1.5rem;
}
.info-item { display: flex; flex-direction: column; }
.info-item label {
font-size: 0.75rem;
color: #64748b;
margin-bottom: 0.35rem;
font-weight: 600;
text-transform: uppercase;
}
.info-item span {
font-size: 1rem;
color: #0f172a;
font-weight: 500;
word-break: break-word;
line-height: 1.4;
}
/* Timeline */
.timeline { position: relative; padding-left: 2rem; border-left: 2px solid #e2e8f0; margin-left: 0.5rem; }
.timeline-item { position: relative; padding-bottom: 2rem; padding-left: 1.5rem; }
.timeline-item::before {
content: '';
position: absolute;
left: -2.4rem; /* Ajuste fino para centralizar na linha */
top: 0.25rem;
width: 14px;
height: 14px;
border-radius: 50%;
background: #cbd5e1;
border: 3px solid white;
box-shadow: 0 0 0 1px #e2e8f0;
}
.timeline-item:last-child { padding-bottom: 0; }
.timeline-header { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 0.25rem; font-size: 0.95rem; align-items: baseline; }
.timeline-user { font-weight: 600; color: #1e293b; }
.timeline-action { color: #64748b; }
.timeline-date { font-size: 0.8rem; color: #94a3b8; margin-left: auto; }
.timeline-justificativa {
margin-top: 0.75rem;
padding: 1rem;
background: #f8fafc;
border-radius: 8px;
font-size: 0.9rem;
color: #334155;
border: 1px solid #e2e8f0;
font-style: italic;
}
/* Botões */
.btn-action {
padding: 0.6rem 1.2rem;
border-radius: 6px;
font-size: 0.85rem;
font-weight: 600;
cursor: pointer;
border: 1px solid transparent;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 6px;
transition: all 0.2s;
}
.btn-action-primary { background: #2563eb; color: white; border: 1px solid #1d4ed8; }
.btn-action-primary:hover { background: #1d4ed8; box-shadow: 0 2px 4px rgba(37,99,235,0.2); }
.btn-action-success { background: #10b981; color: white; border: 1px solid #059669; }
.btn-action-success:hover { background: #059669; box-shadow: 0 2px 4px rgba(16,185,129,0.2); }
.btn-action-danger { background: white; color: #ef4444; border: 1px solid #ef4444; }
.btn-action-danger:hover { background: #fef2f2; }
/* Forms Internos */
.form-decisao {
background: #f8fafc;
padding: 1.5rem;
border-radius: 12px;
margin-top: 1.5rem;
border: 1px solid #e2e8f0;
}
.form-group input, .form-group select, .form-group textarea {
width: 100%;
padding: 0.75rem;
border: 1px solid #cbd5e1;
border-radius: 6px;
font-family: inherit;
font-size: 0.95rem;
transition: border-color 0.2s;
}
.form-group input:focus, .form-group select:focus, .form-group textarea:focus {
outline: none;
border-color: #2563eb;
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
}
/* Seta de expansão */
.chevron-icon {
transition: transform 0.3s ease;
display: inline-block;
font-size: 0.8rem;
}
.request-row.expanded .chevron-icon {
transform: rotate(180deg);
}
/* Modal */
#modal-aprovacao {
backdrop-filter: blur(4px);
}
.modal-content {
background-color: white;
padding: 30px;
border-radius: 12px;
max-width: 500px;
width: 90%;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
border: 1px solid #e2e8f0;
animation: modalFadeIn 0.2s ease-out;
}
@keyframes modalFadeIn {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
@media (max-width: 640px) {
.dashboard-header h1 { font-size: 1.5rem; }
.metrics-grid { grid-template-columns: 1fr 1fr; gap: 1rem; }
.metric-value { font-size: 1.75rem; }
.info-grid { grid-template-columns: 1fr; }
.btn-action { width: 100%; justify-content: center; margin-bottom: 0.5rem; }
.details-wrapper { padding: 1rem; }
}
</style>
{% endblock %}
{% block content %}
<div class="dashboard-header">
<h1>SGMP - Movimentação de Pessoas</h1>
<p>
Olá, <strong>{{ usuario_sistema.nome }}</strong>!
<span style="display: inline-block; background: #e2e8f0; padding: 2px 8px; border-radius: 4px; font-size: 0.8rem; margin-left: 8px; color: #475569;">
{{ usuario_sistema.get_perfil_display }}
</span>
<br>
<small style="color: #94a3b8;">Matrícula: {{ usuario_sistema.matricula }}</small>
</p>
</div>
{% if messages %}
<div class="messages">
<ul>
{% for message in messages %}
<li class="{{ message.tags }}">
{% if message.tags == 'error' %}⚠️{% elif message.tags == 'success' %}✅{% else %}{% endif %}
&nbsp;{{ message }}
</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if usuario_sistema.perfil == 'GESTOR' %}
<div class="section">
<div style="background: #fffbeb; border: 1px solid #fcd34d; border-radius: 12px; padding: 16px; margin-bottom: 24px; display: flex; gap: 12px; align-items: flex-start;">
<span style="font-size: 1.5rem;">💡</span>
<div>
<h4 style="margin: 0 0 4px 0; color: #92400e;">Lembrete Rápido</h4>
<p style="margin: 0; color: #b45309; font-size: 0.95rem;">
Solicitações em <strong>Rascunho</strong> só são visíveis para você.
Lembre-se de clicar em <strong>"Enviar para Aprovação"</strong> na página de detalhes para iniciar o fluxo.
</p>
</div>
</div>
</div>
{% endif %}
<!-- Cards de Métricas -->
<div class="metrics-grid">
<div class="metric-card card-total active" onclick="filtrarTabela('todos', this)">
<div class="metric-label">Total</div>
<div class="metric-value">{{ total }}</div>
</div>
<div class="metric-card card-pending" onclick="filtrarTabela('pendente', this)">
<div class="metric-label">Pendentes</div>
<div class="metric-value">{{ pendentes }}</div>
</div>
</div>
<div class="section">
<h3>
{% if usuario_sistema.perfil == 'GESTOR' %}
📋 Minhas Solicitações
{% else %}
⏳ Pendentes de Aprovação
{% endif %}
</h3>
{% if usuario_sistema.perfil != 'GESTOR' %}
<div style="background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 8px; padding: 12px; margin-bottom: 20px; color: #1e40af; font-size: 0.9rem; display: flex; align-items: center; gap: 8px;">
Você está vendo solicitações com status <strong>Enviada</strong> aguardando sua análise.
</div>
{% endif %}
{% if solicitacoes %}
<div class="table-responsive">
<table id="tabela-solicitacoes">
<thead>
<tr>
<th>Tipo</th>
<th>Colaborador</th>
<th>Status</th>
<th>Data</th>
<th>Ações</th>
</tr>
</thead>
<tbody>
{% for item in solicitacoes_com_acao %}
{% with solicitacao=item.solicitacao %}
<tr class="request-row"
data-id="{{ solicitacao.id }}"
data-status="{% if solicitacao.status == 'FINALIZADA' %}aprovado{% elif solicitacao.status == 'REPROVADA' %}reprovado{% else %}pendente{% endif %}"
onclick="toggleDetails('{{ solicitacao.id }}', this)"
style="cursor: pointer;">
<td><strong>{{ solicitacao.get_tipo_display }}</strong></td>
<td>
{% if solicitacao.funcionario %}
{{ solicitacao.funcionario.nome }}
{% else %}
<span style="color: #94a3b8;">N/A</span>
{% endif %}
</td>
<td>
<span class="status-badge status-{{ solicitacao.status }}">
{{ solicitacao.get_status_display }}
</span>
</td>
<td style="color: #64748b;">{{ solicitacao.criado_em|date:"d/m/Y H:i" }}</td>
<td>
<div style="display: flex; gap: 12px; align-items: center;">
<span class="expand-btn" style="color: #3b82f6; font-weight: 600; font-size: 0.85rem;">
Detalhes <span class="chevron-icon"></span>
</span>
{% if item.pode_dar_parecer %}
<a href="{% url 'solicitacoes:solicitacao_detalhe' solicitacao.id %}"
class="btn-action btn-action-primary"
style="padding: 4px 10px; font-size: 0.75rem;"
title="Fornecer parecer técnico">
📝 Parecer
</a>
{% endif %}
{% if item.pode_aprovar %}
<div style="display: flex; gap: 4px;">
<button class="btn-action btn-action-success"
onclick="event.stopPropagation(); abrirModalAprovacao('{{ solicitacao.id }}', 'APROVADO')"
style="padding: 4px 8px; font-size: 0.75rem;" title="Aprovar">
</button>
<button class="btn-action btn-action-danger"
onclick="event.stopPropagation(); abrirModalAprovacao('{{ solicitacao.id }}', 'REPROVADO')"
style="padding: 4px 8px; font-size: 0.75rem;" title="Reprovar">
</button>
</div>
{% endif %}
</div>
</td>
</tr>
<!-- Linha de detalhes expansível -->
<tr id="details-{{ solicitacao.id }}" class="details-row" style="display:none;">
<td colspan="5">
<div class="details-wrapper" id="wrapper-{{ solicitacao.id }}">
<!-- Barra de navegação das abas -->
<div class="tab-nav">
<button class="tab-btn active" onclick="switchTab(event, 'solicitacao', '{{ solicitacao.id }}')">
📄 Solicitação
</button>
{% if solicitacao.funcionario %}
<button class="tab-btn" onclick="switchTab(event, 'rm', '{{ solicitacao.id }}')">
🏢 RM (Totvs)
</button>
{% endif %}
{% for item_acao in solicitacoes_com_acao %}
{% if item_acao.solicitacao.id == solicitacao.id and item_acao.dados_winthor_organizados %}
<button class="tab-btn" onclick="switchTab(event, 'winthor', '{{ solicitacao.id }}')">
💼 Winthor
</button>
{% endif %}
{% endfor %}
<button class="tab-btn" onclick="switchTab(event, 'auditoria', '{{ solicitacao.id }}')">
📝 Histórico & Auditoria
</button>
</div>
<!-- ABA: SOLICITAÇÃO -->
<div class="tab-pane active" data-tab="solicitacao">
<div class="details-section">
<h5>Dados Gerais</h5>
<div class="info-grid" style="margin-bottom: 1.5rem;">
<div class="info-item">
<label>Solicitante</label>
<span>{{ solicitacao.solicitante.nome }} <small style="color:#64748b">({{ solicitacao.solicitante.matricula }})</small></span>
</div>
<div class="info-item">
<label>Tipo</label>
<span style="font-weight:700; color:#2563eb;">{{ solicitacao.get_tipo_display }}</span>
</div>
<div class="info-item">
<label>Data Criação</label>
<span>{{ solicitacao.criado_em|date:"d/m/Y H:i" }}</span>
</div>
{% if solicitacao.enviada_em %}
<div class="info-item">
<label>Enviada em</label>
<span>{{ solicitacao.enviada_em|date:"d/m/Y H:i" }}</span>
</div>
{% endif %}
{% if solicitacao.finalizada_em %}
<div class="info-item">
<label>Finalizada em</label>
<span>{{ solicitacao.finalizada_em|date:"d/m/Y H:i" }}</span>
</div>
{% endif %}
</div>
<!-- Detalhes específicos por tipo -->
{% if solicitacao.tipo == 'DESLIGAMENTO' and solicitacao.desligamento %}
<h5>Detalhes do Desligamento</h5>
<div class="info-grid" style="margin-bottom: 1.5rem;">
{% if solicitacao.desligamento.tipo_desligamento %}
<div class="info-item">
<label>Tipo de Desligamento</label>
<span>
{% if solicitacao.desligamento.tipo_desligamento == 'PEDIDO_DEMISSAO' %}
📝 Pedido de Demissão
{% elif solicitacao.desligamento.tipo_desligamento == 'SEM_JUSTA_CAUSA' %}
🚪 Sem Justa Causa
{% elif solicitacao.desligamento.tipo_desligamento == 'COM_JUSTA_CAUSA' %}
⚠️ Por Justa Causa
{% elif solicitacao.desligamento.tipo_desligamento == 'TERMINO_CONTRATO' %}
📅 Término de Contrato
{% elif solicitacao.desligamento.tipo_desligamento == 'OUTROS' %}
📋 Outros
{% else %}
{{ solicitacao.desligamento.get_tipo_desligamento_display|default:"—" }}
{% endif %}
</span>
</div>
{% endif %}
{% if solicitacao.desligamento.aviso_previo %}
<div class="info-item">
<label>Aviso Prévio</label>
<span>
{% if solicitacao.desligamento.aviso_previo == 'TRABALHADO' %}
✅ Trabalhado
{% elif solicitacao.desligamento.aviso_previo == 'INDENIZADO' %}
💰 Indenizado
{% elif solicitacao.desligamento.aviso_previo == 'DISPENSADO' %}
📤 Dispensado
{% else %}
{{ solicitacao.desligamento.get_aviso_previo_display|default:"—" }}
{% endif %}
</span>
</div>
{% endif %}
<div class="info-item">
<label>Data Prevista de Saída</label>
<span>{{ solicitacao.desligamento.data_prevista_desligamento|date:"d/m/Y"|default:"—" }}</span>
</div>
<div class="info-item" style="grid-column: 1 / -1;">
<label>Detalhamento / Justificativa</label>
<span style="white-space: pre-wrap;">{{ solicitacao.desligamento.motivo|default:"—" }}</span>
</div>
{% if solicitacao.desligamento.arquivo_pedido %}
<div class="info-item" style="grid-column: 1 / -1;">
<label>Carta de Pedido</label>
<span>
<a href="{{ solicitacao.desligamento.arquivo_pedido.url }}" target="_blank" style="color: #2563eb; text-decoration: none;">
📎 Ver arquivo
</a>
</span>
</div>
{% endif %}
{% if solicitacao.desligamento.observacoes %}
<div class="info-item" style="grid-column: 1 / -1;">
<label>Observações Adicionais</label>
<span style="white-space: pre-wrap;">{{ solicitacao.desligamento.observacoes }}</span>
</div>
{% endif %}
</div>
{% elif solicitacao.tipo == 'MOVIMENTACAO' and solicitacao.movimentacao %}
<h5>Detalhes da Movimentação</h5>
<div class="info-grid" style="margin-bottom: 1.5rem;">
<div class="info-item">
<label>Data de Efetivação</label>
<span>{{ solicitacao.movimentacao.data_efetivacao|date:"d/m/Y"|default:"—" }}</span>
</div>
{% if solicitacao.movimentacao.altera_funcao %}
<div class="info-item">
<label>Nova Função</label>
<span>{{ solicitacao.movimentacao.novo_cod_funcao|default:"—" }}</span>
</div>
{% endif %}
{% if solicitacao.movimentacao.altera_centro_custo %}
<div class="info-item">
<label>Nova Seção</label>
<span>{{ solicitacao.movimentacao.novo_cod_secao|default:"—" }}</span>
</div>
{% endif %}
{% if solicitacao.movimentacao.novo_salario %}
<div class="info-item">
<label>Novo Salário</label>
<span>R$ {{ solicitacao.movimentacao.novo_salario }}</span>
</div>
{% endif %}
<div class="info-item" style="grid-column: 1 / -1;">
<label>Justificativa</label>
<span>{{ solicitacao.movimentacao.justificativa|default:"—" }}</span>
</div>
</div>
{% elif solicitacao.tipo == 'ADM_SUBSTITUICAO' and solicitacao.admissao_substituicao %}
<h5>Detalhes da Admissão por Substituição</h5>
<div class="info-grid" style="margin-bottom: 1.5rem;">
<div class="info-item">
<label>Data Prevista</label>
<span>{{ solicitacao.admissao_substituicao.data_previsao_contratacao|date:"d/m/Y"|default:"—" }}</span>
</div>
<div class="info-item">
<label>Coligada Destino</label>
<span>{{ solicitacao.admissao_substituicao.cod_coligada_destino|default:"—" }}</span>
</div>
<div class="info-item">
<label>Filial Destino</label>
<span>{{ solicitacao.admissao_substituicao.cod_filial_destino|default:"—" }}</span>
</div>
<div class="info-item">
<label>Seção Destino</label>
<span>{{ solicitacao.admissao_substituicao.cod_secao_destino|default:"—" }}</span>
</div>
<div class="info-item">
<label>Função Destino</label>
<span>{{ solicitacao.admissao_substituicao.cod_funcao_destino|default:"—" }}</span>
</div>
<div class="info-item" style="grid-column: 1 / -1;">
<label>Justificativa</label>
<span>{{ solicitacao.admissao_substituicao.justificativa|default:"—" }}</span>
</div>
</div>
{% elif solicitacao.tipo == 'ADM_AUMENTO' and solicitacao.admissao_aumento %}
<h5>Detalhes da Admissão por Aumento de Quadro</h5>
<div class="info-grid" style="margin-bottom: 1.5rem;">
<div class="info-item">
<label>Data Prevista</label>
<span>{{ solicitacao.admissao_aumento.data_previsao_contratacao|date:"d/m/Y"|default:"—" }}</span>
</div>
<div class="info-item">
<label>Coligada Destino</label>
<span>{{ solicitacao.admissao_aumento.cod_coligada_destino|default:"—" }}</span>
</div>
<div class="info-item">
<label>Filial Destino</label>
<span>{{ solicitacao.admissao_aumento.cod_filial_destino|default:"—" }}</span>
</div>
<div class="info-item">
<label>Seção Destino</label>
<span>{{ solicitacao.admissao_aumento.cod_secao_destino|default:"—" }}</span>
</div>
<div class="info-item">
<label>Função Destino</label>
<span>{{ solicitacao.admissao_aumento.cod_funcao_destino|default:"—" }}</span>
</div>
<div class="info-item" style="grid-column: 1 / -1;">
<label>Justificativa Estratégica</label>
<span>{{ solicitacao.admissao_aumento.justificativa_estrategica|default:"—" }}</span>
</div>
</div>
{% endif %}
<!-- Ações -->
<div style="margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid #e2e8f0;">
{% if solicitacao.pode_enviar and solicitacao.solicitante.id == usuario_sistema.id %}
<a href="{% url 'solicitacoes:enviar_solicitacao' solicitacao.id %}" class="btn-action btn-action-primary">
🚀 Enviar para Aprovação
</a>
{% endif %}
</div>
</div>
</div>
<!-- ABA: RM (Totvs) -->
{% if solicitacao.funcionario %}
<div class="tab-pane" data-tab="rm">
<div class="details-section section-highlight">
<div style="display: flex; align-items: center; gap: 8px; margin: -1rem 0 1.5rem 0; color: #2563eb; background: #dbeafe; padding: 8px 12px; border-radius: 6px; display: inline-flex;">
<span>💾</span>
<span style="font-size: 0.85rem; font-weight: 600;">Snapshot TOTVS RM</span>
</div>
<h5>Dados Básicos</h5>
<div class="info-grid" style="margin-bottom: 1.5rem;">
<div class="info-item">
<label>Matrícula</label>
<span><strong>{{ solicitacao.funcionario.matricula }}</strong></span>
</div>
<div class="info-item">
<label>Nome Completo</label>
<span><strong>{{ solicitacao.funcionario.nome }}</strong></span>
</div>
{% if solicitacao.funcionario.cpf %}
<div class="info-item">
<label>CPF</label>
<span>{{ solicitacao.funcionario.cpf }}</span>
</div>
{% endif %}
{% if solicitacao.funcionario.data_admissao %}
<div class="info-item">
<label>Data de Admissão</label>
<span>{{ solicitacao.funcionario.data_admissao|date:"d/m/Y" }}</span>
</div>
{% endif %}
{% if solicitacao.funcionario.situacao %}
<div class="info-item">
<label>Situação</label>
<span>{{ solicitacao.funcionario.situacao }}</span>
</div>
{% endif %}
</div>
<h5>Dados Profissionais</h5>
<div class="info-grid" style="margin-bottom: 1.5rem;">
<div class="info-item">
<label>Cargo/Função</label>
<span>{{ solicitacao.funcionario.cargo }}</span>
</div>
<div class="info-item">
<label>Código da Função</label>
<span>{{ solicitacao.funcionario.cod_funcao|default:"N/A" }}</span>
</div>
<div class="info-item">
<label>Setor/Seção</label>
<span>{{ solicitacao.funcionario.setor }}</span>
</div>
<div class="info-item">
<label>Centro de Custo</label>
<span>{{ solicitacao.funcionario.centro_custo }}</span>
</div>
{% if solicitacao.funcionario.salario %}
<div class="info-item">
<label>Salário Atual</label>
<span><strong style="color: #10b981; font-size: 1.1rem;">R$ {{ solicitacao.funcionario.salario|floatformat:2 }}</strong></span>
</div>
{% endif %}
</div>
{% if solicitacao.funcionario.saldo_banco_horas_minutos is not None %}
<h5>Banco de Horas</h5>
<div class="info-grid" style="margin-bottom: 1.5rem;">
<div class="info-item">
<label>Saldo Atual</label>
<span>
{% if solicitacao.funcionario.saldo_banco_horas_minutos >= 0 %}
<strong style="color: #10b981; font-size: 1.1rem;">
+{{ solicitacao.funcionario.saldo_banco_horas_minutos }} min
</strong>
{% else %}
<strong style="color: #ef4444; font-size: 1.1rem;">
{{ solicitacao.funcionario.saldo_banco_horas_minutos }} min
</strong>
{% endif %}
</span>
</div>
</div>
{% endif %}
</div>
</div>
{% endif %}
<!-- ABA: WINTHOR -->
{% for item_acao in solicitacoes_com_acao %}
{% if item_acao.solicitacao.id == solicitacao.id and item_acao.dados_winthor_organizados %}
{% with win=item_acao.dados_winthor_organizados %}
<div class="tab-pane" data-tab="winthor">
<div class="details-section section-highlight" style="border-left-color: #10b981;">
<div style="display: flex; align-items: center; gap: 8px; margin: -1rem 0 1.5rem 0; color: #059669; background: #d1fae5; padding: 8px 12px; border-radius: 6px; display: inline-flex;">
<span>💼</span>
<span style="font-size: 0.85rem; font-weight: 600;">Sistema Winthor</span>
</div>
{% if win.basicos %}
<h5>Dados Básicos</h5>
<div class="info-grid" style="margin-bottom: 1.5rem;">
{% if win.basicos.matricula %}
<div class="info-item">
<label>Matrícula</label>
<span><strong>{{ win.basicos.matricula }}</strong></span>
</div>
{% endif %}
{% if win.basicos.nome %}
<div class="info-item">
<label>Nome</label>
<span><strong>{{ win.basicos.nome }}</strong></span>
</div>
{% endif %}
{% if win.basicos.cpf %}
<div class="info-item">
<label>CPF</label>
<span>{{ win.basicos.cpf }}</span>
</div>
{% endif %}
</div>
{% endif %}
{% if win.admissao %}
<h5>Admissão e Situação</h5>
<div class="info-grid" style="margin-bottom: 1.5rem;">
{% if win.admissao.admissao %}
<div class="info-item">
<label>Data de Admissão</label>
<span>
{% if win.admissao.admissao|date:"d/m/Y" %}
{{ win.admissao.admissao|date:"d/m/Y" }}
{% else %}
{{ win.admissao.admissao }}
{% endif %}
</span>
</div>
{% endif %}
{% if win.admissao.situacao %}
<div class="info-item">
<label>Situação</label>
<span>{{ win.admissao.situacao }}</span>
</div>
{% endif %}
{% if win.admissao.dt_exclusao %}
<div class="info-item">
<label>Data de Exclusão</label>
<span>
{% if win.admissao.dt_exclusao|date:"d/m/Y" %}
{{ win.admissao.dt_exclusao|date:"d/m/Y" }}
{% else %}
{{ win.admissao.dt_exclusao }}
{% endif %}
</span>
</div>
{% endif %}
</div>
{% endif %}
{% if win.endereco %}
<h5>Endereço</h5>
<div class="info-grid" style="margin-bottom: 1.5rem;">
{% if win.endereco.endereco %}
<div class="info-item" style="grid-column: 1 / -1;">
<label>Endereço</label>
<span>{{ win.endereco.endereco }}</span>
</div>
{% endif %}
{% if win.endereco.bairro %}
<div class="info-item">
<label>Bairro</label>
<span>{{ win.endereco.bairro }}</span>
</div>
{% endif %}
{% if win.endereco.cidade %}
<div class="info-item">
<label>Cidade</label>
<span>{{ win.endereco.cidade }}</span>
</div>
{% endif %}
{% if win.endereco.estado %}
<div class="info-item">
<label>Estado</label>
<span>{{ win.endereco.estado }}</span>
</div>
{% endif %}
</div>
{% endif %}
</div>
</div>
{% endwith %}
{% endif %}
{% endfor %}
<!-- ABA: AUDITORIA -->
<div class="tab-pane" data-tab="auditoria">
<div class="details-section">
<h5>Status Atual</h5>
<div class="info-grid" style="margin-bottom: 1.5rem;">
<div class="info-item">
<label>Status</label>
<span class="status-badge status-{{ solicitacao.status }}">{{ solicitacao.get_status_display }}</span>
</div>
<div class="info-item">
<label>Última Modificação</label>
<span>{{ solicitacao.atualizado_em|date:"d/m/Y H:i" }}</span>
</div>
</div>
<h5>Histórico de Aprovações</h5>
{% if solicitacao.aprovacoes.all %}
<div class="timeline">
{% for aprovacao in solicitacao.aprovacoes.all %}
<div class="timeline-item">
<div class="timeline-header">
<span class="timeline-user">{{ aprovacao.usuario.nome }}</span>
<span class="timeline-action">
{% if aprovacao.decisao == 'APROVADO' %}
<span style="color: #10b981;">aprovou</span>
{% else %}
<span style="color: #ef4444;">reprovou</span>
{% endif %}
em {{ aprovacao.get_etapa_display }}
</span>
<span class="timeline-date">{{ aprovacao.decidido_em|date:"d/m/Y H:i" }}</span>
</div>
{% if aprovacao.justificativa %}
<div class="timeline-justificativa">"{{ aprovacao.justificativa }}"</div>
{% endif %}
</div>
{% endfor %}
</div>
{% else %}
<p style="color: #64748b; font-style: italic; background: #f8fafc; padding: 10px; border-radius: 6px;">
⏳ Nenhuma aprovação registrada ainda.
</p>
{% endif %}
<!-- Pareceres -->
{% if solicitacao.pareceres.all %}
<h5 style="margin-top: 1.5rem;">Pareceres Técnicos</h5>
<div style="margin-bottom: 1.5rem;">
{% for parecer in solicitacao.pareceres.all %}
<div style="background: white; padding: 16px; border-radius: 8px; margin-bottom: 12px; border: 1px solid #e2e8f0; box-shadow: 0 1px 2px rgba(0,0,0,0.05);">
<div style="display: flex; justify-content: space-between; margin-bottom: 8px;">
<strong style="color: #1e293b;">{{ parecer.get_etapa_display }}</strong>
<small style="color: #64748b;">{{ parecer.criado_em|date:"d/m/Y H:i" }}</small>
</div>
<small style="color: #64748b; display: block; margin-bottom: 8px;">Autor: {{ parecer.usuario.nome }}</small>
<p style="margin: 0; color: #334155; line-height: 1.5;">{{ parecer.texto }}</p>
{% if parecer.anexo %}
<div style="margin-top: 12px; padding-top: 12px; border-top: 1px dashed #e2e8f0;">
<a href="{{ parecer.anexo.url }}" target="_blank" style="color: #3b82f6; text-decoration: none; font-size: 0.9rem; font-weight: 500;">
📎 Visualizar Anexo ({{ parecer.anexo.name|slice:"20:" }})
</a>
</div>
{% endif %}
</div>
{% endfor %}
</div>
{% endif %}
<!-- Form de parecer -->
{% comment %} {% for item_acao in solicitacoes_com_acao %}
{% if item_acao.solicitacao.id == solicitacao.id %}
{% if item_acao.pode_dar_parecer %}
<div class="form-decisao" style="background: #f0f9ff; border: 2px solid #3b82f6;">
<h5 style="margin-top: 0; color: #1e40af; border: none;">📝 Registrar Parecer Técnico</h5>
<p style="color: #64748b; font-size: 0.9rem; margin-bottom: 1rem;">
Forneça sua análise técnica sobre a solicitação. Este parecer será avaliado pela Diretoria para a decisão final.
</p>
<form method="post" action="{% url 'solicitacoes:registrar_parecer' solicitacao.id %}" enctype="multipart/form-data">
{% csrf_token %}
<div class="form-group">
<label for="texto-{{ solicitacao.id }}">Análise: <span style="color: #ef4444;">*</span></label>
<textarea name="texto" id="texto-{{ solicitacao.id }}" rows="4" required placeholder="Descreva sua análise detalhada..."></textarea>
</div>
<div class="form-group">
<label for="anexo-{{ solicitacao.id }}">Anexo (opcional):</label>
<input type="file" name="anexo" id="anexo-{{ solicitacao.id }}" accept=".pdf,.doc,.docx,.xls,.xlsx,.jpg,.jpeg,.png">
<small style="color: #64748b; font-size: 0.85rem;">Formatos aceitos: PDF, Office, Imagens</small>
</div>
<button type="submit" class="btn-action btn-action-primary" style="margin-top: 10px; width: 100%; justify-content: center;">
Salvar Parecer
</button>
</form>
</div>
{% endif %}
{% endif %}
{% endfor %} {% endcomment %}
{% comment %} <!-- Form de decisão (igual ao de solicitacao_detalhe.html) -->
{% for item_acao in solicitacoes_com_acao %}
{% if item_acao.solicitacao.id == solicitacao.id %}
{% if item_acao.pode_aprovar and solicitacao.solicitante.id != usuario_sistema.id %}
<div class="action-card" style="background: #fffbeb; border: 2px solid #f59e0b;">
<h3 style="color: #92400e; margin-top: 0;">⚖️ Decisão Final - Diretoria</h3>
<p style="color: #b45309; font-size: 0.9rem; margin-bottom: 1.5rem;">
Esta é a etapa final. Por favor, revise os pareceres técnicos antes de decidir.
</p>
<form method="post" action="{% url 'solicitacoes:decidir_solicitacao' solicitacao.id %}" id="form-decisao-{{ solicitacao.id }}" class="form-decisao-dashboard">
{% csrf_token %}
<div style="margin-bottom: 15px;">
<label for="decisao-{{ solicitacao.id }}" style="display:block; margin-bottom:5px; font-weight:600; color:#334155;">Veredito <span style="color:red">*</span></label>
<select name="decisao" id="decisao-{{ solicitacao.id }}" class="form-control decisao-select" required>
<option value="">Selecione...</option>
<option value="APROVADO">✅ APROVAR SOLICITAÇÃO</option>
<option value="REPROVADO">❌ REPROVAR SOLICITAÇÃO</option>
</select>
</div>
<div style="margin-bottom: 20px;">
<label for="justificativa-{{ solicitacao.id }}" style="display:block; margin-bottom:5px; font-weight:600; color:#334155;">
Justificativa <span id="justificativa-required-{{ solicitacao.id }}" class="justificativa-required" style="color:red; display:none;">*</span>
</label>
<textarea name="justificativa" id="justificativa-{{ solicitacao.id }}" class="form-control justificativa-input" rows="4" placeholder="Obrigatória apenas se reprovado..."></textarea>
</div>
<button type="submit" class="btn btn-warning" style="width: 100%; justify-content: center; font-size: 1rem;">Confirmar Decisão</button>
</form>
</div>
{% endif %}
{% endif %}
{% endfor %}
{% endcomment %}
{% if solicitacao.solicitante.id == usuario_sistema.id and solicitacao.status != 'RASCUNHO' %}
<div style="background: #f1f5f9; border-radius: 8px; padding: 15px; margin-top: 1.5rem; color: #64748b; text-align: center; font-size: 0.9rem;">
👁️ Você é o solicitante. Acompanhe o status acima.
</div>
{% endif %}
</div>
</div>
</div>
</td>
</tr>
{% endwith %}
{% endfor %}
</tbody>
</table>
</div>
<div id="no-results" class="empty-state" style="display: none;">
<p>🔍 Nenhuma solicitação encontrada com este filtro.</p>
</div>
{% if solicitacoes.has_other_pages %}
<div class="pagination">
{% if solicitacoes.has_previous %}
<a href="?page=1">« Primeira</a>
<a href="?page={{ solicitacoes.previous_page_number }}"> Anterior</a>
{% endif %}
<span class="current">
{{ solicitacoes.number }} / {{ solicitacoes.paginator.num_pages }}
</span>
{% if solicitacoes.has_next %}
<a href="?page={{ solicitacoes.next_page_number }}">Próxima </a>
<a href="?page={{ solicitacoes.paginator.num_pages }}">Última »</a>
{% endif %}
</div>
{% endif %}
{% else %}
<div class="empty-state">
<p>🎉 Nenhuma solicitação encontrada.</p>
{% if usuario_sistema.perfil == 'GESTOR' %}
<p style="font-size: 0.9rem; margin-top: 10px;">Use o botão "Nova Solicitação" para começar.</p>
{% endif %}
</div>
{% endif %}
</div>
{% endblock %}
{% block scripts %}
<script>
function filtrarTabela(status, cardElement) {
// Remove active de todos os cards
document.querySelectorAll('.metric-card').forEach(c => c.classList.remove('active'));
// Adiciona active no card clicado
if (cardElement) cardElement.classList.add('active');
const rows = document.querySelectorAll('.request-row');
let visibleCount = 0;
rows.forEach(row => {
const categoria = row.getAttribute('data-status'); // pendente | aprovado | reprovado (aprovado/reprovado não são mais filtrados)
let show = false;
if (status === 'todos') {
show = true;
} else if (status === 'pendente') {
show = (categoria === 'pendente');
} else {
show = false; // Filtros de aprovado/reprovado removidos
}
if (show) {
row.style.display = '';
visibleCount++;
} else {
row.style.display = 'none';
// Fecha detalhes se estiver aberto
const solicitacaoId = row.getAttribute('data-id');
const detailsRow = document.getElementById('details-' + solicitacaoId);
if (detailsRow && detailsRow.style.display !== 'none') {
detailsRow.style.display = 'none';
row.classList.remove('expanded');
}
}
});
// Mostra mensagem se não houver resultados
const noResults = document.getElementById('no-results');
const tableContainer = document.querySelector('.table-responsive');
if (noResults && tableContainer) {
if (visibleCount === 0) {
noResults.style.display = 'block';
tableContainer.style.display = 'none';
} else {
noResults.style.display = 'none';
tableContainer.style.display = 'block';
}
}
}
function toggleDetails(solicitacaoId, rowElement) {
const detailsRow = document.getElementById('details-' + solicitacaoId);
if (!detailsRow) return;
if (detailsRow.style.display === 'none' || detailsRow.style.display === '') {
// Fecha todas as outras linhas
document.querySelectorAll('.details-row').forEach(row => {
row.style.display = 'none';
});
document.querySelectorAll('.request-row').forEach(row => {
row.classList.remove('expanded');
});
// Abre esta linha
detailsRow.style.display = 'table-row';
if(rowElement) rowElement.classList.add('expanded');
} else {
// Fecha esta linha
detailsRow.style.display = 'none';
if(rowElement) rowElement.classList.remove('expanded');
}
}
function switchTab(event, tabName, solicitacaoId) {
event.preventDefault();
event.stopPropagation();
const wrapper = document.getElementById('wrapper-' + solicitacaoId);
if (!wrapper) return;
const buttons = wrapper.querySelectorAll('.tab-btn');
buttons.forEach(btn => btn.classList.remove('active'));
const panes = wrapper.querySelectorAll('.tab-pane');
panes.forEach(pane => pane.classList.remove('active'));
event.currentTarget.classList.add('active');
const targetPane = wrapper.querySelector(`.tab-pane[data-tab="${tabName}"]`);
if (targetPane) {
targetPane.classList.add('active');
}
}
document.addEventListener('DOMContentLoaded', function() {
const cardTotal = document.querySelector('.card-total');
if (cardTotal) {
filtrarTabela('todos', cardTotal);
}
});
function abrirModalAprovacao(solicitacaoId, decisao) {
const modal = document.getElementById('modal-aprovacao');
const form = document.getElementById('form-aprovacao');
const decisaoInput = document.getElementById('decisao-input');
const justificativaDiv = document.getElementById('justificativa-div');
const justificativaInput = document.getElementById('justificativa-input');
const tituloModal = document.getElementById('titulo-modal');
decisaoInput.value = decisao;
justificativaInput.value = '';
justificativaInput.removeAttribute('required');
if (decisao === 'REPROVADO') {
tituloModal.textContent = 'Reprovar Solicitação';
tituloModal.style.color = '#ef4444';
justificativaDiv.style.display = 'block';
justificativaInput.setAttribute('required', 'required');
justificativaInput.placeholder = 'Descreva o motivo da reprovação...';
} else {
tituloModal.textContent = 'Aprovar Solicitação';
tituloModal.style.color = '#10b981';
justificativaDiv.style.display = 'none';
justificativaInput.removeAttribute('required');
}
form.action = '/solicitacao/' + solicitacaoId + '/decidir/';
modal.style.display = 'flex';
}
function fecharModalAprovacao() {
const modal = document.getElementById('modal-aprovacao');
modal.style.display = 'none';
}
window.onclick = function(event) {
const modal = document.getElementById('modal-aprovacao');
if (event.target === modal) {
fecharModalAprovacao();
}
}
// Decisão Final (form igual ao de solicitacao_detalhe): toggle justificativa e validação
document.querySelectorAll('.decisao-select').forEach(function(select) {
select.addEventListener('change', function() {
var form = this.closest('form');
var justificativa = form.querySelector('.justificativa-input');
var requiredMarker = form.querySelector('.justificativa-required');
if (this.value === 'REPROVADO') {
justificativa.setAttribute('required', 'required');
if (requiredMarker) requiredMarker.style.display = 'inline';
justificativa.placeholder = 'Descreva o motivo da reprovação...';
} else {
justificativa.removeAttribute('required');
if (requiredMarker) requiredMarker.style.display = 'none';
justificativa.placeholder = 'Opcional para aprovação...';
}
});
});
document.querySelectorAll('.form-decisao-dashboard').forEach(function(form) {
form.addEventListener('submit', function(e) {
var decisao = form.querySelector('.decisao-select').value;
var justificativa = form.querySelector('.justificativa-input').value.trim();
if (decisao === 'REPROVADO' && !justificativa) {
e.preventDefault();
alert('A justificativa é obrigatória para reprovações.');
form.querySelector('.justificativa-input').focus();
}
});
});
</script>
<!-- Modal de Aprovação/Reprovação -->
<div id="modal-aprovacao" style="display: none; position: fixed; z-index: 1000; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); align-items: center; justify-content: center;">
<div class="modal-content">
<h3 id="titulo-modal" style="margin-top: 0; margin-bottom: 20px; font-size: 1.25rem;">Aprovar Solicitação</h3>
<form id="form-aprovacao" method="post" action="">
{% csrf_token %}
<input type="hidden" id="decisao-input" name="decisao" value="">
<div id="justificativa-div" style="display: none; margin-bottom: 20px;">
<label for="justificativa-input" style="display: block; margin-bottom: 8px; font-weight: 600; color: #334155;">
Justificativa Obrigatória <span style="color: #ef4444;">*</span>
</label>
<textarea id="justificativa-input" name="justificativa" rows="4"
style="width: 100%; padding: 10px; border: 1px solid #cbd5e1; border-radius: 6px; font-family: inherit; font-size: 0.95rem;"
placeholder="Descreva o motivo..."></textarea>
</div>
<div style="display: flex; gap: 10px; justify-content: flex-end; margin-top: 24px;">
<button type="button" onclick="fecharModalAprovacao()"
style="padding: 10px 20px; background: white; color: #64748b; border: 1px solid #cbd5e1; border-radius: 6px; cursor: pointer; font-weight: 600;">
Cancelar
</button>
<button type="submit"
style="padding: 10px 24px; background: #2563eb; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: 600; box-shadow: 0 1px 2px rgba(0,0,0,0.1);">
Confirmar
</button>
</div>
</form>
</div>
</div>
{% endblock %}