Vision_Meta0 / DeploymentMap.jsx
lattmamb's picture
Upload 32 files
b33d337 verified
import React, { useState, useEffect } from 'react';
import './DeploymentMap.css';
// Mock data for demonstration purposes
const mockDeploymentData = {
phases: [
{
id: 1,
name: "Phase 1",
timeline: "Q3 2025 - Q1 2026",
status: "planning",
locations: [
{ id: 101, name: "Springfield Downtown", lat: 39.801, lng: -89.644, status: "planned", type: "urban", capacity: 12 },
{ id: 102, name: "Champaign-Urbana Hub", lat: 40.116, lng: -88.243, status: "planned", type: "urban", capacity: 8 },
{ id: 103, name: "Bloomington-Normal", lat: 40.484, lng: -88.993, status: "planned", type: "urban", capacity: 8 },
{ id: 104, name: "Peoria Central", lat: 40.694, lng: -89.589, status: "planned", type: "urban", capacity: 8 }
]
},
{
id: 2,
name: "Phase 2",
timeline: "Q2 2026 - Q4 2026",
status: "future",
locations: [
{ id: 201, name: "Rockford Hub", lat: 42.271, lng: -89.093, status: "future", type: "urban", capacity: 8 },
{ id: 202, name: "Quad Cities", lat: 41.512, lng: -90.578, status: "future", type: "urban", capacity: 8 },
{ id: 203, name: "Carbondale", lat: 37.727, lng: -89.216, status: "future", type: "urban", capacity: 6 },
{ id: 204, name: "Quincy", lat: 39.936, lng: -91.410, status: "future", type: "urban", capacity: 6 },
{ id: 205, name: "I-55 Corridor North", lat: 41.097, lng: -88.841, status: "future", type: "corridor", capacity: 4 },
{ id: 206, name: "I-55 Corridor South", lat: 39.144, lng: -89.479, status: "future", type: "corridor", capacity: 4 }
]
},
{
id: 3,
name: "Phase 3",
timeline: "Q1 2027 - Q3 2027",
status: "future",
locations: [
{ id: 301, name: "Decatur", lat: 39.840, lng: -88.954, status: "future", type: "urban", capacity: 6 },
{ id: 302, name: "Danville", lat: 40.124, lng: -87.630, status: "future", type: "urban", capacity: 6 },
{ id: 303, name: "Galesburg", lat: 40.947, lng: -90.371, status: "future", type: "urban", capacity: 4 },
{ id: 304, name: "Mt. Vernon", lat: 38.317, lng: -88.903, status: "future", type: "urban", capacity: 4 },
{ id: 305, name: "I-57 Corridor North", lat: 41.201, lng: -87.866, status: "future", type: "corridor", capacity: 4 },
{ id: 306, name: "I-57 Corridor South", lat: 38.729, lng: -88.978, status: "future", type: "corridor", capacity: 4 },
{ id: 307, name: "I-70 Corridor", lat: 39.123, lng: -88.542, status: "future", type: "corridor", capacity: 4 },
{ id: 308, name: "I-74 Corridor", lat: 40.681, lng: -89.893, status: "future", type: "corridor", capacity: 4 }
]
}
],
demographics: {
population: [
{ county: "Cook", population: 5150233, evAdoption: 2.8, chargingStations: 487 },
{ county: "DuPage", population: 922921, evAdoption: 3.2, chargingStations: 112 },
{ county: "Lake", population: 696535, evAdoption: 2.9, chargingStations: 78 },
{ county: "Will", population: 677560, evAdoption: 2.1, chargingStations: 54 },
{ county: "Kane", population: 532403, evAdoption: 2.3, chargingStations: 42 },
{ county: "McHenry", population: 307774, evAdoption: 1.9, chargingStations: 28 },
{ county: "Winnebago", population: 285350, evAdoption: 1.2, chargingStations: 18 },
{ county: "Madison", population: 264461, evAdoption: 0.9, chargingStations: 14 },
{ county: "St. Clair", population: 259686, evAdoption: 0.7, chargingStations: 12 },
{ county: "Champaign", population: 205865, evAdoption: 1.8, chargingStations: 22 }
],
evProjections: [
{ year: 2025, percentage: 3.5 },
{ year: 2026, percentage: 5.2 },
{ year: 2027, percentage: 7.8 },
{ year: 2028, percentage: 11.5 },
{ year: 2029, percentage: 16.3 },
{ year: 2030, percentage: 22.0 }
]
},
impactMetrics: {
economic: {
jobsCreated: 450,
localBusinessImpact: 28,
taxRevenue: 3.2
},
environmental: {
co2Reduction: 12500,
gasDisplaced: 1.4,
renewableIntegration: 65
},
social: {
underservedCommunities: 14,
accessibilityScore: 72,
publicTransportIntegration: 8
}
}
};
function DeploymentMap() {
const [activeTab, setActiveTab] = useState('map');
const [loading, setLoading] = useState(true);
const [selectedPhase, setSelectedPhase] = useState(1);
const [mapView, setMapView] = useState('phases');
const [selectedLocation, setSelectedLocation] = useState(null);
const [mapZoom, setMapZoom] = useState(1);
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setLoading(false);
}, 1500);
return () => clearTimeout(timer);
}, []);
const handlePhaseChange = (phaseId) => {
setSelectedPhase(phaseId);
setSelectedLocation(null);
};
const handleMapViewChange = (view) => {
setMapView(view);
setSelectedLocation(null);
};
const handleLocationSelect = (location) => {
setSelectedLocation(location);
setMapZoom(2);
};
const handleZoomChange = (zoom) => {
setMapZoom(zoom);
if (zoom === 1) {
setSelectedLocation(null);
}
};
// Helper function to render the Illinois map with deployment locations
const renderMap = () => {
const currentPhase = mockDeploymentData.phases.find(phase => phase.id === selectedPhase);
const allLocations = mockDeploymentData.phases.flatMap(phase => phase.locations);
const displayLocations = mapView === 'phases' ? currentPhase.locations : allLocations;
return (
<div className="map-container">
<div className="illinois-map">
<img src="/images/illinois_map.png" alt="Illinois Map" className="map-image" />
{displayLocations.map(location => (
<div
key={location.id}
className={`map-marker ${location.status} ${location.type} ${selectedLocation && selectedLocation.id === location.id ? 'selected' : ''}`}
style={{
top: `${(1 - (location.lat - 37) / 6) * 100}%`,
left: `${((location.lng + 91.5) / 4) * 100}%`
}}
onClick={() => handleLocationSelect(location)}
>
<div className="marker-pulse"></div>
{mapZoom > 1 && (
<div className="marker-label">{location.name}</div>
)}
</div>
))}
{selectedLocation && (
<div
className="location-detail-popup"
style={{
top: `${(1 - (selectedLocation.lat - 37) / 6) * 100}%`,
left: `${((selectedLocation.lng + 91.5) / 4) * 100}%`
}}
>
<h4>{selectedLocation.name}</h4>
<div className="detail-item">
<span className="detail-label">Status:</span>
<span className={`detail-value ${selectedLocation.status}`}>
{selectedLocation.status.charAt(0).toUpperCase() + selectedLocation.status.slice(1)}
</span>
</div>
<div className="detail-item">
<span className="detail-label">Type:</span>
<span className="detail-value">
{selectedLocation.type === 'urban' ? 'Urban Hub' : 'Corridor Station'}
</span>
</div>
<div className="detail-item">
<span className="detail-label">Capacity:</span>
<span className="detail-value">{selectedLocation.capacity} Chargers</span>
</div>
<div className="detail-item">
<span className="detail-label">Phase:</span>
<span className="detail-value">
{mockDeploymentData.phases.find(phase =>
phase.locations.some(loc => loc.id === selectedLocation.id)
).name}
</span>
</div>
<button className="close-popup" onClick={() => setSelectedLocation(null)}>Γ—</button>
</div>
)}
</div>
<div className="map-controls">
<div className="zoom-controls">
<button
className={`zoom-button ${mapZoom === 1 ? 'active' : ''}`}
onClick={() => handleZoomChange(1)}
>
State View
</button>
<button
className={`zoom-button ${mapZoom === 2 ? 'active' : ''}`}
onClick={() => handleZoomChange(2)}
>
Detailed View
</button>
</div>
<div className="view-controls">
<button
className={`view-button ${mapView === 'phases' ? 'active' : ''}`}
onClick={() => handleMapViewChange('phases')}
>
Phase View
</button>
<button
className={`view-button ${mapView === 'all' ? 'active' : ''}`}
onClick={() => handleMapViewChange('all')}
>
All Locations
</button>
</div>
</div>
<div className="map-legend">
<h4>Map Legend</h4>
<div className="legend-items">
<div className="legend-item">
<div className="legend-marker planned"></div>
<span>Planned (Phase 1)</span>
</div>
<div className="legend-item">
<div className="legend-marker future"></div>
<span>Future Phases</span>
</div>
<div className="legend-item">
<div className="legend-marker urban"></div>
<span>Urban Hub</span>
</div>
<div className="legend-item">
<div className="legend-marker corridor"></div>
<span>Corridor Station</span>
</div>
</div>
</div>
</div>
);
};
// Helper function to render the deployment phases
const renderPhases = () => {
return (
<div className="deployment-phases">
<div className="phase-tabs">
{mockDeploymentData.phases.map(phase => (
<button
key={phase.id}
className={`phase-tab ${selectedPhase === phase.id ? 'active' : ''} ${phase.status}`}
onClick={() => handlePhaseChange(phase.id)}
>
{phase.name}
</button>
))}
</div>
{mockDeploymentData.phases.map(phase => (
phase.id === selectedPhase && (
<div key={phase.id} className="phase-details">
<div className="phase-header">
<h3>{phase.name}</h3>
<div className="phase-timeline">{phase.timeline}</div>
<div className={`phase-status ${phase.status}`}>
{phase.status.charAt(0).toUpperCase() + phase.status.slice(1)}
</div>
</div>
<div className="phase-metrics">
<div className="metric-card">
<div className="metric-value">{phase.locations.length}</div>
<div className="metric-label">Charging Locations</div>
</div>
<div className="metric-card">
<div className="metric-value">
{phase.locations.reduce((sum, location) => sum + location.capacity, 0)}
</div>
<div className="metric-label">Total Chargers</div>
</div>
<div className="metric-card">
<div className="metric-value">
{phase.locations.filter(location => location.type === 'urban').length}
</div>
<div className="metric-label">Urban Hubs</div>
</div>
<div className="metric-card">
<div className="metric-value">
{phase.locations.filter(location => location.type === 'corridor').length}
</div>
<div className="metric-label">Corridor Stations</div>
</div>
</div>
<div className="location-list">
<h4>Deployment Locations</h4>
<div className="location-grid">
{phase.locations.map(location => (
<div
key={location.id}
className={`location-card ${selectedLocation && selectedLocation.id === location.id ? 'selected' : ''}`}
onClick={() => handleLocationSelect(location)}
>
<div className="location-name">{location.name}</div>
<div className="location-type">
{location.type === 'urban' ? 'Urban Hub' : 'Corridor Station'}
</div>
<div className="location-capacity">{location.capacity} Chargers</div>
</div>
))}
</div>
</div>
</div>
)
))}
</div>
);
};
// Helper function to render the demographics tab
const renderDemographics = () => {
return (
<div className="demographics-tab">
<div className="demographics-section">
<h3>Population & EV Adoption by County</h3>
<div className="county-chart">
<div className="chart-header">
<div className="chart-column county">County</div>
<div className="chart-column population">Population</div>
<div className="chart-column adoption">EV Adoption %</div>
<div className="chart-column stations">Charging Stations</div>
</div>
{mockDeploymentData.demographics.population.map((county, index) => (
<div key={index} className="chart-row">
<div className="chart-column county">{county.county}</div>
<div className="chart-column population">{county.population.toLocaleString()}</div>
<div className="chart-column adoption">
<div className="adoption-bar-container">
<div
className="adoption-bar"
style={{ width: `${county.evAdoption * 10}%` }}
></div>
<span className="adoption-value">{county.evAdoption}%</span>
</div>
</div>
<div className="chart-column stations">{county.chargingStations}</div>
</div>
))}
</div>
</div>
<div className="demographics-section">
<h3>EV Adoption Projections</h3>
<div className="projection-chart">
<div className="projection-bars">
{mockDeploymentData.demographics.evProjections.map((projection, index) => (
<div key={index} className="projection-bar-container">
<div className="projection-year">{projection.year}</div>
<div className="projection-bar-wrapper">
<div
className="projection-bar"
style={{ height: `${projection.percentage * 3}%` }}
></div>
</div>
<div className="projection-value">{projection.percentage}%</div>
</div>
))}
</div>
<div className="projection-axis">
<div className="axis-label">Projected EV Adoption Rate (%)</div>
</div>
</div>
<div className="projection-notes">
<p>Projections based on current adoption trends, state incentives, and manufacturer production forecasts.</p>
<p>The Link deployment strategy is designed to stay ahead of projected adoption curves to ensure adequate charging infrastructure.</p>
</div>
</div>
</div>
);
};
// Helper function to render the impact metrics tab
const renderImpactMetrics = () => {
const { economic, environmental, social } = mockDeploymentData.impactMetrics;
return (
<div className="impact-metrics-tab">
<div className="impact-section economic">
<h3>Economic Impact</h3>
<div className="impact-metrics">
<div className="impact-metric">
<div className="impact-icon">πŸ’Ό</div>
<div className="impact-value">{economic.jobsCreated}</div>
<div className="impact-label">Jobs Created</div>
<div className="impact-description">
Direct and indirect employment opportunities across all deployment phases
</div>
</div>
<div className="impact-metric">
<div className="impact-icon">πŸͺ</div>
<div className="impact-value">{economic.localBusinessImpact}%</div>
<div className="impact-label">Local Business Growth</div>
<div className="impact-description">
Projected increase in revenue for businesses near charging hubs
</div>
</div>
<div className="impact-metric">
<div className="impact-icon">πŸ’°</div>
<div className="impact-value">${economic.taxRevenue}M</div>
<div className="impact-label">Annual Tax Revenue</div>
<div className="impact-description">
Estimated additional tax revenue for state and local governments
</div>
</div>
</div>
</div>
<div className="impact-section environmental">
<h3>Environmental Impact</h3>
<div className="impact-metrics">
<div className="impact-metric">
<div className="impact-icon">🌱</div>
<div className="impact-value">{environmental.co2Reduction}</div>
<div className="impact-label">Tons COβ‚‚ Reduced</div>
<div className="impact-description">
Annual carbon emissions reduction from EV adoption enabled by The Link
</div>
</div>
<div className="impact-metric">
<div className="impact-icon">β›½</div>
<div className="impact-value">{environmental.gasDisplaced}M</div>
<div className="impact-label">Gallons Gas Displaced</div>
<div className="impact-description">
Annual reduction in gasoline consumption
</div>
</div>
<div className="impact-metric">
<div className="impact-icon">β˜€οΈ</div>
<div className="impact-value">{environmental.renewableIntegration}%</div>
<div className="impact-label">Renewable Energy</div>
<div className="impact-description">
Percentage of charging energy from renewable sources
</div>
</div>
</div>
</div>
<div className="impact-section social">
<h3>Social Impact</h3>
<div className="impact-metrics">
<div className="impact-metric">
<div className="impact-icon">🏘️</div>
<div className="impact-value">{social.underservedCommunities}</div>
<div className="impact-label">Underserved Communities</div>
<div className="impact-description">
Number of underserved communities with improved EV access
</div>
</div>
<div className="impact-metric">
<div className="impact-icon">β™Ώ</div>
<div className="impact-value">{social.accessibilityScore}/100</div>
<div className="impact-label">Accessibility Score</div>
<div className="impact-description">
Rating of charging infrastructure accessibility for all users
</div>
</div>
<div className="impact-metric">
<div className="impact-icon">🚌</div>
<div className="impact-value">{social.publicTransportIntegration}</div>
<div className="impact-label">Transit Connections</div>
<div className="impact-description">
Number of charging hubs with public transportation connections
</div>
</div>
</div>
</div>
</div>
);
};
if (loading) {
return (
<div className="deployment-map loading">
<div className="loading-logo">
<img src="/images/unity_fleet_logo.png" alt="Unity Fleet" />
<div className="loading-pulse"></div>
</div>
<p>Loading Deployment Map...</p>
</div>
);
}
return (
<div className="deployment-map">
<header className="map-header">
<div className="map-title">
<h1>Deployment Map</h1>
<span className="map-subtitle">The Link Charging Network</span>
</div>
</header>
<div className="map-navigation">
<button
className={`nav-button ${activeTab === 'map' ? 'active' : ''}`}
onClick={() => setActiveTab('map')}
>
Interactive Map
</button>
<button
className={`nav-button ${activeTab === 'phases' ? 'active' : ''}`}
onClick={() => setActiveTab('phases')}
>
Deployment Phases
</button>
<button
className={`nav-button ${activeTab === 'demographics' ? 'active' : ''}`}
onClick={() => setActiveTab('demographics')}
>
Demographics
</button>
<button
className={`nav-button ${activeTab === 'impact' ? 'active' : ''}`}
onClick={() => setActiveTab('impact')}
>
Impact Metrics
</button>
</div>
<main className="map-content">
{activeTab === 'map' && renderMap()}
{activeTab === 'phases' && renderPhases()}
{activeTab === 'demographics' && renderDemographics()}
{activeTab === 'impact' && renderImpactMetrics()}
</main>
<footer className="map-footer">
<div className="footer-info">
<span>Unity Fleet Deployment Strategy</span>
<span>Powered by Atlas Intelligence</span>
</div>
<div className="footer-actions">
<button className="footer-button">Download Map Data</button>
<button className="footer-button">Share Deployment Plan</button>
</div>
</footer>
</div>
);
}
export default DeploymentMap;