feat: update system branding and improve user interface consistency

- Changed all instances of "Sistema de Gerenciamento da Secretaria de Esportes" to "Sistema de Gerenciamento de Secretaria" for a more concise branding.
- Enhanced the PrintModal component with a user-friendly interface for selecting sections to include in PDF generation.
- Improved error handling and user feedback during PDF generation processes.
- Updated various components and routes to reflect the new branding, ensuring consistency across the application.
This commit is contained in:
2025-11-18 03:10:10 -03:00
parent 7c8be8a818
commit 71550874ce
20 changed files with 1697 additions and 1432 deletions

View File

@@ -382,7 +382,9 @@
async function atribuirResponsavel() {
if (!ticketSelecionado || !assignResponsavel) {
assignFeedback = "Escolha um ticket e um responsável";
assignFeedback = !ticketSelecionado
? "Selecione um chamado primeiro"
: "Selecione um responsável";
return;
}
try {
@@ -409,8 +411,11 @@
}
async function prorrogarChamado() {
if (!prorrogacaoForm.ticketId || !prorrogacaoForm.motivo.trim()) {
prorrogacaoFeedback = "Selecione um ticket e preencha o motivo da prorrogação";
// Usar o ticket selecionado se não houver ticketId no formulário
const ticketIdParaProrrogar = prorrogacaoForm.ticketId || ticketSelecionado;
if (!ticketIdParaProrrogar || !prorrogacaoForm.motivo.trim()) {
prorrogacaoFeedback = "Selecione um chamado e preencha o motivo da prorrogação";
return;
}
if (prorrogacaoForm.horasAdicionais <= 0) {
@@ -420,12 +425,11 @@
try {
prorrogacaoFeedback = null;
await client.mutation(api.chamados.prorrogarChamado, {
ticketId: prorrogacaoForm.ticketId as Id<"tickets">,
ticketId: ticketIdParaProrrogar as Id<"tickets">,
horasAdicionais: prorrogacaoForm.horasAdicionais,
prazo: prorrogacaoForm.prazo,
motivo: prorrogacaoForm.motivo.trim(),
});
const ticketIdProrrogado = prorrogacaoForm.ticketId;
prorrogacaoFeedback = "Prazo prorrogado com sucesso";
prorrogacaoForm = {
ticketId: "",
@@ -438,8 +442,8 @@
responsavelId: filtroResponsavel === "todos" ? undefined : filtroResponsavel,
setor: filtroSetor === "todos" ? undefined : filtroSetor,
});
if (ticketIdProrrogado) {
selecionarTicket(ticketIdProrrogado as Id<"tickets">);
if (ticketIdParaProrrogar) {
selecionarTicket(ticketIdParaProrrogar as Id<"tickets">);
}
} catch (error) {
prorrogacaoFeedback = error instanceof Error ? error.message : "Erro ao prorrogar prazo";
@@ -808,47 +812,159 @@
</div>
</section>
<section class="grid gap-6 lg:grid-cols-2">
<div class="rounded-3xl border border-base-200 bg-base-100/80 p-6 shadow-lg">
<h3 class="text-lg font-semibold text-base-content">Detalhes do chamado</h3>
{#if !detalheSelecionado}
<p class="text-sm text-base-content/60">Selecione um chamado na tabela.</p>
{:else}
<div class="mt-4 space-y-3">
<div class="flex items-center justify-between">
<div>
<p class="text-xs text-base-content/60">Solicitante</p>
<p class="font-semibold text-base-content">{detalheSelecionado.solicitanteNome}</p>
<!-- Seção: Gerenciamento do Chamado Selecionado -->
{#if !detalheSelecionado}
<section class="rounded-3xl border-2 border-dashed border-base-300 bg-base-100/50 p-12 text-center shadow-lg">
<div class="mx-auto max-w-md">
<svg
xmlns="http://www.w3.org/2000/svg"
class="mx-auto mb-4 h-16 w-16 text-base-content/30"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
/>
</svg>
<h3 class="mb-2 text-xl font-semibold text-base-content">Nenhum chamado selecionado</h3>
<p class="text-sm text-base-content/60">
Selecione um chamado na tabela acima para visualizar detalhes e realizar ações.
</p>
</div>
</section>
{:else}
<!-- Container Principal: Gerenciamento do Chamado Selecionado -->
<section class="rounded-3xl border-2 border-primary/30 bg-base-100/95 p-6 shadow-2xl">
<!-- Cabeçalho do Chamado Selecionado -->
<div class="mb-6 rounded-2xl border-2 border-primary/20 bg-gradient-to-r from-primary/10 via-primary/5 to-base-100/80 p-6 shadow-lg">
<div class="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
<div class="flex-1">
<div class="mb-2 flex items-center gap-3">
<div class="rounded-lg bg-primary/20 px-3 py-1">
<span class="text-sm font-bold text-primary">{detalheSelecionado.numero}</span>
</div>
<span class={getStatusBadge(detalheSelecionado.status)}>
{getStatusLabel(detalheSelecionado.status)}
</span>
</div>
<h2 class="mb-2 text-2xl font-bold text-base-content">{detalheSelecionado.titulo}</h2>
<div class="flex flex-wrap items-center gap-4 text-sm text-base-content/70">
<div class="flex items-center gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
/>
</svg>
<span class="font-semibold">Solicitante:</span>
<span>{detalheSelecionado.solicitanteNome}</span>
</div>
{#if detalheSelecionado.prazoConclusao}
<div class="flex items-center gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<span class="font-semibold">Prazo:</span>
<span>{prazoRestante(detalheSelecionado.prazoConclusao) ?? "--"}</span>
</div>
{/if}
</div>
<span class={getStatusBadge(detalheSelecionado.status)}>
{getStatusLabel(detalheSelecionado.status)}
</span>
</div>
<p class="text-sm text-base-content/70">{detalheSelecionado.descricao}</p>
<div class="grid grid-cols-2 gap-3 text-sm text-base-content/70">
<div>
<p class="text-xs text-base-content/50">Prazo resposta</p>
<p>{prazoRestante(detalheSelecionado.prazoResposta) ?? "--"}</p>
</div>
</div>
<!-- Grid de Ações do Chamado Selecionado -->
<div class="grid gap-6 lg:grid-cols-2">
<!-- Detalhes do Chamado -->
<div class="rounded-3xl border-2 border-primary/20 bg-base-100/90 p-6 shadow-lg">
<div class="mb-4 flex items-center gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5 text-primary"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<h3 class="text-lg font-semibold text-base-content">Detalhes do chamado</h3>
</div>
<div class="space-y-4">
<div>
<p class="mb-2 text-sm font-medium text-base-content/70">Descrição</p>
<p class="rounded-lg bg-base-200/50 p-3 text-sm text-base-content/80">
{detalheSelecionado.descricao}
</p>
</div>
<div class="grid grid-cols-2 gap-3">
<div class="rounded-lg bg-base-200/30 p-3">
<p class="text-xs font-medium text-base-content/50">Prazo de resposta</p>
<p class="mt-1 text-sm font-semibold text-base-content">
{prazoRestante(detalheSelecionado.prazoResposta) ?? "--"}
</p>
</div>
<div>
<p class="text-xs text-base-content/50">Prazo conclusão</p>
<p>{prazoRestante(detalheSelecionado.prazoConclusao) ?? "--"}</p>
<div class="rounded-lg bg-base-200/30 p-3">
<p class="text-xs font-medium text-base-content/50">Prazo de conclusão</p>
<p class="mt-1 text-sm font-semibold text-base-content">
{prazoRestante(detalheSelecionado.prazoConclusao) ?? "--"}
</p>
</div>
</div>
<div class="mt-4">
<p class="mb-3 text-sm font-medium text-base-content/70">Histórico e Timeline</p>
<TicketTimeline timeline={detalheSelecionado.timeline ?? []} />
</div>
</div>
{/if}
</div>
</div>
<!-- Nova seção: Responder e Gerenciar Chamado -->
<div class="rounded-3xl border border-base-200 bg-base-100/80 p-6 shadow-lg">
<h3 class="text-lg font-semibold text-base-content">Responder chamado</h3>
{#if !detalheSelecionado}
<p class="text-sm text-base-content/60 mt-4">Selecione um chamado na tabela para responder.</p>
{:else}
<div class="mt-4 space-y-4">
<!-- Responder Chamado -->
<div class="rounded-3xl border-2 border-primary/20 bg-base-100/90 p-6 shadow-lg">
<div class="mb-4 flex items-center gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5 text-primary"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
/>
</svg>
<h3 class="text-lg font-semibold text-base-content">Responder chamado</h3>
</div>
<div class="space-y-4">
<div class="form-control">
<label class="label">
<span class="label-text font-semibold">Resposta *</span>
@@ -923,58 +1039,78 @@
</button>
</div>
</div>
{/if}
</div>
<div class="rounded-3xl border border-base-200 bg-base-100/80 p-6 shadow-lg">
<h3 class="text-lg font-semibold text-base-content">Atribuir responsável</h3>
<div class="mt-4 space-y-3">
<select class="select select-bordered w-full" bind:value={assignResponsavel}>
<option value="">Selecione o responsável</option>
{#if usuariosTI.length === 0}
<option disabled>Carregando usuários...</option>
{:else}
{#each usuariosTI as usuario (usuario._id)}
<option value={usuario._id}>{usuario.nome}</option>
{/each}
{/if}
</select>
<textarea
class="textarea textarea-bordered w-full"
rows="3"
placeholder="Motivo/observação (opcional)"
bind:value={assignMotivo}
></textarea>
{#if assignFeedback}
<div class={`alert ${assignFeedback.includes('sucesso') ? 'alert-success' : 'alert-error'} text-sm`}>
{assignFeedback}
</div>
{/if}
<button class="btn btn-primary w-full" type="button" onclick={atribuirResponsavel}>
Atribuir responsável
</button>
</div>
<!-- Seção Prorrogar prazo dentro de Atribuir responsável -->
<div class="mt-6 border-t border-base-300 pt-6">
<h3 class="text-lg font-semibold text-base-content mb-1">Prorrogar prazo</h3>
<p class="text-xs text-base-content/60 mb-4">Recurso exclusivo para a equipe de TI</p>
<!-- Atribuir Responsável -->
<div class="rounded-3xl border-2 border-primary/20 bg-base-100/90 p-6 shadow-lg">
<div class="mb-4 flex items-center gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5 text-primary"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z"
/>
</svg>
<h3 class="text-lg font-semibold text-base-content">Atribuir responsável</h3>
</div>
<div class="space-y-3">
<div class="form-control">
<label class="label">
<span class="label-text font-semibold">Ticket *</span>
</label>
<select class="select select-bordered w-full" bind:value={prorrogacaoForm.ticketId}>
<option value="">Selecione o ticket</option>
{#if tickets.length === 0}
<option disabled>Carregando tickets...</option>
{:else}
{#each tickets as ticket (ticket._id)}
<option value={ticket._id}>{ticket.numero} - {ticket.titulo}</option>
{/each}
{/if}
</select>
<select class="select select-bordered w-full" bind:value={assignResponsavel}>
<option value="">Selecione o responsável</option>
{#if usuariosTI.length === 0}
<option disabled>Carregando usuários...</option>
{:else}
{#each usuariosTI as usuario (usuario._id)}
<option value={usuario._id}>{usuario.nome}</option>
{/each}
{/if}
</select>
<textarea
class="textarea textarea-bordered w-full"
rows="3"
placeholder="Motivo/observação (opcional)"
bind:value={assignMotivo}
></textarea>
{#if assignFeedback}
<div class={`alert ${assignFeedback.includes('sucesso') ? 'alert-success' : 'alert-error'} text-sm`}>
{assignFeedback}
</div>
{/if}
<button class="btn btn-primary w-full" type="button" onclick={atribuirResponsavel}>
Atribuir responsável
</button>
</div>
</div>
<!-- Prorrogar Prazo -->
<div class="rounded-3xl border-2 border-primary/20 bg-base-100/90 p-6 shadow-lg">
<div class="mb-4 flex items-center gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5 text-primary"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<div class="flex-1">
<h3 class="text-lg font-semibold text-base-content">Prorrogar prazo</h3>
<p class="text-xs text-base-content/60">Recurso exclusivo para a equipe de TI</p>
</div>
</div>
<div class="space-y-3">
<div class="form-control">
<label class="label">
<span class="label-text font-semibold">Prazo a prorrogar *</span>
@@ -1016,14 +1152,15 @@
class="btn btn-warning w-full"
type="button"
onclick={prorrogarChamado}
disabled={!prorrogacaoForm.ticketId || !prorrogacaoForm.motivo.trim()}
disabled={!prorrogacaoForm.motivo.trim() || !ticketSelecionado}
>
Prorrogar prazo
</button>
</div>
</div>
</div>
</section>
</section>
{/if}
<!-- Seção: SLAs Existentes - Visualização Detalhada -->
<section class="rounded-3xl border border-base-200 bg-base-100/80 p-6 shadow-xl">
@@ -1057,7 +1194,7 @@
</div>
{:else}
<!-- Resumo de SLAs -->
<div class="mb-4 grid grid-cols-2 gap-3 md:grid-cols-4">
<div class="mb-4 grid grid-cols-2 gap-3 md:grid-cols-3 lg:grid-cols-5">
<div class="rounded-xl border border-base-300 bg-base-200/30 p-3 text-center">
<div class="text-2xl font-bold text-base-content">{slaConfigsAtivos.length}</div>
<div class="text-xs text-base-content/60">Total de SLAs</div>
@@ -1071,8 +1208,12 @@
<div class="text-xs text-base-content/60">Prioridade Média</div>
</div>
<div class="rounded-xl border border-base-300 bg-base-200/30 p-3 text-center">
<div class="text-2xl font-bold text-warning">{slaConfigsPorPrioridadeCount.alta + slaConfigsPorPrioridadeCount.critica}</div>
<div class="text-xs text-base-content/60">Prioridade Alta/Crítica</div>
<div class="text-2xl font-bold text-warning">{slaConfigsPorPrioridadeCount.alta}</div>
<div class="text-xs text-base-content/60">Prioridade Alta</div>
</div>
<div class="rounded-xl border border-base-300 bg-base-200/30 p-3 text-center">
<div class="text-2xl font-bold text-error">{slaConfigsPorPrioridadeCount.critica}</div>
<div class="text-xs text-base-content/60">Prioridade Crítica</div>
</div>
</div>