feat: modularize game filtering logic and UI into a custom hook and dedicated components.feat: modularize game filtering logic and UI into a custom hook and dedicated components.

This commit is contained in:
2025-12-19 14:06:31 +01:00
parent d916014872
commit 5b397e2265
5 changed files with 267 additions and 205 deletions
@@ -0,0 +1,94 @@
import { Link } from "react-router-dom";
import { GameImage } from "../GameImage";
interface FilteredGamesListProps {
filteredGames: string[];
gameToPositive: Map<string, Set<string>>;
selectedPeopleCount: number;
}
export function FilteredGamesList({
filteredGames,
gameToPositive,
selectedPeopleCount,
}: FilteredGamesListProps) {
if (selectedPeopleCount === 0) return null;
return (
<div>
<h3>Games Everyone Would Play ({filteredGames.length})</h3>
{filteredGames.length > 0 ? (
<ul className="grid-container">
{filteredGames.map((game) => {
const positiveCount = gameToPositive.get(game)?.size || 0;
const neutralCount = selectedPeopleCount - positiveCount;
return (
<Link
to={`/game/${encodeURIComponent(game)}`}
key={game}
className="list-item game-entry"
style={{
textDecoration: "none",
color: "inherit",
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
<div>
<strong>{game}</strong>
<div
style={{
fontSize: "0.9em",
color: "#4caf50",
marginTop: "0.5rem",
}}
>
<span></span> {positiveCount} selected would play
</div>
{neutralCount > 0 && (
<div
style={{
fontSize: "0.9em",
color: "#d4d400",
marginTop: "0.3rem",
}}
>
<span>?</span> {neutralCount}{" "}
{neutralCount > 1 ? "are" : "is"} neutral
</div>
)}
</div>
<GameImage game={game} />
</Link>
);
})}
</ul>
) : (
<EmptyState />
)}
</div>
);
}
function EmptyState() {
return (
<div
style={{
padding: "3rem",
textAlign: "center",
background: "var(--secondary-alt-bg)",
borderRadius: "16px",
border: "1px dashed var(--border-color)",
color: "var(--text-muted)",
}}
>
<div style={{ fontSize: "2rem", marginBottom: "1rem" }}>🔍</div>
<p>No games found where all selected people would play.</p>
<p style={{ fontSize: "0.9rem" }}>
Try selecting fewer people or adding more opinions!
</p>
</div>
);
}
@@ -0,0 +1,55 @@
import { Person } from "../../items";
interface PersonSelectorProps {
people: Person[];
selectedPeople: Set<string>;
onTogglePerson: (name: string) => void;
}
export function PersonSelector({
people,
selectedPeople,
onTogglePerson,
}: PersonSelectorProps) {
return (
<div style={{ marginBottom: "3rem" }}>
<h3>Select People</h3>
<div
className="grid-container"
style={{
display: "flex",
flexWrap: "wrap",
gap: "1rem",
justifyContent: "center",
}}
>
{people.map((person) => (
<div
key={person.name}
className="list-item gamefilter-entry"
style={{
borderColor: selectedPeople.has(person.name)
? "var(--accent-color)"
: "var(--border-color)",
cursor: "pointer",
}}
onClick={() => onTogglePerson(person.name)}
>
<div style={{ gap: "0.5rem" }}>
<strong>{person.name}</strong>
</div>
<div
style={{
fontSize: "0.9em",
color: "var(--text-muted)",
marginTop: "0.5rem",
}}
>
{person.opinion.length} opinion(s)
</div>
</div>
))}
</div>
</div>
);
}