From 91046ee005d70643babf35e6aa460355320c2334 Mon Sep 17 00:00:00 2001 From: Alyx Cornu Date: Thu, 28 May 2026 09:47:29 +0200 Subject: [PATCH 1/2] feat: added metadata, added games page --- src/app/[lang]/articles/page.tsx | 15 +- src/app/[lang]/events/page.tsx | 15 +- src/app/[lang]/games/[game]/page.tsx | 59 ++++++++ src/app/[lang]/games/page.tsx | 45 ++++++ src/app/[lang]/layout.tsx | 45 +++++- src/app/[lang]/page.tsx | 22 +-- src/app/[lang]/projects/[project]/page.tsx | 1 - src/app/[lang]/projects/page.tsx | 15 +- src/components/ArticleCard.tsx | 45 ------ src/components/Cards.tsx | 160 +++++++++++++++++++++ src/components/ComiteeBar.tsx | 2 +- src/components/ComiteeCard.tsx | 40 ------ src/components/EventCard.tsx | 29 ---- src/components/GameEmbed.tsx | 27 ++++ src/components/Navigation.tsx | 6 +- src/components/ProjectCard.tsx | 26 ---- src/styles/components/_game-embed.scss | 13 ++ src/styles/components/_index.scss | 1 + 18 files changed, 389 insertions(+), 177 deletions(-) create mode 100644 src/app/[lang]/games/[game]/page.tsx create mode 100644 src/app/[lang]/games/page.tsx delete mode 100644 src/components/ArticleCard.tsx create mode 100644 src/components/Cards.tsx delete mode 100644 src/components/ComiteeCard.tsx delete mode 100644 src/components/EventCard.tsx create mode 100644 src/components/GameEmbed.tsx delete mode 100644 src/components/ProjectCard.tsx create mode 100644 src/styles/components/_game-embed.scss diff --git a/src/app/[lang]/articles/page.tsx b/src/app/[lang]/articles/page.tsx index 2a02528..3aecd0f 100644 --- a/src/app/[lang]/articles/page.tsx +++ b/src/app/[lang]/articles/page.tsx @@ -2,7 +2,20 @@ import { directus } from "@/directus"; import { capitalize, useTranslationTable } from "@/locales"; import { readItems } from "@directus/sdk"; import { GameStarArticle } from "@/types/aliases"; -import ArticleCard from "@/components/ArticleCard"; +import { ArticleCard } from "@/components/Cards"; + +export async function generateMetadata({ + params, +}: { + params: Promise<{ lang: string }>; +}) { + const { lang } = await params; + const tt = await useTranslationTable(lang); + + return { + title: `${capitalize(tt["article"])}s | Game*`, + }; +} export default async function Articles({ params, diff --git a/src/app/[lang]/events/page.tsx b/src/app/[lang]/events/page.tsx index 069eb3b..12702f4 100644 --- a/src/app/[lang]/events/page.tsx +++ b/src/app/[lang]/events/page.tsx @@ -2,7 +2,20 @@ import { directus } from "@/directus"; import { capitalize, queryTranslations, useTranslationTable } from "@/locales"; import { readItems } from "@directus/sdk"; import { GameStarEvent } from "@/types/aliases"; -import EventCard from "@/components/EventCard"; +import { EventCard } from "@/components/Cards"; + +export async function generateMetadata({ + params, +}: { + params: Promise<{ lang: string }>; +}) { + const { lang } = await params; + const tt = await useTranslationTable(lang); + + return { + title: `${capitalize(tt["event"])}s | Game*`, + }; +} export default async function Events({ params, diff --git a/src/app/[lang]/games/[game]/page.tsx b/src/app/[lang]/games/[game]/page.tsx new file mode 100644 index 0000000..e37a6ef --- /dev/null +++ b/src/app/[lang]/games/[game]/page.tsx @@ -0,0 +1,59 @@ +import GameEmbed from "@/components/GameEmbed"; +import { directus } from "@/directus"; +import { capitalize, getTranslation, queryTranslations } from "@/locales"; +import { GameStarGame } from "@/types/aliases"; +import { readItems } from "@directus/sdk"; +import { notFound } from "next/navigation"; +import { Suspense } from "react"; +import Markdown from "react-markdown"; + +async function getGame(project_slug: string): Promise { + const games = (await directus().request( + readItems("game_star_games", { + limit: 1, + ...queryTranslations, + }), + )) as GameStarGame[]; + + const project = games[0] ?? null; + + if (!project) { + notFound(); + } + + return project; +} + +export async function generateMetadata({ + params, +}: { + params: Promise<{ game: string; lang: string }>; +}) { + const { game: game_slug, lang } = await params; + + const gameData = await getGame(game_slug); + const translation = getTranslation(gameData, lang); + + return { + title: gameData.name, + description: translation.description, + }; +} + +export default async function Game({ + params, +}: { + params: Promise<{ game: string; lang: string }>; +}) { + const { game: game_slug, lang } = await params; + const gameData = await getGame(game_slug); + const translation = getTranslation(gameData, lang); + + return ( +
+

{capitalize(gameData.name)}

+ + {translation.content} +
+ ); +} diff --git a/src/app/[lang]/games/page.tsx b/src/app/[lang]/games/page.tsx new file mode 100644 index 0000000..207ee74 --- /dev/null +++ b/src/app/[lang]/games/page.tsx @@ -0,0 +1,45 @@ +import { directus } from "@/directus"; +import { capitalize, queryTranslations, useTranslationTable } from "@/locales"; +import { readItems } from "@directus/sdk"; +import { GameStarGame } from "@/types/aliases"; +import { GameCard } from "@/components/Cards"; + +export async function generateMetadata({ + params, +}: { + params: Promise<{ lang: string }>; +}) { + const { lang } = await params; + const tt = await useTranslationTable(lang); + + return { + title: `${capitalize(tt["games"])} | Game*`, + }; +} + +export default async function Games({ + params, +}: { + params: Promise<{ lang: string }>; +}) { + const { lang } = await params; + const tt = await useTranslationTable(lang); + + let games = (await directus().request( + readItems("game_star_games", { + ...queryTranslations, + }), + )) as GameStarGame[]; + + return ( +
+

{capitalize(tt["games"])}

+
+ {games.map((game) => ( + + ))} +
+ {games.length === 0 ?

{tt["gamestar.comingSoon"]} !

: null} +
+ ); +} diff --git a/src/app/[lang]/layout.tsx b/src/app/[lang]/layout.tsx index 9656681..b7aa58c 100644 --- a/src/app/[lang]/layout.tsx +++ b/src/app/[lang]/layout.tsx @@ -1,9 +1,35 @@ import Footer from "@/components/Footer"; import Navigation from "@/components/Navigation"; import { directus } from "@/directus"; -import { capitalize, useTranslationTable } from "@/locales"; +import { + capitalize, + getTranslation, + queryTranslations, + useTranslationTable, +} from "@/locales"; import "@/styles/style.scss"; -import { readItems } from "@directus/sdk"; +import { GameStar } from "@/types/aliases"; +import { readItems, readSingleton } from "@directus/sdk"; + +export async function generateMetadata({ + params, +}: { + params: Promise<{ lang: string }>; +}) { + const { lang } = await params; + + const game_star = (await directus().request( + readSingleton("game_star", { + ...queryTranslations, + }), + )) as GameStar; + const game_star_translation = getTranslation(game_star, lang); + + return { + title: "Game*", + description: game_star_translation.about_text, + }; +} export default async function RootLayout({ children, @@ -28,10 +54,17 @@ export default async function RootLayout({
{children}
diff --git a/src/app/[lang]/page.tsx b/src/app/[lang]/page.tsx index 2c397a2..4cf5042 100644 --- a/src/app/[lang]/page.tsx +++ b/src/app/[lang]/page.tsx @@ -10,29 +10,9 @@ import { AssociationMembership, Member } from "@/types/aliases"; import ComiteeBar from "@/components/ComiteeBar"; import { GameStar, GameStarEvent } from "@/types/aliases"; import Image from "next/image"; -import EventCard from "@/components/EventCard"; import Link from "next/link"; import ForwardArrowIcon from "@/components/icons/ForwardArrowIcon"; - -export async function generateMetadata({ - params, -}: { - params: Promise<{ lang: string }>; -}) { - const { lang } = await params; - - const game_star = (await directus().request( - readSingleton("game_star", { - ...queryTranslations, - }), - )) as GameStar; - const game_star_translation = getTranslation(game_star, lang); - - return { - title: "Game*", - description: game_star_translation.about_text, - }; -} +import { EventCard } from "@/components/Cards"; export default async function Home({ params, diff --git a/src/app/[lang]/projects/[project]/page.tsx b/src/app/[lang]/projects/[project]/page.tsx index 18591fb..fce168b 100644 --- a/src/app/[lang]/projects/[project]/page.tsx +++ b/src/app/[lang]/projects/[project]/page.tsx @@ -29,7 +29,6 @@ export async function generateMetadata({ params: Promise<{ project: string; lang: string }>; }) { const { project: project_slug, lang } = await params; - const translation = getTranslation(await getProject(project_slug), lang); return { diff --git a/src/app/[lang]/projects/page.tsx b/src/app/[lang]/projects/page.tsx index 4124db9..aee0ea3 100644 --- a/src/app/[lang]/projects/page.tsx +++ b/src/app/[lang]/projects/page.tsx @@ -2,7 +2,20 @@ import { directus } from "@/directus"; import { capitalize, queryTranslations, useTranslationTable } from "@/locales"; import { readItems } from "@directus/sdk"; import { GameStarProject } from "@/types/aliases"; -import ProjectCard from "@/components/ProjectCard"; +import { ProjectCard } from "@/components/Cards"; + +export async function generateMetadata({ + params, +}: { + params: Promise<{ lang: string }>; +}) { + const { lang } = await params; + const tt = await useTranslationTable(lang); + + return { + title: `${capitalize(tt["project"])}s | Game*`, + }; +} export default async function Projects({ params, diff --git a/src/components/ArticleCard.tsx b/src/components/ArticleCard.tsx deleted file mode 100644 index c70cd47..0000000 --- a/src/components/ArticleCard.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { getTranslation, useTranslationTable } from "@/locales"; -import { - GameStarArticle, - GameStarArticleMember, - Member, -} from "@/types/aliases"; -import Link from "next/link"; - -export default async function ArticleCard({ - article, - lang, -}: { - article: GameStarArticle; - lang: string; -}) { - const tt = await useTranslationTable(lang); - - const translation = getTranslation(article, lang); - - let names = (article.authors as GameStarArticleMember[])?.map( - (author) => - (author.members_id as Member).name + - " " + - (author.members_id as Member).surname, - ); - - let start = names.slice(0, -1).join(", "); - let last = names[names.length - 1]; - - let res = start === "" ? ` ${last}` : ` ${start} ${tt["and"]} ${last}`; - - return ( -
- -
-

{translation.title}

-
- {article.authors?.length === 0 ? tt["anonymous"] : tt["by"] + res} -
-
-

{translation.description}

- -
- ); -} diff --git a/src/components/Cards.tsx b/src/components/Cards.tsx new file mode 100644 index 0000000..aab14f2 --- /dev/null +++ b/src/components/Cards.tsx @@ -0,0 +1,160 @@ +import { getTranslation, useTranslationTable } from "@/locales"; +import { + GameStarArticle, + GameStarArticleMember, + GameStarEvent, + GameStarGame, + GameStarProject, + Member, +} from "@/types/aliases"; +import Link from "next/link"; +import DirectusImage from "./DirectusImage"; + +export async function ProjectCard({ + project, + lang, +}: { + project: GameStarProject; + lang: string; +}) { + const tt = await useTranslationTable(lang); + + const translation = getTranslation(project, lang); + + return ( +
+ +
+

{translation.title}

+
+

{translation.description}

+ +
+ ); +} + +export function EventCard({ + event, + lang, +}: { + event: GameStarEvent; + lang: string; +}) { + const translation = getTranslation(event, lang); + let start_date = new Date(event.start!).toLocaleDateString("fr-FR", { + hour: "2-digit", + minute: "2-digit", + }); + return ( +
+ +
+

{translation.title}

+
{start_date}
+
+ +

{translation.description}

+ +
+ ); +} + +export async function GameCard({ + game, + lang, +}: { + game: GameStarGame; + lang: string; +}) { + const tt = await useTranslationTable(lang); + + const translation = getTranslation(game, lang); + + return ( +
+ +
+

{game.name}

+
+

{translation.description}

+ +
+ ); +} + +export async function ArticleCard({ + article, + lang, +}: { + article: GameStarArticle; + lang: string; +}) { + const tt = await useTranslationTable(lang); + + const translation = getTranslation(article, lang); + + let names = (article.authors as GameStarArticleMember[])?.map( + (author) => + (author.members_id as Member).name + + " " + + (author.members_id as Member).surname, + ); + + let start = names.slice(0, -1).join(", "); + let last = names[names.length - 1]; + + let res = start === "" ? ` ${last}` : ` ${start} ${tt["and"]} ${last}`; + + return ( +
+ +
+

{translation.title}

+
+ {article.authors?.length === 0 ? tt["anonymous"] : tt["by"] + res} +
+
+

{translation.description}

+ +
+ ); +} + +export function ComiteeCard({ + name, + surname, + role, + link, + image, +}: { + name: string; + surname: string; + role: string; + link: string; + image: string; +}) { + return ( + +
+ +
+

+ {name} {surname} +

+

{role}

+
+
+
+ ); +} diff --git a/src/components/ComiteeBar.tsx b/src/components/ComiteeBar.tsx index 6fd632d..3a8da78 100644 --- a/src/components/ComiteeBar.tsx +++ b/src/components/ComiteeBar.tsx @@ -1,4 +1,4 @@ -import ComiteeCard from "./ComiteeCard"; +import { ComiteeCard } from "./Cards"; export default function ComiteeBar({ comitees, diff --git a/src/components/ComiteeCard.tsx b/src/components/ComiteeCard.tsx deleted file mode 100644 index c63b459..0000000 --- a/src/components/ComiteeCard.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import DirectusImage from "./DirectusImage"; - -export default function ComiteeCard({ - name, - surname, - role, - link, - image, -}: { - name: string; - surname: string; - role: string; - link: string; - image: string; -}) { - return ( - -
- -
-

- {name} {surname} -

-

{role}

-
-
-
- ); -} diff --git a/src/components/EventCard.tsx b/src/components/EventCard.tsx deleted file mode 100644 index 5f5117e..0000000 --- a/src/components/EventCard.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { getTranslation } from "@/locales"; -import { GameStarEvent } from "@/types/aliases"; -import Link from "next/link"; - -export default function EventCard({ - event, - lang, -}: { - event: GameStarEvent; - lang: string; -}) { - const translation = getTranslation(event, lang); - let start_date = new Date(event.start!).toLocaleDateString("fr-FR", { - hour: "2-digit", - minute: "2-digit", - }); - return ( -
- -
-

{translation.title}

-
{start_date}
-
- -

{translation.description}

- -
- ); -} diff --git a/src/components/GameEmbed.tsx b/src/components/GameEmbed.tsx new file mode 100644 index 0000000..e2fec4b --- /dev/null +++ b/src/components/GameEmbed.tsx @@ -0,0 +1,27 @@ +"use client"; + +import { GameStarGame } from "@/types/aliases"; +import { useEffect, useState } from "react"; + +export default function GameEmbed({ game }: { game: GameStarGame }) { + const [isClient, setIsClient] = useState(false); + + useEffect(() => { + setIsClient(true); + }, []); + + return ( +
+ {isClient ? ( + + ) : null} +
+ ); +} diff --git a/src/components/Navigation.tsx b/src/components/Navigation.tsx index 0e613a4..a124ea4 100644 --- a/src/components/Navigation.tsx +++ b/src/components/Navigation.tsx @@ -2,11 +2,7 @@ import NavBar from "@components/NavBar"; import NavMenu from "@components/NavMenu"; import { useState } from "react"; -import { - disableBodyScroll, - enableBodyScroll, - clearAllBodyScrollLocks, -} from "body-scroll-lock"; +import { disableBodyScroll, enableBodyScroll } from "body-scroll-lock"; import DropdownCard from "./Dropdown"; import LangIcon from "./icons/LangIcon"; import { usePathname, useRouter } from "next/navigation"; diff --git a/src/components/ProjectCard.tsx b/src/components/ProjectCard.tsx deleted file mode 100644 index 6617c11..0000000 --- a/src/components/ProjectCard.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { getTranslation, useTranslationTable } from "@/locales"; -import { GameStarProject } from "@/types/aliases"; -import Link from "next/link"; - -export default async function ProjectCard({ - project, - lang, -}: { - project: GameStarProject; - lang: string; -}) { - const tt = await useTranslationTable(lang); - - const translation = getTranslation(project, lang); - - return ( -
- -
-

{translation.title}

-
-

{translation.description}

- -
- ); -} diff --git a/src/styles/components/_game-embed.scss b/src/styles/components/_game-embed.scss new file mode 100644 index 0000000..a01e107 --- /dev/null +++ b/src/styles/components/_game-embed.scss @@ -0,0 +1,13 @@ +.game-embed { + width: 640px; + height: 360px; + align-self: center; + margin-bottom: 2em; +} + +.game-embed-skeleton { + width: 640px; + height: 360px; + margin-bottom: 2em; + background-color: #eee; +} diff --git a/src/styles/components/_index.scss b/src/styles/components/_index.scss index 18cb250..d8acad8 100644 --- a/src/styles/components/_index.scss +++ b/src/styles/components/_index.scss @@ -6,3 +6,4 @@ @forward "content"; @forward "not-found"; @forward "cards"; +@forward "game-embed"; From 9293d73b3e2d8e4b3d997bd426467e054622f55f Mon Sep 17 00:00:00 2001 From: Alyx Cornu Date: Thu, 28 May 2026 13:38:38 +0200 Subject: [PATCH 2/2] fix: typo --- directus-config | 2 +- package.json | 2 +- src/app/[lang]/games/[game]/page.tsx | 4 ++-- src/components/PeopleBar.tsx | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/directus-config b/directus-config index b3e465d..7f0389d 160000 --- a/directus-config +++ b/directus-config @@ -1 +1 @@ -Subproject commit b3e465df5ef95b992eff4aac2ecf9967b3e5956c +Subproject commit 7f0389d9d878b49488ef656f9c599e3731809863 diff --git a/package.json b/package.json index c3a23e2..766be30 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "build": "next build", "start": "next start", "lint": "next lint", - "prepare": "husky && git submodule update --init", + "prepare": "husky", "load-directus": "sh directus-config/load.sh", "save-directus": "sh directus-config/save.sh", "save-directus-data": "sh directus-config/save-data.sh", diff --git a/src/app/[lang]/games/[game]/page.tsx b/src/app/[lang]/games/[game]/page.tsx index e37a6ef..6909ee4 100644 --- a/src/app/[lang]/games/[game]/page.tsx +++ b/src/app/[lang]/games/[game]/page.tsx @@ -4,12 +4,12 @@ import { capitalize, getTranslation, queryTranslations } from "@/locales"; import { GameStarGame } from "@/types/aliases"; import { readItems } from "@directus/sdk"; import { notFound } from "next/navigation"; -import { Suspense } from "react"; import Markdown from "react-markdown"; -async function getGame(project_slug: string): Promise { +async function getGame(game_slug: string): Promise { const games = (await directus().request( readItems("game_star_games", { + filter: { slug: { _eq: game_slug } }, limit: 1, ...queryTranslations, }), diff --git a/src/components/PeopleBar.tsx b/src/components/PeopleBar.tsx index bceebca..7ed1e25 100644 --- a/src/components/PeopleBar.tsx +++ b/src/components/PeopleBar.tsx @@ -1,4 +1,4 @@ -import ComiteeCard from "./ComiteeCard"; +import { ComiteeCard } from "./Cards"; export default function PeopleBar({ people,