Files
sgse-app/apps/web/src/routes/(dashboard)/solicitar-acesso/+page.svelte

260 lines
8.6 KiB
Svelte

<script lang="ts">
import { goto } from "$app/navigation";
import { useConvexClient } from "convex-svelte";
import { api } from "@sgse-app/backend/convex/_generated/api";
import { createForm } from "@tanstack/svelte-form";
import z from "zod";
const convex = useConvexClient();
// Estado para mensagens
let notice = $state<{ type: "success" | "error"; message: string } | null>(null);
// Schema de validação
const formSchema = z.object({
nome: z.string().min(3, "Nome deve ter no mínimo 3 caracteres"),
matricula: z.string().min(1, "Matrícula é obrigatória"),
email: z.string().email("E-mail inválido"),
telefone: z.string().min(14, "Telefone inválido"),
});
// Criar o formulário
const form = createForm(() => ({
defaultValues: {
nome: "",
matricula: "",
email: "",
telefone: "",
},
onSubmit: async ({ value }) => {
try {
notice = null;
await convex.mutation(api.solicitacoesAcesso.create, {
nome: value.nome,
matricula: value.matricula,
email: value.email,
telefone: value.telefone,
});
notice = {
type: "success",
message: "Solicitação de acesso enviada com sucesso! Aguarde a análise da equipe de TI.",
};
// Limpar o formulário
form.reset();
// Redirecionar após 3 segundos
setTimeout(() => {
goto("/");
}, 3000);
} catch (error: any) {
notice = {
type: "error",
message: error.message || "Erro ao enviar solicitação. Tente novamente.",
};
}
},
}));
// Máscaras
function maskTelefone(value: string): string {
const cleaned = value.replace(/\D/g, "");
if (cleaned.length <= 10) {
return cleaned
.replace(/^(\d{2})(\d)/, "($1) $2")
.replace(/(\d{4})(\d)/, "$1-$2");
}
return cleaned
.replace(/^(\d{2})(\d)/, "($1) $2")
.replace(/(\d{5})(\d)/, "$1-$2");
}
function handleCancel() {
goto("/");
}
</script>
<main class="container mx-auto px-4 py-4 max-w-4xl">
<div class="mb-6">
<h1 class="text-3xl font-bold text-primary mb-2">Solicitar Acesso ao SGSE</h1>
<p class="text-base-content/70">
Preencha o formulário abaixo para solicitar acesso ao Sistema de Gerenciamento da Secretaria de Esportes.
Sua solicitação será analisada pela equipe de Tecnologia da Informação.
</p>
</div>
{#if notice}
<div class="alert {notice.type === 'success' ? 'alert-success' : 'alert-error'} mb-6">
<svg
xmlns="http://www.w3.org/2000/svg"
class="stroke-current shrink-0 h-6 w-6"
fill="none"
viewBox="0 0 24 24"
>
{#if notice.type === "success"}
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
{:else}
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
{/if}
</svg>
<span>{notice.message}</span>
</div>
{/if}
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<form
onsubmit={(e) => {
e.preventDefault();
e.stopPropagation();
form.handleSubmit();
}}
>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<!-- Nome -->
<form.Field name="nome" validators={{ onChange: formSchema.shape.nome }}>
{#snippet children(field)}
<div class="form-control md:col-span-2">
<label class="label" for="nome">
<span class="label-text">Nome Completo *</span>
</label>
<input
id="nome"
type="text"
placeholder="Digite seu nome completo"
class="input input-bordered w-full"
value={field.state.value}
onblur={field.handleBlur}
oninput={(e) => field.handleChange(e.currentTarget.value)}
/>
{#if field.state.meta.errors.length > 0}
<span class="text-error text-sm mt-1">{field.state.meta.errors[0]}</span>
{/if}
</div>
{/snippet}
</form.Field>
<!-- Matrícula -->
<form.Field name="matricula" validators={{ onChange: formSchema.shape.matricula }}>
{#snippet children(field)}
<div class="form-control">
<label class="label" for="matricula">
<span class="label-text">Matrícula *</span>
</label>
<input
id="matricula"
type="text"
placeholder="Digite sua matrícula"
class="input input-bordered w-full"
value={field.state.value}
onblur={field.handleBlur}
oninput={(e) => field.handleChange(e.currentTarget.value)}
/>
{#if field.state.meta.errors.length > 0}
<span class="text-error text-sm mt-1">{field.state.meta.errors[0]}</span>
{/if}
</div>
{/snippet}
</form.Field>
<!-- E-mail -->
<form.Field name="email" validators={{ onChange: formSchema.shape.email }}>
{#snippet children(field)}
<div class="form-control">
<label class="label" for="email">
<span class="label-text">E-mail *</span>
</label>
<input
id="email"
type="email"
placeholder="seu@email.com"
class="input input-bordered w-full"
value={field.state.value}
onblur={field.handleBlur}
oninput={(e) => field.handleChange(e.currentTarget.value)}
/>
{#if field.state.meta.errors.length > 0}
<span class="text-error text-sm mt-1">{field.state.meta.errors[0]}</span>
{/if}
</div>
{/snippet}
</form.Field>
<!-- Telefone -->
<form.Field name="telefone" validators={{ onChange: formSchema.shape.telefone }}>
{#snippet children(field)}
<div class="form-control md:col-span-2">
<label class="label" for="telefone">
<span class="label-text">Telefone *</span>
</label>
<input
id="telefone"
type="text"
placeholder="(00) 00000-0000"
class="input input-bordered w-full"
value={field.state.value}
onblur={field.handleBlur}
oninput={(e) => {
const masked = maskTelefone(e.currentTarget.value);
e.currentTarget.value = masked;
field.handleChange(masked);
}}
maxlength="15"
/>
{#if field.state.meta.errors.length > 0}
<span class="text-error text-sm mt-1">{field.state.meta.errors[0]}</span>
{/if}
</div>
{/snippet}
</form.Field>
</div>
<div class="card-actions justify-end mt-6 gap-2">
<button type="button" class="btn btn-ghost" onclick={handleCancel}>
Cancelar
</button>
<button type="submit" class="btn btn-primary">
Solicitar Acesso
</button>
</div>
</form>
</div>
</div>
<div class="alert alert-info mt-6">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
class="stroke-current shrink-0 w-6 h-6"
>
<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"
></path>
</svg>
<div>
<h3 class="font-bold">Informações Importantes</h3>
<div class="text-sm">
<ul class="list-disc list-inside mt-2">
<li>Todos os campos marcados com * são obrigatórios</li>
<li>Sua solicitação será analisada pela equipe de TI em até 48 horas úteis</li>
<li>Você receberá um e-mail com o resultado da análise</li>
<li>Em caso de dúvidas, entre em contato com o suporte técnico</li>
</ul>
</div>
</div>
</div>
</main>