refacto: cover split and simplify
This commit is contained in:
@@ -1,7 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Cover } from "@/components/ui/cover";
|
import { SeriesCover } from "@/components/ui/series-cover";
|
||||||
import { useTranslate } from "@/hooks/useTranslate";
|
import { useTranslate } from "@/hooks/useTranslate";
|
||||||
|
import { KomgaSeries } from "@/types/komga";
|
||||||
|
|
||||||
interface OptimizedHeroSeries {
|
interface OptimizedHeroSeries {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -31,9 +32,8 @@ export function HeroSection({ series }: HeroSectionProps) {
|
|||||||
key={series.id}
|
key={series.id}
|
||||||
className="relative aspect-[2/3] bg-muted rounded-lg overflow-hidden"
|
className="relative aspect-[2/3] bg-muted rounded-lg overflow-hidden"
|
||||||
>
|
>
|
||||||
<Cover
|
<SeriesCover
|
||||||
type="series"
|
series={series as KomgaSeries}
|
||||||
id={series.id}
|
|
||||||
alt={t("home.hero.coverAlt", { title: series.metadata.title })}
|
alt={t("home.hero.coverAlt", { title: series.metadata.title })}
|
||||||
quality={25}
|
quality={25}
|
||||||
sizes="(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 16.666vw"
|
sizes="(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 16.666vw"
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
import { Cover } from "@/components/ui/cover";
|
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { ClientOfflineBookService } from "@/lib/services/client-offlinebook.service";
|
import { KomgaBook, KomgaSeries } from "@/types/komga";
|
||||||
import { KomgaBook } from "@/types/komga";
|
import { BookCover } from "../ui/book-cover";
|
||||||
|
import { SeriesCover } from "../ui/series-cover";
|
||||||
|
|
||||||
interface BaseItem {
|
interface BaseItem {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -128,24 +128,9 @@ function MediaCard({ item, onClick }: MediaCardProps) {
|
|||||||
>
|
>
|
||||||
<div className="relative aspect-[2/3] bg-muted">
|
<div className="relative aspect-[2/3] bg-muted">
|
||||||
{isSeries ? (
|
{isSeries ? (
|
||||||
<Cover
|
<SeriesCover series={item as KomgaSeries} alt={`Couverture de ${title}`} quality={100} />
|
||||||
type={isSeries ? "series" : "book"}
|
|
||||||
id={item.id}
|
|
||||||
alt={`Couverture de ${title}`}
|
|
||||||
quality={100}
|
|
||||||
readBooks={item.booksReadCount}
|
|
||||||
totalBooks={item.booksCount}
|
|
||||||
isCompleted={item.booksCount === item.booksReadCount}
|
|
||||||
/>
|
|
||||||
) : (
|
) : (
|
||||||
<Cover
|
<BookCover book={item as KomgaBook} alt={`Couverture de ${title}`} quality={100} />
|
||||||
type="book"
|
|
||||||
id={item.id}
|
|
||||||
alt={`Couverture de ${title}`}
|
|
||||||
quality={100}
|
|
||||||
currentPage={ClientOfflineBookService.getCurrentPage(item as KomgaBook)}
|
|
||||||
totalPages={item.media?.pagesCount}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
{/* Overlay avec les informations au survol */}
|
{/* Overlay avec les informations au survol */}
|
||||||
<div className="absolute inset-0 bg-black/60 opacity-0 hover:opacity-100 transition-opacity duration-200 flex flex-col justify-end p-3">
|
<div className="absolute inset-0 bg-black/60 opacity-0 hover:opacity-100 transition-opacity duration-200 flex flex-col justify-end p-3">
|
||||||
|
|||||||
@@ -1,105 +0,0 @@
|
|||||||
import { Book } from "lucide-react";
|
|
||||||
import { Cover } from "@/components/ui/cover";
|
|
||||||
import { KomgaLibrary } from "@/types/komga";
|
|
||||||
import { useTranslate } from "@/hooks/useTranslate";
|
|
||||||
|
|
||||||
interface LibraryGridProps {
|
|
||||||
libraries: KomgaLibrary[];
|
|
||||||
onLibraryClick?: (library: KomgaLibrary) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Utility function to format date safely
|
|
||||||
const formatDate = (dateString: string, locale: string): string => {
|
|
||||||
try {
|
|
||||||
const date = new Date(dateString);
|
|
||||||
if (isNaN(date.getTime())) {
|
|
||||||
return "Date unavailable";
|
|
||||||
}
|
|
||||||
return new Intl.DateTimeFormat(locale, {
|
|
||||||
year: "numeric",
|
|
||||||
month: "long",
|
|
||||||
day: "numeric",
|
|
||||||
}).format(date);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error formatting date:", error);
|
|
||||||
return "Date unavailable";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export function LibraryGrid({ libraries, onLibraryClick }: LibraryGridProps) {
|
|
||||||
const { t } = useTranslate();
|
|
||||||
|
|
||||||
if (!libraries.length) {
|
|
||||||
return (
|
|
||||||
<div className="text-center p-8">
|
|
||||||
<p className="text-muted-foreground">{t("library.empty")}</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
||||||
{libraries.map((library) => (
|
|
||||||
<LibraryCard key={library.id} library={library} onClick={() => onLibraryClick?.(library)} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface LibraryCardProps {
|
|
||||||
library: KomgaLibrary;
|
|
||||||
onClick?: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
function LibraryCard({ library, onClick }: LibraryCardProps) {
|
|
||||||
const { t, i18n } = useTranslate();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
onClick={onClick}
|
|
||||||
className="group relative flex flex-col h-48 rounded-lg border bg-card text-card-foreground shadow-sm hover:bg-accent hover:text-accent-foreground transition-colors overflow-hidden"
|
|
||||||
>
|
|
||||||
{/* Cover image */}
|
|
||||||
<div className="absolute inset-0 bg-muted">
|
|
||||||
<div className="w-full h-full opacity-20 group-hover:opacity-30 transition-opacity">
|
|
||||||
<Cover
|
|
||||||
type="series"
|
|
||||||
id={library.id}
|
|
||||||
alt={t("library.coverAlt", { name: library.name })}
|
|
||||||
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
|
|
||||||
quality={25}
|
|
||||||
readBooks={library.booksReadCount}
|
|
||||||
totalBooks={library.booksCount}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Content */}
|
|
||||||
<div className="relative h-full flex flex-col p-6">
|
|
||||||
<div className="flex items-center gap-3 mb-4">
|
|
||||||
<Book className="h-6 w-6 shrink-0" />
|
|
||||||
<h3 className="text-lg font-semibold line-clamp-1">{library.name}</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-auto space-y-2">
|
|
||||||
<div className="flex items-center justify-between text-sm">
|
|
||||||
<span
|
|
||||||
className={`px-2 py-0.5 rounded-full text-xs ${
|
|
||||||
library.unavailable
|
|
||||||
? "bg-destructive/10 text-destructive"
|
|
||||||
: "bg-green-500/10 text-green-500"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{t(library.unavailable ? "library.status.unavailable" : "library.status.available")}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="text-xs text-muted-foreground">
|
|
||||||
{t("library.lastUpdated", {
|
|
||||||
date: formatDate(library.lastModified, i18n.language === "fr" ? "fr-FR" : "en-US"),
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
import { KomgaSeries } from "@/types/komga";
|
import { KomgaSeries } from "@/types/komga";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { Cover } from "@/components/ui/cover";
|
import { SeriesCover } from "@/components/ui/series-cover";
|
||||||
import { useTranslate } from "@/hooks/useTranslate";
|
import { useTranslate } from "@/hooks/useTranslate";
|
||||||
|
|
||||||
interface SeriesGridProps {
|
interface SeriesGridProps {
|
||||||
@@ -65,13 +65,11 @@ export function SeriesGrid({ series }: SeriesGridProps) {
|
|||||||
series.booksCount === series.booksReadCount && "opacity-50"
|
series.booksCount === series.booksReadCount && "opacity-50"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Cover
|
<SeriesCover
|
||||||
type="series"
|
series={series as KomgaSeries}
|
||||||
id={series.id}
|
|
||||||
alt={t("series.coverAlt", { title: series.metadata.title })}
|
alt={t("series.coverAlt", { title: series.metadata.title })}
|
||||||
isCompleted={series.booksCount === series.booksReadCount}
|
quality={25}
|
||||||
readBooks={series.booksReadCount}
|
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
|
||||||
totalBooks={series.booksCount}
|
|
||||||
/>
|
/>
|
||||||
<div className="absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/60 to-transparent p-4 space-y-2 translate-y-full group-hover:translate-y-0 transition-transform duration-200">
|
<div className="absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/60 to-transparent p-4 space-y-2 translate-y-full group-hover:translate-y-0 transition-transform duration-200">
|
||||||
<h3 className="font-medium text-sm text-white line-clamp-2">{series.metadata.title}</h3>
|
<h3 className="font-medium text-sm text-white line-clamp-2">{series.metadata.title}</h3>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { KomgaBook } from "@/types/komga";
|
import { KomgaBook } from "@/types/komga";
|
||||||
import { formatDate } from "@/lib/utils";
|
import { formatDate } from "@/lib/utils";
|
||||||
import { Cover } from "@/components/ui/cover";
|
import { BookCover } from "@/components/ui/book-cover";
|
||||||
import { MarkAsReadButton } from "@/components/ui/mark-as-read-button";
|
import { MarkAsReadButton } from "@/components/ui/mark-as-read-button";
|
||||||
import { MarkAsUnreadButton } from "@/components/ui/mark-as-unread-button";
|
import { MarkAsUnreadButton } from "@/components/ui/mark-as-unread-button";
|
||||||
import { BookOfflineButton } from "@/components/ui/book-offline-button";
|
import { BookOfflineButton } from "@/components/ui/book-offline-button";
|
||||||
@@ -106,7 +106,6 @@ export function BookGrid({ books, onBookClick }: BookGridProps) {
|
|||||||
const statusInfo = getReadingStatusInfo(book, t);
|
const statusInfo = getReadingStatusInfo(book, t);
|
||||||
const isRead = book.readProgress?.completed || false;
|
const isRead = book.readProgress?.completed || false;
|
||||||
const hasReadProgress = book.readProgress !== null;
|
const hasReadProgress = book.readProgress !== null;
|
||||||
const currentPage = ClientOfflineBookService.getCurrentPage(book);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -117,15 +116,11 @@ export function BookGrid({ books, onBookClick }: BookGridProps) {
|
|||||||
onClick={() => onBookClick(book)}
|
onClick={() => onBookClick(book)}
|
||||||
className="w-full h-full hover:opacity-100 transition-all"
|
className="w-full h-full hover:opacity-100 transition-all"
|
||||||
>
|
>
|
||||||
<Cover
|
<BookCover
|
||||||
type="book"
|
book={book}
|
||||||
id={book.id}
|
|
||||||
alt={t("books.coverAlt", {
|
alt={t("books.coverAlt", {
|
||||||
title: book.metadata.title || `Tome ${book.metadata.number}`,
|
title: book.metadata.title || `Tome ${book.metadata.number}`,
|
||||||
})}
|
})}
|
||||||
isCompleted={isRead}
|
|
||||||
currentPage={currentPage}
|
|
||||||
totalPages={book.media.pagesCount}
|
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import { KomgaSeries } from "@/types/komga";
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
import { Cover } from "@/components/ui/cover";
|
|
||||||
import { RefreshButton } from "@/components/library/RefreshButton";
|
import { RefreshButton } from "@/components/library/RefreshButton";
|
||||||
import { AppError } from "@/utils/errors";
|
import { AppError } from "@/utils/errors";
|
||||||
import { ERROR_CODES } from "@/constants/errorCodes";
|
import { ERROR_CODES } from "@/constants/errorCodes";
|
||||||
import { getErrorMessage } from "@/utils/errors";
|
import { getErrorMessage } from "@/utils/errors";
|
||||||
import { useTranslate } from "@/hooks/useTranslate";
|
import { useTranslate } from "@/hooks/useTranslate";
|
||||||
|
import { SeriesCover } from "@/components/ui/series-cover";
|
||||||
|
|
||||||
interface SeriesHeaderProps {
|
interface SeriesHeaderProps {
|
||||||
series: KomgaSeries;
|
series: KomgaSeries;
|
||||||
@@ -122,9 +122,8 @@ export const SeriesHeader = ({ series, refreshSeries }: SeriesHeaderProps) => {
|
|||||||
<div className="relative min-h-[300px] md:h-[300px] w-screen -ml-[calc((100vw-100%)/2)] overflow-hidden">
|
<div className="relative min-h-[300px] md:h-[300px] w-screen -ml-[calc((100vw-100%)/2)] overflow-hidden">
|
||||||
{/* Image de fond */}
|
{/* Image de fond */}
|
||||||
<div className="absolute inset-0">
|
<div className="absolute inset-0">
|
||||||
<Cover
|
<SeriesCover
|
||||||
type="series"
|
series={series as KomgaSeries}
|
||||||
id={series.id}
|
|
||||||
alt={t("series.header.coverAlt", { title: series.metadata.title })}
|
alt={t("series.header.coverAlt", { title: series.metadata.title })}
|
||||||
className="blur-sm scale-105 brightness-50"
|
className="blur-sm scale-105 brightness-50"
|
||||||
quality={60}
|
quality={60}
|
||||||
@@ -136,9 +135,8 @@ export const SeriesHeader = ({ series, refreshSeries }: SeriesHeaderProps) => {
|
|||||||
<div className="flex flex-col md:flex-row gap-6 items-center md:items-start w-full">
|
<div className="flex flex-col md:flex-row gap-6 items-center md:items-start w-full">
|
||||||
{/* Image principale */}
|
{/* Image principale */}
|
||||||
<div className="relative w-[180px] aspect-[2/3] rounded-lg overflow-hidden shadow-lg bg-muted flex-shrink-0">
|
<div className="relative w-[180px] aspect-[2/3] rounded-lg overflow-hidden shadow-lg bg-muted flex-shrink-0">
|
||||||
<Cover
|
<SeriesCover
|
||||||
type="series"
|
series={series as KomgaSeries}
|
||||||
id={series.id}
|
|
||||||
alt={t("series.header.coverAlt", { title: series.metadata.title })}
|
alt={t("series.header.coverAlt", { title: series.metadata.title })}
|
||||||
quality={90}
|
quality={90}
|
||||||
/>
|
/>
|
||||||
|
|||||||
37
src/components/ui/book-cover.tsx
Normal file
37
src/components/ui/book-cover.tsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { CoverClient } from "./cover-client";
|
||||||
|
import { ProgressBar } from "./progress-bar";
|
||||||
|
import { BookCoverProps, getImageUrl } from "./cover-utils";
|
||||||
|
import { ClientOfflineBookService } from "@/lib/services/client-offlinebook.service";
|
||||||
|
|
||||||
|
export function BookCover({
|
||||||
|
book,
|
||||||
|
alt = "Image de couverture",
|
||||||
|
className,
|
||||||
|
quality = 80,
|
||||||
|
sizes = "100vw",
|
||||||
|
}: BookCoverProps) {
|
||||||
|
if (!book) return null;
|
||||||
|
|
||||||
|
const imageUrl = getImageUrl("book", book.id);
|
||||||
|
const isCompleted = book.readProgress?.completed || false;
|
||||||
|
|
||||||
|
const currentPage = ClientOfflineBookService.getCurrentPage(book);
|
||||||
|
const totalPages = book.media.pagesCount;
|
||||||
|
const showProgress = currentPage && totalPages && currentPage > 0 && !isCompleted;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative w-full h-full">
|
||||||
|
<CoverClient
|
||||||
|
imageUrl={imageUrl}
|
||||||
|
alt={alt}
|
||||||
|
className={className}
|
||||||
|
quality={quality}
|
||||||
|
sizes={sizes}
|
||||||
|
isCompleted={isCompleted}
|
||||||
|
/>
|
||||||
|
{showProgress && <ProgressBar progress={currentPage} total={totalPages} type="book" />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
23
src/components/ui/cover-utils.tsx
Normal file
23
src/components/ui/cover-utils.tsx
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { KomgaBook, KomgaSeries } from "@/types/komga";
|
||||||
|
|
||||||
|
export interface BaseCoverProps {
|
||||||
|
alt?: string;
|
||||||
|
className?: string;
|
||||||
|
quality?: number;
|
||||||
|
sizes?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BookCoverProps extends BaseCoverProps {
|
||||||
|
book?: KomgaBook;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SeriesCoverProps extends BaseCoverProps {
|
||||||
|
series: KomgaSeries;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getImageUrl(type: "series" | "book", id: string) {
|
||||||
|
if (type === "series") {
|
||||||
|
return `/api/komga/images/series/${id}/thumbnail`;
|
||||||
|
}
|
||||||
|
return `/api/komga/images/books/${id}/thumbnail`;
|
||||||
|
}
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { CoverClient } from "./cover-client";
|
|
||||||
import { ProgressBar } from "./progress-bar";
|
|
||||||
|
|
||||||
interface BaseCoverProps {
|
|
||||||
type: "series" | "book";
|
|
||||||
id: string;
|
|
||||||
alt?: string;
|
|
||||||
className?: string;
|
|
||||||
quality?: number;
|
|
||||||
sizes?: string;
|
|
||||||
isCompleted?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BookCoverProps extends BaseCoverProps {
|
|
||||||
type: "book";
|
|
||||||
currentPage?: number;
|
|
||||||
totalPages?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SeriesCoverProps extends BaseCoverProps {
|
|
||||||
type: "series";
|
|
||||||
readBooks?: number;
|
|
||||||
totalBooks?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
type CoverProps = BookCoverProps | SeriesCoverProps;
|
|
||||||
|
|
||||||
function getImageUrl(type: "series" | "book", id: string) {
|
|
||||||
if (type === "series") {
|
|
||||||
return `/api/komga/images/series/${id}/thumbnail`;
|
|
||||||
}
|
|
||||||
return `/api/komga/images/books/${id}/thumbnail`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Cover(props: CoverProps) {
|
|
||||||
const {
|
|
||||||
type,
|
|
||||||
id,
|
|
||||||
alt = "Image de couverture",
|
|
||||||
className,
|
|
||||||
quality = 80,
|
|
||||||
sizes = "100vw",
|
|
||||||
isCompleted = false,
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const imageUrl = getImageUrl(type, id);
|
|
||||||
|
|
||||||
const showProgress = () => {
|
|
||||||
if (type === "book") {
|
|
||||||
const { currentPage, totalPages } = props;
|
|
||||||
return currentPage && totalPages && currentPage > 0 && !isCompleted ? (
|
|
||||||
<ProgressBar progress={currentPage} total={totalPages} type="book" />
|
|
||||||
) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === "series") {
|
|
||||||
const { readBooks, totalBooks } = props;
|
|
||||||
return readBooks && totalBooks && readBooks > 0 && !isCompleted ? (
|
|
||||||
<ProgressBar progress={readBooks} total={totalBooks} type="series" />
|
|
||||||
) : null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative w-full h-full">
|
|
||||||
<CoverClient
|
|
||||||
imageUrl={imageUrl}
|
|
||||||
alt={alt}
|
|
||||||
className={className}
|
|
||||||
quality={quality}
|
|
||||||
sizes={sizes}
|
|
||||||
isCompleted={isCompleted}
|
|
||||||
/>
|
|
||||||
{showProgress()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
36
src/components/ui/series-cover.tsx
Normal file
36
src/components/ui/series-cover.tsx
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { CoverClient } from "./cover-client";
|
||||||
|
import { ProgressBar } from "./progress-bar";
|
||||||
|
import { SeriesCoverProps, getImageUrl } from "./cover-utils";
|
||||||
|
|
||||||
|
export function SeriesCover({
|
||||||
|
series,
|
||||||
|
alt = "Image de couverture",
|
||||||
|
className,
|
||||||
|
quality = 80,
|
||||||
|
sizes = "100vw",
|
||||||
|
}: SeriesCoverProps) {
|
||||||
|
if (!series) return null;
|
||||||
|
|
||||||
|
const imageUrl = getImageUrl("series", series.id);
|
||||||
|
const isCompleted = series.booksCount === series.booksReadCount;
|
||||||
|
|
||||||
|
const readBooks = series.booksReadCount;
|
||||||
|
const totalBooks = series.booksCount;
|
||||||
|
const showProgress = readBooks && totalBooks && readBooks > 0 && !isCompleted;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative w-full h-full">
|
||||||
|
<CoverClient
|
||||||
|
imageUrl={imageUrl}
|
||||||
|
alt={alt}
|
||||||
|
className={className}
|
||||||
|
quality={quality}
|
||||||
|
sizes={sizes}
|
||||||
|
isCompleted={isCompleted}
|
||||||
|
/>
|
||||||
|
{showProgress && <ProgressBar progress={readBooks} total={totalBooks} type="series" />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user