Compare commits
4 Commits
8090fd13a3
...
720596f6f8
Author | SHA1 | Date | |
---|---|---|---|
720596f6f8 | |||
dd2af50722 | |||
f56b030435 | |||
4e12ab32e8 |
47
src/App.tsx
47
src/App.tsx
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Routes, Route } from 'react-router-dom';
|
||||
import React, { useEffect } from 'react';
|
||||
import { Routes, Route, useLocation } from 'react-router-dom';
|
||||
import Navbar from './components/Navbar';
|
||||
import HeroSection from './components/HeroSection';
|
||||
import StatisticsSection from './components/StatisticsSection';
|
||||
@@ -14,17 +14,42 @@ import MatrixBackground from './components/MatrixBackground';
|
||||
import './App.css';
|
||||
|
||||
// HomePage component
|
||||
const HomePage: React.FC = () => (
|
||||
<main className="flex-grow">
|
||||
<HeroSection />
|
||||
<div className="w-32 h-1 bg-gradient-to-r from-indigo-400 to-purple-400 mx-auto mt-12 rounded-full"></div>
|
||||
<StatisticsSection />
|
||||
<div className="w-32 h-1 bg-gradient-to-r from-indigo-400 to-purple-400 mx-auto mt-6 rounded-full"></div>
|
||||
<FeaturesSection />
|
||||
</main>
|
||||
);
|
||||
const HomePage: React.FC = () => {
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
if (location.hash) {
|
||||
const element = document.getElementById(location.hash.substring(1));
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
}
|
||||
}, [location]);
|
||||
|
||||
return (
|
||||
<main className="flex-grow">
|
||||
<HeroSection />
|
||||
<div className="w-32 h-1 bg-gradient-to-r from-indigo-400 to-purple-400 mx-auto mt-12 rounded-full"></div>
|
||||
<StatisticsSection />
|
||||
<div className="w-32 h-1 bg-gradient-to-r from-indigo-400 to-purple-400 mx-auto mt-6 rounded-full"></div>
|
||||
<FeaturesSection />
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
function App() {
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
const segment = location.hash;
|
||||
if (segment) {
|
||||
const element = document.getElementById(segment.replace('#', ''));
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
}
|
||||
}, [location]);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen w-full flex flex-col relative" style={{ backgroundColor: 'transparent' }}>
|
||||
<MatrixBackground />
|
||||
|
@@ -2,6 +2,7 @@ import { BaseApiClient } from './base';
|
||||
import { API_CONFIG } from '../config/api';
|
||||
import type { CandidateAssets, CandidateDetails, CandidateExpenses, CandidateIncome, CandidateRedesSociais, CandidateSearchResult, CpfRevealResult, OpenCandDataAvailabilityStats, OpenCandDatabaseStats, PlatformStats, RandomCandidate } from './apiModels';
|
||||
import type { EnrichmentResponse, StatisticsConfig, ValueSumRequest, ValueSumResponse } from './apiStatisticsModels';
|
||||
import type { StatisticsRequestFilters, StatisticsRequestOptions } from '../components/StatisticsPage/statisticsRequests';
|
||||
|
||||
/**
|
||||
* OpenCand API client for interacting with the OpenCand platform
|
||||
@@ -101,8 +102,28 @@ export class OpenCandApi extends BaseApiClient {
|
||||
/**
|
||||
* Get the enrichment statistics for candidates
|
||||
*/
|
||||
async getStatisticsEnrichment(): Promise<EnrichmentResponse[]> {
|
||||
return this.get<EnrichmentResponse[]>(`/v1/estatistica/enriquecimento`, { timeout: 90000 });
|
||||
async getStatisticsEnrichment(filters?: StatisticsRequestFilters): Promise<EnrichmentResponse[]> {
|
||||
let url = `/v1/estatistica/enriquecimento`;
|
||||
if (filters) {
|
||||
const params = new URLSearchParams();
|
||||
if (filters.partido !== null && filters.partido !== undefined) {
|
||||
params.append('partido', filters.partido);
|
||||
}
|
||||
if (filters.uf !== null && filters.uf !== undefined) {
|
||||
params.append('uf', filters.uf);
|
||||
}
|
||||
if (filters.ano !== null && filters.ano !== undefined) {
|
||||
params.append('ano', String(filters.ano));
|
||||
}
|
||||
if (filters.cargo !== null && filters.cargo !== undefined) {
|
||||
params.append('cargo', filters.cargo);
|
||||
}
|
||||
const queryString = params.toString();
|
||||
if (queryString) {
|
||||
url += `?${queryString}`;
|
||||
}
|
||||
}
|
||||
return this.get<EnrichmentResponse[]>(url, { timeout: 90000 });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,8 +1,23 @@
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import SearchBar from './SearchBar';
|
||||
import RandomCandButton from '../shared/RandomCandButton';
|
||||
|
||||
const HeroSection: React.FC = () => {
|
||||
const headers = [
|
||||
"Explore Dados Eleitorais",
|
||||
"Analise Dados Eleitorais",
|
||||
"Consulte Dados Eleitorais",
|
||||
"Verifique Dados Eleitorais",
|
||||
"Descubra Dados Eleitorais",
|
||||
"Acesse Informações Eleitorais",
|
||||
"Verifique Candidatos",
|
||||
"Descubra Candidatos",
|
||||
"Pesquise Candidaturas",
|
||||
"Consulte Candidatos",
|
||||
"Navegue pelos Dados do TSE"
|
||||
];
|
||||
const [header] = useState(headers[Math.floor(Math.random() * headers.length)]);
|
||||
|
||||
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"
|
||||
@@ -12,7 +27,7 @@ const HeroSection: React.FC = () => {
|
||||
<div className="absolute bottom-0 left-0 right-0 h-24 bg-gradient-to-t from-[#0a0f1a] to-transparent"></div>
|
||||
<div className="relative z-10 text-center max-w-6xl">
|
||||
<h1 className="text-5xl md:text-7xl font-bold mb-6">
|
||||
Explore Dados Eleitorais
|
||||
{header}
|
||||
</h1>
|
||||
<p className="text-lg md:text-xl mb-10 text-gray-300">
|
||||
OpenCand oferece acesso fácil e visualizações intuitivas de dados abertos do Tribunal Superior Eleitoral (TSE).
|
||||
|
@@ -79,7 +79,7 @@ const StatisticsFilters: React.FC<StatisticsFiltersProps> = ({
|
||||
{/* Party Filter */}
|
||||
<div className="space-y-2">
|
||||
<label className="block text-sm font-semibold text-gray-600 uppercase tracking-wide">
|
||||
Partido (Opcional)
|
||||
Partido
|
||||
</label>
|
||||
<select
|
||||
value={localFilters.partido || ''}
|
||||
@@ -99,7 +99,7 @@ const StatisticsFilters: React.FC<StatisticsFiltersProps> = ({
|
||||
{/* UF Filter */}
|
||||
<div className="space-y-2">
|
||||
<label className="block text-sm font-semibold text-gray-600 uppercase tracking-wide">
|
||||
UF (Opcional)
|
||||
UF
|
||||
</label>
|
||||
<select
|
||||
value={localFilters.uf || ''}
|
||||
@@ -119,7 +119,7 @@ const StatisticsFilters: React.FC<StatisticsFiltersProps> = ({
|
||||
{/* Year Filter */}
|
||||
<div className="space-y-2">
|
||||
<label className="block text-sm font-semibold text-gray-600 uppercase tracking-wide">
|
||||
Ano (Opcional)
|
||||
Ano
|
||||
</label>
|
||||
<select
|
||||
value={localFilters.ano || ''}
|
||||
@@ -139,7 +139,7 @@ const StatisticsFilters: React.FC<StatisticsFiltersProps> = ({
|
||||
{/* Cargo Filter */}
|
||||
<div className="space-y-2">
|
||||
<label className="block text-sm font-semibold text-gray-600 uppercase tracking-wide">
|
||||
Cargo (Opcional)
|
||||
Cargo
|
||||
</label>
|
||||
<select
|
||||
value={localFilters.cargo || ''}
|
||||
|
@@ -24,12 +24,14 @@ export interface StatisticsData {
|
||||
}
|
||||
|
||||
export interface StatisticsRequestOptions {
|
||||
filters?: {
|
||||
partido?: string | null;
|
||||
uf?: string | null;
|
||||
ano?: number | null;
|
||||
cargo?: CargoFilter;
|
||||
};
|
||||
filters?: StatisticsRequestFilters;
|
||||
}
|
||||
|
||||
export interface StatisticsRequestFilters {
|
||||
partido?: string | null;
|
||||
uf?: string | null;
|
||||
ano?: number | null;
|
||||
cargo?: CargoFilter;
|
||||
}
|
||||
|
||||
// First Row Requests
|
||||
@@ -44,9 +46,9 @@ export async function getCandidatesWithMostAssets(options?: StatisticsRequestOpt
|
||||
return Array.isArray(response) ? response : [response];
|
||||
}
|
||||
|
||||
export async function getEnrichmentData(): Promise<EnrichmentResponse[] | null> {
|
||||
export async function getEnrichmentData(filters?: StatisticsRequestFilters): Promise<EnrichmentResponse[] | null> {
|
||||
try {
|
||||
return await openCandApi.getStatisticsEnrichment();
|
||||
return await openCandApi.getStatisticsEnrichment(filters);
|
||||
} catch (error) {
|
||||
console.error('Error fetching enrichment data:', error);
|
||||
return null;
|
||||
@@ -167,7 +169,7 @@ export async function fetchAllStatisticsData(options?: StatisticsRequestOptions)
|
||||
statesWithMostRevenue
|
||||
] = await Promise.all([
|
||||
getCandidatesWithMostAssets(options),
|
||||
getEnrichmentData(),
|
||||
getEnrichmentData(options?.filters),
|
||||
getCandidatesWithMostRevenue(options),
|
||||
getCandidatesWithMostExpenses(options),
|
||||
getPartiesWithMostAssets(options),
|
||||
|
@@ -12,7 +12,14 @@ const firebaseConfig = {
|
||||
measurementId: import.meta.env.VITE_FIREBASE_MEASUREMENT_ID
|
||||
};
|
||||
|
||||
const app = initializeApp(firebaseConfig);
|
||||
const analytics = getAnalytics(app);
|
||||
let app = null;
|
||||
let analytics = null;
|
||||
|
||||
if (firebaseConfig.apiKey) {
|
||||
app = initializeApp(firebaseConfig);
|
||||
analytics = getAnalytics(app);
|
||||
} else {
|
||||
console.warn("Firebase API key is not set. Firebase will not be initialized.");
|
||||
}
|
||||
|
||||
export { app, analytics };
|
||||
|
Reference in New Issue
Block a user