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 27421b1
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 116 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.
7 changes: 3 additions & 4 deletions client/src/components/nav/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import * as React from 'react';
import { AppBar, Box, Button, MenuItem, Toolbar, IconButton, Typography, Menu, Container } from '@mui/material';
import MenuIcon from '@mui/icons-material/Menu';
import StarIcon from '@mui/icons-material/Star';
import { useRouter } from 'next/navigation';
import { keyframes } from '@mui/system';
import { usePathname } from 'next/navigation';
Expand All @@ -33,11 +32,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 All @@ -51,6 +49,7 @@ function NavBar() {
handleCloseNavMenu();
router.push(links[page]);
};
};

// Below is testing for active page link
const currentPath = usePathname();
Expand Down Expand Up @@ -202,5 +201,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 27421b1

Please sign in to comment.