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) => (
+
+
+
+
+ ))}
+
+ ))}
+
+
+
+
+ );
+}
+
+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) => (
+
+
+
+
+ )
+ )}
+
+ ))}
+
+
+
+
+ );
+}
+
+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) => (
+
+
+
+
+ ))}
+
+ ))}
+
+
+
+
+ );
+}
+
+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) => (
+
+
+
+
+ ))}
+
+ ))}
+
+
+
+
+ );
+}
+
+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 {
);
@@ -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) => (
+
+
+
+
+ ))}
+
+
+
+
+ );
+}
+
+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) => (
+
+
+
+
+ )
+ )}
+
+
+
+ ))}
+
+ ))}
+
+ ))}
+
+
+
+
+
+ );
+}
+
+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) => (
+
+
+
+
+ ))}
+
+ ))}
+
+
+
+
+ );
+}
+
+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({
)}
-
+
);
}