diff --git a/frontend/public/favicon.svg b/frontend/public/favicon.svg deleted file mode 100644 index 6893eb1..0000000 --- a/frontend/public/favicon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend/public/icons.svg b/frontend/public/icons.svg deleted file mode 100644 index e952219..0000000 --- a/frontend/public/icons.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/App.css b/frontend/src/App.css deleted file mode 100644 index e69de29..0000000 diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index d3db457..235f3d9 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,18 +1,8 @@ -import './App.css'; -import 'bootstrap/dist/css/bootstrap.min.css'; -import GameDetails from './GameDetails.tsx'; -import Header from './Header.tsx'; -import GamesGrid from './GamesGrid.tsx'; +import "bootstrap/dist/css/bootstrap.min.css"; +import GamesGrid from "./pages/GamesGrid/GamesGrid"; function App() { - - return ( - <> - {/*
*/} - {/* */} - - - ) + return ; } export default App; diff --git a/frontend/src/GameDetails.tsx b/frontend/src/GameDetails.tsx deleted file mode 100644 index 960d866..0000000 --- a/frontend/src/GameDetails.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { Container, Image } from "react-bootstrap"; -import Header from "./Header"; - -function fetchGameDetails(){ - -} - -export default function GameDetails() { - return ( - <> -
- - - Release year | Age rating - -

- Game Title -

-
- Game Image -
-
NTNU Rating:
-

Your Rating:

-
-
-
Tags:
-
Developer(s):
-
Publisher(s):
-
Platform(s):
-

- Description -

- -
- - ); -} \ No newline at end of file diff --git a/frontend/src/GamesGrid.tsx b/frontend/src/GamesGrid.tsx deleted file mode 100644 index 288b80b..0000000 --- a/frontend/src/GamesGrid.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { Container, Image } from "react-bootstrap"; -import Header from "./Header"; - -export default function GamesGrid () { - return ( - <> -
- {/* Filtering, paging and sorting */} - {/* Grid itself */} - - Game Image -
-
Game Title
-
Release year
-
Tags
-
Rating
-
- -
- - ); -} \ No newline at end of file diff --git a/frontend/src/Header.tsx b/frontend/src/Header.tsx deleted file mode 100644 index 95d51fe..0000000 --- a/frontend/src/Header.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Button, Container, Form, InputGroup, Nav, Navbar } from 'react-bootstrap'; -import { Search } from 'react-bootstrap-icons'; - - -export default function Header() { - return ( - - - NTNU Game Ratings(WIP) -
- - - -
- My Ratings -
-
- ); -} \ No newline at end of file diff --git a/frontend/src/MyRatings.tsx b/frontend/src/MyRatings.tsx deleted file mode 100644 index 0dafea7..0000000 --- a/frontend/src/MyRatings.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import Header from './Header.tsx' - -export default function MyRatings () { - return ( - <> -
- - ); -} \ No newline at end of file diff --git a/frontend/src/components/GameListItem/GameListItem.module.css b/frontend/src/components/GameListItem/GameListItem.module.css new file mode 100644 index 0000000..10da3c9 --- /dev/null +++ b/frontend/src/components/GameListItem/GameListItem.module.css @@ -0,0 +1,34 @@ +.row { + display: flex; + align-items: center; + flex: 1 1 0; + min-height: var(--row-min-height); + gap: 1.5rem; + padding: 0 1.5rem; +} + +.odd { + background-color: var(--color-stripe-odd); +} + +.even { + background-color: var(--color-stripe-even); +} + +.image { + width: 95px; + height: 95px; + object-fit: cover; + background-color: #ddd; + flex-shrink: 0; +} + +.info p { + margin: 0; +} + +.title { + margin: 0 0 0.25rem; + font-size: 1.1rem; + font-weight: 700; +} diff --git a/frontend/src/components/GameListItem/GameListItem.tsx b/frontend/src/components/GameListItem/GameListItem.tsx new file mode 100644 index 0000000..e682427 --- /dev/null +++ b/frontend/src/components/GameListItem/GameListItem.tsx @@ -0,0 +1,31 @@ +import styles from "./GameListItem.module.css"; + +interface GameListItemProps { + title: string; + year: number; + tags: string; + rating: number; + image: string; + striped: boolean; +} + +export default function GameListItem({ + title, + year, + tags, + rating, + image, + striped, +}: GameListItemProps) { + return ( +
+ {title} +
+

{title}

+

{year}

+

{tags}

+

{rating.toFixed(1)}/10

+
+
+ ); +} diff --git a/frontend/src/components/Header/Header.module.css b/frontend/src/components/Header/Header.module.css new file mode 100644 index 0000000..7fde7a7 --- /dev/null +++ b/frontend/src/components/Header/Header.module.css @@ -0,0 +1,42 @@ +.header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; + height: var(--header-height); + padding: 0 1.5rem; + background-color: var(--color-brand); +} + +.brand, +.link { + font-weight: 700; + color: var(--color-text); + white-space: nowrap; +} + +.link { + text-decoration: none; +} + +.search { + flex: 1; + max-width: 420px; +} + +.search :global(.input-group) { + border-radius: 999px; + overflow: hidden; +} + +.search :global(.form-control) { + border: none; +} + +.search :global(.form-control:focus) { + box-shadow: none; +} + +.search :global(.btn) { + border: none; +} diff --git a/frontend/src/components/Header/Header.tsx b/frontend/src/components/Header/Header.tsx new file mode 100644 index 0000000..b846e7f --- /dev/null +++ b/frontend/src/components/Header/Header.tsx @@ -0,0 +1,27 @@ +import { Button, Form, InputGroup } from "react-bootstrap"; +import { Search } from "react-bootstrap-icons"; +import styles from "./Header.module.css"; + +export default function Header() { + return ( +
+ Working Title + +
+ + + + +
+ + + My Ratings + +
+ ); +} diff --git a/frontend/src/components/Layout/Layout.module.css b/frontend/src/components/Layout/Layout.module.css new file mode 100644 index 0000000..52611a0 --- /dev/null +++ b/frontend/src/components/Layout/Layout.module.css @@ -0,0 +1,5 @@ +.shell { + width: 100%; + max-width: var(--content-width); + margin: 0 auto; +} diff --git a/frontend/src/components/Layout/Layout.tsx b/frontend/src/components/Layout/Layout.tsx new file mode 100644 index 0000000..ced9683 --- /dev/null +++ b/frontend/src/components/Layout/Layout.tsx @@ -0,0 +1,6 @@ +import type { ReactNode } from "react"; +import styles from "./Layout.module.css"; + +export default function Layout({ children }: { children: ReactNode }) { + return
{children}
; +} diff --git a/frontend/src/components/Toolbar/Toolbar.module.css b/frontend/src/components/Toolbar/Toolbar.module.css new file mode 100644 index 0000000..51f876a --- /dev/null +++ b/frontend/src/components/Toolbar/Toolbar.module.css @@ -0,0 +1,33 @@ +.toolbar { + display: flex; + align-items: center; + justify-content: space-between; + height: var(--toolbar-height); + padding: 0 1.5rem; + background-color: #fff; + border-bottom: 1px solid var(--color-border); + color: var(--color-muted); +} + +.action { + display: inline-flex; + align-items: center; + gap: 0.4rem; + border: none; + background: none; + color: inherit; + font: inherit; + cursor: pointer; +} + +.pagination { + display: inline-flex; + align-items: center; + gap: 0.75rem; +} + +.sort { + display: inline-flex; + align-items: center; + gap: 1rem; +} diff --git a/frontend/src/components/Toolbar/Toolbar.tsx b/frontend/src/components/Toolbar/Toolbar.tsx new file mode 100644 index 0000000..e2eadbb --- /dev/null +++ b/frontend/src/components/Toolbar/Toolbar.tsx @@ -0,0 +1,33 @@ +import { + ArrowDownUp, + ChevronDown, + ChevronLeft, + ChevronRight, + Funnel, +} from "react-bootstrap-icons"; +import styles from "./Toolbar.module.css"; + +export default function Toolbar() { + return ( +
+ + +
+ + Page 1 + +
+ +
+ + +
+
+ ); +} diff --git a/frontend/src/index.css b/frontend/src/index.css index e69de29..8082bd9 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -0,0 +1,34 @@ +:root { + /* 50% of a 1920px viewport — the grid stays centered at this width until + the viewport can't fit it anymore, then it fills the screen (see Layout). */ + --content-width: 960px; + --header-height: 72px; + --toolbar-height: 56px; + /* Rows grow to fill the list height evenly, split across however many + games are currently rendered, but never shrink below this. Once rows + hit the floor, the list clips instead of squeezing them unreadably + thin (see GamesGrid.module.css and GameListItem.module.css). */ + --row-min-height: 112px; + --color-brand: #a8504f; + --color-text: #1a1a1a; + --color-muted: #495057; + --color-stripe-odd: #f6d9d8; + --color-stripe-even: #fdf6f5; + --color-border: #e9cfce; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + background-color: var(--color-stripe-odd); + color: var(--color-text); + font-family: + system-ui, + -apple-system, + "Segoe UI", + Roboto, + sans-serif; +} diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index bef5202..15753af 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -1,10 +1,10 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import './index.css' -import App from './App.tsx' +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import "./index.css"; +import App from "./App"; -createRoot(document.getElementById('root')!).render( +createRoot(document.getElementById("root")!).render( , -) +); diff --git a/frontend/src/pages/GameDetails/GameDetails.tsx b/frontend/src/pages/GameDetails/GameDetails.tsx new file mode 100644 index 0000000..3415488 --- /dev/null +++ b/frontend/src/pages/GameDetails/GameDetails.tsx @@ -0,0 +1,32 @@ +import { Container, Image } from "react-bootstrap"; +import Header from "../../components/Header/Header"; + +export default function GameDetails() { + return ( + <> +
+ + Release year | Age rating +

Game Title

+
+ Game Image +
+
NTNU Rating:
+

Your Rating:

+
+
+
Tags:
+
Developer(s):
+
Publisher(s):
+
Platform(s):
+

Description

+
+ + ); +} diff --git a/frontend/src/pages/GamesGrid/GamesGrid.module.css b/frontend/src/pages/GamesGrid/GamesGrid.module.css new file mode 100644 index 0000000..4dd75cc --- /dev/null +++ b/frontend/src/pages/GamesGrid/GamesGrid.module.css @@ -0,0 +1,6 @@ +.list { + display: flex; + flex-direction: column; + height: calc(100vh - var(--header-height) - var(--toolbar-height)); + overflow: hidden; +} diff --git a/frontend/src/pages/GamesGrid/GamesGrid.tsx b/frontend/src/pages/GamesGrid/GamesGrid.tsx new file mode 100644 index 0000000..93c6aff --- /dev/null +++ b/frontend/src/pages/GamesGrid/GamesGrid.tsx @@ -0,0 +1,63 @@ +import Header from "../../components/Header/Header"; +import Layout from "../../components/Layout/Layout"; +import Toolbar from "../../components/Toolbar/Toolbar"; +import GameListItem from "../../components/GameListItem/GameListItem"; +import styles from "./GamesGrid.module.css"; + +const games = [ + { + title: "Heroes of Might and Magic: Olden Era", + year: 2026, + tags: "Turn-based strategy", + rating: 9.3, + image: "", + }, + { + title: "Minecraft", + year: 2009, + tags: "Indie, Survival, Adventure, Sandbox", + rating: 9.1, + image: "", + }, + { + title: "Ori and the Blind Forest", + year: 2015, + tags: "Metroidvania", + rating: 8.8, + image: "", + }, + { + title: "Pokémon Legends: Z-A", + year: 2025, + tags: "Action RPG", + rating: 0.1, + image: "", + }, + { + title: "Hades", + year: 2020, + tags: "Roguelike", + rating: 9.4, + image: "", + }, +]; + +export default function GamesGrid() { + return ( + <> +
+ + +
+ {games.map((game, index) => ( + + ))} +
+
+ + ); +} diff --git a/frontend/src/pages/MyRatings/MyRatings.tsx b/frontend/src/pages/MyRatings/MyRatings.tsx new file mode 100644 index 0000000..aede895 --- /dev/null +++ b/frontend/src/pages/MyRatings/MyRatings.tsx @@ -0,0 +1,9 @@ +import Header from "../../components/Header/Header"; + +export default function MyRatings() { + return ( + <> +
+ + ); +}