aboutsummaryrefslogtreecommitdiff
path: root/app/resultados
diff options
context:
space:
mode:
authorzwlucas <lucas.oliveira1676@etec.sp.gov.br>2025-04-02 03:16:18 +0000
committerzwlucas <lucas.oliveira1676@etec.sp.gov.br>2025-04-02 03:16:18 +0000
commita74ad96c06d0998415a27d90864dcd8607d13500 (patch)
tree92260920e0d288d5151606b9b0cf8084f09606e6 /app/resultados
parenta7a6e965979f7c46c2511a33cb601172573f3c84 (diff)
downloadeleicoes-a74ad96c06d0998415a27d90864dcd8607d13500.tar.gz
eleicoes-a74ad96c06d0998415a27d90864dcd8607d13500.zip
new results page
Diffstat (limited to 'app/resultados')
-rw-r--r--app/resultados/loading.tsx3
-rw-r--r--app/resultados/page.tsx192
2 files changed, 195 insertions, 0 deletions
diff --git a/app/resultados/loading.tsx b/app/resultados/loading.tsx
new file mode 100644
index 0000000..4349ac3
--- /dev/null
+++ b/app/resultados/loading.tsx
@@ -0,0 +1,3 @@
+export default function Loading() {
+ return null;
+}
diff --git a/app/resultados/page.tsx b/app/resultados/page.tsx
new file mode 100644
index 0000000..fdd644d
--- /dev/null
+++ b/app/resultados/page.tsx
@@ -0,0 +1,192 @@
+"use client";
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import { getSupabaseClient } from "@/lib/supabase";
+import { Loader2 } from "lucide-react";
+import { useEffect, useState } from "react";
+
+type voteResult = {
+ total: number;
+ options: {
+ [key: string]: {
+ votes: number;
+ percentage: number;
+ };
+ };
+};
+
+export default function Home() {
+ const [results, setResults] = useState<voteResult | null>(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState<string | null>(null);
+
+ useEffect(() => {
+ const fetchResults = async () => {
+ try {
+ setLoading(true);
+ const supabase = getSupabaseClient();
+
+ const { data, error } = await supabase.from("votes").select("option");
+
+ if (error) throw new Error(error.message);
+
+ if (!data || data.length === 0) {
+ setResults({ total: 0, options: {} });
+ return;
+ }
+
+ const total = data.length;
+ const count: { [key: string]: number } = {};
+
+ data.forEach((vote) => {
+ const option = vote.option as string;
+ count[option] = (count[option] || 0) + 1;
+ });
+
+ const options: {
+ [key: string]: { votes: number; percentage: number };
+ } = {};
+
+ Object.keys(count).forEach((option) => {
+ if (option !== "NULO") {
+ options[option] = {
+ votes: count[option],
+ percentage: Number.parseFloat(
+ ((count[option] / total) * 100).toFixed(2)
+ ),
+ };
+ }
+ });
+
+ setResults({ total, options });
+ } catch (error) {
+ console.error("Erro ao buscar resultados:", error);
+ setError("Ocorreu um erro ao carregar os resultados da votação.");
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchResults();
+ }, []);
+
+ const getBarColor = (option: string) => {
+ if (option === "SIE") return "bg-blue-500";
+ if (option === "Liderança Jovem") return "bg-green-500";
+ if (option === "NULO") return "bg-red-500";
+ return "bg-purple-500";
+ };
+
+ return (
+ <div className="flex min-h-screen flex-col items-center justify-center bg-[#f0f5fa] px-4 sm:px-6 lg:px-8">
+ <div className="w-full max-w-3xl">
+ <div className="mb-6 flex items-center justify-center">
+ <div className="flex flex-col items-center">
+ <div className="mb-2 text-center text-2xl font-bold text-[#004a93] sm:text-3xl">
+ JUSTIÇA ELEITORAL ESTUDANTIL
+ </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-lg sm:text-2xl">
+ RESULTADOS DA VOTAÇÃO
+ </CardTitle>
+ <CardDescription className="text-sm text-gray-100 sm:text-base">
+ Estatísticas de participação e votos por candidato
+ </CardDescription>
+ </CardHeader>
+ <CardContent className="p-4 sm:p-6">
+ {loading ? (
+ <div className="flex flex-col items-center justify-center py-10">
+ <Loader2 className="h-8 w-8 animate-spin text-[#004a93] sm:h-10 sm:w-10" />
+ <p className="mt-4 text-sm text-[#004a93] sm:text-base">
+ Carregando resultados...
+ </p>
+ </div>
+ ) : error ? (
+ <div className="rounded-lg border-2 border-red-500 bg-red-50 p-4 text-center text-sm text-red-700 sm:text-base">
+ {error}
+ </div>
+ ) : (
+ <div className="space-y-6">
+ <div className="rounded-lg border-2 border-[#004a93] bg-white p-4 sm:p-6">
+ <h3 className="mb-4 text-lg font-bold text-[#004a93] sm:text-xl">
+ Total de Votos
+ </h3>
+ <div className="text-center">
+ <span className="text-4xl font-bold text-[#004a93] sm:text-5xl">
+ {results?.total || 0}
+ </span>
+ <p className="mt-2 text-xs text-gray-600 sm:text-sm">
+ eleitores participaram da votação
+ </p>
+ </div>
+ </div>
+
+ <div className="rounded-lg border-2 border-[#004a93] bg-white p-4 sm:p-6">
+ <h3 className="mb-4 text-lg font-bold text-[#004a93] sm:text-xl">
+ Distribuição dos Votos
+ </h3>
+
+ {results && results.total > 0 ? (
+ <div className="space-y-4 sm:space-y-6">
+ {Object.keys(results.options).map((opcao) => (
+ <div key={opcao} className="space-y-2">
+ <div className="flex items-center justify-between">
+ <span className="text-sm font-medium text-[#004a93] sm:text-base">
+ {opcao}
+ </span>
+ <span className="text-sm font-bold text-[#004a93] sm:text-base">
+ {results.options[opcao].votes} votos (
+ {results.options[opcao].percentage}%)
+ </span>
+ </div>
+ <div className="h-4 w-full overflow-hidden rounded-full bg-gray-200 sm:h-6">
+ <div
+ className={`h-full ${getBarColor(
+ opcao
+ )} transition-all duration-500 ease-in-out`}
+ style={{
+ width: `${results.options[opcao].percentage}%`,
+ }}
+ ></div>
+ </div>
+ </div>
+ ))}
+ </div>
+ ) : (
+ <p className="text-center text-sm text-gray-500 sm:text-base">
+ Nenhum voto registrado até o momento.
+ </p>
+ )}
+ </div>
+
+ <div className="rounded-lg border-2 border-amber-500 bg-amber-50 p-4">
+ <p className="text-center text-xs text-amber-800 sm:text-sm">
+ Os resultados são atualizados automaticamente a cada vez que
+ a página é carregada.
+ </p>
+ </div>
+ </div>
+ )}
+ </CardContent>
+ </Card>
+
+ <div className="mt-4 flex justify-center">
+ <div className="text-center text-xs text-[#004a93] sm:text-sm">
+ © {new Date().getFullYear()} Justiça Eleitoral Estudantil
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+}