aboutsummaryrefslogtreecommitdiff
path: root/app/page.tsx
diff options
context:
space:
mode:
authorzwlucas <lucas.oliveira1676@etec.sp.gov.br>2025-04-01 02:34:04 +0000
committerzwlucas <lucas.oliveira1676@etec.sp.gov.br>2025-04-01 02:34:04 +0000
commit79670b4c51ebbdd242b894a5f0678618054cc2ef (patch)
tree654b351c13016b7d83137409b56031110cb46628 /app/page.tsx
parent0d54368efc5e91bf1beea8961655fa77f51b3074 (diff)
downloadeleicoes-79670b4c51ebbdd242b894a5f0678618054cc2ef.tar.gz
eleicoes-79670b4c51ebbdd242b894a5f0678618054cc2ef.zip
create eletrocast-eleicoes
Diffstat (limited to 'app/page.tsx')
-rw-r--r--app/page.tsx317
1 files changed, 221 insertions, 96 deletions
diff --git a/app/page.tsx b/app/page.tsx
index 88f0cc9..56ac851 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -1,103 +1,228 @@
-import Image from "next/image";
+"use client";
+
+import type React from "react";
+
+import { useState } from "react";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import { useRouter } from "next/navigation";
export default function Home() {
+ const [rm, setRm] = useState("");
+ const [nome, setNome] = useState("");
+ const [cpf, setCpf] = useState("");
+ const [errors, setErrors] = useState<{
+ rm?: string;
+ nome?: string;
+ cpf?: string;
+ }>({});
+ const router = useRouter();
+
+ function validateCPF(cpf: string): boolean {
+ cpf = cpf.replace(/\D/g, "");
+
+ if (cpf.length !== 11 || /^(\d)\1{10}$/.test(cpf)) return false;
+
+ const calc = (factor: number) =>
+ cpf
+ .split("")
+ .slice(0, factor - 1)
+ .reduce(
+ (sum, num, index) => sum + parseInt(num) * (factor - index),
+ 0
+ ) %
+ 11 <
+ 2
+ ? 0
+ : 11 -
+ (cpf
+ .split("")
+ .slice(0, factor - 1)
+ .reduce(
+ (sum, num, index) => sum + parseInt(num) * (factor - index),
+ 0
+ ) %
+ 11);
+
+ return calc(10) === parseInt(cpf[9]) && calc(11) === parseInt(cpf[10]);
+ }
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+
+ const newErrors: { rm?: string; nome?: string; cpf?: string } = {};
+
+ if (!/^\d{5}$/.test(rm)) {
+ newErrors.rm = "O RM deve conter exatamente 5 dígitos numéricos.";
+ }
+
+ if (!nome || nome.trim().length < 3) {
+ newErrors.nome = "Por favor, insira seu nome completo.";
+ }
+
+ if (!validateCPF(cpf)) {
+ newErrors.cpf = "CPF inválido. Insira um CPF válido com 11 dígitos.";
+ }
+
+ if (Object.keys(newErrors).length > 0) {
+ setErrors(newErrors);
+ return;
+ }
+
+ setErrors({});
+
+ router.push(
+ `/confirmar?rm=${rm}&nome=${encodeURIComponent(nome)}&cpf=${cpf}`
+ );
+ };
+
+ const formatCPF = (value: string) => {
+ const cpfClean = value.replace(/\D/g, "");
+ let formatted = cpfClean;
+
+ if (cpfClean.length > 3) {
+ formatted = cpfClean.substring(0, 3) + "." + cpfClean.substring(3);
+ }
+ if (cpfClean.length > 6) {
+ formatted = formatted.substring(0, 7) + "." + cpfClean.substring(6, 9);
+ }
+ if (cpfClean.length > 9) {
+ formatted = formatted.substring(0, 11) + "-" + cpfClean.substring(9, 11);
+ }
+
+ return formatted;
+ };
+
+ const handleCPFChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+ const value = e.target.value;
+ const formatted = formatCPF(value);
+ setCpf(formatted);
+
+ if (errors.cpf) {
+ setErrors((prev) => ({ ...prev, cpf: undefined }));
+ }
+ };
+
return (
- <div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
- <main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
- <Image
- className="dark:invert"
- src="/next.svg"
- alt="Next.js logo"
- width={180}
- height={38}
- priority
- />
- <ol className="list-inside list-decimal text-sm/6 text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
- <li className="mb-2 tracking-[-.01em]">
- Get started by editing{" "}
- <code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-[family-name:var(--font-geist-mono)] font-semibold">
- app/page.tsx
- </code>
- .
- </li>
- <li className="tracking-[-.01em]">
- Save and see your changes instantly.
- </li>
- </ol>
-
- <div className="flex gap-4 items-center flex-col sm:flex-row">
- <a
- className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
- href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
- target="_blank"
- rel="noopener noreferrer"
- >
- <Image
- className="dark:invert"
- src="/vercel.svg"
- alt="Vercel logomark"
- width={20}
- height={20}
- />
- Deploy now
- </a>
- <a
- className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
- href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
- target="_blank"
- rel="noopener noreferrer"
- >
- Read our docs
- </a>
+ <div className="flex min-h-screen flex-col items-center justify-center bg-[#f0f5fa]">
+ <div className="w-full max-w-md">
+ <div className="mb-6 flex items-center justify-center">
+ <div className="flex flex-col items-center">
+ <div className="mb-2 text-center text-3xl font-bold text-[#004a93]">
+ JUSTIÇA ELEITORAL
+ </div>
+ <div className="h-2 w-full bg-gradient-to-r from-[#009c3b] via-[#ffdf00] to-[#002776]"></div>
+ </div>
+ </div>
+
+ <Card className="border-2 border-[#004a93] shadow-lg overflow-hidden">
+ <CardHeader className="bg-[#004a93] text-center text-white">
+ <CardTitle className="text-2xl">ELEIÇÕES ESTUDANTIS</CardTitle>
+ <CardDescription className="text-gray-100">
+ Identificação do Eleitor
+ </CardDescription>
+ </CardHeader>
+ <CardContent className="pt-6">
+ <form onSubmit={handleSubmit} className="space-y-4">
+ <div className="space-y-2">
+ <label
+ htmlFor="rm"
+ className="text-sm font-medium text-[#004a93]"
+ >
+ Digite seu RM:
+ </label>
+ <Input
+ id="rm"
+ type="text"
+ value={rm}
+ onChange={(e) => {
+ setRm(e.target.value);
+ if (errors.rm) {
+ setErrors((prev) => ({ ...prev, rm: undefined }));
+ }
+ }}
+ placeholder="Digite os 5 dígitos do seu RM"
+ className="border-2 border-[#004a93]"
+ maxLength={5}
+ />
+ {errors.rm && (
+ <p className="text-sm text-red-500">{errors.rm}</p>
+ )}
+ </div>
+
+ <div className="space-y-2">
+ <label
+ htmlFor="nome"
+ className="text-sm font-medium text-[#004a93]"
+ >
+ Digite seu nome completo:
+ </label>
+ <Input
+ id="nome"
+ type="text"
+ value={nome}
+ onChange={(e) => {
+ setNome(e.target.value);
+ if (errors.nome) {
+ setErrors((prev) => ({ ...prev, nome: undefined }));
+ }
+ }}
+ placeholder="Digite seu nome completo"
+ className="border-2 border-[#004a93]"
+ />
+ {errors.nome && (
+ <p className="text-sm text-red-500">{errors.nome}</p>
+ )}
+ </div>
+
+ <div className="space-y-2">
+ <label
+ htmlFor="cpf"
+ className="text-sm font-medium text-[#004a93]"
+ >
+ Digite seu CPF:
+ </label>
+ <Input
+ id="cpf"
+ type="text"
+ value={cpf}
+ onChange={handleCPFChange}
+ placeholder="Digite seu CPF"
+ className="border-2 border-[#004a93]"
+ maxLength={14}
+ />
+ {errors.cpf && (
+ <p className="text-sm text-red-500">{errors.cpf}</p>
+ )}
+ </div>
+
+ <Button
+ type="submit"
+ className="w-full bg-[#004a93] text-white hover:bg-[#003a73]"
+ >
+ CONFIRMAR
+ </Button>
+ </form>
+ </CardContent>
+ <CardFooter className="flex justify-center border-t border-[#004a93] bg-[#f8f8f8] py-3 text-sm text-[#004a93] rounded-b-lg">
+ Seu voto é secreto e seguro.
+ </CardFooter>
+ </Card>
+
+ <div className="mt-4 flex justify-center">
+ <div className="text-center text-sm text-[#004a93]">
+ © {new Date().getFullYear()} Justiça Eleitoral Estudantil
+ </div>
</div>
- </main>
- <footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
- <a
- className="flex items-center gap-2 hover:underline hover:underline-offset-4"
- href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
- target="_blank"
- rel="noopener noreferrer"
- >
- <Image
- aria-hidden
- src="/file.svg"
- alt="File icon"
- width={16}
- height={16}
- />
- Learn
- </a>
- <a
- className="flex items-center gap-2 hover:underline hover:underline-offset-4"
- href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
- target="_blank"
- rel="noopener noreferrer"
- >
- <Image
- aria-hidden
- src="/window.svg"
- alt="Window icon"
- width={16}
- height={16}
- />
- Examples
- </a>
- <a
- className="flex items-center gap-2 hover:underline hover:underline-offset-4"
- href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
- target="_blank"
- rel="noopener noreferrer"
- >
- <Image
- aria-hidden
- src="/globe.svg"
- alt="Globe icon"
- width={16}
- height={16}
- />
- Go to nextjs.org →
- </a>
- </footer>
+ </div>
</div>
);
}