diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 4b16112..e410207 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -95,6 +95,8 @@ "welcomeTitle": "Find the videogames that your mind can't", "welcomeFilters": "Use the filters on the left to narrow down your search.", "welcomeSearch": "Hit Search when you're ready to explore.", + "cardView": "Cards", + "listView": "List", "noResultsTitle": "No games found", "noResultsDescription": "Try adjusting your filters or search term to find more games." }, diff --git a/src/features/dashboard/ResultsGrid.tsx b/src/features/dashboard/ResultsGrid.tsx index 2ee947c..009b612 100644 --- a/src/features/dashboard/ResultsGrid.tsx +++ b/src/features/dashboard/ResultsGrid.tsx @@ -3,7 +3,8 @@ import { useTranslation } from 'react-i18next'; import type { GameResult } from '@/models/AppTypes'; import type { MouseEvent } from 'react'; import { Badge } from '@/components/ui/badge'; -import { Star } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { LayoutGrid, List, Star } from 'lucide-react'; import { Pagination, PaginationContent, @@ -58,6 +59,7 @@ export const ResultsGrid = ({ const [randomCatPath] = useState(() => getRandomCat()); const [animationData, setAnimationData] = useState(null); + const [viewMode, setViewMode] = useState<'card' | 'list'>('card'); const ITEMS_PER_PAGE = 20; @@ -74,9 +76,9 @@ export const ResultsGrid = ({ } }, [hasSearched, results.length, randomCatPath]); - const handleCardClick = (e: MouseEvent, index: number) => { - const card = e.currentTarget; - const rect = card.getBoundingClientRect(); + const handleCardClick = (e: MouseEvent, index: number) => { + const element = e.currentTarget; + const rect = element.getBoundingClientRect(); const origin: ClickOrigin = { x: rect.left, y: rect.top, @@ -150,50 +152,104 @@ export const ResultsGrid = ({ return (
-
- {paginatedResults.map((game, index) => ( -
handleCardClick(e, (currentPage - 1) * ITEMS_PER_PAGE + index)} - className="group relative aspect-[264/374] bg-white border-2 border-border rounded-base shadow-shadow flex flex-col justify-between overflow-visible hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-none transition-all cursor-pointer" +
+
+ + +
+
- {/* Image Area */} -
- {game.coverUrl ? ( - {game.title} - ) : ( - {t('results.noImage')} - )} +
+ {paginatedResults.map((game, index) => { + const gameIndex = (currentPage - 1) * ITEMS_PER_PAGE + index; + return viewMode === 'card' ? ( +
handleCardClick(e, gameIndex)} + className="group relative aspect-[264/374] bg-white border-2 border-border rounded-base shadow-shadow flex flex-col justify-between overflow-visible hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-none transition-all cursor-pointer" + > +
+ {game.rating && ( + + + {(game.rating / 10).toFixed(1)} + + )} + {game.matchScore !== undefined && ( + + {t('results.match')}: {game.matchScore.toFixed(3)} + + )} +
+ +
+ {game.coverUrl ? ( + {game.title} + ) : ( + {t('results.noImage')} + )} +
+ +
+

{game.title}

+ + {game.year ?? '—'} + +
- - {/* Meta Overlay */} -
-

{game.title}

- - {game.year ?? '—'} - + ) : ( +
handleCardClick(e, gameIndex)} + className="group border-2 border-border rounded-base bg-white shadow-shadow hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-none transition-all cursor-pointer p-3 flex items-center gap-4" + > +
+ {game.coverUrl ? ( + {game.title} + ) : ( + {t('results.noImage')} + )} +
+
+

{game.title}

+ {game.year ?? '—'} +
+
+ {game.rating && ( + + + {(game.rating / 10).toFixed(1)} + + )} + {game.matchScore !== undefined && ( + + {t('results.match')}: {game.matchScore.toFixed(3)} + + )} +
-
- ))} + ); + })}
{/* Pagination */}