216 lines
7.3 KiB
TypeScript
216 lines
7.3 KiB
TypeScript
import { useMemo, useState } from "react";
|
|
import { Link, useNavigate, useParams, useSearchParams } from "react-router";
|
|
import { ArrowLeft } from "lucide-react";
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { CollectionTemplateGallery } from "@/components/CollectionTemplateGallery";
|
|
import { useAdminLibraries } from "@/hooks/queries/admin/libraries";
|
|
import { useAdminCollections } from "@/hooks/queries/admin/collections";
|
|
import { useDocumentTitle } from "@/hooks/useDocumentTitle";
|
|
|
|
import {
|
|
buildAdminCollectionsReturnPath,
|
|
CollectionEditForm,
|
|
CollectionForm,
|
|
MDBListImportForm,
|
|
SourceTypeSelector,
|
|
TMDBPresetForm,
|
|
TraktPresetForm,
|
|
type CollectionSourceType,
|
|
} from "./adminCollectionsShared";
|
|
import SmartCollectionWizard from "./SmartCollectionWizard";
|
|
|
|
function inferCollectionSourceType(collectionType?: string): CollectionSourceType {
|
|
if (collectionType === "mdblist") return "mdblist";
|
|
if (collectionType === "tmdb") return "tmdb";
|
|
if (collectionType === "trakt") return "trakt";
|
|
return "manual";
|
|
}
|
|
|
|
function isImportedAdminCollectionType(collectionType?: string): boolean {
|
|
return collectionType === "mdblist" || collectionType === "tmdb" || collectionType === "trakt";
|
|
}
|
|
|
|
export default function AdminCollectionEditor() {
|
|
const navigate = useNavigate();
|
|
const { id } = useParams<{ id: string }>();
|
|
const [searchParams] = useSearchParams();
|
|
const initialLibraryId = Number(searchParams.get("libraryId")) || null;
|
|
const returnPath = buildAdminCollectionsReturnPath(initialLibraryId);
|
|
const isCreate = !id;
|
|
const { data: libraries = [] } = useAdminLibraries();
|
|
const { data: collections = [], isLoading } = useAdminCollections();
|
|
const collection = useMemo(
|
|
() => collections.find((entry) => entry.id === id) ?? null,
|
|
[collections, id],
|
|
);
|
|
const [sourceType, setSourceType] = useState<CollectionSourceType | null>(null);
|
|
const [galleryOpen, setGalleryOpen] = useState(false);
|
|
|
|
const activeSourceType = collection
|
|
? inferCollectionSourceType(collection.collection_type)
|
|
: sourceType;
|
|
|
|
const sourceTypeTitles: Record<CollectionSourceType, string> = {
|
|
manual: "New Manual Collection",
|
|
mdblist: "Import MDBList Collection",
|
|
tmdb: "Import TMDB Collection",
|
|
trakt: "Import Trakt Collection",
|
|
};
|
|
const title = collection
|
|
? `Edit ${collection.title}`
|
|
: (activeSourceType && sourceTypeTitles[activeSourceType]) || "Add Collection";
|
|
|
|
const description = collection
|
|
? "Collections now open in a dedicated workspace so rules, artwork, and preview can stay visible."
|
|
: activeSourceType === null
|
|
? "Choose how this collection should be created."
|
|
: "Build the collection in a full-page editor instead of a cramped dialog.";
|
|
|
|
useDocumentTitle(title);
|
|
|
|
if (isLoading && libraries.length === 0) {
|
|
return <div className="page-shell py-8">Loading collection editor...</div>;
|
|
}
|
|
|
|
if (!isCreate && !collection && !isLoading) {
|
|
return (
|
|
<div className="page-shell space-y-4 py-4 sm:py-6">
|
|
<Button asChild variant="ghost" className="w-fit px-0">
|
|
<Link to={returnPath}>
|
|
<ArrowLeft className="mr-2 h-4 w-4" />
|
|
Back to Collections
|
|
</Link>
|
|
</Button>
|
|
<Card className="surface-panel rounded-2xl border-0 shadow-none">
|
|
<CardHeader>
|
|
<CardTitle>Collection not found</CardTitle>
|
|
<CardDescription>The selected collection could not be loaded.</CardDescription>
|
|
</CardHeader>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// The wizard owns its own page chrome (back button, title, step indicator).
|
|
// Short-circuit the legacy editor shell so we don't render nested headers.
|
|
const useWizard =
|
|
(collection && collection.collection_type === "smart") ||
|
|
(!collection && activeSourceType === "manual");
|
|
if (useWizard) {
|
|
return (
|
|
<SmartCollectionWizard
|
|
mode="admin"
|
|
collection={collection}
|
|
libraries={libraries}
|
|
initialLibraryId={initialLibraryId}
|
|
onClose={() => navigate(returnPath)}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="page-shell space-y-6 py-4 sm:py-6">
|
|
<div className="page-header gap-5">
|
|
<div className="space-y-3">
|
|
<Button asChild variant="ghost" className="w-fit px-0">
|
|
<Link to={returnPath}>
|
|
<ArrowLeft className="mr-2 h-4 w-4" />
|
|
Back to Collections
|
|
</Link>
|
|
</Button>
|
|
<div>
|
|
<h1 className="page-title text-[clamp(2rem,4vw,3rem)]">{title}</h1>
|
|
<p className="page-subtitle mt-1 text-sm sm:text-base">{description}</p>
|
|
</div>
|
|
</div>
|
|
|
|
{!collection && activeSourceType !== null ? (
|
|
<Button variant="outline" onClick={() => setSourceType(null)}>
|
|
Change Source Type
|
|
</Button>
|
|
) : null}
|
|
</div>
|
|
|
|
{!collection && activeSourceType === null ? (
|
|
<Card className="surface-panel rounded-2xl border-0 shadow-none">
|
|
<CardHeader>
|
|
<CardTitle>Choose a Collection Type</CardTitle>
|
|
<CardDescription>
|
|
Smart/manual collections open the full query builder. Imports keep their
|
|
source-specific setup.
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<SourceTypeSelector
|
|
showTemplates
|
|
onSelect={(type) => {
|
|
if (type === "templates") {
|
|
setGalleryOpen(true);
|
|
} else {
|
|
setSourceType(type);
|
|
}
|
|
}}
|
|
/>
|
|
</CardContent>
|
|
</Card>
|
|
) : null}
|
|
|
|
<CollectionTemplateGallery
|
|
open={galleryOpen}
|
|
onOpenChange={setGalleryOpen}
|
|
libraries={libraries}
|
|
initialLibraryId={initialLibraryId}
|
|
onCreated={() => {
|
|
if (isCreate) navigate(returnPath);
|
|
}}
|
|
/>
|
|
|
|
{collection ? (
|
|
// Smart admin collections route to the wizard above; here we only see
|
|
// imported (mdblist/tmdb/trakt) or legacy manual collections.
|
|
isImportedAdminCollectionType(collection.collection_type) ? (
|
|
<CollectionEditForm
|
|
libraries={libraries}
|
|
collection={collection}
|
|
initialLibraryId={initialLibraryId}
|
|
onClose={() => navigate(returnPath)}
|
|
/>
|
|
) : (
|
|
<CollectionForm
|
|
libraries={libraries}
|
|
collection={collection}
|
|
initialLibraryId={initialLibraryId}
|
|
onClose={() => navigate(returnPath)}
|
|
/>
|
|
)
|
|
) : null}
|
|
|
|
{!collection && activeSourceType === "mdblist" ? (
|
|
<MDBListImportForm
|
|
libraries={libraries}
|
|
initialLibraryId={initialLibraryId}
|
|
onClose={() => navigate(returnPath)}
|
|
/>
|
|
) : null}
|
|
|
|
{!collection && activeSourceType === "tmdb" ? (
|
|
<TMDBPresetForm
|
|
libraries={libraries}
|
|
initialLibraryId={initialLibraryId}
|
|
onClose={() => navigate(returnPath)}
|
|
/>
|
|
) : null}
|
|
|
|
{!collection && activeSourceType === "trakt" ? (
|
|
<TraktPresetForm
|
|
libraries={libraries}
|
|
initialLibraryId={initialLibraryId}
|
|
onClose={() => navigate(returnPath)}
|
|
/>
|
|
) : null}
|
|
</div>
|
|
);
|
|
}
|