feat: add progressbar on lists
This commit is contained in:
@@ -63,6 +63,8 @@ function LibraryCard({ library, onClick }: LibraryCardProps) {
|
||||
alt={`Couverture de ${library.name}`}
|
||||
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
|
||||
quality={25}
|
||||
readBooks={library.booksReadCount}
|
||||
totalBooks={library.booksCount}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -65,6 +65,8 @@ export function SeriesGrid({ series }: SeriesGridProps) {
|
||||
id={series.id}
|
||||
alt={`Couverture de ${series.metadata.title}`}
|
||||
isCompleted={series.booksCount === series.booksReadCount}
|
||||
readBooks={series.booksReadCount}
|
||||
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">
|
||||
<h3 className="font-medium text-sm text-white line-clamp-2">{series.metadata.title}</h3>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Cover } from "@/components/ui/cover";
|
||||
import { MarkAsReadButton } from "@/components/ui/mark-as-read-button";
|
||||
import { MarkAsUnreadButton } from "@/components/ui/mark-as-unread-button";
|
||||
import { BookOfflineButton } from "@/components/ui/book-offline-button";
|
||||
import { ProgressBar } from "@/components/ui/progress-bar";
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
interface BookGridProps {
|
||||
@@ -97,6 +98,7 @@ export function BookGrid({ books, onBookClick }: BookGridProps) {
|
||||
{localBooks.map((book) => {
|
||||
const statusInfo = getReadingStatusInfo(book);
|
||||
const isRead = book.readProgress?.completed || false;
|
||||
const currentPage = book.readProgress?.page || 0;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -112,6 +114,8 @@ export function BookGrid({ books, onBookClick }: BookGridProps) {
|
||||
id={book.id}
|
||||
alt={`Couverture de ${book.metadata.title || `Tome ${book.metadata.number}`}`}
|
||||
isCompleted={isRead}
|
||||
currentPage={currentPage}
|
||||
totalPages={book.media.pagesCount}
|
||||
/>
|
||||
</button>
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { CoverClient } from "./cover-client";
|
||||
import { ProgressBar } from "./progress-bar";
|
||||
|
||||
interface CoverProps {
|
||||
interface BaseCoverProps {
|
||||
type: "series" | "book";
|
||||
id: string;
|
||||
alt?: string;
|
||||
@@ -10,6 +11,20 @@ interface CoverProps {
|
||||
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`;
|
||||
@@ -17,7 +32,8 @@ function getImageUrl(type: "series" | "book", id: string) {
|
||||
return `/api/komga/images/books/${id}/thumbnail`;
|
||||
}
|
||||
|
||||
export function Cover({
|
||||
export function Cover(props: CoverProps) {
|
||||
const {
|
||||
type,
|
||||
id,
|
||||
alt = "Image de couverture",
|
||||
@@ -25,10 +41,28 @@ export function Cover({
|
||||
quality = 80,
|
||||
sizes = "100vw",
|
||||
isCompleted = false,
|
||||
}: CoverProps) {
|
||||
} = 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} />
|
||||
) : null;
|
||||
}
|
||||
|
||||
if (type === "series") {
|
||||
const { readBooks, totalBooks } = props;
|
||||
return readBooks && totalBooks && readBooks > 0 && !isCompleted ? (
|
||||
<ProgressBar progress={readBooks} total={totalBooks} />
|
||||
) : null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative w-full h-full">
|
||||
<CoverClient
|
||||
imageUrl={imageUrl}
|
||||
alt={alt}
|
||||
@@ -37,5 +71,7 @@ export function Cover({
|
||||
sizes={sizes}
|
||||
isCompleted={isCompleted}
|
||||
/>
|
||||
{showProgress()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
19
src/components/ui/progress-bar.tsx
Normal file
19
src/components/ui/progress-bar.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
interface ProgressBarProps {
|
||||
progress: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
export function ProgressBar({ progress, total }: ProgressBarProps) {
|
||||
const percentage = Math.round((progress / total) * 100);
|
||||
|
||||
return (
|
||||
<div className="absolute bottom-0 left-0 right-0 px-3 py-2 bg-black/50 backdrop-blur-sm border-t border-white/10">
|
||||
<div className="h-2 bg-white/30 rounded-full overflow-hidden">
|
||||
<div
|
||||
className="h-full bg-white rounded-full transition-all duration-300"
|
||||
style={{ width: `${percentage}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -17,6 +17,8 @@ export interface KomgaLibrary {
|
||||
importLastModified: string;
|
||||
lastModified: string;
|
||||
unavailable: boolean;
|
||||
booksCount: number;
|
||||
booksReadCount: number;
|
||||
}
|
||||
|
||||
export interface KomgaSeries {
|
||||
|
||||
Reference in New Issue
Block a user