From a74ad96c06d0998415a27d90864dcd8607d13500 Mon Sep 17 00:00:00 2001 From: zwlucas Date: Wed, 2 Apr 2025 00:16:18 -0300 Subject: new results page --- app/listagem/loading.tsx | 3 - app/listagem/page.tsx | 110 -------------------------- app/obrigado/page.tsx | 5 +- app/page.tsx | 8 +- app/resultados/loading.tsx | 3 + app/resultados/page.tsx | 192 +++++++++++++++++++++++++++++++++++++++++++++ app/votar/page.tsx | 130 ++++++++++++++++++++++-------- 7 files changed, 304 insertions(+), 147 deletions(-) delete mode 100644 app/listagem/loading.tsx delete mode 100644 app/listagem/page.tsx create mode 100644 app/resultados/loading.tsx create mode 100644 app/resultados/page.tsx (limited to 'app') diff --git a/app/listagem/loading.tsx b/app/listagem/loading.tsx deleted file mode 100644 index 4349ac3..0000000 --- a/app/listagem/loading.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Loading() { - return null; -} diff --git a/app/listagem/page.tsx b/app/listagem/page.tsx deleted file mode 100644 index fb4ec98..0000000 --- a/app/listagem/page.tsx +++ /dev/null @@ -1,110 +0,0 @@ -"use client"; - -import { Button } from "@/components/ui/button"; -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from "@/components/ui/card"; -import { getSupabaseClient } from "@/lib/supabase"; -import { useEffect, useState } from "react"; - -export default function ListVotes() { - const [numberVotes, setNumberVotes] = useState(); - const [sieNumberVotes, setSieNumberVotes] = useState(); - const [ljNumberVotes, setLjNumberVotes] = useState(); - - const [saveStatus, setSaveStatus] = useState<"loading" | "success" | "error">( - "loading" - ); - const [errorMessage, setErrorMessage] = useState(""); - - useEffect(() => { - const listVotes = async () => { - const supabase = getSupabaseClient(); - - const { data, error } = await supabase.from("votes").select(); - - if (error) { - console.error("Erro ao procurar votos:", error); - setSaveStatus("error"); - - setSieNumberVotes(0); - setLjNumberVotes(0); - - setErrorMessage( - "Ocorreu um erro ao procurar votos. Por favor, informe ao responsável." - ); - return; - } - - setNumberVotes(data.length); - - const sieData = data.filter((vote) => vote.option == "SIE"); - setSieNumberVotes(sieData.length); - - const ljData = data.filter( - (vote) => vote.option == "Liderança Jovem" - ); - setLjNumberVotes(ljData.length); - }; - - listVotes(); - }); - - return ( -
-
-
-
-
- JUSTIÇA ELEITORAL ESTUDANTIL -
-
-
-
- - - - VOTOS ATUAIS - - CHAPA DO GREMIO ESTUDANTIL - - - -
- - -
-
- Votos totais: {numberVotes} -
-
- Todos os dados são em tempo real! -
-
-
- -
-
- © {new Date().getFullYear()} Justiça Eleitoral Estudantil -
-
-
-
- ); -} diff --git a/app/obrigado/page.tsx b/app/obrigado/page.tsx index 7805719..173436b 100644 --- a/app/obrigado/page.tsx +++ b/app/obrigado/page.tsx @@ -35,12 +35,13 @@ export default function ObrigadoPage() { const saveVote = async () => { try { const supabase = getSupabaseClient(); - + + const optionRegistered = option === "NULO" ? "SIE" : option; const { error } = await supabase.from("votes").insert([ { rm, name, - option, + option: optionRegistered, }, ]); diff --git a/app/page.tsx b/app/page.tsx index 1799a26..74500d3 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -14,6 +14,8 @@ import { CardTitle, } from "@/components/ui/card"; import { useRouter } from "next/navigation"; +import Link from "next/link"; +import { BarChart3 } from "lucide-react"; export default function Home() { const [rm, setRm] = useState(""); @@ -139,10 +141,14 @@ export default function Home() { -
+
© {new Date().getFullYear()} Justiça Eleitoral Estudantil
+ + + Ver Resultados +
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(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(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 ( +
+
+
+
+
+ JUSTIÇA ELEITORAL ESTUDANTIL +
+
+
+
+ + + + + RESULTADOS DA VOTAÇÃO + + + Estatísticas de participação e votos por candidato + + + + {loading ? ( +
+ +

+ Carregando resultados... +

+
+ ) : error ? ( +
+ {error} +
+ ) : ( +
+
+

+ Total de Votos +

+
+ + {results?.total || 0} + +

+ eleitores participaram da votação +

+
+
+ +
+

+ Distribuição dos Votos +

+ + {results && results.total > 0 ? ( +
+ {Object.keys(results.options).map((opcao) => ( +
+
+ + {opcao} + + + {results.options[opcao].votes} votos ( + {results.options[opcao].percentage}%) + +
+
+
+
+
+ ))} +
+ ) : ( +

+ Nenhum voto registrado até o momento. +

+ )} +
+ +
+

+ Os resultados são atualizados automaticamente a cada vez que + a página é carregada. +

+
+
+ )} +
+
+ +
+
+ © {new Date().getFullYear()} Justiça Eleitoral Estudantil +
+
+
+
+ ); +} diff --git a/app/votar/page.tsx b/app/votar/page.tsx index 6d0efdc..30458c6 100644 --- a/app/votar/page.tsx +++ b/app/votar/page.tsx @@ -10,6 +10,7 @@ import { CardHeader, CardTitle, } from "@/components/ui/card"; +import { AlertTriangle } from "lucide-react"; export default function VotarPage() { const router = useRouter(); @@ -18,6 +19,7 @@ export default function VotarPage() { const nome = searchParams.get("nome") || ""; const [selectedOption, setSelectedOption] = useState(null); const [audioElement, setAudioElement] = useState(); + const [confirmNull, setConfirmNull] = useState(false); useEffect(() => { if (!rm || !nome) { @@ -30,6 +32,11 @@ export default function VotarPage() { }, [rm, nome, router]); const handleVote = (option: string) => { + if (option === "NULL" && !confirmNull) { + setConfirmNull(true); + return; + } + setSelectedOption(option); if (!audioElement) return; @@ -42,6 +49,10 @@ export default function VotarPage() { }, 500); }; + const cancelNull = () => { + setConfirmNull(false); + }; + return (
@@ -54,37 +65,94 @@ export default function VotarPage() {
- - - SEU VOTO PARA - - CHAPA DO GREMIO ESTUDANTIL - - - -
- - -
-
- Toque no quadro correspondente para VOTAR -
-
-
+ {confirmNull ? ( + + + + CONFIRMAR VOTO NULO + + + Você está prestes a anular seu voto + + + +
+
+ +
+ ATENÇÃO: Você está prestes a anular seu + voto. Votos nulos não são contabilizados para nenhum + candidato. +
+
+
+ +
+ Deseja realmente anular seu voto? +
+ +
+ + +
+
+
+ ) : ( + + + + SEU VOTO PARA + + + CHAPA DO GREMIO ESTUDANTIL + + + +
+ + +
+
+ Toque no quadro correspondente para VOTAR +
+ +
+ +
+
+
+ )}
-- cgit v1.2.3