refactor: update vacation management structure and enhance status handling

- Renamed and refactored vacation-related types and components for clarity, transitioning from 'SolicitacaoFerias' to 'PeriodoFerias'.
- Improved the handling of vacation statuses, including the addition of 'EmFérias' to the status options.
- Streamlined the vacation request and approval components to better reflect individual vacation periods.
- Enhanced data handling in backend queries and schema to support the new structure and ensure accurate status updates.
- Improved user experience by refining UI elements related to vacation periods and their statuses.
This commit is contained in:
2025-11-13 15:54:59 -03:00
parent 4ae5baffcc
commit c058865817
11 changed files with 1150 additions and 949 deletions

View File

@@ -18,9 +18,7 @@
'meu-perfil' | 'minhas-ferias' | 'minhas-ausencias' | 'aprovar-ferias' | 'aprovar-ausencias'
>('meu-perfil');
let solicitacaoSelecionada = $state<FunctionReturnType<typeof api.ferias.obterDetalhes> | null>(
null
);
let periodoSelecionado = $state<Id<'ferias'> | null>(null);
let mostrarModalFoto = $state(false);
let uploadandoFoto = $state(false);
@@ -192,12 +190,12 @@
}))
);
// Estatísticas das minhas férias
// Estatísticas das minhas férias (períodos individuais)
const statsMinhasFerias = $derived({
total: minhasSolicitacoes.length,
aguardando: minhasSolicitacoes.filter((s) => s.status === 'aguardando_aprovacao').length,
aprovadas: minhasSolicitacoes.filter(
(s) => s.status === 'aprovado' || s.status === 'data_ajustada_aprovada'
(s) => s.status === 'aprovado' || s.status === 'data_ajustada_aprovada' || s.status === 'EmFérias'
).length,
reprovadas: minhasSolicitacoes.filter((s) => s.status === 'reprovado').length,
emFerias: funcionario?.statusFerias === 'em_ferias' ? 1 : 0
@@ -212,14 +210,21 @@
});
async function recarregar() {
solicitacaoSelecionada = null;
periodoSelecionado = null;
}
async function selecionarSolicitacao(solicitacaoId: string) {
const detalhes = await client.query(api.ferias.obterDetalhes, {
solicitacaoId: solicitacaoId as Id<'solicitacoesFerias'>
});
solicitacaoSelecionada = detalhes;
async function selecionarPeriodo(feriasId: Id<'ferias'>) {
periodoSelecionado = feriasId;
}
// Função para formatar data sem problemas de timezone
function formatarDataString(dataString: string): string {
if (!dataString) return '';
// Dividir a string da data (formato YYYY-MM-DD)
const partes = dataString.split('-');
if (partes.length !== 3) return dataString;
// Retornar no formato DD/MM/YYYY
return `${partes[2]}/${partes[1]}/${partes[0]}`;
}
function getStatusBadge(status: string) {
@@ -227,7 +232,8 @@
aguardando_aprovacao: 'badge-warning',
aprovado: 'badge-success',
reprovado: 'badge-error',
data_ajustada_aprovada: 'badge-info'
data_ajustada_aprovada: 'badge-info',
EmFérias: 'badge-info'
};
return badges[status] || 'badge-neutral';
}
@@ -237,7 +243,8 @@
aguardando_aprovacao: 'Aguardando',
aprovado: 'Aprovado',
reprovado: 'Reprovado',
data_ajustada_aprovada: 'Ajustado'
data_ajustada_aprovada: 'Ajustado',
EmFérias: 'Em Férias'
};
return textos[status] || status;
}
@@ -1335,6 +1342,7 @@
<option value="aprovado">Aprovado</option>
<option value="reprovado">Reprovado</option>
<option value="data_ajustada_aprovada">Data Ajustada</option>
<option value="EmFérias">Em Férias</option>
</select>
</div>
</div>
@@ -1371,27 +1379,27 @@
<thead>
<tr>
<th>Ano</th>
<th>Períodos</th>
<th>Total Dias</th>
<th>Período</th>
<th>Dias</th>
<th>Status</th>
<th>Solicitado em</th>
</tr>
</thead>
<tbody>
{#each solicitacoesFiltradas as solicitacao (solicitacao._id)}
{#each solicitacoesFiltradas as periodo (periodo._id)}
<tr>
<td>{solicitacao.anoReferencia}</td>
<td>{solicitacao.periodos.length} período(s)</td>
<td class="font-bold"
>{solicitacao.periodos.reduce((acc, p) => acc + p.diasCorridos, 0)} dias</td
>
<td>{periodo.anoReferencia}</td>
<td>
<div class={`badge ${getStatusBadge(solicitacao.status)}`}>
{getStatusTexto(solicitacao.status)}
{formatarDataString(periodo.dataInicio)} - {formatarDataString(periodo.dataFim)}
</td>
<td class="font-bold">{periodo.diasFerias} dias</td>
<td>
<div class={`badge ${getStatusBadge(periodo.status)}`}>
{getStatusTexto(periodo.status)}
</div>
</td>
<td class="text-xs"
>{new Date(solicitacao._creationTime).toLocaleDateString('pt-BR')}</td
>{new Date(periodo._creationTime).toLocaleDateString('pt-BR')}</td
>
</tr>
{/each}
@@ -1741,50 +1749,50 @@
<th class="font-bold">Funcionário</th>
<th class="font-bold">Time</th>
<th class="font-bold">Ano</th>
<th class="font-bold">Períodos</th>
<th class="font-bold">Período</th>
<th class="font-bold">Dias</th>
<th class="font-bold">Status</th>
<th class="font-bold">Ações</th>
</tr>
</thead>
<tbody>
{#each solicitacoesSubordinados as solicitacao (solicitacao._id)}
{#each solicitacoesSubordinados as periodo (periodo._id)}
<tr class="hover:bg-base-200 transition-colors">
<td>
<div class="font-bold">
{solicitacao.funcionario?.nome}
{periodo.funcionario?.nome}
</div>
</td>
<td>
{#if solicitacao.time}
{#if periodo.time}
<div
class="badge badge-lg font-semibold"
style="background-color: {solicitacao.time
.cor}20; border-color: {solicitacao.time.cor}; color: {solicitacao
.time.cor}"
style="background-color: {periodo.time
.cor}20; border-color: {periodo.time.cor}; color: {periodo.time
.cor}"
>
{solicitacao.time.nome}
{periodo.time.nome}
</div>
{/if}
</td>
<td class="font-semibold">{solicitacao.anoReferencia}</td>
<td class="font-semibold">{solicitacao.periodos.length}</td>
<td class="text-lg font-bold"
>{solicitacao.periodos.reduce((acc, p) => acc + p.diasCorridos, 0)}</td
>
<td class="font-semibold">{periodo.anoReferencia}</td>
<td class="font-semibold">
{formatarDataString(periodo.dataInicio)} - {formatarDataString(periodo.dataFim)}
</td>
<td class="text-lg font-bold">{periodo.diasFerias}</td>
<td>
<div
class={`badge badge-lg font-semibold ${getStatusBadge(solicitacao.status)}`}
class={`badge badge-lg font-semibold ${getStatusBadge(periodo.status)}`}
>
{getStatusTexto(solicitacao.status)}
{getStatusTexto(periodo.status)}
</div>
</td>
<td>
{#if solicitacao.status === 'aguardando_aprovacao'}
{#if periodo.status === 'aguardando_aprovacao'}
<button
type="button"
class="btn btn-primary btn-sm gap-2 shadow-lg transition-transform hover:scale-105"
onclick={() => selecionarSolicitacao(solicitacao._id)}
onclick={() => selecionarPeriodo(periodo._id)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -1806,7 +1814,7 @@
<button
type="button"
class="btn btn-sm gap-2"
onclick={() => selecionarSolicitacao(solicitacao._id)}
onclick={() => selecionarPeriodo(periodo._id)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -1997,26 +2005,28 @@
</div>
<!-- Modal de Aprovação de Férias -->
{#if solicitacaoSelecionada}
<dialog class="modal modal-open">
<div class="modal-box max-w-4xl">
{#if currentUser.data}
<AprovarFerias
solicitacao={solicitacaoSelecionada}
gestorId={currentUser.data._id}
onSucesso={recarregar}
onCancelar={() => (solicitacaoSelecionada = null)}
/>
{/if}
</div>
<form method="dialog" class="modal-backdrop">
<button
type="button"
onclick={() => (solicitacaoSelecionada = null)}
aria-label="Fechar modal">Fechar</button
>
</form>
</dialog>
{#if periodoSelecionado && currentUser.data}
{#await client.query(api.ferias.obterDetalhes, { feriasId: periodoSelecionado }) then detalhes}
{#if detalhes}
<dialog class="modal modal-open">
<div class="modal-box max-w-4xl">
<AprovarFerias
periodo={detalhes}
gestorId={currentUser.data._id}
onSucesso={recarregar}
onCancelar={() => (periodoSelecionado = null)}
/>
</div>
<form method="dialog" class="modal-backdrop">
<button
type="button"
onclick={() => (periodoSelecionado = null)}
aria-label="Fechar modal">Fechar</button
>
</form>
</dialog>
{/if}
{/await}
{/if}
<!-- Modal de Upload de Foto / Escolher Avatar -->