retro achievement integration

This commit is contained in:
roormonger
2026-03-26 07:13:42 -04:00
parent 3d593b83f0
commit 7ccc48ae0f
2 changed files with 184 additions and 45 deletions

View File

@@ -14,6 +14,43 @@ export interface Platform {
iconUrl?: string;
}
export interface RAAchievement {
ra_id: number | null;
title: string | null;
description: string | null;
points: number | null;
num_awarded: number | null;
num_awarded_hardcore: number | null;
badge_id: string | null;
}
export interface RomRAMetadata {
first_release_date?: number | null;
genres?: string[];
companies?: string[];
achievements: RAAchievement[];
}
export interface EarnedAchievement {
id: string;
date: string;
date_hardcore?: string;
}
export interface RAProgressionResult {
rom_ra_id: number | null;
max_possible: number | null;
num_awarded: number | null;
num_awarded_hardcore: number | null;
most_recent_awarded_date?: string | null;
earned_achievements: EarnedAchievement[];
}
export interface RAProgression {
total: number;
results: RAProgressionResult[];
}
export interface DetailedGame extends Game {
summary?: string;
developers?: string[];
@@ -35,6 +72,8 @@ export interface DetailedGame extends Game {
platformId?: number;
videoUrl?: string; // Direct video link
youtubeId?: string; // YouTube ID for embed
ra_id?: number;
merged_ra_metadata?: RomRAMetadata;
}
export interface RommCollection {
@@ -51,6 +90,8 @@ export interface UserProfile {
username: string;
avatarUrl?: string;
roleName: string;
ra_username?: string | null;
ra_progression?: RAProgression | null;
}
// Function to safely extract base URL
@@ -341,6 +382,8 @@ export const rommApiClient = {
platformId: json.platform_id,
videoUrl: getFullImageUrl(json.url_video || json.ss_metadata?.video_url),
youtubeId: json.youtube_video_id || json.igdb_metadata?.youtube_video_id || json.launchbox_metadata?.youtube_video_id,
ra_id: json.ra_id,
merged_ra_metadata: json.merged_ra_metadata,
screenshots: Array.from(new Set([
...(json.merged_screenshots || []).map((s: string) => getFullImageUrl(s)),
...(json.screenshots || []).map((s: any) => s.url ? getFullImageUrl(s.url) || '' : ''),
@@ -475,6 +518,8 @@ export const rommApiClient = {
username: json.username || 'Unknown',
avatarUrl: constructedAvatarUrl,
roleName: json.role?.role_name || json.role?.name || String(json.role) || 'User',
ra_username: json.ra_username,
ra_progression: json.ra_progression,
};
}
};

View File

@@ -3,7 +3,7 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { FocusContext, useFocusable, setFocus } from '@noriginmedia/norigin-spatial-navigation';
import { CollectionModal } from './CollectionModal';
import { ManualModal } from './ManualModal';
import { rommApiClient, Platform, Game } from '../api/client';
import { rommApiClient, Platform, Game, RAAchievement, RAProgression } from '../api/client';
import { useInputMode } from '../context/InputModeContext';
import { EmulatorOverlay } from './EmulatorOverlay';
@@ -52,6 +52,94 @@ const FocusableItem = ({ onFocus, onEnterPress, onClick, children, className, fo
);
};
const AchievementList = ({ raId, achievements, userProgression }: {
raId?: number,
achievements?: RAAchievement[],
userProgression?: RAProgression | null
}) => {
if (!raId || !achievements || achievements.length === 0) {
return (
<div className="col-span-6 bg-white/5 rounded-xl p-5 border border-white/5 flex flex-col items-center justify-center opacity-40 h-[30rem]">
<span className="material-symbols-outlined text-4xl mb-3 text-white/20">trophy</span>
<div className="text-[0.625rem] geist-mono uppercase tracking-widest text-center">Achievements Unavailable</div>
</div>
);
}
const gameProgress = userProgression?.results.find(r => r.rom_ra_id === raId);
const earnedIds = new Set(gameProgress?.earned_achievements.map(a => String(a.id)) || []);
const earnedCount = gameProgress?.num_awarded || 0;
const totalCount = achievements.length;
const progressPercent = totalCount > 0 ? (earnedCount / totalCount) * 100 : 0;
return (
<div className="col-span-6 bg-white/5 rounded-xl p-5 border border-white/5 flex flex-col shadow-2xl h-[30rem] overflow-hidden">
{/* Header */}
<div className="flex items-center justify-between mb-4 shrink-0">
<div className="flex items-center gap-3">
<span className="material-symbols-outlined text-[#2563eb] filled">workspace_premium</span>
<h3 className="text-[0.625rem] geist-mono uppercase tracking-[0.3em] font-black text-white/60">RetroAchievements</h3>
</div>
<div className="text-[0.625rem] geist-mono text-[#2563eb] font-bold">
{earnedCount} / {totalCount}
</div>
</div>
{/* Progress Bar */}
<div className="h-1 w-full bg-white/5 rounded-full mb-6 overflow-hidden shrink-0">
<div
className="h-full bg-[#2563eb] transition-all duration-1000 ease-out"
style={{ width: `${progressPercent}%` }}
></div>
</div>
{/* Achievement List */}
<div className="flex-1 overflow-y-auto space-y-3 pr-2 scrollbar-none">
{achievements.map((achievement) => {
const isEarned = earnedIds.has(String(achievement.ra_id));
const badgeId = achievement.badge_id || '00000';
return (
<div
key={achievement.ra_id}
className={`flex items-center gap-4 p-3 rounded-lg border transition-all duration-300 ${isEarned ? 'bg-[#2563eb]/10 border-[#2563eb]/30' : 'bg-white/5 border-white/5 opacity-50'}`}
>
{/* Badge */}
<div className="w-10 h-10 rounded-md bg-black/40 flex items-center justify-center overflow-hidden border border-white/10 shrink-0">
<img
src={`https://media.retroachievements.org/Badge/${badgeId}.png`}
className={`w-full h-full object-contain ${isEarned ? '' : 'grayscale contrast-50 opacity-40'}`}
alt=""
/>
</div>
{/* Text */}
<div className="flex-1 min-w-0">
<div className="flex items-center justify-between gap-2">
<h4 className={`text-[0.6875rem] font-bold uppercase truncate ${isEarned ? 'text-white' : 'text-white/40'}`}>
{achievement.title}
</h4>
<span className="text-[0.5625rem] geist-mono text-[#2563eb] font-black shrink-0">
{achievement.points} pts
</span>
</div>
<p className="text-[0.5625rem] text-white/30 line-clamp-1 leading-tight mt-0.5">
{achievement.description}
</p>
</div>
{/* Status Icon */}
{isEarned && (
<span className="material-symbols-outlined text-[#2563eb] text-sm filled shrink-0">verified</span>
)}
</div>
);
})}
</div>
</div>
);
};
const PlatformItem = ({ platform, active, onSelect }: { platform: Platform, active: boolean, onSelect: () => void }) => (
<FocusableItem
onFocus={onSelect}
@@ -61,8 +149,8 @@ const PlatformItem = ({ platform, active, onSelect }: { platform: Platform, acti
active={active}
>
{(focused) => (
<div className="flex flex-col items-center gap-2 group cursor-pointer w-[110px]">
<div className={`w-[84px] h-[84px] rounded-[12px] transition-all duration-500 border-2 flex items-center justify-center p-3
<div className="flex flex-col items-center gap-2 group cursor-pointer w-[6.875rem]">
<div className={`w-[5.25rem] h-[5.25rem] rounded-xl transition-all duration-500 border-2 flex items-center justify-center p-3
${focused || active ? 'border-[#2563eb] bg-transparent scale-110 z-20 shadow-[0_5px_15px_rgba(37,99,235,0.2)]' :
'bg-white/5 border-white/5 group-hover:border-white/10'}`}
>
@@ -76,7 +164,7 @@ const PlatformItem = ({ platform, active, onSelect }: { platform: Platform, acti
<span className={`material-symbols-outlined text-3xl transition-colors ${focused || active ? 'text-white' : 'text-white/20'}`}>sports_esports</span>
)}
</div>
<div className={`text-[9px] font-black uppercase geist-mono tracking-widest text-center transition-all duration-300 w-full px-2 truncate
<div className={`text-[0.5625rem] font-black uppercase geist-mono tracking-widest text-center transition-all duration-300 w-full px-2 truncate
${focused || active ? 'text-[#2563eb] translate-y-1' : 'text-white/20'}`}
>
{platform.name}
@@ -97,7 +185,7 @@ const GameListItem = ({ game, active, onFocus }: { game: Game, active: boolean,
>
{(focused) => (
<div className={`flex items-center gap-4 px-6 py-2.5 cursor-pointer transition-all duration-300 border-l-4 ${focused ? 'bg-[#2563eb]/20 border-[#2563eb] z-10' : active ? 'bg-[#2563eb]/10 border-[#2563eb]/70' : 'border-transparent hover:bg-white/5'}`}>
<div className="w-8 h-11 bg-white/5 rounded-[4px] overflow-hidden shrink-0 border border-white/5">
<div className="w-8 h-11 bg-white/5 rounded overflow-hidden shrink-0 border border-white/5">
<img src={game.coverUrl} alt="" className="w-full h-full object-cover" />
</div>
<div className="min-w-0 flex-1 overflow-hidden">
@@ -106,7 +194,7 @@ const GameListItem = ({ game, active, onFocus }: { game: Game, active: boolean,
{game.title}
</span>
</div>
<div className="text-[10px] text-white/40 font-medium geist-mono uppercase tracking-tight">
<div className="text-[0.625rem] text-white/40 font-medium geist-mono uppercase tracking-tight">
{game.size}
</div>
</div>
@@ -135,7 +223,7 @@ const AlphabetScroller = ({
scrollOptions={{ behavior: 'smooth', block: 'center' }}
>
{(focused) => (
<div className={`text-[13px] font-black geist-mono transition-all duration-200
<div className={`text-[0.8125rem] font-black geist-mono transition-all duration-200
${focused ? 'text-[#2563eb] scale-125' : 'text-white/30 hover:text-white/70'}`}
>
{letter}
@@ -158,6 +246,12 @@ export const GamesPage = () => {
const [activeZone, setActiveZone] = useState<'platforms' | 'games' | 'alphabet' | 'details' | null>(null);
const detailTimeoutRef = React.useRef<any>(null);
const userQuery = useQuery({
queryKey: ['userProfile'],
queryFn: () => rommApiClient.fetchCurrentUser(),
staleTime: 1000 * 60 * 5, // 5 minutes cache
});
const { ref: platformsRef, focusKey: platformsFocusKey } = useFocusable({
focusKey: 'PLATFORMS_ZONE',
trackChildren: true,
@@ -446,12 +540,12 @@ export const GamesPage = () => {
{/* Main Split Layout */}
<div className="flex-1 flex overflow-hidden">
{/* Left Column: Game List + Alphabet */}
<div className="w-[500px] border-r border-white/5 flex overflow-hidden bg-black/20">
<div className="w-[31.25rem] border-r border-white/5 flex overflow-hidden bg-black/20">
<div className="flex-1 flex flex-col overflow-hidden">
<FocusContext.Provider value={gamesFocusKey}>
<div ref={gamesRef} className={`flex-1 overflow-y-auto scrollbar-hoverable ${activeZone === 'games' ? 'scrollbar-active' : ''}`}>
{gamesQuery.isLoading ? (
<div className="p-12 text-center text-white/20 geist-mono text-[10px] uppercase">Retrieving index...</div>
<div className="p-12 text-center text-white/20 geist-mono text-[0.625rem] uppercase">Retrieving index...</div>
) : (
gamesQuery.data?.map(game => (
<GameListItem
@@ -492,10 +586,10 @@ export const GamesPage = () => {
<FocusContext.Provider value={detailsFocusKey}>
<div ref={detailsRef} className={`flex-1 overflow-y-auto relative scrollbar-hoverable ${activeZone === 'details' ? 'scrollbar-active' : ''}`}>
{detailsQuery.data ? (
<div className="pt-[40px] pl-[40px] pr-16 pb-16">
<div className="flex gap-[40px] items-start h-[480px]">
<div className="pt-10 pl-10 pr-16 pb-16">
<div className="flex gap-10 items-start h-[30rem]">
{/* Poster */}
<div className="w-[320px] h-full rounded-[16px] overflow-hidden shadow-[0_20px_50px_rgba(0,0,0,0.5)] border border-white/10 shrink-0 relative group">
<div className="w-[20rem] h-full rounded-2xl overflow-hidden shadow-[0_20px_50px_rgba(0,0,0,0.5)] border border-white/10 shrink-0 relative group">
<img
src={detailsQuery.data.coverUrl}
className="w-full h-full object-cover transition-transform duration-1000 group-hover:scale-110"
@@ -507,17 +601,17 @@ export const GamesPage = () => {
{/* Metadata Content */}
<div className="flex-1 flex flex-col h-full min-w-0">
{/* Badges Row */}
<div className="flex items-center gap-3 mb-0 uppercase geist-mono font-black text-[10px] tracking-widest">
<div className="bg-[#2563eb] text-white px-3 py-1 rounded-[4px] shadow-lg shadow-[#2563eb]/20">
<div className="flex items-center gap-3 mb-0 uppercase geist-mono font-black text-[0.625rem] tracking-widest">
<div className="bg-[#2563eb] text-white px-3 py-1 rounded shadow-lg shadow-[#2563eb]/20">
Playable
</div>
<div className="bg-white/10 text-white/60 px-3 py-1 rounded-[4px] border border-white/5">
<div className="bg-white/10 text-white/60 px-3 py-1 rounded border border-white/5">
{detailsQuery.data.players || '1 Player'}
</div>
</div>
{/* Title */}
<h1 className="text-[54px] font-black text-white leading-[0.9] tracking-tighter uppercase mb-3 geist-mono truncate" title={detailsQuery.data.title}>
<h1 className="text-[3.375rem] font-black text-white leading-[0.9] tracking-tighter uppercase mb-3 geist-mono truncate" title={detailsQuery.data.title}>
{detailsQuery.data.title}
</h1>
@@ -525,12 +619,12 @@ export const GamesPage = () => {
<div className="border-t border-white/10 w-full mb-3"></div>
{/* Metadata List - Three Combined Rows */}
<div className="space-y-2 mb-4 text-[11px] font-black tracking-[0.2em] geist-mono uppercase">
<div className="space-y-2 mb-4 text-[0.6875rem] font-black tracking-[0.2em] geist-mono uppercase">
{/* Combined Row 1: Region & Release Date */}
<div className="flex items-center gap-10 overflow-hidden">
<div className="flex gap-2 min-w-0 shrink-0">
<span className="text-[#2563eb] w-[140px] shrink-0">Region:</span>
<span className="text-white truncate max-w-[200px]">{detailsQuery.data.regions?.join(', ') || 'N/A'}</span>
<span className="text-[#2563eb] w-[8.75rem] shrink-0">Region:</span>
<span className="text-white truncate max-w-[12.5rem]">{detailsQuery.data.regions?.join(', ') || 'N/A'}</span>
</div>
<div className="flex gap-2 shrink-0">
<span className="text-[#2563eb] shrink-0">Release date:</span>
@@ -541,8 +635,8 @@ export const GamesPage = () => {
{/* Combined Row 2: Franchise & Companies */}
<div className="flex items-center gap-10 overflow-hidden">
<div className="flex gap-2 min-w-0 shrink-0">
<span className="text-[#2563eb] w-[140px] shrink-0">Franchise:</span>
<span className="text-white truncate max-w-[200px]">{detailsQuery.data.collections?.join(', ') || 'N/A'}</span>
<span className="text-[#2563eb] w-[8.75rem] shrink-0">Franchise:</span>
<span className="text-white truncate max-w-[12.5rem]">{detailsQuery.data.collections?.join(', ') || 'N/A'}</span>
</div>
<div className="flex gap-2 min-w-0">
<span className="text-[#2563eb] shrink-0">Companies:</span>
@@ -555,7 +649,7 @@ export const GamesPage = () => {
{/* Combined Row 3: Age Rating & Genres */}
<div className="flex items-center gap-10 overflow-hidden">
<div className="flex gap-2 min-w-0 shrink-0">
<span className="text-[#2563eb] w-[140px] shrink-0">Age Rating:</span>
<span className="text-[#2563eb] w-[8.75rem] shrink-0">Age Rating:</span>
<span className="text-white">{detailsQuery.data.esrbRating || 'NR'}</span>
</div>
<div className="flex gap-2 min-w-0">
@@ -566,8 +660,8 @@ export const GamesPage = () => {
</div>
{/* Brief Summary Container */}
<div className="bg-white/5 rounded-[8px] p-5 mb-8 flex-1 overflow-y-auto border border-white/5 scrollbar-hide">
<p className="text-[15px] text-white/80 leading-[1.8] font-medium">
<div className="bg-white/5 rounded-lg p-5 mb-8 flex-1 overflow-y-auto border border-white/5 scrollbar-hide">
<p className="text-[0.9375rem] text-white/80 leading-[1.8] font-medium">
{detailsQuery.data.summary || 'No summary available for this entry.'}
</p>
</div>
@@ -588,8 +682,8 @@ export const GamesPage = () => {
focusKey="DETAILS_PLAY"
>
{(focused) => (
<button className={`h-[54px] px-8 rounded-[12px] font-black uppercase text-[12px] tracking-[0.1em] flex items-center gap-3 transition-all duration-300 shadow-2xl ${focused ? 'bg-white text-black scale-110 ring-4 ring-[#2563eb] shadow-[0_0_25px_rgba(37,99,235,0.4)]' : 'bg-white text-black hover:scale-105 hover:bg-white/90'}`}>
<span className="material-symbols-outlined text-[24px] filled" style={{ fontVariationSettings: "'FILL' 1" }}>play_arrow</span>
<button className={`h-[3.375rem] px-8 rounded-xl font-black uppercase text-[0.75rem] tracking-[0.1em] flex items-center gap-3 transition-all duration-300 shadow-2xl ${focused ? 'bg-white text-black scale-110 ring-4 ring-[#2563eb] shadow-[0_0_25px_rgba(37,99,235,0.4)]' : 'bg-white text-black hover:scale-105 hover:bg-white/90'}`}>
<span className="material-symbols-outlined text-[1.5rem] filled" style={{ fontVariationSettings: "'FILL' 1" }}>play_arrow</span>
Start Game
</button>
)}
@@ -603,8 +697,8 @@ export const GamesPage = () => {
focusKey="DETAILS_DOWNLOAD"
>
{(focused) => (
<button className={`h-[54px] px-8 rounded-[12px] font-black uppercase text-[12px] tracking-[0.1em] flex items-center gap-3 transition-all duration-300 border-2 ${focused ? 'scale-110 border-[#2563eb] bg-[#2563eb]/20 text-white ring-4 ring-[#2563eb]/20 shadow-[0_0_20px_rgba(37,99,235,0.3)]' : 'bg-white/5 border-white/10 text-white hover:bg-white/10 hover:border-white/20'}`}>
<span className="material-symbols-outlined text-[24px]">download</span>
<button className={`h-[3.375rem] px-8 rounded-xl font-black uppercase text-[0.75rem] tracking-[0.1em] flex items-center gap-3 transition-all duration-300 border-2 ${focused ? 'scale-110 border-[#2563eb] bg-[#2563eb]/20 text-white ring-4 ring-[#2563eb]/20 shadow-[0_0_20px_rgba(37,99,235,0.3)]' : 'bg-white/5 border-white/10 text-white hover:bg-white/10 hover:border-white/20'}`}>
<span className="material-symbols-outlined text-[1.5rem]">download</span>
Download
</button>
)}
@@ -635,12 +729,12 @@ export const GamesPage = () => {
{(focused) => (
<button
title="Favorite"
className={`w-[54px] h-[54px] flex items-center justify-center transition-all duration-300 rounded-[12px] border-2
className={`w-[3.375rem] h-[3.375rem] flex items-center justify-center transition-all duration-300 rounded-xl border-2
${focused ? 'scale-110 border-[#2563eb] bg-[#2563eb]/20 ring-4 ring-[#2563eb]/20 shadow-[0_0_15px_rgba(37,99,235,0.3)]' : 'bg-white/5 border-white/10 hover:bg-white/10'}
`}
>
<span
className={`material-symbols-outlined text-[24px]
className={`material-symbols-outlined text-[1.5rem]
${detailsQuery.data?.favorite ? 'filled text-[#2563eb]' : 'text-white/40'}
${focused ? '!text-white' : ''}
`}
@@ -660,8 +754,8 @@ export const GamesPage = () => {
focusKey="DETAILS_COLLECTION"
>
{(focused) => (
<button title="Add to Collection" className={`w-[54px] h-[54px] flex items-center justify-center transition-all duration-300 rounded-[12px] border-2 ${focused ? 'scale-110 border-[#2563eb] text-white bg-[#2563eb]/20 ring-4 ring-[#2563eb]/20 shadow-[0_0_15px_rgba(37,99,235,0.3)]' : 'bg-white/5 border-white/10 text-white/40 hover:bg-white/10 hover:text-white'}`}>
<span className="material-symbols-outlined text-[24px]">library_add</span>
<button title="Add to Collection" className={`w-[3.375rem] h-[3.375rem] flex items-center justify-center transition-all duration-300 rounded-xl border-2 ${focused ? 'scale-110 border-[#2563eb] text-white bg-[#2563eb]/20 ring-4 ring-[#2563eb]/20 shadow-[0_0_15px_rgba(37,99,235,0.3)]' : 'bg-white/5 border-white/10 text-white/40 hover:bg-white/10 hover:text-white'}`}>
<span className="material-symbols-outlined text-[1.5rem]">library_add</span>
</button>
)}
</FocusableItem>
@@ -674,8 +768,8 @@ export const GamesPage = () => {
focusKey="DETAILS_MANUAL"
>
{(focused) => (
<button title="Manual" className={`w-[54px] h-[54px] flex items-center justify-center transition-all duration-300 rounded-[12px] border-2 ${focused ? 'scale-110 border-[#2563eb] text-white bg-[#2563eb]/20 ring-4 ring-[#2563eb]/20 shadow-[0_0_15px_rgba(37,99,235,0.3)]' : 'bg-white/5 border-white/10 text-white/40 hover:bg-white/10 hover:text-white'}`}>
<span className="material-symbols-outlined text-[24px]">menu_book</span>
<button title="Manual" className={`w-[3.375rem] h-[3.375rem] flex items-center justify-center transition-all duration-300 rounded-xl border-2 ${focused ? 'scale-110 border-[#2563eb] text-white bg-[#2563eb]/20 ring-4 ring-[#2563eb]/20 shadow-[0_0_15px_rgba(37,99,235,0.3)]' : 'bg-white/5 border-white/10 text-white/40 hover:bg-white/10 hover:text-white'}`}>
<span className="material-symbols-outlined text-[1.5rem]">menu_book</span>
</button>
)}
</FocusableItem>
@@ -687,10 +781,10 @@ export const GamesPage = () => {
{/* Media Galleria Container */}
{mediaItems.length > 0 && (
<div className="mt-12 grid grid-cols-12 gap-8">
<div className="col-span-6 bg-white/5 rounded-[12px] p-5 border border-white/5 flex flex-col shadow-2xl">
<div className="mt-12 grid grid-cols-12 gap-8 items-stretch">
<div className="col-span-6 bg-white/5 rounded-xl p-5 border border-white/5 flex flex-col shadow-2xl h-[30rem] overflow-hidden">
{/* Active Media Slot */}
<div className="aspect-video w-full rounded-[10px] bg-black/40 border border-white/10 overflow-hidden relative mb-5 group">
<div className="aspect-video w-full rounded-[0.625rem] bg-black/40 border border-white/10 overflow-hidden relative mb-5 group">
{mediaItems[activeMediaIndex].type === 'video' ? (
mediaItems[activeMediaIndex].youtubeId ? (
<iframe
@@ -729,7 +823,7 @@ export const GamesPage = () => {
focusKey={`THUMB_${idx}`}
>
{(focused) => (
<div className={`w-[124px] aspect-video rounded-[6px] border-2 transition-all duration-300 overflow-hidden relative ${focused || activeMediaIndex === idx ? 'border-[#2563eb] scale-105 z-10' : 'border-white/10 opacity-40 grayscale hover:opacity-100 hover:grayscale-0'}`}>
<div className={`w-[7.75rem] aspect-video rounded-md border-2 transition-all duration-300 overflow-hidden relative ${focused || activeMediaIndex === idx ? 'border-[#2563eb] scale-105 z-10' : 'border-white/10 opacity-40 grayscale hover:opacity-100 hover:grayscale-0'}`}>
{item.type === 'video' ? (
<div className="w-full h-full bg-black/60 flex items-center justify-center">
<span className="material-symbols-outlined text-[#2563eb] text-3xl filled">play_circle</span>
@@ -747,12 +841,12 @@ export const GamesPage = () => {
</div>
</div>
{/* Placeholder for the "something else" mentioned by user */}
<div className="col-span-6 bg-white/5 rounded-[12px] p-5 border border-white/5 border-dashed flex items-center justify-center opacity-20">
<div className="text-center geist-mono uppercase tracking-[0.3em] text-[10px]">
Future Content Zone
</div>
</div>
{/* Achievement List Component */}
<AchievementList
raId={detailsQuery.data.ra_id}
achievements={detailsQuery.data.merged_ra_metadata?.achievements}
userProgression={userQuery.data?.ra_progression}
/>
</div>
)}
</div>
@@ -760,7 +854,7 @@ export const GamesPage = () => {
<div className="h-full flex items-center justify-center">
<div className="text-center">
<div className="material-symbols-outlined text-4xl text-white/10 animate-spin mb-4">progress_activity</div>
<div className="text-[10px] geist-mono text-white/20 uppercase tracking-widest">Hydrating Metadata...</div>
<div className="text-[0.625rem] geist-mono text-white/20 uppercase tracking-widest">Hydrating Metadata...</div>
</div>
</div>
) : (