From 98992f9f36551178084624b1f698c539811d4c5b Mon Sep 17 00:00:00 2001 From: Bruno Ramos <37979213+Kuriel23@users.noreply.github.com> Date: Sun, 12 Jul 2026 00:17:01 +0100 Subject: [PATCH 1/5] feat: enhance details page layout and add review functionality --- bun.lock | 1 + .../pages/details/details-page-layout.tsx | 4 +- src/components/ui/image-zoom.tsx | 4 +- src/routes/anime/$slug.tsx | 492 +++++++------ src/routes/book/$slug.tsx | 373 +++++----- src/routes/game/$slug.tsx | 412 ++++++----- src/routes/manga/$slug.tsx | 357 ++++----- src/routes/movie/$slug.tsx | 497 +++++++------ src/routes/tv/$slug.tsx | 675 +++++++++--------- 9 files changed, 1488 insertions(+), 1327 deletions(-) diff --git a/bun.lock b/bun.lock index 7da5571..63a8913 100644 --- a/bun.lock +++ b/bun.lock @@ -1,5 +1,6 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "name": "trackverse", diff --git a/src/components/pages/details/details-page-layout.tsx b/src/components/pages/details/details-page-layout.tsx index 5c535f6..b5af5a5 100644 --- a/src/components/pages/details/details-page-layout.tsx +++ b/src/components/pages/details/details-page-layout.tsx @@ -8,8 +8,8 @@ interface DetailsPageLayoutProps { export function DetailsPageLayout({ sidebar, children }: DetailsPageLayoutProps) { return ( -
-
+
+
{sidebar} diff --git a/src/components/ui/image-zoom.tsx b/src/components/ui/image-zoom.tsx index 3bf8e9d..b228682 100644 --- a/src/components/ui/image-zoom.tsx +++ b/src/components/ui/image-zoom.tsx @@ -30,11 +30,11 @@ export const ImageZoom = ({ className, backdropClassName, ...props }: ImageZoomP classDialog={cn( "[&::backdrop]:hidden", "[&[open]]:fixed [&[open]]:m-0 [&[open]]:h-dvh [&[open]]:max-h-none [&[open]]:w-dvw [&[open]]:max-w-none [&[open]]:overflow-hidden [&[open]]:border-0 [&[open]]:bg-transparent [&[open]]:p-0", - "[&_[data-rmiz-modal-overlay]]:absolute [&_[data-rmiz-modal-overlay]]:inset-0 [&_[data-rmiz-modal-overlay]]:transition-all", + "[&_[data-rmiz-modal-overlay]]:absolute [&_[data-rmiz-modal-overlay]]:inset-0 [&_[data-rmiz-modal-overlay]]:transition-all [&_[data-rmiz-modal-overlay]]:duration-300 [&_[data-rmiz-modal-overlay]]:ease-out", '[&_[data-rmiz-modal-overlay="hidden"]]:bg-transparent', '**:data-[rmiz-modal-overlay="visible"]:bg-transparent! [&_[data-rmiz-modal-overlay="visible"]]:backdrop-blur-md', "[&_[data-rmiz-modal-content]]:relative [&_[data-rmiz-modal-content]]:size-full", - "[&_[data-rmiz-modal-img]]:absolute [&_[data-rmiz-modal-img]]:origin-top-left [&_[data-rmiz-modal-img]]:cursor-zoom-out [&_[data-rmiz-modal-img]]:transition-transform", + "[&_[data-rmiz-modal-img]]:absolute [&_[data-rmiz-modal-img]]:origin-top-left [&_[data-rmiz-modal-img]]:cursor-zoom-out [&_[data-rmiz-modal-img]]:transition-transform [&_[data-rmiz-modal-img]]:duration-300 [&_[data-rmiz-modal-img]]:ease-out", "motion-reduce:[&_[data-rmiz-modal-img]]:transition-none motion-reduce:[&_[data-rmiz-modal-overlay]]:transition-none", backdropClassName, )} diff --git a/src/routes/anime/$slug.tsx b/src/routes/anime/$slug.tsx index 5353b1c..c96e51b 100644 --- a/src/routes/anime/$slug.tsx +++ b/src/routes/anime/$slug.tsx @@ -22,6 +22,8 @@ import { ErrorComponent } from "@/components/shared/error.tsx"; import { LoadingDetails } from "@/components/shared/loadings/details.tsx"; import { EpisodicContentModal } from "@/components/shared/modals/episodic-content"; import { RefreshData } from "@/components/shared/modals/refresh-data"; +import { Button } from "@/components/ui/button"; +import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "@/components/ui/empty"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { type ApiTypes, api, apiEndpoints } from "@/lib/api.ts"; import { useSession } from "@/lib/auth.ts"; @@ -54,6 +56,7 @@ function AnimeDetailsRoute() { const { slug } = Route.useParams(); const { anime: loaderAnime } = Route.useLoaderData(); const { t } = useTranslation(); + const [moreOpen, setMoreOpen] = useState(false); const [mySeason, _setMySeason] = useState({ totalEpisodes: 12, watchedEpisodes: [1, 2, 3, 4, 5], @@ -160,6 +163,8 @@ function AnimeDetailsRoute() { subtitle={anime.season && anime.year ? `${anime.season} ${anime.year}` : undefined} description={anime.synopsis} triggerLabel={t("library:moreOptions")} + open={moreOpen} + onOpenChange={setMoreOpen} > @@ -264,253 +269,272 @@ function AnimeDetailsRoute() { ); return ( - -

- {anime.title} -

+ <> + +

+ {anime.title} +

-
- {!reviewsQuery.isLoading && !reviewsQuery.isError && reviews?.total >= 1 && ( -
-
- - - - - +
+ {!reviewsQuery.isLoading && !reviewsQuery.isError && reviews?.total >= 1 && ( +
+
+ + + + + +
+ {rating} + + ({reviews.total} {t("library:reviews")}) +
- {rating} - - ({reviews.total} {t("library:reviews")}) - -
- )} -
- - -
- - {t("library:info")} - {t("library:relations")} - {!episodesQuery.isLoading && !episodesQuery.isError && episodes?.length > 0 && ( - {t("library:episode_other")} - )} - {t("library:cast")} - {t("library:characters")} - {!reviewsQuery.isLoading && !reviewsQuery.isError && reviews?.total >= 1 && ( - - {t("library:reviews")} ({reviews.total}) - - )} - {t("library:lists")} (30) - + )}
- -
-

{t("library:genres")}

- getGenreLabel(t, g)} /> -
- -
-

{t("library:synopsis")}

-

{anime.synopsis}

-
-
-

{t("library:animeCharacteristics")}

- - {anime.type && ( - } - description={anime.type} - /> - )} - {anime.source && ( - } - description={anime.source} - /> - )} - {anime.numberOfEpisodes && ( - } - description={anime.numberOfEpisodes} - /> + +
+ + {t("library:info")} + {t("library:relations")} + {!episodesQuery.isLoading && !episodesQuery.isError && episodes?.length > 0 && ( + {t("library:episode_other")} )} - {anime.broadcast.string && ( - } - description={anime.broadcast.string} - /> - )} - {anime.rating && ( - } - description={anime.rating} - /> - )} - {anime.duration && ( - } - description={anime.duration} - /> - )} - {anime.studios.length >= 1 && ( - } - description={anime.studios.map((st: { name: string; malId: number }, index: number) => ( - - {st.name} - {index < anime.studios.length - 1 && ", "} - - ))} - /> - )} - {anime.producers.length >= 1 && ( - } - description={anime.producers.map((pd: { name: string; malId: number }, index: number) => ( - - {pd.name} - {index < anime.producers.length - 1 && ", "} - - ))} - /> - )} - + {t("library:cast")} + {t("library:characters")} + {t("library:lists")} (30) +
+ +
+

{anime.synopsis}

+

{t("library:genres")}

+ getGenreLabel(t, g)} /> +
- {isAuthenticated && } +
+

{t("library:animeCharacteristics")}

+ + {anime.type && ( + } + description={anime.type} + /> + )} + {anime.source && ( + } + description={anime.source} + /> + )} + {anime.numberOfEpisodes && ( + } + description={anime.numberOfEpisodes} + /> + )} + {anime.broadcast.string && ( + } + description={anime.broadcast.string} + /> + )} + {anime.rating && ( + } + description={anime.rating} + /> + )} + {anime.duration && ( + } + description={anime.duration} + /> + )} + {anime.studios.length >= 1 && ( + } + description={anime.studios.map((st: { name: string; malId: number }, index: number) => ( + + {st.name} + {index < anime.studios.length - 1 && ", "} + + ))} + /> + )} + {anime.producers.length >= 1 && ( + } + description={anime.producers.map((pd: { name: string; malId: number }, index: number) => ( + + {pd.name} + {index < anime.producers.length - 1 && ", "} + + ))} + /> + )} + +
-
-

{t("library:communityStatistics")}

- -
+ {isAuthenticated && } + +
+

{t("library:communityStatistics")}

+ +
- {anime.trailer.embedUrl && ( -