Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions backend/backend-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,9 @@ Et spill med detaljer og relasjoner.
| `website` | `String` | Offisiell nettside. |
| `added` | `Int!` | Antall RAWG-brukere som har lagt spillet til i en liste. Brukes som mål på popularitet. |
| `metacritic` | `Int` | Metacritic-score, 0–100. |
| `rating` | `Float` | RAWG-brukernes snittvurdering, 0–5. |
| `ratingsCount` | `Int!` | Antall vurderinger bak `rating`. |
| `playtime` | `Int` | Gjennomsnittlig spilletid i timer. |
| `esrbRating` | `String` | Amerikansk aldersgrense, f.eks. `Mature`. |
| `dominantColor` | `String` | Hovedfargen i forsidebildet som hex, f.eks. `0f0f0f`. |
| `backgroundImage` | `String` | URL til forsidebildet. |
Expand Down
3 changes: 3 additions & 0 deletions backend/src/resolvers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { assertName } from 'graphql';

Check failure on line 1 in backend/src/resolvers.ts

View workflow job for this annotation

GitHub Actions / lint-and-test (backend)

backend/src/resolvers.ts#L1

'assertName' is defined but never used
import { pool } from './db.js';

// type definitions
Expand Down Expand Up @@ -31,6 +31,9 @@
website,
added,
metacritic,
rating,
ratings_count AS "ratingsCount",
playtime,
esrb_rating AS "esrbRating",
dominant_color AS "dominantColor",
background_image AS "backgroundImage",
Expand Down
3 changes: 3 additions & 0 deletions backend/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ export const typeDefs = `#graphql
website: String,
added: Int!,
metacritic: Int,
rating: Float,
ratingsCount: Int!,
playtime: Int,
esrbRating: String,
dominantColor: String,
backgroundImage: String,
Expand Down
9 changes: 9 additions & 0 deletions frontend/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,12 @@
font-family: var(--mono);
font-size: 14px;
}

/* Antall treff under overskriften, i samme monospace som menyen */
.results-count {
min-height: 1.5em; /* holder plassen, så lista ikke hopper når teksten kommer */
margin: 8px 0 0;
font-family: var(--mono);
font-size: 13px;
color: var(--text);
}
43 changes: 33 additions & 10 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { useState } from 'react';
import { useQuery } from '@apollo/client/react';
import { GameList } from './components/GameList/GameList';
import { mockGames } from './mocks/games';
import type { Suggestion } from './graphql/searchSuggestions';
import { Header } from './components/Header/Header';
import { GAMES } from './graphql/games';
import type { Suggestion } from './graphql/searchSuggestions';
import type { GameSummary } from './types/game';
import './App.css';

/**
* Midlertidig forside: viser spillene med mockdata til GraphQL er klart.
* Forsiden: søk, resultatliste fra databasen og favoritter.
* Favoritter og valgt visning ligger bare i minnet her og skal senere inn i state management.
*/
function App() {
Expand All @@ -22,12 +24,28 @@ function App() {
// Den som lager detaljsiden bytter denne linjen til navigering.
const handleSelectGame = (game: Suggestion) => setQuery(game.name);

// MIDLERTIDIG: filtrerer mockdata på klienten for å kunne teste søket.
// Når GraphQL er klart, sendes `query` til backend og filtreres i databasen.
const games = mockGames.filter(
(g) =>
g.name.toLowerCase().includes(query.toLowerCase()) && (!showFavorites || favorites.has(g.id)),
);
// Henter første side fra databasen. Kjøres på nytt bare når `query` endres (ved Enter).
// Tomt søk (null) gir de mest populære spillene.
const { data, previousData, loading, error } = useQuery(GAMES, {
variables: { search: query || null, first: 20 },
});
const result = data ?? previousData; // forrige liste vises mens den nye lastes

// Backend gir sjangre som objekter ({ name }), spillkortet forventer bare navnene
const allGames: GameSummary[] =
result?.games.edges.map(({ node }) => ({
...node,
genres: node.genres.map((g) => g.name),
})) ?? [];

// MIDLERTIDIG: favoritter filtreres blant spillene som er lastet inn.
// Når favoritter lagres i databasen, bør de hentes derfra i stedet.
const games = showFavorites ? allGames.filter((g) => favorites.has(g.id)) : allGames;

// Teksten over lista. role="status" gjør at skjermlesere leser den opp når den endres
let status = '';
if (loading && !result) status = 'Laster …';
else if (result && !showFavorites) status = `${result.games.totalCount.toLocaleString('nb-NO')} treff`;

const toggleFavorite = (id: string) => {
setFavorites((prev) => {
Expand Down Expand Up @@ -73,6 +91,11 @@ function App() {
</div>
</div>

<p role="status" className="results-count">
{status}
</p>
{error && <p role="alert">Kunne ikke hente spill: {error.message}</p>}

<GameList
games={games}
view={view}
Expand All @@ -84,4 +107,4 @@ function App() {
);
}

export default App;
export default App;
49 changes: 49 additions & 0 deletions frontend/src/graphql/games.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { gql, type TypedDocumentNode } from '@apollo/client';

/** Ett spill slik backend sender det til resultatlista */
interface GameNode {
id: string;
name: string;
released: string | null;
backgroundImage: string | null;
rating: number | null;
metacritic: number | null;
genres: { name: string }[];
}

interface GamesData {
games: {
totalCount: number;
edges: { node: GameNode }[];
};
}

interface GamesVars {
search: string | null;
first: number;
}

/**
* Henter én side med spill til hovedlista. Uten søk gir backend de mest populære.
* Bare feltene spillkortet viser hentes, så svaret holdes lite.
*/
export const GAMES: TypedDocumentNode<GamesData, GamesVars> = gql`
query Games($search: String, $first: Int) {
games(search: $search, first: $first) {
totalCount
edges {
node {
id
name
released
backgroundImage
rating
metacritic
genres {
name
}
}
}
}
}
`;
Loading