feat: improve website styling
This commit is contained in:
@@ -11,6 +11,12 @@
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
.animate-fade-in { animation: fadeIn 0.3s ease-out; }
|
||||
|
||||
@keyframes slideInUp {
|
||||
from { opacity: 0; transform: translateY(20px) scale(0.95); }
|
||||
to { opacity: 1; transform: translateY(0) scale(1); }
|
||||
}
|
||||
.animate-slide-in-up { animation: slideInUp 0.35s cubic-bezier(0.21, 1.02, 0.73, 1) forwards; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-slate-50 text-slate-800 antialiased">
|
||||
|
||||
@@ -13,8 +13,8 @@ function Nav() {
|
||||
className={({ isActive }) =>
|
||||
`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
|
||||
isActive
|
||||
? "bg-indigo-600 text-white"
|
||||
: "text-slate-600 hover:bg-slate-200"
|
||||
? "bg-white/20 text-white shadow-sm backdrop-blur-sm"
|
||||
: "text-indigo-100 hover:bg-white/10 hover:text-white"
|
||||
}`
|
||||
}
|
||||
>
|
||||
@@ -23,10 +23,15 @@ function Nav() {
|
||||
);
|
||||
|
||||
return (
|
||||
<nav className="bg-white border-b border-slate-200 sticky top-0 z-50">
|
||||
<nav className="bg-gradient-to-r from-indigo-600 via-indigo-700 to-purple-700 sticky top-0 z-50 shadow-lg">
|
||||
<div className="max-w-6xl mx-auto px-4 py-3 flex items-center gap-2">
|
||||
<h1 className="text-lg font-bold text-indigo-700 mr-6">
|
||||
House ELO
|
||||
<h1 className="text-lg font-extrabold tracking-tight mr-6 flex items-center gap-2">
|
||||
<span className="inline-flex items-center justify-center w-8 h-8 rounded-lg bg-white/20 backdrop-blur-sm text-white text-base">
|
||||
🏠
|
||||
</span>
|
||||
<span className="bg-gradient-to-r from-white to-indigo-200 bg-clip-text text-transparent">
|
||||
House ELO
|
||||
</span>
|
||||
</h1>
|
||||
{link("/", "Compare")}
|
||||
{link("/rankings", "Rankings")}
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { api, type Listing, type Matchup, type CompareResult } from "../api";
|
||||
import ListingCard from "../components/ListingCard";
|
||||
|
||||
interface Toast {
|
||||
id: number;
|
||||
result: CompareResult;
|
||||
}
|
||||
|
||||
export default function CompareView() {
|
||||
const [matchup, setMatchup] = useState<Matchup | null>(null);
|
||||
const [result, setResult] = useState<CompareResult | null>(null);
|
||||
const [toasts, setToasts] = useState<Toast[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [comparisonCount, setComparisonCount] = useState(0);
|
||||
const toastId = useRef(0);
|
||||
|
||||
const fetchMatchup = useCallback(async () => {
|
||||
setLoading(true);
|
||||
@@ -35,6 +42,11 @@ export default function CompareView() {
|
||||
const res = await api.submitComparison(winner.global_id, loser.global_id);
|
||||
setResult(res);
|
||||
setComparisonCount((c) => c + 1);
|
||||
|
||||
// Add toast
|
||||
const id = ++toastId.current;
|
||||
setToasts((prev) => [...prev, { id, result: res }]);
|
||||
setTimeout(() => setToasts((prev) => prev.filter((t) => t.id !== id)), 3000);
|
||||
} catch (e) {
|
||||
setError(e instanceof Error ? e.message : "Failed to submit comparison");
|
||||
} finally {
|
||||
@@ -78,18 +90,6 @@ export default function CompareView() {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{result && (
|
||||
<div className="mb-6 bg-green-50 border border-green-200 rounded-lg p-4 text-center animate-fade-in">
|
||||
<p className="text-green-800 font-medium">
|
||||
ELO change: +{result.elo_change.toFixed(1)} / -{result.elo_change.toFixed(1)}
|
||||
</p>
|
||||
<p className="text-green-600 text-sm">
|
||||
Winner: {result.new_winner_elo.toFixed(0)} · Loser:{" "}
|
||||
{result.new_loser_elo.toFixed(0)}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-6">
|
||||
<ListingCard
|
||||
listing={matchup.listing_a}
|
||||
@@ -121,6 +121,36 @@ export default function CompareView() {
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* ELO change toasts */}
|
||||
<div className="fixed bottom-6 right-6 flex flex-col gap-3 z-50 pointer-events-none">
|
||||
{toasts.map((t) => (
|
||||
<div
|
||||
key={t.id}
|
||||
className="pointer-events-auto bg-white border border-slate-200 rounded-xl shadow-xl px-5 py-4 min-w-[260px] animate-slide-in-up"
|
||||
>
|
||||
<div className="flex items-center gap-3 mb-1">
|
||||
<span className="inline-flex items-center justify-center w-8 h-8 rounded-full bg-green-100 text-green-600 text-lg font-bold">
|
||||
↑
|
||||
</span>
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-slate-800">ELO Updated</p>
|
||||
<p className="text-xs text-slate-500">Comparison recorded</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between mt-2 text-sm">
|
||||
<span className="text-green-600 font-bold">
|
||||
Winner: {t.result.new_winner_elo.toFixed(0)}{" "}
|
||||
<span className="text-green-500 text-xs">(+{t.result.elo_change.toFixed(1)})</span>
|
||||
</span>
|
||||
<span className="text-red-500 font-bold">
|
||||
Loser: {t.result.new_loser_elo.toFixed(0)}{" "}
|
||||
<span className="text-red-400 text-xs">(-{t.result.elo_change.toFixed(1)})</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user