import { useEffect, useState } from "react";
import type { Collection, CreateCollectionRequest, UpdateCollectionRequest } from "@/api/types";
import { useProfiles } from "@/hooks/queries/profiles";
import { useUserLibraries } from "@/hooks/queries/libraries";
import { useCurrentProfile } from "@/hooks/useCurrentProfile";
import {
useCreateCollection,
useDeleteUserCollectionImage,
useUpdateCollection,
} from "@/hooks/queries/collections";
import { buildUserCollectionCatalogHref as buildCatalogHrefForUserCollection } from "@/pages/catalogSearchParams";
import {
collectionMediaFilterLabel,
collectionWatchFilterLabel,
queryDefinitionToDisplayFilters,
} from "@/lib/collectionDisplayFilters";
import { CollectionLibraryPicker } from "@/pages/adminCollectionsShared";
import CollectionBuilder, {
createCollectionBuilderValue,
type CollectionBuilderValue,
} from "@/components/collections/CollectionBuilder";
import { ImageUploadField } from "@/components/ImageUploadField";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
export function buildUserCollectionEditorPath(id: "new" | string) {
return id === "new" ? "/collections/new" : `/collections/${id}/edit`;
}
export function buildUserCollectionCatalogHref(id: string, title?: string) {
return buildCatalogHrefForUserCollection(id, title);
}
// Imported types (mdblist/tmdb/trakt) collapse to "manual" in the builder
// since the sync service owns their items; the original type stays in the DB.
function builderCollectionType(raw: Collection["collection_type"] | undefined): "manual" | "smart" {
return raw === "smart" ? "smart" : "manual";
}
export function toUserCollectionBuilderValue(
collection: Collection | null,
): CollectionBuilderValue {
return createCollectionBuilderValue({
title: collection?.name ?? "",
collection_type: builderCollectionType(collection?.collection_type ?? "smart"),
query_definition: collection?.query_definition,
sort_config: collection?.sort_config ?? {},
access: {
is_shared: collection?.is_shared ?? false,
allowed_profile_ids: collection?.allowed_profile_ids ?? [],
},
include_in_server_collections: collection?.include_in_server_collections ?? false,
display_query_definition: collection?.display_query_definition,
});
}
export function toCreateCollectionBody(value: CollectionBuilderValue): CreateCollectionRequest {
const body: CreateCollectionRequest = {
name: value.title,
collection_type: value.collection_type,
is_shared: value.access.is_shared,
allowed_profile_ids: value.access.allowed_profile_ids,
query_definition: value.collection_type === "smart" ? value.query_definition : undefined,
sort_config: value.collection_type === "smart" ? value.sort_config : undefined,
include_in_server_collections: value.include_in_server_collections,
};
if (value.collection_type === "manual") {
body.display_query_definition = value.display_query_definition;
}
return body;
}
export function toUpdateCollectionBody(value: CollectionBuilderValue): UpdateCollectionRequest {
const body: UpdateCollectionRequest = {
name: value.title,
is_shared: value.access.is_shared,
allowed_profile_ids: value.access.allowed_profile_ids,
query_definition: value.collection_type === "smart" ? value.query_definition : undefined,
sort_config: value.collection_type === "smart" ? value.sort_config : undefined,
include_in_server_collections: value.include_in_server_collections,
};
if (value.collection_type === "manual") {
body.display_query_definition = value.display_query_definition;
}
return body;
}
export function isCollectionReadOnly(
collection: Collection | null,
currentProfileId?: string | null,
): boolean {
if (!collection || !currentProfileId) {
return false;
}
return collection.creator_profile_id !== currentProfileId;
}
function UserCollectionSummary({
draft,
collection,
libraries,
}: {
draft: CollectionBuilderValue;
collection: Collection | null;
libraries: Array<{ id: number; name: string }>;
}) {
const profileSummary =
draft.access.allowed_profile_ids.length > 0
? `${draft.access.allowed_profile_ids.length} selected`
: "All profiles";
const { watch: displayWatch, media: displayMedia } = queryDefinitionToDisplayFilters(
draft.display_query_definition,
);
const selectedLibraryIDs = draft.query_definition.library_ids;
const librarySummary =
selectedLibraryIDs.length === 0
? "All libraries"
: libraries
.filter((lib) => selectedLibraryIDs.includes(lib.id))
.map((lib) => lib.name)
.join(", ") || `${selectedLibraryIDs.length} selected`;
return (
Limit this collection to specific libraries. Leave empty to span every library you can see.
Upload a custom poster or paste an image URL. Leave empty to fall back to the default card.