Files
elysium/apps/web/src/components/game/AchievementPanel.tsx
T

88 lines
3.1 KiB
TypeScript

import type { Achievement } from "@elysium/types";
import { useState } from "react";
import { useGame } from "../../context/GameContext.js";
import { formatNumber } from "../../utils/format.js";
import { LockToggle } from "../ui/LockToggle.js";
const conditionDescription = (achievement: Achievement): string => {
const { condition } = achievement;
switch (condition.type) {
case "totalGoldEarned":
return `Earn ${formatNumber(condition.amount)} total gold`;
case "totalClicks":
return `Click ${formatNumber(condition.amount)} times`;
case "bossesDefeated":
return `Defeat ${condition.amount} boss${condition.amount > 1 ? "es" : ""}`;
case "questsCompleted":
return `Complete ${condition.amount} quest${condition.amount > 1 ? "s" : ""}`;
case "adventurerTotal":
return `Recruit ${formatNumber(condition.amount)} total adventurers`;
case "prestigeCount":
return `Prestige ${condition.amount} time${condition.amount > 1 ? "s" : ""}`;
case "equipmentOwned":
return `Own ${condition.amount} equipment item${condition.amount > 1 ? "s" : ""}`;
}
};
interface AchievementCardProps {
achievement: Achievement;
}
const AchievementCard = ({ achievement }: AchievementCardProps): React.JSX.Element => {
const isUnlocked = achievement.unlockedAt !== null;
return (
<div className={`achievement-card ${isUnlocked ? "unlocked" : "locked"}`}>
<div className="achievement-icon">{achievement.icon}</div>
<div className="achievement-info">
<h3>{achievement.name}</h3>
<p>{achievement.description}</p>
<p className="achievement-condition">{conditionDescription(achievement)}</p>
{achievement.reward?.crystals != null && (
<p className="achievement-reward">💎 +{achievement.reward.crystals} Crystals</p>
)}
</div>
<div className="achievement-status">
{isUnlocked ? (
<span className="achievement-unlocked-badge">✓ Unlocked</span>
) : (
<span className="achievement-locked-badge">🔒</span>
)}
</div>
</div>
);
};
export const AchievementPanel = (): React.JSX.Element => {
const { state } = useGame();
const [showLocked, setShowLocked] = useState(true);
if (!state) return <section className="panel"><p>Loading...</p></section>;
const achievements = state.achievements ?? [];
const unlocked = achievements.filter((a) => a.unlockedAt !== null);
const locked = achievements.filter((a) => a.unlockedAt === null);
const visible = showLocked ? achievements : unlocked;
return (
<section className="panel achievement-panel">
<div className="panel-header">
<h2>Achievements</h2>
<LockToggle
lockedCount={locked.length}
showLocked={showLocked}
onToggle={() => { setShowLocked((v) => !v); }}
/>
</div>
<p className="achievement-progress">
{unlocked.length} / {achievements.length} unlocked
</p>
<div className="achievement-list">
{visible.map((achievement) => (
<AchievementCard key={achievement.id} achievement={achievement} />
))}
</div>
</section>
);
};