455 lines
46 KiB
HTML
455 lines
46 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}Dashboard - SGMP{% endblock %}
|
||
|
||
{% block css %}
|
||
<style>
|
||
.tab-pane { display: none; }
|
||
.tab-pane.active { display: block; }
|
||
.tab-btn.active { color: #2563eb; border-bottom-color: #2563eb; font-weight: 600; }
|
||
.details-row td { padding: 0 !important; border-bottom: none; }
|
||
.request-row.expanded .chevron-icon { transform: rotate(180deg); }
|
||
.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); }
|
||
.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; }
|
||
</style>
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
|
||
<div class="dashboard-header mb-8 flex flex-col gap-2">
|
||
<h1 class="text-2xl md:text-3xl font-bold text-slate-800 m-0 tracking-tight">SGMP - Movimentação de Pessoas</h1>
|
||
<p class="text-slate-500 text-sm md:text-base leading-relaxed m-0">
|
||
Olá, <strong>{{ usuario_sistema.nome }}</strong>!
|
||
<span class="inline-block bg-slate-200 py-0.5 px-2 rounded text-xs ml-2 text-slate-600">{{ usuario_sistema.get_perfil_display }}</span>
|
||
<br>
|
||
<small class="text-slate-400">Matrícula: {{ usuario_sistema.matricula }}</small>
|
||
</p>
|
||
</div>
|
||
|
||
{% if messages %}
|
||
<ul class="list-none p-0 m-0 mb-5">
|
||
{% for message in messages %}
|
||
<li class="py-3.5 px-4 mb-3 rounded-lg border text-sm flex items-center
|
||
{% if message.tags == 'error' %}bg-red-50 text-red-800 border-red-200
|
||
{% elif message.tags == 'success' %}bg-green-50 text-green-800 border-green-200
|
||
{% else %}bg-blue-50 text-blue-800 border-blue-200{% endif %}">
|
||
{% if message.tags == 'error' %}⚠️{% elif message.tags == 'success' %}✅{% else %}ℹ️{% endif %}
|
||
{{ message }}
|
||
</li>
|
||
{% endfor %}
|
||
</ul>
|
||
{% endif %}
|
||
|
||
{% if usuario_sistema.perfil == 'GESTOR' %}
|
||
<div class="mb-8">
|
||
<div class="bg-amber-50 border border-amber-300 rounded-xl p-4 mb-6 flex gap-3 items-start">
|
||
<span class="text-2xl">💡</span>
|
||
<div>
|
||
<h4 class="m-0 mb-1 text-amber-800 font-semibold">Lembrete Rápido</h4>
|
||
<p class="m-0 text-amber-700 text-sm">
|
||
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 %}
|
||
|
||
<div class="metrics-grid grid grid-cols-2 md:grid-cols-[repeat(auto-fit,minmax(220px,1fr))] gap-4 md:gap-6 mb-10">
|
||
<div class="metric-card bg-white p-6 rounded-xl border border-slate-200 shadow-sm border-l-4 border-l-blue-500 cursor-pointer transition-all hover:-translate-y-1 hover:shadow-md active" onclick="filtrarTabela('todos', this)">
|
||
<div class="metric-label text-xs uppercase tracking-wider text-slate-500 font-bold mb-2">Total</div>
|
||
<div class="metric-value text-3xl font-extrabold leading-none text-blue-500">{{ total }}</div>
|
||
</div>
|
||
<div class="metric-card bg-white p-6 rounded-xl border border-slate-200 shadow-sm border-l-4 border-l-amber-500 cursor-pointer transition-all hover:-translate-y-1 hover:shadow-md" onclick="filtrarTabela('pendente', this)">
|
||
<div class="metric-label text-xs uppercase tracking-wider text-slate-500 font-bold mb-2">Pendentes</div>
|
||
<div class="metric-value text-3xl font-extrabold leading-none text-amber-600">{{ pendentes }}</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section mb-8">
|
||
<h3 class="text-slate-800 text-xl font-semibold mb-4 flex items-center gap-2">
|
||
{% if usuario_sistema.perfil == 'GESTOR' %}📋 Minhas Solicitações{% else %}⏳ Pendentes de Aprovação{% endif %}
|
||
</h3>
|
||
|
||
{% if usuario_sistema.perfil != 'GESTOR' %}
|
||
<div class="bg-blue-50 border border-blue-200 rounded-lg py-3 px-4 mb-5 text-blue-800 text-sm flex items-center gap-2">
|
||
ℹ️ Você está vendo solicitações com status <strong>Enviada</strong> aguardando sua análise.
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if solicitacoes %}
|
||
<div class="table-responsive w-full overflow-x-auto bg-white rounded-xl shadow border border-slate-200">
|
||
<table id="tabela-solicitacoes" class="w-full border-collapse whitespace-nowrap">
|
||
<thead>
|
||
<tr>
|
||
<th class="p-4 text-left border-b border-slate-200 bg-slate-50 font-semibold text-slate-600 text-xs uppercase tracking-wide">Tipo</th>
|
||
<th class="p-4 text-left border-b border-slate-200 bg-slate-50 font-semibold text-slate-600 text-xs uppercase tracking-wide">Colaborador</th>
|
||
<th class="p-4 text-left border-b border-slate-200 bg-slate-50 font-semibold text-slate-600 text-xs uppercase tracking-wide">Status</th>
|
||
<th class="p-4 text-left border-b border-slate-200 bg-slate-50 font-semibold text-slate-600 text-xs uppercase tracking-wide">Data</th>
|
||
<th class="p-4 text-left border-b border-slate-200 bg-slate-50 font-semibold text-slate-600 text-xs uppercase tracking-wide">Ações</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for item in solicitacoes_com_acao %}
|
||
{% with solicitacao=item.solicitacao %}
|
||
<tr class="request-row border-b border-slate-200 hover:bg-slate-50 cursor-pointer"
|
||
data-id="{{ solicitacao.id }}"
|
||
data-status="{% if solicitacao.status == 'FINALIZADA' %}aprovado{% elif solicitacao.status == 'REPROVADA' %}reprovado{% else %}pendente{% endif %}"
|
||
onclick="toggleDetails('{{ solicitacao.id }}', this)">
|
||
<td class="p-4"><strong>{{ solicitacao.get_tipo_display }}</strong></td>
|
||
<td class="p-4">
|
||
{% if solicitacao.funcionario %}{{ solicitacao.funcionario.nome }}{% else %}<span class="text-slate-400">N/A</span>{% endif %}
|
||
</td>
|
||
<td class="p-4">
|
||
<span class="status-badge inline-flex items-center py-1 px-2.5 rounded-full text-xs font-bold status-{{ solicitacao.status }}">{{ solicitacao.get_status_display }}</span>
|
||
</td>
|
||
<td class="p-4 text-slate-500">{{ solicitacao.criado_em|date:"d/m/Y H:i" }}</td>
|
||
<td class="p-4">
|
||
<div class="flex gap-3 items-center">
|
||
<span class="expand-btn text-blue-600 font-semibold text-sm">Detalhes <span class="chevron-icon inline-block text-xs transition-transform">▼</span></span>
|
||
{% if item.pode_dar_parecer %}
|
||
<a href="{% url 'solicitacoes:solicitacao_detalhe' solicitacao.id %}" class="btn-action btn-action-primary inline-flex items-center gap-1.5 py-2.5 px-4 rounded-md text-sm font-semibold bg-primary text-white border border-primary-hover hover:bg-primary-hover no-underline" title="Fornecer parecer técnico">📝 Parecer</a>
|
||
{% endif %}
|
||
{% if item.pode_aprovar %}
|
||
<div class="flex gap-1">
|
||
<button type="button" class="btn-action btn-action-success py-1 px-2 text-sm bg-emerald-500 text-white border border-emerald-600 rounded hover:bg-emerald-600" onclick="event.stopPropagation(); abrirModalAprovacao('{{ solicitacao.id }}', 'APROVADO')" title="Aprovar">✅</button>
|
||
<button type="button" class="btn-action btn-action-danger py-1 px-2 text-sm bg-white text-red-500 border border-red-500 rounded hover:bg-red-50" onclick="event.stopPropagation(); abrirModalAprovacao('{{ solicitacao.id }}', 'REPROVADO')" title="Reprovar">❌</button>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
<tr id="details-{{ solicitacao.id }}" class="details-row bg-slate-50" style="display:none;">
|
||
<td colspan="5">
|
||
<div class="details-wrapper p-8" id="wrapper-{{ solicitacao.id }}">
|
||
<div class="tab-nav flex border-b border-slate-200 mb-6 gap-8 overflow-x-auto">
|
||
<button type="button" class="tab-btn active pb-3 border-b-2 border-transparent text-slate-500 text-sm font-medium whitespace-nowrap hover:text-primary border-b-primary text-primary font-semibold" onclick="switchTab(event, 'solicitacao', '{{ solicitacao.id }}')">📄 Solicitação</button>
|
||
{% if solicitacao.funcionario %}
|
||
<button type="button" class="tab-btn pb-3 border-b-2 border-transparent text-slate-500 text-sm font-medium whitespace-nowrap hover:text-primary" 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 type="button" class="tab-btn pb-3 border-b-2 border-transparent text-slate-500 text-sm font-medium whitespace-nowrap hover:text-primary" onclick="switchTab(event, 'winthor', '{{ solicitacao.id }}')">💼 Winthor</button>
|
||
{% endif %}
|
||
{% endfor %}
|
||
<button type="button" class="tab-btn pb-3 border-b-2 border-transparent text-slate-500 text-sm font-medium whitespace-nowrap hover:text-primary" onclick="switchTab(event, 'auditoria', '{{ solicitacao.id }}')">📝 Histórico & Auditoria</button>
|
||
</div>
|
||
|
||
<div class="tab-pane active" data-tab="solicitacao">
|
||
<div class="details-section mb-6 bg-white p-6 rounded-xl border border-slate-200">
|
||
<h5 class="text-slate-400 text-xs uppercase mt-6 mb-4 font-bold tracking-wider border-b border-slate-100 pb-2 first:mt-0">Dados Gerais</h5>
|
||
<div class="info-grid grid grid-cols-1 md:grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-6 mb-6">
|
||
<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Solicitante</label><span class="text-slate-900 font-medium">{{ solicitacao.solicitante.nome }} <small class="text-slate-500">({{ solicitacao.solicitante.matricula }})</small></span></div>
|
||
<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Tipo</label><span class="font-bold text-primary">{{ solicitacao.get_tipo_display }}</span></div>
|
||
<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Data Criação</label><span class="text-slate-900 font-medium">{{ solicitacao.criado_em|date:"d/m/Y H:i" }}</span></div>
|
||
{% if solicitacao.enviada_em %}<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Enviada em</label><span class="text-slate-900 font-medium">{{ solicitacao.enviada_em|date:"d/m/Y H:i" }}</span></div>{% endif %}
|
||
{% if solicitacao.finalizada_em %}<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Finalizada em</label><span class="text-slate-900 font-medium">{{ solicitacao.finalizada_em|date:"d/m/Y H:i" }}</span></div>{% endif %}
|
||
</div>
|
||
|
||
{% if solicitacao.tipo == 'DESLIGAMENTO' and solicitacao.desligamento %}
|
||
<h5 class="text-slate-400 text-xs uppercase mt-6 mb-4 font-bold tracking-wider border-b border-slate-100 pb-2">Detalhes do Desligamento</h5>
|
||
<div class="info-grid grid grid-cols-1 md:grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-6 mb-6">
|
||
{% if solicitacao.desligamento.tipo_desligamento %}
|
||
<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">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 flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">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 flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Data Prevista de Saída</label><span>{{ solicitacao.desligamento.data_prevista_desligamento|date:"d/m/Y"|default:"—" }}</span></div>
|
||
<div class="info-item flex flex-col md:col-span-full"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Detalhamento / Justificativa</label><span class="whitespace-pre-wrap">{{ solicitacao.desligamento.motivo|default:"—" }}</span></div>
|
||
{% if solicitacao.desligamento.arquivo_pedido %}<div class="info-item flex flex-col md:col-span-full"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Carta de Pedido</label><span><a href="{{ solicitacao.desligamento.arquivo_pedido.url }}" target="_blank" class="text-primary no-underline">📎 Ver arquivo</a></span></div>{% endif %}
|
||
{% if solicitacao.desligamento.observacoes %}<div class="info-item flex flex-col md:col-span-full"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Observações Adicionais</label><span class="whitespace-pre-wrap">{{ solicitacao.desligamento.observacoes }}</span></div>{% endif %}
|
||
</div>
|
||
{% elif solicitacao.tipo == 'MOVIMENTACAO' and solicitacao.movimentacao %}
|
||
<h5 class="text-slate-400 text-xs uppercase mt-6 mb-4 font-bold tracking-wider border-b border-slate-100 pb-2">Detalhes da Movimentação</h5>
|
||
<div class="info-grid grid grid-cols-1 md:grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-6 mb-6">
|
||
<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">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 flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Nova Função</label><span>{{ solicitacao.movimentacao.novo_cod_funcao|default:"—" }}</span></div>{% endif %}
|
||
{% if solicitacao.movimentacao.altera_centro_custo %}<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Nova Seção</label><span>{{ solicitacao.movimentacao.novo_cod_secao|default:"—" }}</span></div>{% endif %}
|
||
{% if solicitacao.movimentacao.novo_salario %}<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Novo Salário</label><span>R$ {{ solicitacao.movimentacao.novo_salario }}</span></div>{% endif %}
|
||
<div class="info-item flex flex-col md:col-span-full"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Justificativa</label><span>{{ solicitacao.movimentacao.justificativa|default:"—" }}</span></div>
|
||
</div>
|
||
{% elif solicitacao.tipo == 'ADM_SUBSTITUICAO' and solicitacao.admissao_substituicao %}
|
||
<h5 class="text-slate-400 text-xs uppercase mt-6 mb-4 font-bold tracking-wider border-b border-slate-100 pb-2">Detalhes da Admissão por Substituição</h5>
|
||
<div class="info-grid grid grid-cols-1 md:grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-6 mb-6">
|
||
<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Data Prevista</label><span>{{ solicitacao.admissao_substituicao.data_previsao_contratacao|date:"d/m/Y"|default:"—" }}</span></div>
|
||
<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Coligada Destino</label><span>{{ solicitacao.admissao_substituicao.cod_coligada_destino|default:"—" }}</span></div>
|
||
<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Filial Destino</label><span>{{ solicitacao.admissao_substituicao.cod_filial_destino|default:"—" }}</span></div>
|
||
<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Seção Destino</label><span>{{ solicitacao.admissao_substituicao.cod_secao_destino|default:"—" }}</span></div>
|
||
<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Função Destino</label><span>{{ solicitacao.admissao_substituicao.cod_funcao_destino|default:"—" }}</span></div>
|
||
<div class="info-item flex flex-col md:col-span-full"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Justificativa</label><span>{{ solicitacao.admissao_substituicao.justificativa|default:"—" }}</span></div>
|
||
</div>
|
||
{% elif solicitacao.tipo == 'ADM_AUMENTO' and solicitacao.admissao_aumento %}
|
||
<h5 class="text-slate-400 text-xs uppercase mt-6 mb-4 font-bold tracking-wider border-b border-slate-100 pb-2">Detalhes da Admissão por Aumento de Quadro</h5>
|
||
<div class="info-grid grid grid-cols-1 md:grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-6 mb-6">
|
||
<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Data Prevista</label><span>{{ solicitacao.admissao_aumento.data_previsao_contratacao|date:"d/m/Y"|default:"—" }}</span></div>
|
||
<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Coligada Destino</label><span>{{ solicitacao.admissao_aumento.cod_coligada_destino|default:"—" }}</span></div>
|
||
<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Filial Destino</label><span>{{ solicitacao.admissao_aumento.cod_filial_destino|default:"—" }}</span></div>
|
||
<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Seção Destino</label><span>{{ solicitacao.admissao_aumento.cod_secao_destino|default:"—" }}</span></div>
|
||
<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Função Destino</label><span>{{ solicitacao.admissao_aumento.cod_funcao_destino|default:"—" }}</span></div>
|
||
<div class="info-item flex flex-col md:col-span-full"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Justificativa Estratégica</label><span>{{ solicitacao.admissao_aumento.justificativa_estrategica|default:"—" }}</span></div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<div class="mt-8 pt-6 border-t border-slate-200">
|
||
{% 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 inline-flex items-center gap-1.5 py-2.5 px-4 rounded-md text-sm font-semibold bg-primary text-white border border-primary-hover hover:bg-primary-hover no-underline">🚀 Enviar para Aprovação</a>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{% if solicitacao.funcionario %}
|
||
<div class="tab-pane" data-tab="rm">
|
||
<div class="details-section mb-6 bg-white p-6 rounded-xl border-2 border-primary">
|
||
<div class="inline-flex items-center gap-2 -mt-4 mb-6 text-primary bg-blue-100 py-2 px-3 rounded-md text-sm font-semibold">💾 Snapshot TOTVS RM</div>
|
||
<h5 class="text-slate-400 text-xs uppercase mt-6 mb-4 font-bold tracking-wider border-b border-slate-100 pb-2 first:mt-0">Dados Básicos</h5>
|
||
<div class="info-grid grid grid-cols-1 md:grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-6 mb-6">
|
||
<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Matrícula</label><span><strong>{{ solicitacao.funcionario.matricula }}</strong></span></div>
|
||
<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Nome Completo</label><span><strong>{{ solicitacao.funcionario.nome }}</strong></span></div>
|
||
{% if solicitacao.funcionario.cpf %}<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">CPF</label><span>{{ solicitacao.funcionario.cpf }}</span></div>{% endif %}
|
||
{% if solicitacao.funcionario.data_admissao %}<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">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 flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Situação</label><span>{{ solicitacao.funcionario.situacao }}</span></div>{% endif %}
|
||
</div>
|
||
<h5 class="text-slate-400 text-xs uppercase mt-6 mb-4 font-bold tracking-wider border-b border-slate-100 pb-2">Dados Profissionais</h5>
|
||
<div class="info-grid grid grid-cols-1 md:grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-6 mb-6">
|
||
<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Cargo/Função</label><span>{{ solicitacao.funcionario.cargo }}</span></div>
|
||
<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Código da Função</label><span>{{ solicitacao.funcionario.cod_funcao|default:"N/A" }}</span></div>
|
||
<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Setor/Seção</label><span>{{ solicitacao.funcionario.setor }}</span></div>
|
||
<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Centro de Custo</label><span>{{ solicitacao.funcionario.centro_custo }}</span></div>
|
||
{% if solicitacao.funcionario.salario %}<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Salário Atual</label><span><strong class="text-emerald-600 text-lg">R$ {{ solicitacao.funcionario.salario|floatformat:2 }}</strong></span></div>{% endif %}
|
||
</div>
|
||
{% if solicitacao.funcionario.saldo_banco_horas_minutos is not None %}
|
||
<h5 class="text-slate-400 text-xs uppercase mt-6 mb-4 font-bold tracking-wider border-b border-slate-100 pb-2">Banco de Horas</h5>
|
||
<div class="info-grid grid grid-cols-1 md:grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-6 mb-6">
|
||
<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Saldo Atual</label><span>{% if solicitacao.funcionario.saldo_banco_horas_minutos >= 0 %}<strong class="text-emerald-600 text-lg">+{{ solicitacao.funcionario.saldo_banco_horas_minutos }} min</strong>{% else %}<strong class="text-red-500 text-lg">{{ solicitacao.funcionario.saldo_banco_horas_minutos }} min</strong>{% endif %}</span></div>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% 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 mb-6 bg-white p-6 rounded-xl border-2 border-emerald-500">
|
||
<div class="inline-flex items-center gap-2 -mt-4 mb-6 text-emerald-700 bg-emerald-100 py-2 px-3 rounded-md text-sm font-semibold">💼 Sistema Winthor</div>
|
||
{% if win.basicos %}
|
||
<h5 class="text-slate-400 text-xs uppercase mt-6 mb-4 font-bold tracking-wider border-b border-slate-100 pb-2 first:mt-0">Dados Básicos</h5>
|
||
<div class="info-grid grid grid-cols-1 md:grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-6 mb-6">
|
||
{% if win.basicos.matricula %}<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Matrícula</label><span><strong>{{ win.basicos.matricula }}</strong></span></div>{% endif %}
|
||
{% if win.basicos.nome %}<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Nome</label><span><strong>{{ win.basicos.nome }}</strong></span></div>{% endif %}
|
||
{% if win.basicos.cpf %}<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">CPF</label><span>{{ win.basicos.cpf }}</span></div>{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
{% if win.admissao %}
|
||
<h5 class="text-slate-400 text-xs uppercase mt-6 mb-4 font-bold tracking-wider border-b border-slate-100 pb-2">Admissão e Situação</h5>
|
||
<div class="info-grid grid grid-cols-1 md:grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-6 mb-6">
|
||
{% if win.admissao.admissao %}<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">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 flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Situação</label><span>{{ win.admissao.situacao }}</span></div>{% endif %}
|
||
{% if win.admissao.dt_exclusao %}<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">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 class="text-slate-400 text-xs uppercase mt-6 mb-4 font-bold tracking-wider border-b border-slate-100 pb-2">Endereço</h5>
|
||
<div class="info-grid grid grid-cols-1 md:grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-6 mb-6">
|
||
{% if win.endereco.endereco %}<div class="info-item flex flex-col md:col-span-full"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Endereço</label><span>{{ win.endereco.endereco }}</span></div>{% endif %}
|
||
{% if win.endereco.bairro %}<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Bairro</label><span>{{ win.endereco.bairro }}</span></div>{% endif %}
|
||
{% if win.endereco.cidade %}<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Cidade</label><span>{{ win.endereco.cidade }}</span></div>{% endif %}
|
||
{% if win.endereco.estado %}<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Estado</label><span>{{ win.endereco.estado }}</span></div>{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
{% endwith %}
|
||
{% endif %}
|
||
{% endfor %}
|
||
|
||
<div class="tab-pane" data-tab="auditoria">
|
||
<div class="details-section mb-6 bg-white p-6 rounded-xl border border-slate-200">
|
||
<h5 class="text-slate-400 text-xs uppercase mt-6 mb-4 font-bold tracking-wider border-b border-slate-100 pb-2 first:mt-0">Status Atual</h5>
|
||
<div class="info-grid grid grid-cols-1 md:grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-6 mb-6">
|
||
<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Status</label><span class="status-badge status-{{ solicitacao.status }}">{{ solicitacao.get_status_display }}</span></div>
|
||
<div class="info-item flex flex-col"><label class="text-xs text-slate-500 mb-1 font-semibold uppercase">Última Modificação</label><span>{{ solicitacao.atualizado_em|date:"d/m/Y H:i" }}</span></div>
|
||
</div>
|
||
<h5 class="text-slate-400 text-xs uppercase mt-6 mb-4 font-bold tracking-wider border-b border-slate-100 pb-2">Histórico de Aprovações</h5>
|
||
{% if solicitacao.aprovacoes.all %}
|
||
<div class="timeline pl-8 border-l-2 border-slate-200 ml-2">
|
||
{% for aprovacao in solicitacao.aprovacoes.all %}
|
||
<div class="timeline-item relative pb-8 pl-6 last:pb-0 before:content-[''] before:absolute before:-left-[2.4rem] before:top-1 before:w-3.5 before:h-3.5 before:rounded-full before:bg-slate-300 before:border-[3px] before:border-white before:shadow-[0_0_0_1px_#e2e8f0]">
|
||
<div class="timeline-header flex flex-wrap gap-2 mb-1 text-sm items-baseline">
|
||
<span class="timeline-user font-semibold text-slate-800">{{ aprovacao.usuario.nome }}</span>
|
||
<span class="timeline-action text-slate-500">{% if aprovacao.decisao == 'APROVADO' %}<span class="text-emerald-500">aprovou</span>{% else %}<span class="text-red-500">reprovou</span>{% endif %} em {{ aprovacao.get_etapa_display }}</span>
|
||
<span class="timeline-date text-xs text-slate-400 ml-auto">{{ aprovacao.decidido_em|date:"d/m/Y H:i" }}</span>
|
||
</div>
|
||
{% if aprovacao.justificativa %}<div class="timeline-justificativa mt-3 p-4 bg-slate-50 rounded-lg text-sm text-slate-700 border border-slate-200 italic">"{{ aprovacao.justificativa }}"</div>{% endif %}
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
{% else %}
|
||
<p class="text-slate-500 italic bg-slate-50 p-2.5 rounded-md">⏳ Nenhuma aprovação registrada ainda.</p>
|
||
{% endif %}
|
||
{% if solicitacao.pareceres.all %}
|
||
<h5 class="text-slate-400 text-xs uppercase mt-6 mb-4 font-bold tracking-wider border-b border-slate-100 pb-2">Pareceres Técnicos</h5>
|
||
<div class="mb-6">
|
||
{% for parecer in solicitacao.pareceres.all %}
|
||
<div class="bg-white p-4 rounded-lg mb-3 border border-slate-200 shadow-sm">
|
||
<div class="flex justify-between mb-2"><strong class="text-slate-800">{{ parecer.get_etapa_display }}</strong><small class="text-slate-500">{{ parecer.criado_em|date:"d/m/Y H:i" }}</small></div>
|
||
<small class="text-slate-500 block mb-2">Autor: {{ parecer.usuario.nome }}</small>
|
||
<p class="m-0 text-slate-700 leading-relaxed">{{ parecer.texto }}</p>
|
||
{% if parecer.anexo %}<div class="mt-3 pt-3 border-t border-dashed border-slate-200"><a href="{{ parecer.anexo.url }}" target="_blank" class="text-primary no-underline text-sm font-medium">📎 Visualizar Anexo ({{ parecer.anexo.name|slice:"20:" }})</a></div>{% endif %}
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
{% endif %}
|
||
{% if solicitacao.solicitante.id == usuario_sistema.id and solicitacao.status != 'RASCUNHO' %}
|
||
<div class="bg-slate-100 rounded-lg py-4 px-4 mt-6 text-slate-500 text-center text-sm">👁️ 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 text-center py-16 px-5 text-slate-500 bg-white rounded-xl border-2 border-dashed border-slate-300 hidden">
|
||
<p class="m-0 text-lg">🔍 Nenhuma solicitação encontrada com este filtro.</p>
|
||
</div>
|
||
|
||
{% if solicitacoes.has_other_pages %}
|
||
<div class="pagination mt-6 flex items-center justify-center gap-1.5 flex-wrap">
|
||
{% if solicitacoes.has_previous %}<a href="?page=1" class="py-2 px-3.5 no-underline border border-slate-200 rounded-md text-slate-600 bg-white text-sm transition-all hover:bg-slate-50 hover:border-slate-300 hover:text-slate-800">« Primeira</a><a href="?page={{ solicitacoes.previous_page_number }}" class="py-2 px-3.5 no-underline border border-slate-200 rounded-md text-slate-600 bg-white text-sm transition-all hover:bg-slate-50 hover:border-slate-300 hover:text-slate-800">‹ Anterior</a>{% endif %}
|
||
<span class="current py-2 px-3.5 bg-primary text-white rounded-md font-medium border border-primary">{{ solicitacoes.number }} / {{ solicitacoes.paginator.num_pages }}</span>
|
||
{% if solicitacoes.has_next %}<a href="?page={{ solicitacoes.next_page_number }}" class="py-2 px-3.5 no-underline border border-slate-200 rounded-md text-slate-600 bg-white text-sm transition-all hover:bg-slate-50 hover:border-slate-300 hover:text-slate-800">Próxima ›</a><a href="?page={{ solicitacoes.paginator.num_pages }}" class="py-2 px-3.5 no-underline border border-slate-200 rounded-md text-slate-600 bg-white text-sm transition-all hover:bg-slate-50 hover:border-slate-300 hover:text-slate-800">Última »</a>{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
{% else %}
|
||
<div class="empty-state text-center py-16 px-5 text-slate-500 bg-white rounded-xl border-2 border-dashed border-slate-300">
|
||
<p class="m-0 text-lg">🎉 Nenhuma solicitação encontrada.</p>
|
||
{% if usuario_sistema.perfil == 'GESTOR' %}<p class="text-sm mt-2">Use o botão "Nova Solicitação" para começar.</p>{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
{% endblock %}
|
||
|
||
{% block scripts %}
|
||
<script>
|
||
function filtrarTabela(status, cardElement) {
|
||
document.querySelectorAll('.metric-card').forEach(c => c.classList.remove('active'));
|
||
if (cardElement) cardElement.classList.add('active');
|
||
const rows = document.querySelectorAll('.request-row');
|
||
let visibleCount = 0;
|
||
rows.forEach(row => {
|
||
const categoria = row.getAttribute('data-status');
|
||
let show = (status === 'todos') || (status === 'pendente' && categoria === 'pendente');
|
||
if (show) { row.style.display = ''; visibleCount++; } else {
|
||
row.style.display = 'none';
|
||
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'); }
|
||
}
|
||
});
|
||
const noResults = document.getElementById('no-results');
|
||
const tableContainer = document.querySelector('.table-responsive');
|
||
if (noResults && tableContainer) { noResults.style.display = visibleCount === 0 ? 'block' : 'none'; tableContainer.style.display = visibleCount === 0 ? 'none' : 'block'; }
|
||
}
|
||
function toggleDetails(solicitacaoId, rowElement) {
|
||
const detailsRow = document.getElementById('details-' + solicitacaoId);
|
||
if (!detailsRow) return;
|
||
if (detailsRow.style.display === 'none' || detailsRow.style.display === '') {
|
||
document.querySelectorAll('.details-row').forEach(row => { row.style.display = 'none'; });
|
||
document.querySelectorAll('.request-row').forEach(row => { row.classList.remove('expanded'); });
|
||
detailsRow.style.display = 'table-row';
|
||
if (rowElement) rowElement.classList.add('expanded');
|
||
} else {
|
||
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;
|
||
wrapper.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
|
||
wrapper.querySelectorAll('.tab-pane').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';
|
||
}
|
||
form.action = '/solicitacao/' + solicitacaoId + '/decidir/';
|
||
modal.style.display = 'flex';
|
||
}
|
||
function fecharModalAprovacao() { document.getElementById('modal-aprovacao').style.display = 'none'; }
|
||
window.onclick = function(event) { if (event.target === document.getElementById('modal-aprovacao')) fecharModalAprovacao(); };
|
||
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>
|
||
<div id="modal-aprovacao" class="hidden fixed inset-0 z-[1000] bg-black/50 items-center justify-center" style="display: none;">
|
||
<div class="modal-content bg-white p-8 rounded-xl max-w-md w-[90%] shadow-xl border border-slate-200">
|
||
<h3 id="titulo-modal" class="mt-0 mb-5 text-xl font-semibold">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" class="hidden mb-5">
|
||
<label for="justificativa-input" class="block mb-2 font-semibold text-slate-700">Justificativa Obrigatória <span class="text-red-500">*</span></label>
|
||
<textarea id="justificativa-input" name="justificativa" rows="4" class="w-full p-2.5 border border-slate-300 rounded-md font-sans text-sm focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20" placeholder="Descreva o motivo..."></textarea>
|
||
</div>
|
||
<div class="flex gap-2.5 justify-end mt-6">
|
||
<button type="button" onclick="fecharModalAprovacao()" class="py-2.5 px-5 bg-white text-slate-500 border border-slate-300 rounded-md cursor-pointer font-semibold hover:bg-slate-50">Cancelar</button>
|
||
<button type="submit" class="py-2.5 px-6 bg-primary text-white border-none rounded-md cursor-pointer font-semibold shadow-sm">Confirmar</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
{% endblock %}
|