add random candidato
All checks were successful
Frontend Build and Deploy / build (push) Successful in 1m2s

This commit is contained in:
José Henrique 2025-06-10 20:39:19 -03:00
parent 1e4f288ec4
commit 2764dbdc4e
6 changed files with 76 additions and 12 deletions

View File

@ -3,6 +3,10 @@ export interface CandidateSearchResult {
candidatos: Candidate[]; candidatos: Candidate[];
} }
export interface RandomCandidate {
idCandidato: string;
}
export interface Candidate { export interface Candidate {
idCandidato: string; idCandidato: string;
nome: string; nome: string;

View File

@ -1,6 +1,6 @@
import { BaseApiClient } from './base'; 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, PlatformStats } from './apiModels'; import type { CandidateAssets, CandidateDetails, CandidateExpenses, CandidateIncome, CandidateRedesSociais, CandidateSearchResult, CpfRevealResult, OpenCandDataAvailabilityStats, PlatformStats, RandomCandidate } from './apiModels';
/** /**
* OpenCand API client for interacting with the OpenCand platform * OpenCand API client for interacting with the OpenCand platform
@ -34,6 +34,13 @@ export class OpenCandApi extends BaseApiClient {
return this.get<CandidateSearchResult>(`/v1/candidato/search?q=${encodedQuery}`); return this.get<CandidateSearchResult>(`/v1/candidato/search?q=${encodedQuery}`);
} }
/**
* Get a random candidate from the database
*/
async getRandomCandidate(): Promise<RandomCandidate> {
return this.get<RandomCandidate>(`/v1/candidato/random`);
}
/** /**
* Get detailed information about a specific candidate by ID * Get detailed information about a specific candidate by ID
*/ */

View File

@ -8,6 +8,7 @@ import BasicCandidateInfoComponent from './BasicCandidateInfoComponent';
import SocialMediaComponent from './SocialMediaComponent'; import SocialMediaComponent from './SocialMediaComponent';
import IncomeExpenseComponent from './IncomeExpenseComponent'; import IncomeExpenseComponent from './IncomeExpenseComponent';
import Button from '../../shared/Button'; import Button from '../../shared/Button';
import RandomCandButton from '../../shared/RandomCandButton';
import ErrorPage from '../ErrorPage'; import ErrorPage from '../ErrorPage';
const CandidatePage: React.FC = () => { const CandidatePage: React.FC = () => {
@ -126,14 +127,21 @@ const CandidatePage: React.FC = () => {
<main className="flex-grow p-6 max-w-7xl mx-auto"> <main className="flex-grow p-6 max-w-7xl mx-auto">
{/* Header with back button */} {/* Header with back button */}
<div className="mb-6"> <div className="mb-6">
<Button <div className="flex items-center gap-4 mb-4">
onClick={() => navigate('/')} <Button
className="flex items-center text-white mb-4" onClick={() => navigate('/')}
hasAnimation={false} className="flex items-center text-white"
> hasAnimation={false}
<ArrowLeftIcon className="h-5 w-5 mr-2" /> >
Voltar à busca <ArrowLeftIcon className="h-5 w-5 mr-2" />
</Button> Voltar à busca
</Button>
<RandomCandButton
className="flex items-center text-white"
hasAnimation={false}
/>
</div>
{candidateDetails && ( {candidateDetails && (
<h1 className="text-3xl font-bold text-white">{candidateDetails.nome}</h1> <h1 className="text-3xl font-bold text-white">{candidateDetails.nome}</h1>

View File

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import SearchBar from './SearchBar'; import SearchBar from './SearchBar';
import RandomCandButton from '../shared/RandomCandButton';
const HeroSection: React.FC = () => { const HeroSection: React.FC = () => {
return ( return (
@ -17,6 +18,13 @@ const HeroSection: React.FC = () => {
</p> </p>
<SearchBar /> <SearchBar />
<div className="mt-6 flex justify-center">
<RandomCandButton
className='hover:bg-white/15 hover:scale-[1.01] transition-all duration-200 duration-300 ease-in-out bg-white/5 backdrop-blur-sm'
hasAnimation={false}
/>
</div>
</div> </div>
</section> </section>
); );

View File

@ -31,12 +31,9 @@ const Button: React.FC<ButtonProps> = ({
rounded-full rounded-full
backdrop-blur-xs backdrop-blur-xs
shadow-xl shadow-xl
ring-1
transition-all transition-all
duration-200 duration-200
hover:ring-indigo-400/20
hover:bg-gray-700/40 hover:bg-gray-700/40
ring-gray-900
text-white text-white
transition-colors transition-colors
${animationClasses} ${cursorClasses} ${className}`; ${animationClasses} ${cursorClasses} ${className}`;

View File

@ -0,0 +1,40 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import { ArrowPathIcon } from '@heroicons/react/24/outline';
import Button from './Button';
import { openCandApi } from '../api/openCandApi';
interface RandomCandButtonProps {
className?: string;
hasAnimation?: boolean;
}
const RandomCandButton: React.FC<RandomCandButtonProps> = ({
className = '',
hasAnimation = false
}) => {
const navigate = useNavigate();
const handleRandomCandidate = async () => {
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
}
};
return (
<Button
onClick={handleRandomCandidate}
className={`flex items-center ${className}`}
hasAnimation={hasAnimation}
>
<ArrowPathIcon className="h-4 w-4 mr-2" />
Candidato aleatório
</Button>
);
};
export default RandomCandButton;