diff --git a/src/api/generated/api.schemas.ts b/src/api/generated/api.schemas.ts index 8ef277f..72312cc 100644 --- a/src/api/generated/api.schemas.ts +++ b/src/api/generated/api.schemas.ts @@ -117,6 +117,7 @@ export interface ContentProviderDTO { name: string; url?: string; active?: boolean; + supportsContentFetch?: boolean; } export interface ContentProviderListDTO { @@ -214,9 +215,9 @@ export interface PageMangaImportJobDTO { number?: number; pageable?: PageableObject; numberOfElements?: number; + sort?: SortObject; first?: boolean; last?: boolean; - sort?: SortObject; empty?: boolean; } @@ -277,9 +278,9 @@ export interface PageMangaListDTO { number?: number; pageable?: PageableObject; numberOfElements?: number; + sort?: SortObject; first?: boolean; last?: boolean; - sort?: SortObject; empty?: boolean; } diff --git a/src/features/admin/components/FailedImportJobs.tsx b/src/features/admin/components/FailedImportJobs.tsx index d7714a3..ee0732b 100644 --- a/src/features/admin/components/FailedImportJobs.tsx +++ b/src/features/admin/components/FailedImportJobs.tsx @@ -18,6 +18,8 @@ import { DialogTitle, } from "@/components/ui/dialog.tsx"; import { Input } from "@/components/ui/input.tsx"; +import { Skeleton } from "@/components/ui/skeleton.tsx"; +import { Spinner } from "@/components/ui/spinner.tsx"; import { Select, SelectContent, @@ -46,7 +48,7 @@ export const FailedImportJobs = () => { const [errorDialogOpen, setErrorDialogOpen] = useState(false); const [currentPage, setCurrentPage] = useState(1); - const { data } = useGetMangaImportJobs({ + const { data, isPending } = useGetMangaImportJobs({ status: statusFilter, searchQuery: searchQuery, page: currentPage - 1, @@ -54,6 +56,41 @@ export const FailedImportJobs = () => { }); const importJobsData = data?.data; + if (isPending) { + return ( +
+
+ + +
+ +
+ {Array.from({ length: 5 }).map((_, i) => ( + + + + + ))} +
+ +
+
+ + +
+ +
+ + +
+ + +
+
+
+ ); + } + const formatDate = (dateString: string) => { return new Date(dateString).toLocaleString("en-US", { year: "numeric", @@ -328,23 +365,6 @@ export const FailedImportJobs = () => { > Close - {/* {*/} - {/* // Mock retry action*/} - {/* setJobs((prev) =>*/} - {/* prev.map((j) =>*/} - {/* j.id === selectedJob.id*/} - {/* ? { ...j, status: "PENDING" as ImportStatus, updatedAt: new Date().toISOString() }*/} - {/* : j*/} - {/* )*/} - {/* )*/} - {/* setErrorDialogOpen(false)*/} - {/* }}*/} - {/*>*/} - {/* */} - {/* Retry Import*/} - {/**/} )} diff --git a/src/features/admin/components/IngestReview.tsx b/src/features/admin/components/IngestReview.tsx index ad66fd4..197fef6 100644 --- a/src/features/admin/components/IngestReview.tsx +++ b/src/features/admin/components/IngestReview.tsx @@ -2,9 +2,60 @@ import {Card} from "@/components/ui/card.tsx"; import {AlertCircle} from "lucide-react"; import {useGetMangaIngestReviews} from "@/api/generated/manga-ingest-review/manga-ingest-review.ts"; import {ImportReviewCard} from "@/features/admin/components/ImportReviewCard.tsx"; +import {Skeleton} from "@/components/ui/skeleton.tsx"; +import {Spinner} from "@/components/ui/spinner.tsx"; export const IngestReview = () => { - const { data: importReviewData, queryKey } = useGetMangaIngestReviews(); + const { data: importReviewData, queryKey, isPending } = useGetMangaIngestReviews(); + + if (isPending) { + return ( +
+
+ + +
+ +
+ + +
+ + {Array.from({ length: 2 }).map((_, i) => ( + +
+
+
+ + +
+
+ + + +
+
+
+ + +
+
+ + +
+
+ +
+ + +
+
+
+
+ ))} +
+ ); + } return (
diff --git a/src/features/admin/components/ProviderManager.tsx b/src/features/admin/components/ProviderManager.tsx index 19fed53..d533001 100644 --- a/src/features/admin/components/ProviderManager.tsx +++ b/src/features/admin/components/ProviderManager.tsx @@ -7,6 +7,7 @@ import { import { Badge } from "@/components/ui/badge.tsx"; import { Button } from "@/components/ui/button.tsx"; import { Card } from "@/components/ui/card.tsx"; +import {toast} from "sonner"; export const ProviderManager = () => { const { data } = useGetContentProviders(); @@ -15,12 +16,26 @@ export const ProviderManager = () => { const { mutate: mutateFetchContentProviderMangas, isPending: isPendingFetchContentProviderMangas, - } = useFetchContentProviderMangas(); + } = useFetchContentProviderMangas({ + mutation: { + onSuccess: () => { + toast.success("Content Provider mangas update queued successfully!"); + }, + }, + }); const { mutate: mutateFetchAllContentProviderMangas, isPending: isPendingFetchAllContentProviderMangas, - } = useFetchAllContentProviderMangas(); + } = useFetchAllContentProviderMangas( + { + mutation: { + onSuccess: () => { + toast.success("All Content Provider mangas update queued successfully!"); + }, + }, + } + ); const activeCount = providers?.filter((p) => p.active).length; @@ -104,26 +119,28 @@ export const ProviderManager = () => {

- + {provider.supportsContentFetch && ( + + )}
); diff --git a/src/pages/Admin.tsx b/src/pages/Admin.tsx index 8b8f74e..d421e17 100644 --- a/src/pages/Admin.tsx +++ b/src/pages/Admin.tsx @@ -2,6 +2,8 @@ import {AlertCircle, ArrowLeft, Download, FileStack, Server, Shield} from "lucid import { type ReactNode, useEffect, useState } from "react"; import { useNavigate } from "react-router"; import { Button } from "@/components/ui/button.tsx"; +import { Skeleton } from "@/components/ui/skeleton.tsx"; +import { Spinner } from "@/components/ui/spinner.tsx"; import { useAuth } from "@/contexts/AuthContext.tsx"; import { FailedImportJobs } from "@/features/admin/components/FailedImportJobs.tsx"; import { ProviderManager } from "@/features/admin/components/ProviderManager.tsx"; @@ -70,6 +72,52 @@ const Admin = () => { // }, ]; + if (isLoading) { + return ( +
+
+ + +
+
+ + +
+
+
+
+ ); + } + return (
diff --git a/src/pages/Manga.tsx b/src/pages/Manga.tsx index 976b24c..609622c 100644 --- a/src/pages/Manga.tsx +++ b/src/pages/Manga.tsx @@ -249,18 +249,20 @@ const Manga = () => { {/* Manga Info Section */}
{/* Cover */} -
- {mangaData.data?.title +
+
+ {mangaData.data?.title +
{/* Details */} @@ -359,9 +361,10 @@ const Manga = () => {
)} -

- {mangaData.data?.synopsis} -

+