New readme, screenshots and small fixes

This commit is contained in:
2024-06-22 14:36:24 +04:00
parent 56a0841322
commit 7f4d1bf87d
16 changed files with 77 additions and 43 deletions

View File

@@ -1,36 +1,54 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
# .Torrent frontend
> .Torrent - сервис обмена .torrent файлами видеоигр, фильмов и аудиокниг
## Getting Started
First, run the development server:
```bash
## Стек
- TypeScript
- React 18
- Next.js 14 (App Router)
- Tailwind CSS
- Zod
- SWR
- clsx
- React Hook Form
- и другие
- next-themes
- js-cookie
- jwt-decode
- react-dropzone
## Возможности
- Главная страница со списком популярных видеоигр, фильмов, аудиокниг
- Страницы со списками по категориям (отдельно видеоигры, фильмы, аудиокниги)
- Страница просмотра, редактирования или добавления сущности
- Форма входа или регистрации в виде модального окна с помощью Parallel и Intercepting маршрутов в Next.js
- Адаптивная верстка. Корректное отображение на мобильных устройствах, планшетах, ноутбуках, десктопах
- SEO оптимизация. SSR, метаданные к страницам
- Валидация данных с помощью Zod. Некорректные (или неполные) данные вырезаются (если некорректна одна сущность из списка, то остальные отображаются)
- Структура проекта в соответствии с Feature-Sliced Design
- Цветовая схема Gruvbox. Возможность переключения тёмной и светлой темы
- Вся конфигурация через файл .env (или переменные среды), для удобного запуска в Docker контейнере
## Скриншоты
|![](./screenshots/main_mobile.png)|![](./screenshots/movie_mobile.png)|
|-|-|
|![](./screenshots/main.png)|![](./screenshots/movies.png)|
|![](./screenshots/game.png)|![](./screenshots/movie.png)|
|![](./screenshots/game_editing.png)|![](./screenshots/game_create.png)|
|![](./screenshots/login.png)|![](./screenshots/registration.png)|
## Запуск
### Локально
npm install
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
## ToDo
- [ ] Dockerfile
- [ ] Добавить ссылку на общий репозиторий
- [ ] Теги жанров для сущностей
- [ ] Поиск
- [ ] Динамические метаданные к страницам [section]/*
- [ ] Страница "О проекте"

BIN
screenshots/game.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 656 KiB

BIN
screenshots/game_create.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 KiB

BIN
screenshots/login.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 KiB

BIN
screenshots/main.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 613 KiB

BIN
screenshots/main_mobile.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 KiB

BIN
screenshots/movie.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 716 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 KiB

BIN
screenshots/movies.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 549 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

View File

@@ -0,0 +1,5 @@
import { redirect } from "next/navigation";
export default function Registration() {
redirect("/");
}

View File

@@ -15,7 +15,7 @@ import {
TypesOfItems,
UnionItemType,
} from "./types";
import { EraseCacheByTag } from "@/shared/utils/http";
import { EraseCacheByTags } from "@/shared/utils/http";
export abstract class ItemService {
private static get itemsConfiguration(): {
@@ -122,13 +122,7 @@ export abstract class ItemService {
const item = await this.itemsConfiguration[itemInfo.type].service.Add(
itemInfo
);
if (item)
EraseCacheByTag(
`/${this.itemsConfiguration[itemInfo.type].service.urlPrefix}/${
item.id
}`
);
this.UpdateCachedData(item);
return item;
}
public static async ChangeItem(id: number, itemInfo: ItemCreateType) {
@@ -136,12 +130,14 @@ export abstract class ItemService {
id,
itemInfo
);
if (item)
EraseCacheByTag(
`/${this.itemsConfiguration[itemInfo.type].service.urlPrefix}/${
item.id
}`
);
this.UpdateCachedData(item);
return item;
}
private static UpdateCachedData(item: ItemType | null) {
if (item) {
const tagPrefix = this.itemsConfiguration[item.type].service.urlPrefix;
EraseCacheByTags([`/${tagPrefix}/${item.id}`, `/${tagPrefix}/cards`]);
}
}
}

View File

@@ -16,12 +16,24 @@ type RequestOptions = GetRequestOptions & {
};
export abstract class HTTPService {
private static deepUndefinedToNull(o?: object): object | undefined {
if (o)
return Object.fromEntries(
Object.entries(o).map(([k, v]) => {
if (v === undefined) return [k, null];
if (typeof v === "object") return [k, this.deepUndefinedToNull(v)];
return [k, v];
})
);
}
public static async request<Z extends z.ZodTypeAny>(
method: "GET" | "POST" | "PUT" | "DELETE",
url: string,
schema: Z,
options?: RequestOptions
) {
console.log(options?.body);
return await fetch(process.env.NEXT_PUBLIC_BASE_URL + url, {
method: method,
headers: {
@@ -35,7 +47,9 @@ export abstract class HTTPService {
body:
(options?.stringify ?? true) != true
? (options?.body as BodyInit)
: JSON.stringify(options?.body),
: JSON.stringify(
this.deepUndefinedToNull(options?.body as object | undefined)
),
cache: options?.cache ?? options?.next ? undefined : "no-cache",
next: options?.next ?? {},
})

View File

@@ -52,7 +52,7 @@ export const ItemFragment = ({
className="relative w-full flex items-center justify-around pt-4"
{...(editable ? getFragmentDropRootProps() : {})}
>
<div className="flex flex-col items-center w-[80%] h-20">
<div className="flex flex-col items-center w-[80%] h-14 lp:h-20">
<audio
controls
controlsList="nodownload"

View File

@@ -84,6 +84,7 @@ export const ItemInfo = <T extends ItemType | ItemCreateType>({
const onSubmit = async (formData: ItemCreateType) => {
changeSavedTimeout(null);
console.log(formData);
const updatedItem = ItemService.isExistingItem(item)
? await ItemService.ChangeItem(item.id, formData)
: await ItemService.AddItem(formData);