summaryrefslogtreecommitdiffhomepage
path: root/react-frontend/src
diff options
context:
space:
mode:
Diffstat (limited to 'react-frontend/src')
-rw-r--r--react-frontend/src/components/GameCard.tsx43
-rw-r--r--react-frontend/src/components/Layout.tsx23
-rw-r--r--react-frontend/src/index.css92
-rw-r--r--react-frontend/src/pages/Games.tsx28
-rw-r--r--react-frontend/src/routes/index.tsx2
5 files changed, 180 insertions, 8 deletions
diff --git a/react-frontend/src/components/GameCard.tsx b/react-frontend/src/components/GameCard.tsx
new file mode 100644
index 0000000..4367977
--- /dev/null
+++ b/react-frontend/src/components/GameCard.tsx
@@ -0,0 +1,43 @@
+import { Link } from "react-router-dom";
+
+export type ImageRendering = "auto" | "crisp-edges" | "pixelated";
+export type Tag = {
+ id: number,
+ tag_type: string,
+ name: string,
+};
+export type GameType = {
+ id: number,
+ title: string,
+ titleSlug: string,
+ description: string,
+ github_link: string,
+ img_rendering: ImageRendering,
+ status: string,
+ order: number,
+ created_at: string,
+ updated_at: string,
+ user_id: number,
+ tags: Tag[],
+ card_img: string,
+ char_img: string,
+ title_img: string,
+};
+export type GameCardProps = { link: string, game: GameType };
+
+export default function GameCard ({ link, game }: GameCardProps)
+{
+ return (
+ <>
+ <Link to={ link } role="button" className="block w-min pt-10 px-1">
+ <div className="gameCard">
+ <div className="gameCardWrapper">
+ <img style={{imageRendering: game.img_rendering}} src={`${import.meta.env.VITE_API_TITLE}/api/v1/games_img/realtradam/${game.titleSlug}.png?type=card`} className="gameCardCoverImg" />
+ </div>
+ <img style={{imageRendering: game.img_rendering}} src={`${import.meta.env.VITE_API_TITLE}/api/v1/games_img/realtradam/${game.titleSlug}.png?type=title`} className="gameTitleImg p-5%" />
+ <img style={{imageRendering: game.img_rendering}} src={`${import.meta.env.VITE_API_TITLE}/api/v1/games_img/realtradam/${game.titleSlug}.png?type=char`} className="gameCharacterImg" />
+ </div>
+ </Link>
+ </>
+ );
+}
diff --git a/react-frontend/src/components/Layout.tsx b/react-frontend/src/components/Layout.tsx
index db31b55..274e579 100644
--- a/react-frontend/src/components/Layout.tsx
+++ b/react-frontend/src/components/Layout.tsx
@@ -1,5 +1,5 @@
import { Dispatch } from 'react';
-import { Outlet, useNavigate } from "react-router-dom";
+import { Outlet, Link, useNavigate } from "react-router-dom";
import { IconButton, Button, ButtonGroup } from 'rsuite';
import { Icon } from '@rsuite/icons';
import { FaUser } from "react-icons/fa6";
@@ -28,22 +28,31 @@ export default function Layout(prop : { userData: userData, setUserData : Dispat
const loggedout_element = <IconButton onClick={loginLink} appearance="primary" color="green" icon={<Icon as={FaUser}/>}>Log In</IconButton>;
- const loggedin_element = <ButtonGroup className="flex"><Button appearance="ghost" style={{width:"100%"}}>{prop.userData.name}</Button><Button onClick={logoutLink} appearance="subtle" style={{paddingLeft:"1.4em", paddingRight:"1.4em"}}>Log Out</Button></ButtonGroup>;
+ const loggedin_element = <ButtonGroup className="flex"><Button appearance="ghost" color="red" style={{width:"100%"}}>{prop.userData.name}</Button><Button onClick={logoutLink} appearance="subtle" style={{paddingLeft:"1.4em", paddingRight:"1.4em"}}>Log Out</Button></ButtonGroup>;
console.log(prop);
return(
<>
<div className="w-screen h-screen flex border-none">
- <div className="flex flex-col h-screen overflow-y-auto overflow-x-hidden w-72 bg-stone-100">
+ <div className="flex flex-col h-screen overflow-y-auto overflow-x-hidden w-72 shrink-0 bg-stone-100">
<div className="flex flex-col bg-stone-800">
- <div className="m-4 mb-0 flex flex-col flex-grow">
- { prop.userData.name ? loggedin_element : loggedout_element }
+ <div className="m-4 mb-0 flex flex-col flex-grow">
+ { prop.userData.name ? loggedin_element : loggedout_element }
+ </div>
</div>
+ <Link to="/" role="button">
+ <div className="border-green-500 p-2 text-red-700 bg-stone-800">
+ <Icon as={GiCowboyHolster} style={{width:"100%", height:"100%"}}/>
</div>
- <div className="border-green-500 p-2 mb-2 text-red-700 bg-stone-800 rounded-b-xl"><Icon as={GiCowboyHolster} style={{width:"100%", height:"100%"}}/></div>
+ <div className="border-green-500 p-2 mb-2 text-white font-title text-2xl text-center bg-stone-800 rounded-b-xl">
+ Game Holster
+ </div>
+ </Link>
<div className="flex flex-col px-4 gap-2">
- <IconButton appearance="subtle" size="lg" icon={<Icon as={FaGamepad}/>}>Browse Games</IconButton>
+ <Link to="/games" role="button">
+ <IconButton style={{width: '100%'}} appearance="subtle" size="lg" icon={<Icon as={FaGamepad}/>}>Browse Games</IconButton>
+ </Link>
<IconButton appearance="subtle" size="lg" icon={<Icon as={GrAdd}/>}>Upload Game</IconButton>
</div>
</div>
diff --git a/react-frontend/src/index.css b/react-frontend/src/index.css
index ac216f5..382c958 100644
--- a/react-frontend/src/index.css
+++ b/react-frontend/src/index.css
@@ -2,7 +2,97 @@
@tailwind components;
@tailwind utilities;
+/* debug class */
* {
- /*border: 1px solid red;*/
+ border: 1px solid red;
}
+@layer components {
+ .gameCard {
+ aspect-ratio: 5/7;
+ width: 18.75em; /*300px;*/
+ perspective: 2500px;
+
+ @apply relative flex justify-center items-end;
+ }
+
+ .gameCardCoverImg {
+
+ @apply w-full h-full object-cover rounded-md;
+ }
+
+ .gameCardWrapper {
+ transition: all 0.5s;
+ z-index: -1;
+ box-shadow: -15px 15px 32px -8px rgba(0, 0, 0, 0.75);
+
+ @apply absolute w-full h-full rounded-md;
+ }
+
+ .gameCard:hover .gameCardWrapper {
+ transform: perspective(900px) translateY(-5%) rotateX(25deg) translateZ(0);
+ box-shadow: -10px 35px 32px -8px rgba(0, 0, 0, 0.75);
+ }
+
+ .gameCardWrapper::before,
+ .gameCardWrapper::after {
+ content: "";
+ transition: all 0.5s;
+
+ @apply opacity-0 w-full h-20 absolute left-0 rounded-md;
+ }
+
+ .gameCardWrapper::before {
+ background-image: linear-gradient(
+ to top,
+ transparent 46%,
+ rgba(12, 13, 19, 0.5) 68%,
+ rgba(12, 13, 19) 97%
+ );
+
+ @apply top-0 h-full rounded-md;
+ }
+
+ .gameCardWrapper::after {
+ background-image: linear-gradient(
+ to bottom,
+ transparent 46%,
+ rgba(12, 13, 19, 0.5) 68%,
+ rgba(12, 13, 19) 97%
+ );
+
+ @apply bottom-0 opacity-100 rounded-md;
+ }
+
+ .gameCard:hover .gameCardWrapper::before,
+ .gameCardWrapper::after {
+
+ @apply opacity-100;
+ }
+
+ .gameTitleImg {
+ padding: 5%;
+ transition: transform 0.5s;
+
+ @apply w-full max-h-40 object-contain;
+ }
+
+ .gameCard:hover .gameTitleImg {
+ transform: translate3d(0%, -50px, 100px);
+ }
+
+ .gameCharacterImg {
+ transition: all 0.5s;
+ z-index: -1;
+
+ @apply w-full max-h-80 object-contain opacity-0 absolute;
+ }
+
+ .gameCard:hover .gameCharacterImg {
+ transform: translate3d(0%, -30%, 100px);
+
+ @apply opacity-100;
+ }
+}
+
+
diff --git a/react-frontend/src/pages/Games.tsx b/react-frontend/src/pages/Games.tsx
new file mode 100644
index 0000000..2a9b23a
--- /dev/null
+++ b/react-frontend/src/pages/Games.tsx
@@ -0,0 +1,28 @@
+import { useState, useEffect } from "react";
+import GameCard from "../components/GameCard";
+
+export default function Games () {
+ const [games, setGames] = useState<GameType[]>([]);
+
+ useEffect(() => {
+ const url = `${import.meta.env.VITE_API_TITLE}/api/v1/games`;
+ fetch(url).then((response) => {
+ if (response.ok) {
+ return response.json();
+ }
+ throw new Error("Network response was not ok.");
+ }).then((response) => setGames(response)); //.catch(() => navigate("/"));
+ }, []);
+
+ const allGames = games.map((game) => (
+ <GameCard link={`/game/${game.titleSlug}`} game={game} key={game.id}/>
+ ));
+
+ return (
+ <>
+ <div className="w-full flex flex-wrap gap-12 justify-center">
+ { allGames }
+ </div>
+ </>
+ );
+}
diff --git a/react-frontend/src/routes/index.tsx b/react-frontend/src/routes/index.tsx
index 6876ce5..9b3d599 100644
--- a/react-frontend/src/routes/index.tsx
+++ b/react-frontend/src/routes/index.tsx
@@ -1,6 +1,7 @@
import { useState, useEffect } from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from "../pages/Home";
+import Games from "../pages/Games";
import Layout from "../components/Layout";
import CloseWindow from "../pages/CloseWindow";
@@ -34,6 +35,7 @@ export default function Index()
<Routes>
<Route path="/" element = {<Layout userData={userData} setUserData={setUserData}/>}>
<Route index element={<Home />} />
+ <Route path="/games" element={<Games />} />
<Route path="/closewindow" element={<CloseWindow />} />
</Route>
</Routes>