Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 70 additions & 3 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,41 @@
/**
* @file App.jsx
* @description Main application component for the Superhero Comparison App.
* Provides a table view of superheroes with their stats and a comparison view
* to compare two selected superheroes side-by-side with a winner determination.
*/

import React, { useEffect, useState } from 'react';
import './App.css';

/**
* Main application component that manages superhero data and views.
* Handles fetching superhero data, hero selection, and switching between
* table view and comparison view.
*
* @returns {JSX.Element} The main application UI
*/
function App() {
const [superheroes, setSuperheroes] = useState([]);
const [selectedHeroes, setSelectedHeroes] = useState([]);
const [currentView, setCurrentView] = useState('table'); // 'table' or 'comparison'
// State management
const [superheroes, setSuperheroes] = useState([]); // Array of all superhero objects
const [selectedHeroes, setSelectedHeroes] = useState([]); // Array of up to 2 selected heroes for comparison
const [currentView, setCurrentView] = useState('table'); // Current view: 'table' or 'comparison'

// Fetch superhero data from API on component mount
useEffect(() => {
fetch('/api/superheroes')
.then((response) => response.json())
.then((data) => setSuperheroes(data))
.catch((error) => console.error('Error fetching superheroes:', error));
}, []);

/**
* Handles selection/deselection of heroes for comparison.
* Allows up to 2 heroes to be selected. If a third hero is selected,
* it replaces the first selected hero (FIFO behavior).
*
* @param {Object} hero - The superhero object to select or deselect
*/
const handleHeroSelection = (hero) => {
setSelectedHeroes(prev => {
if (prev.find(h => h.id === hero.id)) {
Expand All @@ -28,34 +51,59 @@ function App() {
});
};

/**
* Checks if a hero is currently selected for comparison.
*
* @param {number|string} heroId - The ID of the hero to check
* @returns {boolean} True if the hero is selected, false otherwise
*/
const isHeroSelected = (heroId) => {
return selectedHeroes.some(h => h.id === heroId);
};

/**
* Switches to the comparison view if exactly 2 heroes are selected.
* Does nothing if the selection requirement is not met.
*/
const handleCompare = () => {
if (selectedHeroes.length === 2) {
setCurrentView('comparison');
}
};

/**
* Returns to the table view and clears the hero selections.
*/
const handleBackToTable = () => {
setCurrentView('table');
setSelectedHeroes([]);
};

/**
* Calculates the winner between two heroes by comparing their stats.
* Each stat category (intelligence, strength, speed, durability, power, combat)
* is compared, and the hero with more winning categories wins overall.
*
* @param {Object} hero1 - First superhero object containing name, id, image, and powerstats properties
* @param {Object} hero2 - Second superhero object containing name, id, image, and powerstats properties
* @returns {Object} Object containing winner (hero object or null for tie) and score string
*/
const calculateWinner = (hero1, hero2) => {
const stats = ['intelligence', 'strength', 'speed', 'durability', 'power', 'combat'];
let hero1Score = 0;
let hero2Score = 0;

// Compare each stat category and increment the winner's score
stats.forEach(stat => {
if (hero1.powerstats[stat] > hero2.powerstats[stat]) {
hero1Score++;
} else if (hero2.powerstats[stat] > hero1.powerstats[stat]) {
hero2Score++;
}
// Ties in individual stats don't increment either score
});

// Determine overall winner based on category wins
if (hero1Score > hero2Score) {
return { winner: hero1, score: `${hero1Score}-${hero2Score}` };
} else if (hero2Score > hero1Score) {
Expand All @@ -65,6 +113,13 @@ function App() {
}
};

/**
* Renders the comparison view displaying two heroes side-by-side.
* Shows hero images, stat-by-stat comparison with visual indicators,
* and the final winner determination.
*
* @returns {JSX.Element|null} The comparison view JSX element, or null if exactly 2 heroes are not selected
*/
const renderComparison = () => {
if (selectedHeroes.length !== 2) return null;

Expand All @@ -79,6 +134,7 @@ function App() {
</button>
<h1>Superhero Comparison</h1>

{/* Hero cards section with images and names */}
<div className="comparison-container">
<div className="hero-card">
<img src={hero1.image} alt={hero1.name} className="hero-image" />
Expand All @@ -95,10 +151,12 @@ function App() {
</div>
</div>

{/* Stat-by-stat comparison with winner highlighting */}
<div className="stats-comparison">
{stats.map(stat => {
const stat1 = hero1.powerstats[stat];
const stat2 = hero2.powerstats[stat];
// Determine which hero wins this stat category
const winner = stat1 > stat2 ? 'hero1' : stat1 < stat2 ? 'hero2' : 'tie';

return (
Expand All @@ -117,6 +175,7 @@ function App() {
})}
</div>

{/* Final result announcement */}
<div className="final-result">
<h2>Final Result</h2>
{result.winner ? (
Expand All @@ -135,9 +194,16 @@ function App() {
);
};

/**
* Renders the main table view displaying all superheroes.
* Includes selection checkboxes, hero stats, and a comparison button.
*
* @returns {JSX.Element} The table view component
*/
const renderTable = () => (
<div className="table-view">
<h1>Superheroes</h1>
{/* Selection status and compare button */}
<div className="selection-info">
<p>Select 2 superheroes to compare ({selectedHeroes.length}/2 selected)</p>
{selectedHeroes.length > 0 && (
Expand All @@ -154,6 +220,7 @@ function App() {
</button>
</div>

{/* Main superhero data table */}
<table>
<thead>
<tr>
Expand Down