mais melhorias gerais

This commit is contained in:
José Henrique 2025-06-19 18:10:49 -03:00
parent 7512f42e2f
commit 7acbc48f43
9 changed files with 168 additions and 16 deletions

View File

@ -20,6 +20,7 @@ RUN rm -rf /usr/share/nginx/html/*
# Replace default nginx.conf
COPY nginx.conf /etc/nginx/nginx.conf
# Copy our built files into nginxs html folder
COPY ./public/assets /usr/share/nginx/html
COPY --from=builder /app/dist /usr/share/nginx/html
# (Optional) If you need any custom nginx.conf, COPY it here—

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

View File

@ -8,6 +8,7 @@ import Footer from './components/Footer';
import CandidatePage from './components/CandidatePage/CandidatePage';
import DataStatsPage from './components/DataStatsPage';
import StatisticsPage from './components/StatisticsPage';
import SobrePage from './components/SobrePage';
import NotFound from './components/NotFound';
import MatrixBackground from './components/MatrixBackground';
import './App.css';
@ -34,6 +35,7 @@ function App() {
<Route path="/candidato/:id" element={<CandidatePage />} />
<Route path="/dados-disponiveis" element={<DataStatsPage />} />
<Route path="/estatisticas" element={<StatisticsPage />} />
<Route path="/sobre" element={<SobrePage />} />
<Route path="*" element={<NotFound />} />
</Routes>
</div>

View File

@ -28,10 +28,10 @@ const DataStatsPage: React.FC = () => {
if (loading) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<div className="animate-spin rounded-full h-16 w-16 border-4 border-indigo-600 border-t-transparent mx-auto mb-4"></div>
<p className="text-lg text-gray-700">Carregando dados...</p>
<div className="flex items-center justify-center min-h-[40vh]">
<div className="backdrop-blur-md bg-white/90 rounded-2xl shadow-xl p-8 max-w-md w-full border border-white/30 flex flex-col items-center">
<div className="animate-spin rounded-full h-10 w-10 border-4 border-indigo-600 border-t-transparent mb"></div>
<p className="text-base text-gray-700">Carregando dados...</p>
</div>
</div>
);

View File

@ -1,5 +1,5 @@
import React from 'react';
import { ArrowDownOnSquareStackIcon, BookOpenIcon, ChartBarIcon, DocumentTextIcon, LightBulbIcon } from '@heroicons/react/24/outline';
import { ArrowDownOnSquareStackIcon, BookOpenIcon, ChartBarIcon, ChartBarSquareIcon, DocumentMagnifyingGlassIcon, DocumentTextIcon, IdentificationIcon, LightBulbIcon, MagnifyingGlassIcon } from '@heroicons/react/24/outline';
import Card from '../shared/Card';
const FeatureCard: React.FC<{ icon: React.ElementType, title: string, children: React.ReactNode }> = ({ icon: Icon, title, children }) => {
@ -23,14 +23,17 @@ const FeaturesSection: React.FC = () => {
<FeatureCard icon={DocumentTextIcon} title="Acesso Simplificado">
Navegue facilmente por dados complexos do TSE com uma interface limpa e amigável.
</FeatureCard>
<FeatureCard icon={ChartBarIcon} title="Visualizações Claras">
Entenda as tendências e padrões com gráficos e resumos visuais dos dados eleitorais.
<FeatureCard icon={IdentificationIcon} title="Visualizações Claras">
Visualização detalhada de perfis, redes sociais e histórico eleitoral.
</FeatureCard>
<FeatureCard icon={LightBulbIcon} title="Insights Valiosos">
Obtenha informações relevantes sobre candidatos, partidos e financiamento de campanhas.
<FeatureCard icon={DocumentMagnifyingGlassIcon} title="Declaração de Bens">
Visualização acerca de bens declarados para cada candidato.
</FeatureCard>
<FeatureCard icon={BookOpenIcon} title="Open Source">
Contribua para um projeto aberto e transparente, ajudando a melhorar a plataforma para todos.
<FeatureCard icon={MagnifyingGlassIcon} title="Informações de Campanha">
Análise de receitas e despesas de campanha quando disponível.
</FeatureCard>
<FeatureCard icon={ChartBarSquareIcon} title="Estatísticas">
Estatísticas e gráficos interativos para entender melhor o cenário eleitoral.
</FeatureCard>
<FeatureCard icon={ArrowDownOnSquareStackIcon} title="Dados Abertos">
Os dados são acessíveis através do TSE e também disponibilizados em nosso repositório GitHub, garantindo transparência e confiabilidade.

View File

@ -8,7 +8,7 @@ const Footer: React.FC = () => {
&copy; {new Date().getFullYear()} OpenCand. Todos os direitos reservados.
</p>
<p className="text-sm">
Democratizando o acesso à informação eleitoral.
Não nos responsabilizamos por quaisquer erros ou inconsistências nos dados apresentados, pois estes são de livre interpretação da plataforma e devem ser verificados com os dados oficiais do TSE.
</p>
</div>
</footer>

View File

@ -6,7 +6,7 @@ const HeroSection: React.FC = () => {
return (
<section
className="min-h-screen flex flex-col justify-center items-center text-white bg-cover bg-center bg-no-repeat bg-gray-900 relative"
style={{ backgroundImage: "url('https://upload.wikimedia.org/wikipedia/commons/thumb/2/21/National_Congress_of_Brazil%2C_Brasilia.jpg/1024px-National_Congress_of_Brazil%2C_Brasilia.jpg')" }}
style={{ backgroundImage: "url('/assets/Congresso_Nacional_hero.jpg')" }}
>
<div className="absolute inset-0 bg-black/60"></div>
<div className="relative z-10 text-center max-w-6xl">
@ -26,6 +26,18 @@ const HeroSection: React.FC = () => {
/>
</div>
</div>
{/* Image credit */}
<span className="absolute left-4 bottom-2 z-20 text-[10px] text-gray-200 bg-black/30 px-1.5 py-0.5 rounded select-none pointer-events-none opacity-55">
Edilson Rodrigues/Agência Senado. Senado Federal,
<a
href="https://creativecommons.org/licenses/by/2.0"
target="_blank"
rel="noopener noreferrer"
className="ml-1 text-gray-200 hover:text-white"
>
CC BY 2.0
</a>, via Wikimedia Commons
</span>
</section>
);
};

View File

@ -0,0 +1,120 @@
import React from 'react';
import { FaGithub, FaLinkedin } from 'react-icons/fa';
const SobrePage: React.FC = () => {
return (
<div className="flex justify-center items-center min-h-[80vh] py-12 px-4">
<div
className="backdrop-blur-md bg-white/90 rounded-2xl shadow-xl p-8 max-w-3xl w-full border border-white/30"
style={{ boxShadow: '0 8px 32px 0 rgba(31, 38, 135, 0.2)' }}
>
<div className="space-y-4 text-gray-700">
<p className="text-lg leading-relaxed">
O <strong>OpenCand</strong> é uma plataforma que visa explorar de forma intuitiva os dados eleitorais brasileiros e possui
o objetivo de ser uma alternativa para o acesso às informações públicas do Tribunal Superior Eleitoral (TSE).
</p>
<p className="leading-relaxed">
O projeto foi desenvolvido por <strong className="inline-flex items-center gap-1">
ivanch
<a
href="https://github.com/ivanch"
target="_blank"
rel="noopener noreferrer"
className="align-middle inline-block hover:scale-110 transition-transform"
aria-label="GitHub"
>
<FaGithub className="text-gray-700 hover:text-black" size={16} />
</a>
<a
href="https://www.linkedin.com/in/joseivanch"
target="_blank"
rel="noopener noreferrer"
className="align-middle inline-block hover:scale-110 transition-transform"
aria-label="LinkedIn"
>
<FaLinkedin className="text-blue-700 hover:text-blue-900" size={16} />
</a>
</strong> como hobby, o código é semi-aberto e está disponível no <strong>GitHub</strong>.
Atualmente apenas o ETL (Extract-Transform-Load) está disponível, juntamente com o banco de dados final.
</p>
<p className="leading-relaxed">
A API também pode ser utilizada para acessar os dados de forma pública, porém ainda não está documentada. A documentação da API será disponibilizada em breve.
</p>
<div className="w-32 h-1 bg-gradient-to-r from-indigo-400 to-purple-400 mx-auto mt-6 rounded-full"></div>
<p className="leading-relaxed">
Não nos responsabilizamos por quaisquer erros ou inconsistências nos dados apresentados, pois estes são de livre interpretação da plataforma e devem ser verificados com os dados oficiais do TSE.
<br />
A plataforma é uma iniciativa independente e não possui qualquer vínculo com o TSE ou órgãos governamentais.
</p>
<div className="w-32 h-1 bg-gradient-to-r from-indigo-400 to-purple-400 mx-auto mt-6 rounded-full"></div>
<div className="mt-6 flex flex-col items-center">
<p className="font-semibold text-gray-800 mb-2 text-center">Links úteis:</p>
<ul className="space-y-2 w-full max-w-md">
<li className="flex items-center justify-center">
<span className="w-2 h-2 bg-indigo-500 rounded-full mr-3"></span>
<a
href="https://divulgacandcontas.tse.jus.br/divulga/#/home"
target="_blank"
rel="noopener noreferrer"
className="text-indigo-700 hover:underline hover:text-indigo-900 transition-colors text-center"
>
DivulgaCand do TSE
</a>
</li>
<li className="flex items-center justify-center">
<span className="w-2 h-2 bg-indigo-500 rounded-full mr-3"></span>
<a
href="https://sig.tse.jus.br/ords/dwapr/r/seai/sig-eleicao/home"
target="_blank"
rel="noopener noreferrer"
className="text-indigo-700 hover:underline hover:text-indigo-900 transition-colors text-center"
>
Estatísticas do TSE
</a>
</li>
<li className="flex items-center justify-center">
<span className="w-2 h-2 bg-indigo-500 rounded-full mr-3"></span>
<a
href="https://dadosabertos.tse.jus.br/dataset/"
target="_blank"
rel="noopener noreferrer"
className="text-indigo-700 hover:underline hover:text-indigo-900 transition-colors text-center"
>
Dataset do TSE
</a>
</li>
</ul>
</div>
<div className="w-32 h-1 bg-gradient-to-r from-indigo-400 to-purple-400 mx-auto mt-6 rounded-full"></div>
<p className="leading-relaxed">
Contato: {[
'opencand',
<span key="at" style={{ userSelect: 'text' }}>@</span>,
'ivanch',
<span key="dot" style={{ userSelect: 'text' }}>.</span>,
'me'
]}
</p>
<div className="mt-6 pt-4 border-t border-gray-300">
<p className="text-sm text-gray-600 text-center">
Desenvolvido com .NET, PostgreSQL, React (TypeScript/Tailwind CSS) e dados abertos do TSE.<br />
<span className="text-xs">
</span>
</p>
</div>
</div>
</div>
</div>
);
};
export default SobrePage;

View File

@ -9,30 +9,44 @@ interface RandomCandButtonProps {
hasAnimation?: boolean;
}
const RandomCandButton: React.FC<RandomCandButtonProps> = ({
className = '',
hasAnimation = false
}) => {
const navigate = useNavigate();
const [loading, setLoading] = React.useState(false);
const handleRandomCandidate = async () => {
setLoading(true);
try {
const randomCandidate = await openCandApi.getRandomCandidate();
navigate(`/candidato/${randomCandidate.idCandidato}`);
} catch (error) {
console.error('Erro ao buscar candidato aleatório:', error);
// You might want to show a toast notification or error message to the user
} finally {
setLoading(false);
}
};
return (
<Button
onClick={handleRandomCandidate}
className={`flex items-center ${className}`}
className={`flex items-center relative overflow-hidden ${className}`}
hasAnimation={hasAnimation}
disabled={loading}
>
<ArrowPathIcon
className={`h-4 w-4 transition-transform duration-500 ${loading ? 'animate-spin' : 'mr-2'}`}
style={{ zIndex: 2 }}
/>
<span
className={`inline-block transition-all duration-300 origin-left whitespace-nowrap ${loading ? 'scale-x-0 max-w-0' : 'scale-x-100 max-w-xs'}`}
style={{ zIndex: 1, transitionProperty: 'transform, max-width' }}
>
<ArrowPathIcon className="h-4 w-4 mr-2" />
Candidato aleatório
</span>
</Button>
);
};