mirror of
https://github.com/StepanovPlaton/torrent_frontend.git
synced 2026-04-03 20:30:48 +04:00
Add games page
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
NEXT_PUBLIC_BASE_URL=http://127.0.0.1:3000/api
|
||||
NEXT_PUBLIC_COVER_FULL_URL=http://127.0.0.1:8000/content/images/cover/full_size
|
||||
NEXT_PUBLIC_COVER_PREVIEW_URL=http://127.0.0.1:8000/content/images/cover/preview
|
||||
25
src/app/games/page.tsx
Normal file
25
src/app/games/page.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { GameService } from "@/entities/game";
|
||||
import { GameCard } from "@/features/gameCard";
|
||||
import { Section } from "@/widgets/section";
|
||||
import { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: ".Torrent: Игры",
|
||||
description:
|
||||
".Torrent: Игры - каталог .torrent файлов для обмена видеоиграми",
|
||||
};
|
||||
|
||||
export default async function Home() {
|
||||
const gameCards = await GameService.getGameCards();
|
||||
return (
|
||||
<div className="w-full h-full max-w-[var(--app-width)] m-auto overflow-y-auto">
|
||||
{gameCards && (
|
||||
<Section>
|
||||
{gameCards.map((card) => (
|
||||
<GameCard key={card.id} card={card} />
|
||||
))}
|
||||
</Section>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -8,7 +8,8 @@ const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: ".Torrent",
|
||||
description: ".torrent file sharing service",
|
||||
description:
|
||||
".Torrent - сервис обмена .torrent файлами видеоигр, фильмов и аудиокниг",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
@@ -17,7 +18,7 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
// suppressHydrationWarning only for html for theme support
|
||||
// suppressHydrationWarning for theme support
|
||||
<html lang="ru" suppressHydrationWarning>
|
||||
<body className={inter.className}>
|
||||
<ThemeProvider enableSystem={false} defaultTheme="light">
|
||||
|
||||
@@ -5,9 +5,13 @@ import { Section } from "@/widgets/section";
|
||||
export default async function Home() {
|
||||
const gameCards = await GameService.getGameCards();
|
||||
return (
|
||||
<div className="w-full max-w-[var(--app-width)] m-auto">
|
||||
<div className="w-full h-full max-w-[var(--app-width)] m-auto overflow-y-auto">
|
||||
{gameCards && (
|
||||
<Section name="Игры">
|
||||
<Section
|
||||
name="Игры"
|
||||
link="/games"
|
||||
invite_text={'Перейти в раздел "Игры"'}
|
||||
>
|
||||
{gameCards.map((card) => (
|
||||
<GameCard key={card.id} card={card} />
|
||||
))}
|
||||
|
||||
@@ -1,19 +1,26 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const gameCardSchema = z.object({
|
||||
export const gameCardSchema = z
|
||||
.object({
|
||||
id: z.number(),
|
||||
title: z.string().min(3),
|
||||
cover: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((u) => {
|
||||
if (!!u) return process.env.NEXT_PUBLIC_COVER_FULL_URL + "/" + u;
|
||||
}),
|
||||
cover: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
release_date: z
|
||||
.string()
|
||||
.min(1)
|
||||
.transform((d) => new Date(d)),
|
||||
})
|
||||
.transform((card) => {
|
||||
return {
|
||||
...card,
|
||||
cover: card.cover
|
||||
? process.env.NEXT_PUBLIC_COVER_FULL_URL + "/" + card.cover
|
||||
: undefined,
|
||||
cover_preview: card.cover
|
||||
? process.env.NEXT_PUBLIC_COVER_PREVIEW_URL + "/" + card.cover
|
||||
: undefined,
|
||||
};
|
||||
});
|
||||
export type GameCardType = z.infer<typeof gameCardSchema>;
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@ import Image from "next/image";
|
||||
export const GameCard = ({ card }: { card: GameCardType }) => {
|
||||
return (
|
||||
<div className="group/gamecard cursor-pointer">
|
||||
{!!card.cover && (
|
||||
{!!card.cover_preview && (
|
||||
<Image
|
||||
src={card.cover}
|
||||
src={card.cover_preview}
|
||||
className="rounded-lg"
|
||||
alt=""
|
||||
width={700}
|
||||
|
||||
@@ -1,17 +1,41 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
export const Section = ({
|
||||
name,
|
||||
invite_text,
|
||||
link,
|
||||
children,
|
||||
}: {
|
||||
name: string;
|
||||
name?: string;
|
||||
invite_text?: string;
|
||||
link?: string;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
// open section onClick
|
||||
<section className="w-full p-5 pt-8">
|
||||
<h2 className="text-4xl pb-2 cursor-pointer w-fit">{name}</h2>
|
||||
<section className="w-full h-fit p-5 mb-20 pt-8">
|
||||
{name && (
|
||||
<h2
|
||||
className="text-4xl pb-2 cursor-pointer w-fit"
|
||||
onClick={() => link && router.push(link)}
|
||||
>
|
||||
{name}
|
||||
</h2>
|
||||
)}
|
||||
<div className="grid grid-cols-1 tb:grid-cols-2 lp:grid-cols-3 gap-y-10 gap-x-3">
|
||||
{children}
|
||||
</div>
|
||||
{link && invite_text && (
|
||||
<div className="w-full flex justify-end pt-5">
|
||||
<Link href={link} className="text-lg">
|
||||
{invite_text}
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user