refactor: Release page

factor it in separate components and update grid styling
This commit is contained in:
Kentai Radiquum 2024-07-31 20:21:32 +05:00
parent c2c3a95e6c
commit e548ce060d
Signed by: Radiquum
GPG key ID: 858E8EE696525EED
9 changed files with 542 additions and 429 deletions

View file

@ -0,0 +1,35 @@
import { Card } from "flowbite-react";
export const ReleaseInfoBasics = (props: {
image: string;
title: { ru: string; original: string };
note: string | null;
description: string;
}) => {
return (
<Card className="h-full">
<div className="flex flex-col w-full h-full gap-4 lg:flex-row">
<img
className="w-[285px] max-h-[385px] object-cover border border-gray-200 rounded-lg shadow-md dark:border-gray-700"
src={props.image}
alt=""
></img>
<div className="flex flex-col max-w-2xl gap-2 text-sm md:text-base">
<div className="flex flex-col gap-1">
<p className="text-xl font-bold text-black md:text-2xl">
{props.title.ru}
</p>
<p className="text-sm text-gray-500 md:text-base">
{props.title.original}
</p>
</div>
{props.note && (
<div className="py-2 bg-blue-100 border-l-4 border-blue-700 rounded-md ">
<div id="note" className="ml-2"></div>
</div>
)}
<p>{props.description}</p>
</div>
</div>
</Card>
);
};

View file

@ -0,0 +1,154 @@
import { Card, Table } from "flowbite-react";
import { ReleaseInfoSearchLink } from "#/components/ReleaseInfo/ReleaseInfo.SearchLink";
import { unixToDate, getSeasonFromUnix, minutesToTime } from "#/api/utils";
const weekDay = [
"_",
"каждый понедельник",
"каждый вторник",
"каждую среду",
"каждый четверг",
"каждую пятницу",
"каждую субботу",
"каждое воскресенье",
];
const YearSeason = ["_", "Зима", "Весна", "Лето", "Осень"];
export const ReleaseInfoInfo = (props: {
country: string | null;
aired_on_date: number | null;
year: number | null;
episodes: { total: number | null; released: number | null };
season: number;
status: string;
duration: number;
category: string;
broadcast: number;
studio: string | null;
author: string | null;
director: string | null;
genres: string;
}) => {
return (
<Card>
<Table>
<Table.Body>
<Table.Row>
<Table.Cell className="py-0">
{props.country ? (
props.country.toLowerCase() == "япония" ? (
<span className="w-8 h-8 iconify-color twemoji--flag-for-japan"></span>
) : (
<span className="w-8 h-8 iconify-color twemoji--flag-for-china"></span>
)
) : (
<span className="w-8 h-8 iconify-color twemoji--flag-for-united-nations "></span>
)}
</Table.Cell>
<Table.Cell className="font-medium text-gray-900 whitespace-nowrap dark:text-white">
{props.country && props.country}
{(props.aired_on_date != 0 || props.year) && ", "}
{props.aired_on_date != 0 &&
`${getSeasonFromUnix(props.aired_on_date)} `}
{props.year && `${props.year} г.`}
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell className="py-0">
<span className="w-8 h-8 iconify-color mdi--animation-play-outline "></span>
</Table.Cell>
<Table.Cell className="font-medium text-gray-900 whitespace-nowrap dark:text-white">
{props.episodes.released ? props.episodes.released : "?"}
{"/"}
{props.episodes.total ? props.episodes.total + " эп. " : "? эп. "}
{props.duration != 0 && `по ${minutesToTime(props.duration)}`}
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell className="py-0">
<span className="w-8 h-8 iconify-color mdi--calendar-outline "></span>
</Table.Cell>
<Table.Cell className="font-medium text-gray-900 dark:text-white">
{props.category}
{", "}
{props.broadcast == 0
? props.status.toLowerCase()
: `выходит ${weekDay[props.broadcast]}`}
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell className="py-0">
<span className="w-8 h-8 iconify-color mdi--people-group-outline "></span>
</Table.Cell>
<Table.Cell className="font-medium text-gray-900 dark:text-white">
{props.studio && (
<>
{"Студия: "}
{props.studio
.split(", ")
.map((studio: string, index: number) => {
return (
<div key={index} className="inline">
{index > 0 && ", "}
<ReleaseInfoSearchLink title={studio} searchBy={1} />
</div>
);
})}
{(props.author || props.director) && ", "}
</>
)}
{props.author && (
<>
{"Автор: "}
<ReleaseInfoSearchLink title={props.author} searchBy={3} />
{props.director && ", "}
</>
)}
{props.director && (
<>
{"Режиссёр: "}
<ReleaseInfoSearchLink title={props.director} searchBy={2} />
</>
)}
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell className="py-0">
<span className="w-8 h-8 iconify-color mdi--tag-outline "></span>
</Table.Cell>
<Table.Cell className="font-medium text-gray-900 dark:text-white">
{props.genres &&
props.genres.split(", ").map((genre: string, index: number) => {
return (
<div key={index} className="inline">
{index > 0 && ", "}
<ReleaseInfoSearchLink title={genre} searchBy={4} />
</div>
);
})}
</Table.Cell>
</Table.Row>
{props.status.toLowerCase() == "анонс" && (
<Table.Row>
<Table.Cell className="py-0">
<span className="w-8 h-8 iconify-color mdi--clock-outline "></span>
</Table.Cell>
<Table.Cell className="font-medium text-gray-900 whitespace-nowrap dark:text-white">
{props.aired_on_date != 0 ? (
unixToDate(props.aired_on_date)
) : props.year ? (
<>
{props.season && props.season != 0
? `${YearSeason[props.season]} `
: ""}
{props.year && `${props.year} г.`}
</>
) : (
"Скоро"
)}
</Table.Cell>
</Table.Row>
)}
</Table.Body>
</Table>
</Card>
);
};

View file

@ -0,0 +1,114 @@
import {
Card,
Rating,
Flowbite,
Button,
CustomFlowbiteTheme,
} from "flowbite-react";
import { numberDeclension } from "#/api/utils";
const RatingTheme: CustomFlowbiteTheme = {
ratingAdvanced: {
progress: {
base: "mx-4 h-5 w-3/4 rounded bg-gray-200 dark:bg-gray-700",
},
},
};
export const ReleaseInfoRating = (props: {
grade: number;
token: string | null;
votes: {
1: number;
2: number;
3: number;
4: number;
5: number;
total: number;
user: number | null;
};
}) => {
return (
<Card>
<div className="flex flex-col gap-2 lg:items-center lg:flex-row">
<Rating>
<Rating.Star />
<p className="ml-2 text-sm font-bold dark:text-white">
{props.grade.toFixed(2)} из 5
</p>
</Rating>
{props.token && (
<>
<span className="mx-1.5 h-1 w-1 rounded-full bg-gray-500 dark:bg-gray-400 hidden lg:block" />
{props.votes.user ? (
<div className="flex items-center gap-2">
<p className="text-sm font-medium text-gray-500 dark:text-gray-400">
ваша оценка: {props.votes.user}
</p>
<Button
size={"xs"}
className="text-gray-500 border border-gray-600 rounded-full"
color="inline"
>
изменить
</Button>
</div>
) : (
<Button
size={"xs"}
className="text-gray-500 border border-gray-600 rounded-full"
color="inline"
>
оценить
</Button>
)}
</>
)}
</div>
<p className="text-sm font-medium text-gray-500 dark:text-gray-400">
{props.votes.total}{" "}
{numberDeclension(props.votes.total, "голос", "голоса", "голосов")}
</p>
<Flowbite theme={{ theme: RatingTheme }}>
<Rating.Advanced
percentFilled={Math.floor(
(props.votes["5"] / props.votes.total) * 100
)}
className="mb-2"
>
5
</Rating.Advanced>
<Rating.Advanced
percentFilled={Math.floor(
(props.votes["4"] / props.votes.total) * 100
)}
className="mb-2"
>
4
</Rating.Advanced>
<Rating.Advanced
percentFilled={Math.floor(
(props.votes["3"] / props.votes.total) * 100
)}
className="mb-2"
>
3
</Rating.Advanced>
<Rating.Advanced
percentFilled={Math.floor(
(props.votes["2"] / props.votes.total) * 100
)}
className="mb-2"
>
2
</Rating.Advanced>
<Rating.Advanced
percentFilled={Math.floor(
(props.votes["1"] / props.votes.total) * 100
)}
>
1
</Rating.Advanced>
</Flowbite>
</Card>
);
};

View file

@ -0,0 +1,49 @@
"use client";
import { Card, Carousel, CustomFlowbiteTheme } from "flowbite-react";
import { ReleaseLink } from "#/components/ReleaseLink/ReleaseLink";
import Link from "next/link";
const CarouselTheme: CustomFlowbiteTheme["carousel"] = {
root: {
base: "relative h-full w-full max-w-[300px]",
},
};
export const ReleaseInfoRelated = (props: {
release_id: number;
related: any;
related_releases: any;
}) => {
return (
<Card>
<div className="flex justify-between py-2 border-b-2 border-black">
<h1>Связанные релизы</h1>
{props.related && (
<Link href={`/related/${props.related.id}`}>
<div className="flex items-center">
<p className="hidden xl:block">Показать все</p>
<span className="w-6 h-6 iconify mdi--arrow-right"></span>
</div>
</Link>
)}
</div>
<div className="flex justify-center mt-2">
<Carousel pauseOnHover={true} theme={CarouselTheme}>
{props.related_releases
.filter((release: any) => {
if (release.id == props.release_id) {
return false;
}
return true;
})
.map((release: any) => {
return (
<ReleaseLink key={release.id} {...release} type={"poster"} />
);
})}
</Carousel>
</div>
</Card>
);
};

View file

@ -0,0 +1,15 @@
import { Card, Carousel } from "flowbite-react";
export const ReleaseInfoScreenshots = (props: {
images: string[];
}) => {
return (
<Card>
<Carousel className="aspect-[16/10]">
{props.images.map((image: string, index: number) => (
<img key={index} className="object-cover" src={image} />
))}
</Carousel>
</Card>
);
};

View file

@ -0,0 +1,88 @@
import { Card, Dropdown, Button } from "flowbite-react";
import { ENDPOINTS } from "#/api/config";
const lists = [
{ list: 0, name: "Не смотрю" },
{ list: 1, name: "Смотрю" },
{ list: 2, name: "В планах" },
{ list: 3, name: "Просмотрено" },
{ list: 4, name: "Отложено" },
{ list: 5, name: "Брошено" },
];
const DropdownTheme = {
floating: {
target:
"flex-1 bg-blue-600 enabled:hover:bg-blue-700 focus:ring-4 focus:outline-none focus:ring-blue-300 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800",
},
};
export const ReleaseInfoUserList = (props: {
userList: number;
isFavorite: boolean;
release_id: number;
token: string | null;
setUserList: any;
setIsFavorite: any;
}) => {
function _addToFavorite() {
if (props.token) {
props.setIsFavorite(!props.isFavorite);
if (props.isFavorite) {
fetch(
`${ENDPOINTS.user.favorite}/delete/${props.release_id}?token=${props.token}`
);
} else {
fetch(
`${ENDPOINTS.user.favorite}/add/${props.release_id}?token=${props.token}`
);
}
}
}
function _addToList(list: number) {
if (props.token) {
props.setUserList(list);
fetch(
`${ENDPOINTS.user.bookmark}/add/${list}/${props.release_id}?token=${props.token}`
);
}
}
return (
<Card>
{props.token ? (
<div className="flex flex-wrap gap-2">
<Dropdown
label={lists[props.userList].name}
dismissOnClick={true}
theme={DropdownTheme}
>
{lists.map((list) => (
<Dropdown.Item
key={list.list}
onClick={() => _addToList(list.list)}
>
{list.name}
</Dropdown.Item>
))}
</Dropdown>
<Button
className="bg-blue-600 enabled:hover:bg-blue-700 focus:ring-4 focus:outline-none focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
onClick={() => {
_addToFavorite();
}}
>
<span
className={`iconify w-6 h-6 ${
props.isFavorite ? "mdi--heart" : "mdi--heart-outline"
}`}
></span>
</Button>
</div>
) : (
<p>Войдите что-бы добавить в избранное или список</p>
)}
</Card>
);
};

View file

@ -1,7 +1,7 @@
import { ReleaseLink169 } from "./ReleaseLink.16_9"; import { ReleaseLink169 } from "./ReleaseLink.16_9";
import { ReleaseLinkPoster } from "./ReleaseLink.Poster"; import { ReleaseLinkPoster } from "./ReleaseLink.Poster";
export const ReleaseLink = (props: any) => { export const ReleaseLink = (props: {type?: "16_9"|"poster"}) => {
const type = props.type || "16_9"; const type = props.type || "16_9";
if (type == "16_9") { if (type == "16_9") {

View file

@ -119,7 +119,7 @@ export const ReleasePlayer = (props: { id: number }) => {
> >
{voiceoverInfo.map((voiceover: any) => ( {voiceoverInfo.map((voiceover: any) => (
<Dropdown.Item <Dropdown.Item
key={voiceover.id} key={`voiceover_${voiceover.id}`}
onClick={() => setSelectedVoiceover(voiceover)} onClick={() => setSelectedVoiceover(voiceover)}
> >
{voiceover.name} {voiceover.name}
@ -132,7 +132,7 @@ export const ReleasePlayer = (props: { id: number }) => {
> >
{sourcesInfo.map((source: any) => ( {sourcesInfo.map((source: any) => (
<Dropdown.Item <Dropdown.Item
key={source.id} key={`source_${source.id}`}
onClick={() => setSelectedSource(source)} onClick={() => setSelectedSource(source)}
> >
{source.name} {source.name}
@ -156,7 +156,7 @@ export const ReleasePlayer = (props: { id: number }) => {
? ButtonThemeActive ? ButtonThemeActive
: ButtonThemeInactive : ButtonThemeInactive
}`} }`}
key={episode.id} key={`episode_${episode.position}`}
onClick={() => { onClick={() => {
setSelectedEpisode(episode); setSelectedEpisode(episode);
episode.is_watched = true; episode.is_watched = true;

View file

@ -5,65 +5,15 @@ import { Spinner } from "#/components/Spinner/Spinner";
const fetcher = (...args: any) => const fetcher = (...args: any) =>
fetch([...args] as any).then((res) => res.json()); fetch([...args] as any).then((res) => res.json());
import { useUserStore } from "#/store/auth"; import { useUserStore } from "#/store/auth";
import {
Card,
Dropdown,
Button,
Carousel,
Rating,
Flowbite,
CustomFlowbiteTheme,
} from "flowbite-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import {
unixToDate, import { ReleaseInfoBasics } from "#/components/ReleaseInfo/ReleaseInfo.Basics";
getSeasonFromUnix, import { ReleaseInfoInfo } from "#/components/ReleaseInfo/ReleaseInfo.Info";
minutesToTime,
numberDeclension,
} from "#/api/utils";
import { ReleaseLink } from "#/components/ReleaseLink/ReleaseLink";
import { ReleasePlayer } from "#/components/ReleasePlayer/ReleasePlayer"; import { ReleasePlayer } from "#/components/ReleasePlayer/ReleasePlayer";
import { ENDPOINTS } from "#/api/config"; import { ReleaseInfoUserList } from "#/components/ReleaseInfo/ReleaseInfo.UserList";
import { Table } from "flowbite-react"; import { ReleaseInfoRating } from "#/components/ReleaseInfo/ReleaseInfo.Rating";
import { ReleaseInfoSearchLink } from "#/components/ReleaseInfo/ReleaseInfo.SearchLink"; import { ReleaseInfoRelated } from "#/components/ReleaseInfo/ReleaseInfo.Related";
import Link from "next/link"; import { ReleaseInfoScreenshots } from "#/components/ReleaseInfo/ReleaseInfo.Screenshots";
const lists = [
{ list: 0, name: "Не смотрю" },
{ list: 1, name: "Смотрю" },
{ list: 2, name: "В планах" },
{ list: 3, name: "Просмотрено" },
{ list: 4, name: "Отложено" },
{ list: 5, name: "Брошено" },
];
const weekDay = [
"_",
"каждый понедельник",
"каждый вторник",
"каждую среду",
"каждый четверг",
"каждую пятницу",
"каждую субботу",
"каждое воскресенье",
];
const YearSeason = ["_", "Зима", "Весна", "Лето", "Осень"];
const DropdownTheme = {
floating: {
target:
"flex-1 bg-blue-600 enabled:hover:bg-blue-700 focus:ring-4 focus:outline-none focus:ring-blue-300 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800",
},
};
const RatingTheme: CustomFlowbiteTheme = {
ratingAdvanced: {
progress: {
base: "mx-4 h-5 w-3/4 rounded bg-gray-200 dark:bg-gray-700",
},
},
};
export const ReleasePage = (props: any) => { export const ReleasePage = (props: any) => {
const token = useUserStore((state) => state.token); const token = useUserStore((state) => state.token);
@ -93,378 +43,86 @@ export const ReleasePage = (props: any) => {
} }
}, [data]); }, [data]);
function _addToFavorite() {
if (data && token) {
setUserFavorite(!userFavorite);
if (userFavorite) {
fetch(
`${ENDPOINTS.user.favorite}/delete/${data.release.id}?token=${token}`
);
} else {
fetch(
`${ENDPOINTS.user.favorite}/add/${data.release.id}?token=${token}`
);
}
}
}
function _addToList(list: number) {
if (data && token) {
setUserList(list);
fetch(
`${ENDPOINTS.user.bookmark}/add/${list}/${data.release.id}?token=${token}`
);
}
}
return data ? ( return data ? (
<main className="container px-4 pt-4 pb-24 mx-auto sm:pb-4"> <main className="container px-4 pt-4 pb-24 mx-auto sm:pb-4">
<div className="grid grid-cols-[100%] lg:grid-cols-[70%_30%] gap-2 justify-center"> <div className="grid grid-cols-[100%] lg:grid-cols-[70%_30%] gap-2 grid-flow-row-dense ">
<div className="[grid-column:1] flex flex-col gap-2"> <div className="[grid-column:1] [grid-row:1/span_2]">
<Card className="lg:[grid-column:1]"> <ReleaseInfoBasics
<div className="flex flex-col w-full h-full gap-4 lg:flex-row"> image={data.release.image}
<img title={{
className="w-[285px] max-h-[385px] object-cover border border-gray-200 rounded-lg shadow-md dark:border-gray-700" ru: data.release.title_ru,
src={data.release.image} original: data.release.title_original,
alt="" }}
></img> description={data.release.description}
<div className="flex flex-col max-w-2xl gap-2 text-sm md:text-base"> note={data.release.note}
<div className="flex flex-col gap-1"> />
{data.release.title_ru && ( </div>
<p className="text-xl font-bold text-black md:text-2xl"> <div>
{data.release.title_ru} <ReleaseInfoInfo
</p> country={data.release.country}
)} aired_on_date={data.release.aired_on_date}
{data.release.title_original && ( year={data.release.year}
<p className="text-sm text-gray-500 md:text-base"> episodes={{
{data.release.title_original} total: data.release.episodes_total,
</p> released: data.release.episodes_released,
)} }}
</div> season={data.release.season}
{data.release.note && ( status={data.release.status.name}
<div className="py-2 bg-blue-100 border-l-4 border-blue-700 rounded-md "> duration={data.release.duration}
<div id="note" className="ml-2"></div> category={data.release.category.name}
</div> broadcast={data.release.broadcast}
)} studio={data.release.studio}
{data.release.description && <p>{data.release.description}</p>} author={data.release.author}
</div> director={data.release.director}
</div> genres={data.release.genres}
</Card> />
{data.release.status.name.toLowerCase() != "анонс" && ( </div>
<div className="-order-1 lg:order-none">
<ReleaseInfoUserList
userList={userList}
isFavorite={userFavorite}
release_id={data.release.id}
token={token}
setUserList={setUserList}
setIsFavorite={setUserFavorite}
/>
</div>
{data.release.status.name.toLowerCase() != "анонс" && (
<div className="[grid-column:1] [grid-row:3/span_4]">
<ReleasePlayer id={props.id} /> <ReleasePlayer id={props.id} />
)}
</div>
<div className="[grid-column:1] lg:[grid-column:2] flex flex-col gap-2">
<Card className="order-2 lg:order-1">
<Table>
<Table.Body>
<Table.Row>
<Table.Cell className="py-0">
{data.release.country ? (
data.release.country.toLowerCase() == "япония" ? (
<span className="w-8 h-8 iconify-color twemoji--flag-for-japan"></span>
) : (
<span className="w-8 h-8 iconify-color twemoji--flag-for-china"></span>
)
) : (
<span className="w-8 h-8 iconify-color twemoji--flag-for-united-nations "></span>
)}
</Table.Cell>
<Table.Cell className="font-medium text-gray-900 whitespace-nowrap dark:text-white">
{data.release.country && data.release.country}
{(data.release.aired_on_date != 0 || data.release.year) &&
", "}
{data.release.aired_on_date != 0 &&
`${getSeasonFromUnix(data.release.aired_on_date)} `}
{data.release.year && `${data.release.year} г.`}
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell className="py-0">
<span className="w-8 h-8 iconify-color mdi--animation-play-outline "></span>
</Table.Cell>
<Table.Cell className="font-medium text-gray-900 whitespace-nowrap dark:text-white">
{data.release.episodes_released
? data.release.episodes_released
: "?"}
{"/"}
{data.release.episodes_total
? data.release.episodes_total + " эп. "
: "? эп. "}
{data.release.duration != 0 &&
`по ${minutesToTime(data.release.duration)}`}
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell className="py-0">
<span className="w-8 h-8 iconify-color mdi--calendar-outline "></span>
</Table.Cell>
<Table.Cell className="font-medium text-gray-900 dark:text-white">
{data.release.category.name}
{", "}
{data.release.broadcast == 0
? data.release.status.name.toLowerCase()
: `выходит ${weekDay[data.release.broadcast]}`}
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell className="py-0">
<span className="w-8 h-8 iconify-color mdi--people-group-outline "></span>
</Table.Cell>
<Table.Cell className="font-medium text-gray-900 dark:text-white">
{data.release.studio && (
<>
{"Студия: "}
{data.release.studio
.split(", ")
.map((studio: string, index: number) => {
return (
<>
{index > 0 && ", "}
<ReleaseInfoSearchLink
title={studio}
searchBy={1}
/>
</>
);
})}
{(data.release.author || data.release.director) && ", "}
</>
)}
{data.release.author && (
<>
{"Автор: "}
<ReleaseInfoSearchLink
title={data.release.author}
searchBy={3}
/>
{data.release.director && ", "}
</>
)}
{data.release.director && (
<>
{"Режиссёр: "}
<ReleaseInfoSearchLink
title={data.release.director}
searchBy={2}
/>
</>
)}
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell className="py-0">
<span className="w-8 h-8 iconify-color mdi--tag-outline "></span>
</Table.Cell>
<Table.Cell className="font-medium text-gray-900 dark:text-white">
{data.release.genres &&
data.release.genres
.split(", ")
.map((genre: string, index: number) => {
return (
<>
{index > 0 && ", "}
<ReleaseInfoSearchLink
title={genre}
searchBy={4}
/>
</>
);
})}
</Table.Cell>
</Table.Row>
{data.release.status.name.toLowerCase() == "анонс" && (
<Table.Row>
<Table.Cell className="py-0">
<span className="w-8 h-8 iconify-color mdi--clock-outline "></span>
</Table.Cell>
<Table.Cell className="font-medium text-gray-900 whitespace-nowrap dark:text-white">
{data.release.aired_on_date != 0 ? (
unixToDate(data.release.aired_on_date)
) : data.release.year ? (
<>
{data.release.season && data.release.season != 0
? `${YearSeason[data.release.season]} `
: ""}
{data.release.year && `${data.release.year} г.`}
</>
) : (
"Скоро"
)}
</Table.Cell>
</Table.Row>
)}
</Table.Body>
</Table>
</Card>
<div className="flex flex-col order-1 gap-2 lg:order-2">
{token && (
<Card>
<div className="flex flex-wrap gap-2">
<Dropdown
label={lists[userList].name}
dismissOnClick={true}
theme={DropdownTheme}
>
{lists.map((list) => (
<Dropdown.Item
key={list.list}
onClick={() => _addToList(list.list)}
>
{list.name}
</Dropdown.Item>
))}
</Dropdown>
<Button
className="bg-blue-600 enabled:hover:bg-blue-700 focus:ring-4 focus:outline-none focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
onClick={() => {
_addToFavorite();
}}
>
<span
className={`iconify w-6 h-6 ${
userFavorite ? "mdi--heart" : "mdi--heart-outline"
}`}
></span>
</Button>
</div>
</Card>
)}
{data.release.status.name.toLowerCase() != "анонс" && (
<Card>
<div className="flex flex-col gap-2 lg:items-center lg:flex-row">
<Rating>
<Rating.Star />
<Rating.Star />
<Rating.Star />
<Rating.Star />
<Rating.Star filled={false} />
<p className="ml-2 text-sm font-medium text-gray-500 dark:text-gray-400">
{data.release.grade.toFixed(2)} из 5
</p>
</Rating>
{token && (
<>
<span className="mx-1.5 h-1 w-1 rounded-full bg-gray-500 dark:bg-gray-400 hidden lg:block" />
{data.release.your_vote ? (
<div className="flex items-center gap-2">
<p className="text-sm font-medium text-gray-500 dark:text-gray-400">
ваша оценка: {data.release.your_vote}
</p>
<Button
size={"xs"}
className="text-gray-500 border border-gray-600 rounded-full"
color="inline"
>
изменить
</Button>
</div>
) : (
<Button
size={"xs"}
className="text-gray-500 border border-gray-600 rounded-full"
color="inline"
>
оценить
</Button>
)}
</>
)}
</div>
<p className="text-sm font-medium text-gray-500 dark:text-gray-400">
{data.release.vote_count}{" "}
{numberDeclension(
data.release.vote_count,
"голос",
"голоса",
"голосов"
)}
</p>
<Flowbite theme={{ theme: RatingTheme }}>
<Rating.Advanced
percentFilled={Math.floor(
(data.release.vote_5_count / data.release.vote_count) *
100
)}
className="mb-2"
>
5
</Rating.Advanced>
<Rating.Advanced
percentFilled={Math.floor(
(data.release.vote_4_count / data.release.vote_count) *
100
)}
className="mb-2"
>
4
</Rating.Advanced>
<Rating.Advanced
percentFilled={Math.floor(
(data.release.vote_3_count / data.release.vote_count) *
100
)}
className="mb-2"
>
3
</Rating.Advanced>
<Rating.Advanced
percentFilled={Math.floor(
(data.release.vote_2_count / data.release.vote_count) *
100
)}
className="mb-2"
>
2
</Rating.Advanced>
<Rating.Advanced
percentFilled={Math.floor(
(data.release.vote_1_count / data.release.vote_count) *
100
)}
>
1
</Rating.Advanced>
</Flowbite>
</Card>
)}
</div> </div>
{data.release.related_releases.length > 0 && ( )}
<Card className="order-3"> {data.release.status.name.toLowerCase() != "анонс" && (
<div> <div>
<div className="flex justify-between py-2 border-b-2 border-black"> <ReleaseInfoRating
<h1>Связанные релизы</h1> grade={data.release.grade}
{data.release.related && ( token={token}
<Link href={`/related/${data.release.related.id}`}> votes={{
<div className="flex items-center"> 1: data.release.vote_1_count,
<p className="hidden sm:block">Показать все</p> 2: data.release.vote_2_count,
<span className="w-6 h-6 iconify mdi--arrow-right"></span> 3: data.release.vote_3_count,
</div> 4: data.release.vote_4_count,
</Link> 5: data.release.vote_5_count,
)} total: data.release.vote_count,
</div> user: data.release.your_vote,
<div className="flex flex-col gap-1 mt-2"> }}
{data.release.related_releases.map((release) => { />
if (release.id == data.release.id) return null; </div>
return <ReleaseLink key={release.id} {...release} />; )}
})} {data.release.screenshot_images.length > 0 && (
</div> <div>
</div> <ReleaseInfoScreenshots images={data.release.screenshot_images} />
</Card> </div>
)} )}
{data.release.screenshot_images.length > 0 && ( {data.release.related_releases.length > 0 && (
<Card className="order-2 lg:order-1"> <div>
<Carousel className="aspect-[16/10]"> <ReleaseInfoRelated
{data.release.screenshot_images.map( release_id={props.id}
(image: string, index: number) => ( related={data.release.related}
<img key={index} className="object-cover" src={image} /> related_releases={data.release.related_releases}
) />
)} </div>
</Carousel> )}
</Card>
)}
</div>
<div className="[grid-column:1] lg:[grid-column:2] flex flex-col gap-2"></div>
</div> </div>
</main> </main>
) : ( ) : (