Compare commits
4 Commits
8090fd13a3
...
720596f6f8
Author | SHA1 | Date | |
---|---|---|---|
720596f6f8 | |||
dd2af50722 | |||
f56b030435 | |||
4e12ab32e8 |
33
src/App.tsx
33
src/App.tsx
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { Routes, Route } from 'react-router-dom';
|
import { Routes, Route, useLocation } from 'react-router-dom';
|
||||||
import Navbar from './components/Navbar';
|
import Navbar from './components/Navbar';
|
||||||
import HeroSection from './components/HeroSection';
|
import HeroSection from './components/HeroSection';
|
||||||
import StatisticsSection from './components/StatisticsSection';
|
import StatisticsSection from './components/StatisticsSection';
|
||||||
@@ -14,7 +14,19 @@ import MatrixBackground from './components/MatrixBackground';
|
|||||||
import './App.css';
|
import './App.css';
|
||||||
|
|
||||||
// HomePage component
|
// HomePage component
|
||||||
const HomePage: React.FC = () => (
|
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">
|
<main className="flex-grow">
|
||||||
<HeroSection />
|
<HeroSection />
|
||||||
<div className="w-32 h-1 bg-gradient-to-r from-indigo-400 to-purple-400 mx-auto mt-12 rounded-full"></div>
|
<div className="w-32 h-1 bg-gradient-to-r from-indigo-400 to-purple-400 mx-auto mt-12 rounded-full"></div>
|
||||||
@@ -22,9 +34,22 @@ const HomePage: React.FC = () => (
|
|||||||
<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="w-32 h-1 bg-gradient-to-r from-indigo-400 to-purple-400 mx-auto mt-6 rounded-full"></div>
|
||||||
<FeaturesSection />
|
<FeaturesSection />
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
function App() {
|
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 (
|
return (
|
||||||
<div className="min-h-screen w-full flex flex-col relative" style={{ backgroundColor: 'transparent' }}>
|
<div className="min-h-screen w-full flex flex-col relative" style={{ backgroundColor: 'transparent' }}>
|
||||||
<MatrixBackground />
|
<MatrixBackground />
|
||||||
|
@@ -2,6 +2,7 @@ import { BaseApiClient } from './base';
|
|||||||
import { API_CONFIG } from '../config/api';
|
import { API_CONFIG } from '../config/api';
|
||||||
import type { CandidateAssets, CandidateDetails, CandidateExpenses, CandidateIncome, CandidateRedesSociais, CandidateSearchResult, CpfRevealResult, OpenCandDataAvailabilityStats, OpenCandDatabaseStats, PlatformStats, RandomCandidate } from './apiModels';
|
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 { EnrichmentResponse, StatisticsConfig, ValueSumRequest, ValueSumResponse } from './apiStatisticsModels';
|
||||||
|
import type { StatisticsRequestFilters, StatisticsRequestOptions } from '../components/StatisticsPage/statisticsRequests';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OpenCand API client for interacting with the OpenCand platform
|
* OpenCand API client for interacting with the OpenCand platform
|
||||||
@@ -101,8 +102,28 @@ export class OpenCandApi extends BaseApiClient {
|
|||||||
/**
|
/**
|
||||||
* Get the enrichment statistics for candidates
|
* Get the enrichment statistics for candidates
|
||||||
*/
|
*/
|
||||||
async getStatisticsEnrichment(): Promise<EnrichmentResponse[]> {
|
async getStatisticsEnrichment(filters?: StatisticsRequestFilters): Promise<EnrichmentResponse[]> {
|
||||||
return this.get<EnrichmentResponse[]>(`/v1/estatistica/enriquecimento`, { timeout: 90000 });
|
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 SearchBar from './SearchBar';
|
||||||
import RandomCandButton from '../shared/RandomCandButton';
|
import RandomCandButton from '../shared/RandomCandButton';
|
||||||
|
|
||||||
const HeroSection: React.FC = () => {
|
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 (
|
return (
|
||||||
<section
|
<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"
|
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="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">
|
<div className="relative z-10 text-center max-w-6xl">
|
||||||
<h1 className="text-5xl md:text-7xl font-bold mb-6">
|
<h1 className="text-5xl md:text-7xl font-bold mb-6">
|
||||||
Explore Dados Eleitorais
|
{header}
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-lg md:text-xl mb-10 text-gray-300">
|
<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).
|
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 */}
|
{/* Party Filter */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="block text-sm font-semibold text-gray-600 uppercase tracking-wide">
|
<label className="block text-sm font-semibold text-gray-600 uppercase tracking-wide">
|
||||||
Partido (Opcional)
|
Partido
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={localFilters.partido || ''}
|
value={localFilters.partido || ''}
|
||||||
@@ -99,7 +99,7 @@ const StatisticsFilters: React.FC<StatisticsFiltersProps> = ({
|
|||||||
{/* UF Filter */}
|
{/* UF Filter */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="block text-sm font-semibold text-gray-600 uppercase tracking-wide">
|
<label className="block text-sm font-semibold text-gray-600 uppercase tracking-wide">
|
||||||
UF (Opcional)
|
UF
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={localFilters.uf || ''}
|
value={localFilters.uf || ''}
|
||||||
@@ -119,7 +119,7 @@ const StatisticsFilters: React.FC<StatisticsFiltersProps> = ({
|
|||||||
{/* Year Filter */}
|
{/* Year Filter */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="block text-sm font-semibold text-gray-600 uppercase tracking-wide">
|
<label className="block text-sm font-semibold text-gray-600 uppercase tracking-wide">
|
||||||
Ano (Opcional)
|
Ano
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={localFilters.ano || ''}
|
value={localFilters.ano || ''}
|
||||||
@@ -139,7 +139,7 @@ const StatisticsFilters: React.FC<StatisticsFiltersProps> = ({
|
|||||||
{/* Cargo Filter */}
|
{/* Cargo Filter */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="block text-sm font-semibold text-gray-600 uppercase tracking-wide">
|
<label className="block text-sm font-semibold text-gray-600 uppercase tracking-wide">
|
||||||
Cargo (Opcional)
|
Cargo
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={localFilters.cargo || ''}
|
value={localFilters.cargo || ''}
|
||||||
|
@@ -24,12 +24,14 @@ export interface StatisticsData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface StatisticsRequestOptions {
|
export interface StatisticsRequestOptions {
|
||||||
filters?: {
|
filters?: StatisticsRequestFilters;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StatisticsRequestFilters {
|
||||||
partido?: string | null;
|
partido?: string | null;
|
||||||
uf?: string | null;
|
uf?: string | null;
|
||||||
ano?: number | null;
|
ano?: number | null;
|
||||||
cargo?: CargoFilter;
|
cargo?: CargoFilter;
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// First Row Requests
|
// First Row Requests
|
||||||
@@ -44,9 +46,9 @@ export async function getCandidatesWithMostAssets(options?: StatisticsRequestOpt
|
|||||||
return Array.isArray(response) ? response : [response];
|
return Array.isArray(response) ? response : [response];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getEnrichmentData(): Promise<EnrichmentResponse[] | null> {
|
export async function getEnrichmentData(filters?: StatisticsRequestFilters): Promise<EnrichmentResponse[] | null> {
|
||||||
try {
|
try {
|
||||||
return await openCandApi.getStatisticsEnrichment();
|
return await openCandApi.getStatisticsEnrichment(filters);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching enrichment data:', error);
|
console.error('Error fetching enrichment data:', error);
|
||||||
return null;
|
return null;
|
||||||
@@ -167,7 +169,7 @@ export async function fetchAllStatisticsData(options?: StatisticsRequestOptions)
|
|||||||
statesWithMostRevenue
|
statesWithMostRevenue
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
getCandidatesWithMostAssets(options),
|
getCandidatesWithMostAssets(options),
|
||||||
getEnrichmentData(),
|
getEnrichmentData(options?.filters),
|
||||||
getCandidatesWithMostRevenue(options),
|
getCandidatesWithMostRevenue(options),
|
||||||
getCandidatesWithMostExpenses(options),
|
getCandidatesWithMostExpenses(options),
|
||||||
getPartiesWithMostAssets(options),
|
getPartiesWithMostAssets(options),
|
||||||
|
@@ -12,7 +12,14 @@ const firebaseConfig = {
|
|||||||
measurementId: import.meta.env.VITE_FIREBASE_MEASUREMENT_ID
|
measurementId: import.meta.env.VITE_FIREBASE_MEASUREMENT_ID
|
||||||
};
|
};
|
||||||
|
|
||||||
const app = initializeApp(firebaseConfig);
|
let app = null;
|
||||||
const analytics = getAnalytics(app);
|
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 };
|
export { app, analytics };
|
||||||
|
Reference in New Issue
Block a user