feat: add collection favorite button and delete button if own collection

fix: private collection loading
fix: wrong lists bar percentages
This commit is contained in:
Kentai Radiquum 2024-08-14 16:24:03 +05:00
parent 9a5d1eb6bd
commit 82e38f02b4
Signed by: Radiquum
GPG key ID: 858E8EE696525EED
6 changed files with 131 additions and 35 deletions

View file

@ -13,13 +13,15 @@ export async function generateMetadata(
const previousOG = (await parent).openGraph;
return {
title: "коллекция - " + collection.collection.title,
description: collection.collection.description,
title: collection.collection
? "коллекция - " + collection.collection.title
: "Приватная коллекция",
description: collection.collection && collection.collection.description,
openGraph: {
...previousOG,
images: [
{
url: collection.collection.image, // Must be an absolute URL
url: collection.collection && collection.collection.image, // Must be an absolute URL
width: 600,
height: 800,
},

View file

@ -14,7 +14,7 @@ export const CollectionInfoBasics = (props: {
updateDate: number;
}) => {
return (
<Card className="w-full max-w-full lg:max-w-[50%]">
<Card className="flex-1 w-full">
<div className="flex flex-col items-end justify-between sm:items-center sm:flex-row">
<div className="flex flex-col gap-1">
<p>создана: {unixToDate(props.creationDate, "full")}</p>

View file

@ -0,0 +1,72 @@
"use client";
import { Card, Button } from "flowbite-react";
import { useState } from "react";
import { useUserStore } from "#/store/auth";
import { ENDPOINTS } from "#/api/config";
import { useRouter } from "next/navigation";
export const CollectionInfoControls = (props: {
isFavorite: boolean;
id: number;
authorId: number;
isPrivate: boolean;
}) => {
const [isFavorite, setIsFavorite] = useState(props.isFavorite);
const userStore = useUserStore();
const router = useRouter();
async function _addToFavorite() {
if (userStore.user) {
setIsFavorite(!isFavorite);
if (isFavorite) {
fetch(
`${ENDPOINTS.collection.favoriteCollections}/delete/${props.id}?token=${userStore.token}`
);
} else {
fetch(
`${ENDPOINTS.collection.favoriteCollections}/add/${props.id}?token=${userStore.token}`
);
}
}
}
async function _deleteCollection() {
if (userStore.user) {
fetch(
`${ENDPOINTS.collection.delete}/${props.id}?token=${userStore.token}`
);
router.push("/collections");
}
}
return (
<Card className="w-full h-fit ">
<Button color={"blue"} onClick={() => _addToFavorite()}>
<span
className={`iconify w-6 h-6 mr-2 ${
isFavorite ? "mdi--heart" : "mdi--heart-outline"
}`}
></span>
{!isFavorite ? "Добавить в избранное" : "Убрать из избранного"}
</Button>
{props.isPrivate && (
<p>Это приватная коллекция, доступ к ней имеете только вы</p>
)}
{userStore.user && userStore.user.id == props.authorId && (
<div className="flex flex-wrap gap-2">
<Button color={"blue"} className="w-full sm:max-w-64">
<span className={`iconify w-6 h-6 mr-2 mdi--pencil`}></span>{" "}
Редактировать
</Button>
<Button
color={"red"}
className="w-full sm:max-w-64"
onClick={() => _deleteCollection()}
>
<span className={`iconify w-6 h-6 mr-2 mdi--trash`}></span> Удалить
</Button>
</div>
)}
</Card>
);
};

View file

@ -9,7 +9,7 @@ export const CollectionInfoLists = (props: {
total: number;
}) => {
return (
<Card className="w-full max-w-full lg:max-w-[48%] h-fit ">
<Card className="w-full h-fit ">
<div
className="flex w-full h-8 overflow-hidden rounded-md"
style={
@ -20,7 +20,7 @@ export const CollectionInfoLists = (props: {
"--watched-percent": `calc(var(--width-of-one) * (${props.completed} / ${props.total} * 100%))`,
"--delayed-percent": `calc(var(--width-of-one) * (${props.delayed} / ${props.total} * 100%))`,
"--abandoned-percent": `calc(var(--width-of-one) * (${props.abandoned} / ${props.total} * 100%))`,
"--no-list-percent": `calc(var(--width-of-one) * (${props.total - (props.watching - props.planned - props.completed - props.delayed - props.abandoned)} / ${props.total} * 100%))`,
"--no-list-percent": `calc(var(--width-of-one) * (${props.total - (props.watching + props.planned + props.completed + props.delayed + props.abandoned)} / ${props.total} * 100%))`,
} as React.CSSProperties
}
>

View file

@ -1,6 +1,9 @@
import { ReleaseLink } from "../ReleaseLink/ReleaseLink";
export const ReleaseSection = (props: {sectionTitle?: string, content: any}) => {
export const ReleaseSection = (props: {
sectionTitle?: string;
content: any;
}) => {
return (
<section>
{props.sectionTitle && (
@ -19,6 +22,7 @@ export const ReleaseSection = (props: {sectionTitle?: string, content: any}) =>
</div>
);
})}
{props.content.length == 1 && <div></div>}
</div>
</div>
</section>

View file

@ -12,6 +12,7 @@ import { ReleaseSection } from "#/components/ReleaseSection/ReleaseSection";
import { CollectionInfoBasics } from "#/components/CollectionInfo/CollectionInfo.Basics";
import { CollectionInfoLists } from "#/components/CollectionInfo/CollectionInfoLists";
import { CollectionInfoControls } from "#/components/CollectionInfo/CollectionInfoControls";
const fetcher = async (url: string) => {
const res = await fetch(url);
@ -81,37 +82,54 @@ export const ViewCollectionPage = (props: { id: number }) => {
return (
<main className="container pt-2 pb-16 mx-auto sm:pt-4 sm:pb-0">
{collectionInfoIsLoading ? (
<Spinner />
<div className="flex items-center justify-center w-full h-screen">
<Spinner />
</div>
) : (
<>
<div className="flex flex-col flex-wrap gap-4 px-2 sm:flex-row">
<CollectionInfoBasics
image={collectionInfo.collection.image}
title={collectionInfo.collection.title}
description={collectionInfo.collection.description}
authorAvatar={collectionInfo.collection.creator.avatar}
authorLogin={collectionInfo.collection.creator.login}
authorId={collectionInfo.collection.creator.id}
creationDate={collectionInfo.collection.creation_date}
updateDate={collectionInfo.collection.last_update_date}
/>
{userStore.token && !isLoading && (
<CollectionInfoLists
completed={collectionInfo.completed_count}
planned={collectionInfo.plan_count}
abandoned={collectionInfo.dropped_count}
delayed={collectionInfo.hold_on_count}
watching={collectionInfo.watching_count}
total={data[0].total_count}
collectionInfo && (
<>
<div className="flex flex-col flex-wrap gap-4 px-2 pb-2 sm:flex-row">
<CollectionInfoBasics
image={collectionInfo.collection.image}
title={collectionInfo.collection.title}
description={collectionInfo.collection.description}
authorAvatar={collectionInfo.collection.creator.avatar}
authorLogin={collectionInfo.collection.creator.login}
authorId={collectionInfo.collection.creator.id}
creationDate={collectionInfo.collection.creation_date}
updateDate={collectionInfo.collection.last_update_date}
/>
{userStore.token && !isLoading && (
<div className="flex flex-col gap-4 w-full max-w-full lg:max-w-[48%]">
<CollectionInfoLists
completed={collectionInfo.completed_count}
planned={collectionInfo.plan_count}
abandoned={collectionInfo.dropped_count}
delayed={collectionInfo.hold_on_count}
watching={collectionInfo.watching_count}
total={data[0].total_count}
/>
<CollectionInfoControls
isFavorite={collectionInfo.collection.is_favorite}
id={collectionInfo.collection.id}
authorId={collectionInfo.collection.creator.id}
isPrivate={collectionInfo.collection.is_private}
/>
</div>
)}
</div>
{isLoading || !content || !isLoadingEnd ? (
<div className="flex items-center justify-center w-full h-screen">
<Spinner />
</div>
) : (
<ReleaseSection
sectionTitle={"Релизов в коллекции: " + data[0].total_count}
content={content}
/>
)}
</div>
{isLoading || !content || !isLoadingEnd ? (
<Spinner />
) : (
<ReleaseSection content={content} />
)}
</>
</>
)
)}
</main>
);