Logo swap, layour changes so the night mode button stays on top. Change src images to Image from next otherwise lint will complain. Api changes to accomendate the new Strapi. Same for type changes to fit into the new Strapi.
This commit is contained in:
parent
3fed14c353
commit
7a0ec01a39
BIN
public/logo.png
BIN
public/logo.png
Binary file not shown.
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 202 KiB |
@ -12,33 +12,27 @@ export default async function ChapterPage(props: { params: paramsType}) {
|
||||
const book = await fetchBookById(bookId);
|
||||
|
||||
const chapters :Chapter[] = book.chapters;
|
||||
const sorted_chapters:Chapter[] = chapters.sort((a, b) => a.Chapter_Number - b.Chapter_Number);
|
||||
const sorted_chapters:Chapter[] = chapters.sort((a, b) => a.number - b.number);
|
||||
const current_chapter = sorted_chapters.find((chapter) => chapter.documentId === chapterId) || undefined;
|
||||
const next_chapter = current_chapter ? sorted_chapters.find((chapter) => chapter.Chapter_Number === current_chapter.Chapter_Number + 1)?.documentId || "" : "";
|
||||
const prev_chapter = current_chapter ? sorted_chapters.find((chapter) => chapter.Chapter_Number === current_chapter.Chapter_Number - 1)?.documentId || "" : "";
|
||||
const next_chapter = current_chapter ? sorted_chapters.find((chapter) => chapter.number === current_chapter.number + 1)?.documentId || "" : "";
|
||||
const prev_chapter = current_chapter ? sorted_chapters.find((chapter) => chapter.number === current_chapter.number - 1)?.documentId || "" : "";
|
||||
// Fetch chapter data
|
||||
|
||||
if (current_chapter === undefined) {
|
||||
return (
|
||||
<div className="relative bg-gray-100 dark:bg-gray-900 text-gray-900 dark:text-gray-100 min-h-screen">
|
||||
<div className="prose dark:prose-invert mx-auto p-6 bg-white dark:bg-gray-800 shadow-md rounded-lg">
|
||||
<div className="prose dark:prose-invert mx-auto p-6 bg-white dark:bg-gray-800 shadow-md rounded-lg mt-4">
|
||||
<div dangerouslySetInnerHTML={{ __html: '<center><h1> Chapter not found !</h1></center>' }}></div>
|
||||
|
||||
{/* Client component for navigation */}
|
||||
<NavigationButtons bookId={bookId} documentId={chapterId} prevChapter={prev_chapter} nextChapter={next_chapter} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className="prose dark:prose-invert mx-auto p-6 bg-white dark:bg-gray-800 shadow-md rounded-lg">
|
||||
<div className="prose dark:prose-invert mx-auto p-6 bg-white dark:bg-gray-800 shadow-md rounded-lg mt-4">
|
||||
<NavigationButtons bookId={bookId} documentId={chapterId} prevChapter={prev_chapter} nextChapter={next_chapter} />
|
||||
<div className="pt-4" dangerouslySetInnerHTML={{ __html: current_chapter.content }}></div>
|
||||
|
||||
<div className="pt-4" dangerouslySetInnerHTML={{ __html: current_chapter.Content }}></div>
|
||||
|
||||
{/* Client component for navigation */}
|
||||
<NavigationButtons bookId={bookId} documentId={chapterId} prevChapter={prev_chapter} nextChapter={next_chapter}/>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { fetchBookChapterLinks } from "@/lib/api";
|
||||
import { fetchBookById, fetchBookChapterLinks } from "@/lib/api";
|
||||
import { Book } from "@/lib/types";
|
||||
import { formatDateToMonthDayYear } from "@/lib/utils";
|
||||
import ChapterDropdown from "@/components/ChapterDropdown";
|
||||
import Image from "next/image";
|
||||
|
||||
export type paramsType = Promise<{ bookId: string}>;
|
||||
|
||||
@ -11,7 +12,7 @@ export default async function BookPage(props: { params: paramsType }) {
|
||||
|
||||
let book: Book;
|
||||
try {
|
||||
book = await fetchBookChapterLinks(bookId);
|
||||
book = await fetchBookById(bookId);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return (
|
||||
@ -21,15 +22,18 @@ export default async function BookPage(props: { params: paramsType }) {
|
||||
);
|
||||
}
|
||||
|
||||
const { Name, Author, Description, chapters } = book;
|
||||
const { title, author, description, chapters, cover } = book;
|
||||
const cover_media = cover?.at(0);
|
||||
const recentChapters = chapters.length > 6 ? chapters.slice(chapters.length - 6, chapters.length) : chapters;
|
||||
return (
|
||||
<div className="max-w-6xl mx-auto py-10 px-4">
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<img src={`${process.env.NEXT_PUBLIC_API_URL}${cover_media?.url}`}
|
||||
alt={cover_media?.alternativeText || `Cover of ${book.title}`}
|
||||
className="rounded-lg object-cover w-64 h-96"
|
||||
/>
|
||||
<div className="flex items-center justify-between mb-4 pt-4">
|
||||
{/* Book Title */}
|
||||
<h1 className="text-5xl font-bold">{Name}</h1>
|
||||
|
||||
{/* Patreon Button */}
|
||||
<h1 className="text-5xl font-bold">{title}</h1>
|
||||
<a
|
||||
href="https://www.patreon.com/c/nulltranslationgroup/membership?view_as=patron" // Replace with your Patreon URL
|
||||
target="_blank"
|
||||
@ -39,13 +43,14 @@ export default async function BookPage(props: { params: paramsType }) {
|
||||
Join Our Patreon for Unreleased Chapters
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-lg text-gray-600 dark:text-gray-400 mb-4">
|
||||
<strong>Author:</strong> {Author}
|
||||
<strong>Author:</strong> {author}
|
||||
<br></br>
|
||||
<strong>Translator:</strong> Null Translation Group
|
||||
</p>
|
||||
<p className="mb-6">{Description}</p>
|
||||
<p className="mb-6">{description}</p>
|
||||
|
||||
<h2 className="text-3xl font-semibold mb-4">Recent Chapters</h2>
|
||||
<ul className="grid gap-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
|
||||
@ -56,10 +61,10 @@ export default async function BookPage(props: { params: paramsType }) {
|
||||
className="block bg-white dark:bg-gray-800 rounded-lg shadow p-4 hover:shadow-lg transition duration-200"
|
||||
>
|
||||
<h3 className="text-xl font-medium">
|
||||
Chapter {chapter.Chapter_Number}: {chapter.Name}
|
||||
Chapter {chapter.number}: {chapter.title}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 mt-2">
|
||||
<strong>Release Date:</strong> {formatDateToMonthDayYear(new Date(chapter.ReleaseDate))}
|
||||
<strong>Release Date:</strong> {formatDateToMonthDayYear(new Date(chapter.release_date))}
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
@ -76,10 +81,10 @@ export default async function BookPage(props: { params: paramsType }) {
|
||||
className="block bg-white dark:bg-gray-800 rounded-lg shadow p-4 hover:shadow-lg transition duration-200"
|
||||
>
|
||||
<h3 className="text-xl font-medium">
|
||||
Chapter {chapter.Chapter_Number}: {chapter.Name}
|
||||
Chapter {chapter.number}: {chapter.title}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 mt-2">
|
||||
<strong>Release Date:</strong> {formatDateToMonthDayYear(new Date(chapter.ReleaseDate))}
|
||||
<strong>Release Date:</strong> {formatDateToMonthDayYear(new Date(chapter.release_date))}
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 50 KiB |
@ -53,7 +53,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
||||
<body className="bg-gray-100 dark:bg-gray-900 text-gray-900 dark:text-gray-100 min-h-screen">
|
||||
<Navbar />
|
||||
<main className="relative">{children}</main>
|
||||
<div className="absolute bottom-4 right-4">
|
||||
<div className="fixed bottom-4 right-4">
|
||||
<NightModeToggle isDarkMode={isDarkMode} toggleDarkMode={toggleDarkMode} />
|
||||
</div>
|
||||
</body>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Book } from "@/lib/types";
|
||||
import { fetchBooks } from "@/lib/api";
|
||||
|
||||
import Image from "next/image";
|
||||
export default async function HomePage() {
|
||||
let books: Book[] = [];
|
||||
|
||||
@ -17,7 +17,6 @@ export default async function HomePage() {
|
||||
|
||||
return (
|
||||
<div className="mx-auto p-6 bg-gray-100 dark:bg-gray-900 text-gray-900 dark:text-gray-100 min-h-screen">
|
||||
{/* Patreon Advertisement */}
|
||||
<div className="hidden md:block bg-yellow-500 text-black py-2 px-4 rounded-lg hover:bg-yellow-600 transition duration-200 mb-6">
|
||||
<a
|
||||
href="https://patreon.com/NullTranslationGroup"
|
||||
@ -29,28 +28,29 @@ export default async function HomePage() {
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Books Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 px-6">
|
||||
{books.map((book: Book) => (
|
||||
{books.map((book: Book) => {
|
||||
const cover = book.cover?.at(0);
|
||||
return (
|
||||
<div
|
||||
key={book.id}
|
||||
className="p-4 bg-white dark:bg-gray-800 rounded-lg shadow-md hover:shadow-lg transition flex flex-col"
|
||||
>
|
||||
{book.Cover?.url && (
|
||||
{book.cover?.at(0)?.url && (
|
||||
<div className="relative w-full aspect-w-3 aspect-h-4 mb-4">
|
||||
<img
|
||||
src={`${process.env.NEXT_PUBLIC_API_URL}${book.Cover.url}`}
|
||||
alt={book.Cover.alternativeText || `Cover of ${book.Name}`}
|
||||
className="rounded-lg object-cover"
|
||||
src={`${process.env.NEXT_PUBLIC_API_URL}${cover?.url}`}
|
||||
alt={cover?.alternativeText || `Cover of ${book.title}`}
|
||||
className="rounded-lg object-cover w-full h-full"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<h2 className="text-2xl font-semibold mb-2">{book.Name}</h2>
|
||||
<h2 className="text-2xl font-semibold mb-2">{book.title}</h2>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
<strong>Author:</strong> {book.Author}
|
||||
<strong>Author:</strong> {book.author}
|
||||
</p>
|
||||
<p className="text-sm mt-2 line-clamp-3">{book.Description}</p>
|
||||
<p className="text-sm mt-2 line-clamp-3">{book.description}</p>
|
||||
|
||||
<a
|
||||
className="mt-4 inline-block bg-blue-500 hover:bg-blue-600 text-white text-sm font-semibold px-4 py-2 rounded text-center"
|
||||
@ -59,7 +59,7 @@ export default async function HomePage() {
|
||||
Read Book
|
||||
</a>
|
||||
</div>
|
||||
))}
|
||||
)})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -4,7 +4,7 @@ export default function ChapterDropdown({
|
||||
chapters,
|
||||
bookId,
|
||||
}: {
|
||||
chapters: { id: number; documentId: string; Chapter_Number: number; Name: string }[];
|
||||
chapters: { id: number; documentId: string; number: number; title: string }[];
|
||||
bookId: string;
|
||||
}) {
|
||||
|
||||
@ -24,7 +24,7 @@ export default function ChapterDropdown({
|
||||
</option>
|
||||
{chapters.map((chapter) => (
|
||||
<option key={chapter.id} value={chapter.documentId}>
|
||||
Chapter {chapter.Chapter_Number}: {chapter.Name}
|
||||
Chapter {chapter.number}: {chapter.title}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
@ -2,6 +2,7 @@
|
||||
import React, { useState } from "react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { Ad } from "@/lib/types";
|
||||
export default function Navbar() {
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
|
||||
@ -11,6 +12,8 @@ export default function Navbar() {
|
||||
<div className="flex items-center space-x-3">
|
||||
<Image
|
||||
src="/logo.png" // Replace with your logo path
|
||||
width={32}
|
||||
height={32}
|
||||
alt="Logo"
|
||||
className="h-8 w-8"
|
||||
/>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
import React from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
import { Ad } from "@/lib/types";
|
||||
|
||||
interface NavigationButtonsProps {
|
||||
bookId: string;
|
||||
@ -13,10 +13,10 @@ interface NavigationButtonsProps {
|
||||
|
||||
const NavigationButtons: React.FC<NavigationButtonsProps> = ({ bookId, documentId, prevChapter, nextChapter }) => {
|
||||
const router = useRouter();
|
||||
console.log(documentId)
|
||||
const navigateToChapter = (destinationId: string) => {
|
||||
router.push(`/books/${bookId}/chapters/${destinationId}`);
|
||||
};
|
||||
Ad.patreon
|
||||
|
||||
const navigateToAllChapters = () => {
|
||||
router.push(`/books/${bookId}`);
|
||||
@ -24,7 +24,7 @@ const NavigationButtons: React.FC<NavigationButtonsProps> = ({ bookId, documentI
|
||||
|
||||
|
||||
return (
|
||||
<div className="mt-8 flex justify-between">
|
||||
<div className="mt-2 flex justify-between">
|
||||
<button
|
||||
className={`
|
||||
bg-teal-500 text-white py-2 px-4 rounded
|
||||
@ -42,19 +42,32 @@ const NavigationButtons: React.FC<NavigationButtonsProps> = ({ bookId, documentI
|
||||
>
|
||||
All Chapters
|
||||
</button>
|
||||
{nextChapter !== "" ? (
|
||||
<button
|
||||
className={`
|
||||
bg-green-500 text-white py-2 px-4 rounded
|
||||
hover:bg-green-600
|
||||
disabled:bg-gray-400 disabled:cursor-not-allowed
|
||||
`}
|
||||
onClick={() =>
|
||||
nextChapter === "" ? navigateToAllChapters() : navigateToChapter(nextChapter)
|
||||
}
|
||||
disabled={nextChapter === ""}
|
||||
onClick={() => navigateToChapter(nextChapter)}
|
||||
>
|
||||
Next Chapter
|
||||
</button>
|
||||
) : (
|
||||
<a
|
||||
href={Ad.patreon}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={`
|
||||
bg-yellow-500 text-white py-2 px-4 rounded
|
||||
hover:bg-yellow-600
|
||||
transition duration-200
|
||||
`}
|
||||
>
|
||||
Unreleased Chapters
|
||||
</a>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -50,8 +50,8 @@ export async function fetchBooks(): Promise<Book[]> {
|
||||
}
|
||||
|
||||
export async function fetchBookChapterLinks(bookId: string): Promise<Book> {
|
||||
const currentDate = new Date().toISOString().split("T")[0];
|
||||
const data = await fetchFromAPI<{ data: Book }>(`/api/books/${bookId}?populate[chapters][filters][ReleaseDate][$lte]=${currentDate}`);
|
||||
const currentDateTime = new Date().toISOString();
|
||||
const data = await fetchFromAPI<{ data: Book }>(`/api/books/${bookId}?populate[chapters][filters][release_datetime][$lte]=${currentDateTime}`);
|
||||
return data.data
|
||||
}
|
||||
|
||||
@ -60,9 +60,9 @@ export async function fetchBookChapterLinks(bookId: string): Promise<Book> {
|
||||
* Filters chapters by release date to include only valid ones.
|
||||
*/
|
||||
export async function fetchBookById(bookId: string): Promise<Book> {
|
||||
const currentDate = new Date().toISOString().split("T")[0];
|
||||
const currentDateTime = new Date().toISOString();
|
||||
const data = await fetchFromAPI<{ data: Book }>(
|
||||
`/api/books/${bookId}?populate[chapters][filters][ReleaseDate][$lte]=${currentDate}`
|
||||
`/api/books/${bookId}?populate[chapters][filters][release_datetime][$lte]=${currentDateTime}&populate=cover`
|
||||
);
|
||||
return data.data;
|
||||
}
|
||||
|
@ -2,10 +2,11 @@
|
||||
export interface Chapter {
|
||||
id: number;
|
||||
documentId: string;
|
||||
Name: string;
|
||||
Chapter_Number: number;
|
||||
ReleaseDate: string;
|
||||
Content: string;
|
||||
number: number;
|
||||
title: string;
|
||||
editor?: Editor;
|
||||
release_date: string;
|
||||
content: string;
|
||||
book?: Book;
|
||||
}
|
||||
|
||||
@ -13,13 +14,14 @@ export interface Editor {
|
||||
id: number;
|
||||
name: string;
|
||||
email: string;
|
||||
books: Book[];
|
||||
discord: string;
|
||||
chapters: Chapter[];
|
||||
}
|
||||
|
||||
export interface Glossary {
|
||||
id: number;
|
||||
name: string;
|
||||
entries: string[];
|
||||
chinese_english: JSON;
|
||||
english_english: JSON;
|
||||
}
|
||||
|
||||
export interface Media {
|
||||
@ -50,16 +52,18 @@ export interface Media {
|
||||
export interface Book {
|
||||
id: number;
|
||||
documentId: string;
|
||||
Name: string;
|
||||
ReleaseDate: string;
|
||||
title: string;
|
||||
raw_title: string;
|
||||
author: string;
|
||||
raw_author: string;
|
||||
cover: Media[] | null;
|
||||
description: string;
|
||||
release_date: string;
|
||||
chapters: Chapter[];
|
||||
Cover: Media | null;
|
||||
Author: string;
|
||||
Complete: boolean;
|
||||
editors: Editor[];
|
||||
RawName: string;
|
||||
RawAuthor: string;
|
||||
glossary: Glossary;
|
||||
Description: string;
|
||||
}
|
||||
|
||||
export const Ad = {
|
||||
patreon: "https://patreon.com/nulltranslationgroup",
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user