Skip to content

Commit

Permalink
Merge branch 'candidate-info' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
Remus287 committed Dec 14, 2024
2 parents f7dc3e0 + 6088a20 commit 12fd08c
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 117 deletions.
Binary file added client/public/bva_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 19 additions & 5 deletions client/src/components/nav/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,10 @@ const links: Record<string, string> = {
'Drop Box Locations': '/dropBoxLocations'
};


function NavBar() {
const [anchorElNav, setAnchorElNav] = React.useState<null | HTMLElement>(null);

const router = useRouter()
const router = useRouter();

const handleOpenNavMenu = (event: React.MouseEvent<HTMLElement>) => {
setAnchorElNav(event.currentTarget);
Expand Down Expand Up @@ -69,8 +68,22 @@ function NavBar() {
<AppBar position="fixed" className="bg-gradient-custom shadow-none text-gray-800 my-0" style={{ zIndex: 1000, top: 0, width: '100%' }}>
<Container maxWidth="xl">
<Toolbar disableGutters>



{/* Boston Voter Logo */}
<Box sx={{ display: { xs: 'none', md: 'none', lg: 'flex' }, mr: 0 }}>
<img
src="/LogoTest.svg"
alt="Boston Voter Logo"
style={{ height: '60px', cursor: 'pointer', padding: 10 }}
onClick={() => handleClick('Upcoming Elections')}
/>
</Box>


{/* BELOW IS FOR STANDARD NAVBAR */}
<StarIcon sx={{ display: { xs: 'none', md: 'none', lg: 'flex' }, mr: 1, fontSize: '20px', color: '#204cdc' }} /> {/* REPLACE WITH STAR LOGO */}
{/* <StarIcon sx={{ display: { xs: 'none', md: 'none', lg: 'flex' }, mr: 1, fontSize: '20px', color: '#204cdc' }} />
<Typography
variant="h6"
noWrap
Expand All @@ -90,7 +103,8 @@ function NavBar() {
>
Boston Voter
</Typography>
</Typography> */}


{/* Page links below */}
{/* This appears to be useless code */}
Expand Down Expand Up @@ -202,5 +216,5 @@ function NavBar() {
</AppBar>
);
}
export default NavBar;

export default NavBar;
216 changes: 104 additions & 112 deletions client/src/pages/candidateInfo/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
'use client';
import React, { useEffect, useState } from 'react';
import SubscribePopup from '../../components/subscribePopup/SubscribePopup';
import { CandidateAPI } from '@/common'; // use this instead of hardcoded link
import { useRouter } from 'next/router';

interface Candidate {
id: number;
Expand All @@ -11,7 +8,6 @@ interface Candidate {
District: string;
Party: string;
ElectionName: string;
Office: string;
Bio?: string;
CampaignSiteLink?: string;
LinkedInLink?: string;
Expand All @@ -24,27 +20,30 @@ const parties = ['Democrat', 'Republican', 'Independent', 'Non Partisan', 'Other
const electionTypes = ['Federal Election', 'State Election', 'Municipal Election', 'Special Election', 'Primary Election', 'Ballot Questions/Referendum'];
const districts = ['District 1', 'District 2', 'District 3', 'District 4']; // Example districts, replace with actual

// Component for Candidate Information Page
export default function CandidateInfo() {
const [candidates, setCandidates] = useState<Candidate[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [expandedCandidateId, setExpandedCandidateId] = useState<number | null>(null);
const [filteredCandidates, setFilteredCandidates] = useState<Candidate[]>([]);
const [filters, setFilters] = useState({
party: '',
electionType: '',
district: '',
});
const [showPopup, setShowPopup] = useState(false);

const router = useRouter();

useEffect(() => {
const fetchCandidateData = async () => {
console.log("Fetching candidate data...");
try {
const response = await fetch('https://pitne-voter-app-production.up.railway.app/api/candidates?populate=Headshot');
console.log("API Response Status:", response.status);

if (response.ok) {
const data = await response.json();
console.log("Fetched data:", data);

if (data.data && data.data.length > 0) {
const fetchedCandidates: Candidate[] = data.data.map((candidate: any) => {
const headshotUrl = candidate.attributes.Headshot?.data?.attributes?.url
Expand All @@ -58,40 +57,30 @@ export default function CandidateInfo() {
},
};
});
console.log("Fetched Candidates with Headshot URL:", fetchedCandidates);
setCandidates(fetchedCandidates);
setFilteredCandidates(fetchedCandidates);
setFilteredCandidates(fetchedCandidates); // Set initial filtered candidates
} else {
console.warn("No candidate data available.");
setError("No candidate data available.");
}
} else {
console.error('Failed to fetch candidate data', response.statusText);
setError('Failed to fetch candidate data');
}
} catch {
} catch (fetchError) {
console.error('Error:', fetchError);
setError('An error occurred while fetching candidate data');
} finally {
setIsLoading(false);
}
};

fetchCandidateData();

// Show popup after a delay (e.g., 1 seconds)
const popupTimer = setTimeout(() => {
setShowPopup(true);
}, 1000);

// Cleanup timer when component unmounts
return () => clearTimeout(popupTimer);

}, []);

const handleClosePopup = () => {
setShowPopup(false);
};

const handleCandidateClick = (name: string) => {
const formattedName = name.replace(/\s+/g, '').toLowerCase(); // Ensure it matches profile URL structure
router.push(`/candidateInfo/${formattedName}`); // Navigate to the candidate's profile page
const toggleExpand = (candidateId: number) => {
setExpandedCandidateId(prevId => (prevId === candidateId ? null : candidateId));
};

const handleFilterChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
Expand All @@ -102,14 +91,7 @@ export default function CandidateInfo() {
}));
};

const handleResetFilters = () => {
setFilters({
party: '',
electionType: '',
district: '',
});
};

// Filter candidates based on selected filters
useEffect(() => {
const filtered = candidates.filter(candidate => {
const matchesParty = filters.party ? candidate.attributes.Party === filters.party : true;
Expand All @@ -126,124 +108,134 @@ export default function CandidateInfo() {
return (
<div style={{ display: 'flex', paddingTop: '120px' }}>
{/* Sidebar for Filters */}
<div style={{ width: '25%', padding: '20px', backgroundColor: '#f0f0f0', boxShadow: '2px 0 5px rgba(0, 0, 0, 0.1)' }}>
<h2>Filter Candidates</h2>

<div>
<label htmlFor="party-filter">Political Affiliation:</label>
<select id="party-filter" name="party" value={filters.party} onChange={handleFilterChange}>
<div style={{
width: '25%',
padding: '20px',
backgroundColor: '#f0f0f0',
boxShadow: '2px 0 5px rgba(0, 0, 0, 0.1)',
borderRadius: '10px',
marginRight: '20px'
}}>
<h2 style={{ marginBottom: '20px' }}>Filter Candidates</h2>

<div style={{ marginBottom: '15px' }}>
<label htmlFor="party-filter" style={{ display: 'block', marginBottom: '5px' }}>Political Affiliation:</label>
<select
id="party-filter"
name="party"
value={filters.party}
onChange={handleFilterChange}
style={{ width: '100%', padding: '8px' }}
>
<option value="">All</option>
{parties.map(party => (
<option key={party} value={party}>{party}</option>
))}
</select>
</div>

<div style={{ marginTop: '10px' }}>
<label htmlFor="election-filter">Election Type:</label>
<select id="election-filter" name="electionType" value={filters.electionType} onChange={handleFilterChange}>

<div style={{ marginBottom: '15px' }}>
<label htmlFor="election-filter" style={{ display: 'block', marginBottom: '5px' }}>Election Type:</label>
<select
id="election-filter"
name="electionType"
value={filters.electionType}
onChange={handleFilterChange}
style={{ width: '100%', padding: '8px' }}
>
<option value="">All</option>
{electionTypes.map(type => (
<option key={type} value={type}>{type}</option>
))}
</select>
</div>

<div style={{ marginTop: '10px' }}>
<label htmlFor="district-filter">District:</label>
<select id="district-filter" name="district" value={filters.district} onChange={handleFilterChange}>

<div style={{ marginBottom: '15px' }}>
<label htmlFor="district-filter" style={{ display: 'block', marginBottom: '5px' }}>District:</label>
<select
id="district-filter"
name="district"
value={filters.district}
onChange={handleFilterChange}
style={{ width: '100%', padding: '8px' }}
>
<option value="">All</option>
{districts.map(district => (
<option key={district} value={district}>{district}</option>
))}
</select>
</div>

{/* Reset Filters Button */}
<button
style={{
marginTop: '20px',
padding: '10px',
backgroundColor: '#007bff',
color: '#fff',
border: 'none',
borderRadius: '5px',
cursor: 'pointer',
width: '100%',
}}
onClick={handleResetFilters}
>
Reset Filters
</button>
</div>

{/* Main Content */}
<div style={{ width: '75%', padding: '20px' }}>
{filteredCandidates.length > 0 ? (
filteredCandidates.map(candidate => (
<div
key={candidate.id}
className="candidate-card"
style={{
<div
key={candidate.id}
className="candidate-card"
style={{
backgroundColor: 'White',
boxShadow: '0px 4px 5px rgba(0, 0, 0, 0.5)',
border: '2px solid #ccc',
padding: '10px',
margin: '10px',
borderRadius: '5px',
}}
border: '2px solid #ccc',
padding: '15px',
marginBottom: '20px',
cursor: 'pointer',
borderRadius: '10px',
}}
onClick={() => toggleExpand(candidate.id)}
>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
{/* Candidate Card Layout */}
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ display: 'flex', alignItems: 'center' }}>
{candidate.attributes.PhotoURL && (
<img
src={candidate.attributes.PhotoURL}
alt={candidate.attributes.Name}
style={{ width: '80px', height: '80px', objectFit: 'cover', borderRadius: '5px', marginRight: '10px' }}
<img
src={candidate.attributes.PhotoURL}
alt={candidate.attributes.Name}
style={{
width: '80px',
height: '80px',
objectFit: 'cover',
borderRadius: '5px',
marginRight: '15px'
}}
/>
)}
<div>
<h2 style={{ margin: 0, fontWeight: 'bold' }}>{candidate.attributes.Name}</h2>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '10px' }}>
<p style={{ margin: '5px 0' }}>
<strong>Party:</strong> {candidate.attributes.Party}
</p>
<p style={{ margin: '5px 0' }}>
<strong>Office:</strong> {candidate.attributes.Role}
</p>
<p style={{ margin: '5px 0' }}>
<strong>Election:</strong> {candidate.attributes.ElectionName}
</p>
<p style={{ margin: '5px 0' }}>
<strong>District:</strong> {candidate.attributes.District}
</p>
</div>
<h2 style={{ margin: 0 }}>{candidate.attributes.Name}</h2>
<p style={{ margin: '5px 0' }}><strong>Party:</strong> {candidate.attributes.Party}</p>
<p style={{ margin: '5px 0' }}><strong>Election:</strong> {candidate.attributes.ElectionName}</p>
</div>
</div>
<button
style={{
backgroundColor: '#007bff',
color: '#fff',
border: 'none',
borderRadius: '5px',
padding: '8px 12px',
cursor: 'pointer',
}}
onClick={() => handleCandidateClick(candidate.attributes.Name)}
>
More Info
</button>
{/* Arrow Icon */}
<div style={{ fontSize: '20px', marginLeft: '10px' }}>
</div>
</div>

{/* Expandable Details */}
{expandedCandidateId === candidate.id && (
<div className="candidate-details" style={{ marginTop: '15px', padding: '10px', borderTop: '1px solid #ccc' }}>
<p><strong>District:</strong> {candidate.attributes.District}</p>
<p><strong>Bio:</strong> {candidate.attributes.Bio}</p>

<div className="questionnaire-section">
<h3>Questionnaire</h3>
{Array.from({ length: 5 }, (_, index) => (
<div key={index}>
<strong>Question {index + 1}:</strong>
<p>{candidate.attributes[`Q${index + 1}`]}</p>
</div>
))}
</div>
</div>
)}
</div>
))
) : (
<p>No candidates match the selected filters.</p>
<p>No candidates found matching the selected filters.</p>
)}
</div>

{/* Subscribe Popup */}
{showPopup && <SubscribePopup onClose={handleClosePopup} />}

</div>
);
}
}

0 comments on commit 12fd08c

Please sign in to comment.