diff --git a/databases/dump/prisma/schema.prisma b/databases/dump/prisma/schema.prisma index a12c862..dd65eca 100644 --- a/databases/dump/prisma/schema.prisma +++ b/databases/dump/prisma/schema.prisma @@ -13,6 +13,10 @@ model character { name String? image_path String? debris_id Int? + character_stories Json? + ex_stories Json? + rod_stories Json? + hidden_stories Json? debris debris? @relation(fields: [debris_id], references: [debris_id], onDelete: NoAction, onUpdate: NoAction, map: "fk_character_debris_debris_id") character_rank_bonus character_rank_bonus[] costume costume[] @@ -360,3 +364,59 @@ model weapon_story_link { @@id([weapon_id, weapon_story_id], map: "pk_weapon_story_link") @@index([weapon_story_id], map: "ix_weapon_story_link_weapon_story_id") } + +model card_story { + id Int @id(map: "pk_card_story") @default(autoincrement()) + name String? + stories Json? +} + +model event { + id Int @id(map: "pk_event") @default(autoincrement()) + name String? + image_path String? + start_date DateTime @db.Timestamptz(6) + end_date DateTime @db.Timestamptz(6) + stories Json? +} + +model lost_archive { + id Int @id(map: "pk_lost_archive") @default(autoincrement()) + name String? + number String? + order Int + story String? + image_path String? +} + +model main_quest_chapter { + chapter_id Int + chapter_text_asset_id Int + route_id Int + chapter_number String? + chapter_title String? + order Decimal @db.Decimal + stories Json? + main_quest_route main_quest_route @relation(fields: [route_id], references: [route_id], onDelete: Cascade, onUpdate: NoAction, map: "fk_main_quest_chapter_main_quest_route_route_id") + + @@id([chapter_id, chapter_text_asset_id], map: "pk_main_quest_chapter") + @@index([route_id], map: "ix_main_quest_chapter_route_id") +} + +model main_quest_route { + route_id Int @id(map: "pk_main_quest_route") @default(autoincrement()) + season_id Int + route_name String? + order Int + main_quest_chapter main_quest_chapter[] + main_quest_season main_quest_season @relation(fields: [season_id], references: [season_id], onDelete: Cascade, onUpdate: NoAction, map: "fk_main_quest_route_main_quest_season_season_id") + + @@index([season_id], map: "ix_main_quest_route_season_id") +} + +model main_quest_season { + season_id Int @id(map: "pk_main_quest_season") @default(autoincrement()) + season_name String? + order Int + main_quest_route main_quest_route[] +} diff --git a/src/components/Layout/StoriesLayout.tsx b/src/components/Layout/StoriesLayout.tsx new file mode 100644 index 0000000..b0985b2 --- /dev/null +++ b/src/components/Layout/StoriesLayout.tsx @@ -0,0 +1,92 @@ +import Link from "next/link"; +import classNames from "classnames"; +import { useRouter } from "next/router"; + +const ITEMS = [ + { + label: "Main story", + href: "/database/stories/main", + }, + { + label: "Characters", + href: "/database/stories/characters", + }, + { + label: "EX characters", + href: "/database/stories/ex-characters", + }, + { + label: "Recollections of Dusk", + href: "/database/stories/rod-characters", + }, + { + label: "Events", + href: "/database/stories/events", + }, + { + label: "Anecdotes", + href: "/database/stories/anecdotes", + }, + { + label: "Hidden stories", + href: "/database/stories/hidden-stories", + disabled: true, + }, + { + label: "Lost archives", + href: "/database/stories/lost-archives", + }, + { + label: "Costumes", + href: "/database/stories/costumes", + }, + { + label: "Weapons", + href: "/database/stories/weapons", + }, + { + label: "Companions", + href: "/database/stories/companions", + }, + { + label: "Memoirs", + href: "/database/stories/memoirs", + }, +]; + +export default function StoriesLayout({ children }): JSX.Element { + const router = useRouter(); + + return ( +
+ + +
{children}
+
+ ); +} diff --git a/src/pages/database/stories/anecdotes.tsx b/src/pages/database/stories/anecdotes.tsx new file mode 100644 index 0000000..116e610 --- /dev/null +++ b/src/pages/database/stories/anecdotes.tsx @@ -0,0 +1,102 @@ +import Layout from "@components/Layout"; +import Meta from "@components/Meta"; +import Link from "next/link"; +import SVG from "react-inlinesvg"; +import prisma from "@libs/prisma"; +import { card_story } from "@prisma/client"; +import { CDN_URL } from "@config/constants"; +import { useState } from "react"; +import { FormControl, InputLabel, MenuItem, Select } from "@mui/material"; +import Squares from "@components/decorations/Squares"; +import StoriesLayout from "@components/Layout/StoriesLayout"; + +interface Props { + anecdotes: card_story[]; +} + +export default function DatabaseStories({ anecdotes }: Props): JSX.Element { + const [anecdoteIndex, setAnecdoteIndex] = useState(anecdotes[0].id); + + return ( + + + + + + +
+
+ + Anecdote + + +
+ +
+ + {anecdotes + .filter((anecdote) => anecdote.id === anecdoteIndex) + .map((anecdote) => ( +
+ {anecdote.stories.map(({ story, image_path }, index) => ( +
+ {`Story +

+
+ ))} +
+ ))} +
+
+
+
+ ); +} + +export async function getStaticProps() { + const anecdotes = await prisma.dump.card_story.findMany(); + + return { + props: JSON.parse( + JSON.stringify({ + anecdotes, + }) + ), + }; +} diff --git a/src/pages/database/stories/characters.tsx b/src/pages/database/stories/characters.tsx new file mode 100644 index 0000000..36e9aee --- /dev/null +++ b/src/pages/database/stories/characters.tsx @@ -0,0 +1,107 @@ +import Layout from "@components/Layout"; +import Meta from "@components/Meta"; +import Link from "next/link"; +import SVG from "react-inlinesvg"; +import prisma from "@libs/prisma"; +import { character } from "@prisma/client"; +import { CDN_URL } from "@config/constants"; +import { useState } from "react"; +import { FormControl, InputLabel, MenuItem, Select } from "@mui/material"; +import Squares from "@components/decorations/Squares"; +import StoriesLayout from "@components/Layout/StoriesLayout"; + +interface Props { + characters: character[]; +} + +export default function DatabaseStories({ characters }: Props): JSX.Element { + const [characterIndex, setCharacterIndex] = useState( + characters[0].character_id + ); + + return ( + + + + + + +
+
+ + Character + + +
+ +
+ + {characters + .filter((character) => character.character_id === characterIndex) + .map((character) => ( +
+ {character.character_stories.map( + ({ story, image_path }, index) => ( +
+ {`Story +

+
+ ) + )} +
+ ))} +
+
+
+
+ ); +} + +export async function getStaticProps() { + const characters = await prisma.dump.character.findMany(); + + return { + props: JSON.parse( + JSON.stringify({ + characters: characters.filter( + (character) => character.character_stories.length > 0 + ), + }) + ), + }; +} diff --git a/src/pages/database/stories/companions.tsx b/src/pages/database/stories/companions.tsx index 159fa06..7733f0d 100644 --- a/src/pages/database/stories/companions.tsx +++ b/src/pages/database/stories/companions.tsx @@ -11,6 +11,7 @@ import { Box } from "@mui/system"; import { StoriesNavbar } from "./index"; import { useSettingsStore } from "@store/settings"; import CompanionThumbnail from "@components/CompanionThumbnail"; +import StoriesLayout from "@components/Layout/StoriesLayout"; interface DatabaseStoriesCompanionsProps { companions: companion[]; @@ -56,11 +57,7 @@ export default function DatabaseStoriesCompanions({ cover="https://nierrein.guide/cover-stories.jpg" /> -
-

Companions stories

- - - +
{ @@ -127,7 +124,7 @@ export default function DatabaseStoriesCompanions({
))} -
+ ); } diff --git a/src/pages/database/stories/costumes.tsx b/src/pages/database/stories/costumes.tsx index 12ed2a4..57aa475 100644 --- a/src/pages/database/stories/costumes.tsx +++ b/src/pages/database/stories/costumes.tsx @@ -18,6 +18,7 @@ import { NextPageContext } from "next"; import { Box } from "@mui/system"; import { StoriesNavbar } from "./index"; import { useSettingsStore } from "@store/settings"; +import StoriesLayout from "@components/Layout/StoriesLayout"; interface DatabaseStoriesCostumesProps { costumes: costume[]; @@ -96,11 +97,7 @@ export default function DatabaseStoriesCostumes({ cover="https://nierrein.guide/cover-stories.jpg" /> -
-

Costumes stories

- - - +
{ @@ -164,7 +161,6 @@ export default function DatabaseStoriesCostumes({ />
-
{costumes .filter((costume) => { @@ -215,7 +211,7 @@ export default function DatabaseStoriesCostumes({
)} -
+ ); } diff --git a/src/pages/database/stories/events.tsx b/src/pages/database/stories/events.tsx new file mode 100644 index 0000000..8f933d7 --- /dev/null +++ b/src/pages/database/stories/events.tsx @@ -0,0 +1,108 @@ +import Layout from "@components/Layout"; +import Meta from "@components/Meta"; +import Link from "next/link"; +import SVG from "react-inlinesvg"; +import prisma from "@libs/prisma"; +import { event } from "@prisma/client"; +import { CDN_URL } from "@config/constants"; +import { useState } from "react"; +import { FormControl, InputLabel, MenuItem, Select } from "@mui/material"; +import Squares from "@components/decorations/Squares"; +import StoriesLayout from "@components/Layout/StoriesLayout"; + +interface Props { + events: event[]; +} + +export default function DatabaseStories({ events }: Props): JSX.Element { + const [eventIndex, setEventIndex] = useState(events[0].id); + + return ( + + + + + + +
+
+ + Event + + +
+ +
+ + {events + .filter((event) => event.id === eventIndex) + .map((event) => ( +
+ {event.stories.map(({ story, image_path }, index) => ( +
+ {`Story +

+
+ ))} +
+ ))} +
+
+
+
+ ); +} + +export async function getStaticProps() { + const events = await prisma.dump.event.findMany(); + + return { + props: JSON.parse( + JSON.stringify({ + events, + }) + ), + }; +} diff --git a/src/pages/database/stories/ex-characters.tsx b/src/pages/database/stories/ex-characters.tsx new file mode 100644 index 0000000..9b4f7f4 --- /dev/null +++ b/src/pages/database/stories/ex-characters.tsx @@ -0,0 +1,105 @@ +import Layout from "@components/Layout"; +import Meta from "@components/Meta"; +import Link from "next/link"; +import SVG from "react-inlinesvg"; +import prisma from "@libs/prisma"; +import { character } from "@prisma/client"; +import { CDN_URL } from "@config/constants"; +import { useState } from "react"; +import { FormControl, InputLabel, MenuItem, Select } from "@mui/material"; +import Squares from "@components/decorations/Squares"; +import StoriesLayout from "@components/Layout/StoriesLayout"; + +interface Props { + characters: character[]; +} + +export default function DatabaseStories({ characters }: Props): JSX.Element { + const [characterIndex, setCharacterIndex] = useState( + characters[0].character_id + ); + + return ( + + + + + + +
+
+ + EX Character + + +
+ +
+ + {characters + .filter((character) => character.character_id === characterIndex) + .map((character) => ( +
+ {character.ex_stories.map(({ story, image_path }, index) => ( +
+ {`Story +

+
+ ))} +
+ ))} +
+
+
+
+ ); +} + +export async function getStaticProps() { + const characters = await prisma.dump.character.findMany(); + + return { + props: JSON.parse( + JSON.stringify({ + characters: characters.filter( + (character) => character.ex_stories.length > 0 + ), + }) + ), + }; +} diff --git a/src/pages/database/stories/index.tsx b/src/pages/database/stories/index.tsx index 7f44fe9..0fa8671 100644 --- a/src/pages/database/stories/index.tsx +++ b/src/pages/database/stories/index.tsx @@ -2,47 +2,58 @@ import Layout from "@components/Layout"; import Meta from "@components/Meta"; import Link from "next/link"; import SVG from "react-inlinesvg"; -import prisma from "@libs/prisma"; import classNames from "classnames"; import { useRouter } from "next/router"; const ITEMS = [ { - label: "Costumes", - href: "/database/stories/costumes", + label: "Main story", + href: "/database/stories/main", }, { - label: "Weapons", - href: "/database/stories/weapons", + label: "Characters", + href: "/database/stories/character", }, { - label: "Companions", - href: "/database/stories/companions", + label: "EX characters", + href: "/database/stories/ex-character", }, { - label: "Memoirs", - href: "/database/stories/memoirs", + label: "Recollections of Dusk", + href: "/database/stories/rod-character", }, { - label: "Events/EX (CMS data)", - href: "/database/stories/content", + label: "Events", + href: "/database/stories/events", }, - /* { - label: "Characters", - href: "#", + { + label: "Anecdotes", + href: "/database/stories/anecdotes", }, { - label: "Events", - href: "#", + label: "Hidden stories", + href: "/database/stories/hidden-stories", }, { - label: "Main story", - href: "#", + label: "Lost archives", + href: "/database/stories/lost-archives", }, { - label: "Hidden stories", - href: "#", - }, */ + label: "Costumes", + href: "/database/stories/costumes", + }, + { + label: "Weapons", + href: "/database/stories/weapons", + }, + { + label: "Companions", + href: "/database/stories/companions", + }, + { + label: "Memoirs", + href: "/database/stories/memoirs", + }, ]; export default function DatabaseStories(): JSX.Element { @@ -56,17 +67,17 @@ export default function DatabaseStories(): JSX.Element {

Stories

- +
+ +
); @@ -76,9 +87,9 @@ export function StoriesNavbar() { const router = useRouter(); return ( -
+
+ ); } - -export async function getStaticProps() { - const [costumes, selectCostumes] = await Promise.all([ - prisma.dump.costume.findMany({ - orderBy: { - character_id: "asc", - }, - }), - prisma.dump.costume.findMany({ - orderBy: { - costume_id: "asc", - }, - select: { - character: true, - title: true, - slug: true, - image_path_base: true, - release_time: true, - }, - }), - ]); - - selectCostumes.sort( - (a, b) => -b.character.name.localeCompare(a.character.name) - ); - - return { - props: JSON.parse( - JSON.stringify({ - costumes, - selectCostumes, - }) - ), - revalidate: 86400, - }; -} diff --git a/src/pages/database/stories/lost-archives.tsx b/src/pages/database/stories/lost-archives.tsx new file mode 100644 index 0000000..680d2dc --- /dev/null +++ b/src/pages/database/stories/lost-archives.tsx @@ -0,0 +1,98 @@ +import Layout from "@components/Layout"; +import Meta from "@components/Meta"; +import Link from "next/link"; +import SVG from "react-inlinesvg"; +import prisma from "@libs/prisma"; +import { lost_archive } from "@prisma/client"; +import { CDN_URL } from "@config/constants"; +import { useState } from "react"; +import { FormControl, InputLabel, MenuItem, Select } from "@mui/material"; +import Squares from "@components/decorations/Squares"; +import StoriesLayout from "@components/Layout/StoriesLayout"; + +interface Props { + lostArchives: lost_archive[]; +} + +export default function DatabaseStories({ lostArchives }: Props): JSX.Element { + const [lostArchiveIndex, setLostArchiveIndex] = useState(lostArchives[0].id); + + return ( + + + + + + +
+
+ + Lost Archive + + +
+ +
+ + {lostArchives + .filter((lostArchive) => lostArchive.id === lostArchiveIndex) + .map((lostArchive) => ( +
+ {`${lostArchive.name} +

+
+ ))} +
+
+
+
+ ); +} + +export async function getStaticProps() { + const lostArchives = await prisma.dump.lost_archive.findMany(); + + return { + props: JSON.parse( + JSON.stringify({ + lostArchives, + }) + ), + }; +} diff --git a/src/pages/database/stories/main.tsx b/src/pages/database/stories/main.tsx new file mode 100644 index 0000000..02567d2 --- /dev/null +++ b/src/pages/database/stories/main.tsx @@ -0,0 +1,264 @@ +import Layout from "@components/Layout"; +import Meta from "@components/Meta"; +import Link from "next/link"; +import SVG from "react-inlinesvg"; +import prisma from "@libs/prisma"; +import { + main_quest_chapter, + main_quest_route, + main_quest_season, +} from "@prisma/client"; +import { CDN_URL } from "@config/constants"; +import { useState } from "react"; +import { + Button, + FormControl, + InputLabel, + MenuItem, + Select, +} from "@mui/material"; +import Squares from "@components/decorations/Squares"; +import { FiArrowLeft, FiArrowRight } from "react-icons/fi"; +import StoriesLayout from "@components/Layout/StoriesLayout"; + +interface Props { + main_quest_season: (main_quest_season & { + main_quest_route: (main_quest_route & { + main_quest_chapter: main_quest_chapter[]; + })[]; + })[]; +} + +export default function DatabaseStories({ + main_quest_season, +}: Props): JSX.Element { + const [seasonIndex, setSeasonIndex] = useState(0); + const [routeIndex, setRouteIndex] = useState(0); + const [chapterIndex, setChapterIndex] = useState(0); + + const hasNextSeason = main_quest_season.length > seasonIndex + 1; + const hasNextRoute = + main_quest_season[seasonIndex].main_quest_route.length > routeIndex + 1; + const hasNextChapter = + main_quest_season[seasonIndex].main_quest_route[routeIndex] + .main_quest_chapter.length > + chapterIndex + 1; + + const hasPreviousSeason = !!main_quest_season[seasonIndex - 1]; + const hasPreviousRoute = + !!main_quest_season[seasonIndex].main_quest_route[routeIndex - 1]; + const hasPreviousChapter = + !!main_quest_season[seasonIndex].main_quest_route[routeIndex] + .main_quest_chapter[chapterIndex - 1]; + + function handlePrevious() { + if (hasPreviousChapter) { + setChapterIndex(chapterIndex - 1); + return; + } + + if (hasPreviousSeason) { + setChapterIndex(0); + setRouteIndex(0); + setSeasonIndex(seasonIndex - 1); + return; + } + } + + function handleNext() { + if (hasNextChapter) { + setChapterIndex(chapterIndex + 1); + return; + } + + if (hasNextRoute) { + setChapterIndex(0); + setRouteIndex(routeIndex + 1); + return; + } + + if (hasNextSeason) { + setChapterIndex(0); + setRouteIndex(routeIndex + 1); + setSeasonIndex(seasonIndex + 1); + return; + } + } + + return ( + + + + + + +
+
+ + Arc + + + + Route + + + + Chapter + + +
+
+ {main_quest_season + .filter((season, index) => index === seasonIndex) + .map((season) => ( +
+ {season.main_quest_route + .filter((route, index) => index === routeIndex) + .map((route) => ( +
+ {route.main_quest_chapter + .filter((route, index) => index === chapterIndex) + .map((chapter) => ( +
+
+

+ {season.season_name} - {chapter.chapter_title} +

+
+ + {chapter.stories?.map( + ({ story, image_path }, index) => ( +
+ {`Story +

+
+ ) + )} + + +
+ ))} +
+ ))} +
+ ))} +
+ +
+
+
+ ); +} + +export async function getStaticProps() { + const main_quest_season = await prisma.dump.main_quest_season.findMany({ + orderBy: { + order: "asc", + }, + include: { + main_quest_route: { + include: { + main_quest_chapter: true, + }, + }, + }, + }); + + return { + props: JSON.parse( + JSON.stringify({ + main_quest_season, + }) + ), + }; +} diff --git a/src/pages/database/stories/memoirs.tsx b/src/pages/database/stories/memoirs.tsx index 25ee5e0..4c85bcb 100644 --- a/src/pages/database/stories/memoirs.tsx +++ b/src/pages/database/stories/memoirs.tsx @@ -11,6 +11,7 @@ import { Box } from "@mui/system"; import { StoriesNavbar } from "./index"; import { useSettingsStore } from "@store/settings"; import MemoirThumbnail from "@components/MemoirThumbnail"; +import StoriesLayout from "@components/Layout/StoriesLayout"; interface DatabaseStoriesMemoirsProps { memoirs: (memoir & { @@ -54,11 +55,7 @@ export default function DatabaseStoriesMemoirs({ cover="https://nierrein.guide/cover-stories.jpg" /> -
-

Memoirs stories

- - - +
{ @@ -119,7 +116,7 @@ export default function DatabaseStoriesMemoirs({
))} -
+ ); } diff --git a/src/pages/database/stories/rod-characters.tsx b/src/pages/database/stories/rod-characters.tsx new file mode 100644 index 0000000..7c5fd0f --- /dev/null +++ b/src/pages/database/stories/rod-characters.tsx @@ -0,0 +1,105 @@ +import Layout from "@components/Layout"; +import Meta from "@components/Meta"; +import Link from "next/link"; +import SVG from "react-inlinesvg"; +import prisma from "@libs/prisma"; +import { character } from "@prisma/client"; +import { CDN_URL } from "@config/constants"; +import { useState } from "react"; +import { FormControl, InputLabel, MenuItem, Select } from "@mui/material"; +import Squares from "@components/decorations/Squares"; +import StoriesLayout from "@components/Layout/StoriesLayout"; + +interface Props { + characters: character[]; +} + +export default function DatabaseStories({ characters }: Props): JSX.Element { + const [characterIndex, setCharacterIndex] = useState( + characters[0].character_id + ); + + return ( + + + + + + +
+
+ + RoD Character + + +
+ +
+ + {characters + .filter((character) => character.character_id === characterIndex) + .map((character) => ( +
+ {character.rod_stories.map(({ story, image_path }, index) => ( +
+ {`Story +

+
+ ))} +
+ ))} +
+
+
+
+ ); +} + +export async function getStaticProps() { + const characters = await prisma.dump.character.findMany(); + + return { + props: JSON.parse( + JSON.stringify({ + characters: characters.filter( + (character) => character.rod_stories.length > 0 + ), + }) + ), + }; +} diff --git a/src/pages/database/stories/weapons.tsx b/src/pages/database/stories/weapons.tsx index 649a692..0d29215 100644 --- a/src/pages/database/stories/weapons.tsx +++ b/src/pages/database/stories/weapons.tsx @@ -17,6 +17,7 @@ import { Box } from "@mui/system"; import { StoriesNavbar } from "./index"; import WeaponThumbnail from "@components/WeaponThumbnail"; import { useSettingsStore } from "@store/settings"; +import StoriesLayout from "@components/Layout/StoriesLayout"; interface DatabaseStoriesWeaponsProps { weapons: (weapon & { @@ -82,11 +83,7 @@ export default function DatabaseStoriesWeapons({ cover="https://nierrein.guide/cover-stories.jpg" /> -
-

Weapons stories

- - - +
{ @@ -197,7 +194,7 @@ export default function DatabaseStoriesWeapons({
)} -
+ ); }