import React, { useEffect, useMemo, useState } from 'react' import UploadSidebar from '../UploadSidebar' import { getContentTypeValue, getContentTypeVisualKey } from '../../../lib/uploadUtils' /** * Step2Details * * Step 2 of the upload wizard: artwork metadata. * Shows uploaded-asset summary, content type selector, * category/subcategory selectors, tags, description, and rights. */ export default function Step2Details({ headingRef, // Asset summary primaryFile, primaryPreviewUrl, isArchive, fileMetadata, screenshots, // Content type + category contentTypes, metadata, metadataErrors, filteredCategoryTree, allRootCategoryOptions, requiresSubCategory, onContentTypeChange, onRootCategoryChange, onSubCategoryChange, // Sidebar (title / tags / description / rights) suggestedTags, onChangeTitle, onChangeTags, onChangeDescription, onToggleMature, onToggleRights, }) { const [isContentTypeChooserOpen, setIsContentTypeChooserOpen] = useState(() => !metadata.contentType) const [isCategoryChooserOpen, setIsCategoryChooserOpen] = useState(() => !metadata.rootCategoryId) const [isSubCategoryChooserOpen, setIsSubCategoryChooserOpen] = useState(() => !metadata.subCategoryId) const [categorySearch, setCategorySearch] = useState('') const [subCategorySearch, setSubCategorySearch] = useState('') const contentTypeOptions = useMemo( () => (Array.isArray(contentTypes) ? contentTypes : []).map((item) => { const normalizedName = String(item?.name || '').trim().toLowerCase() const normalizedSlug = String(item?.slug || '').trim().toLowerCase() if (normalizedName === 'other' || normalizedSlug === 'other') { return { ...item, name: 'Others', } } return item }), [contentTypes] ) const selectedContentType = useMemo( () => contentTypeOptions.find((item) => String(getContentTypeValue(item)) === String(metadata.contentType || '')) ?? null, [contentTypeOptions, metadata.contentType] ) const selectedRoot = useMemo( () => filteredCategoryTree.find((item) => String(item.id) === String(metadata.rootCategoryId || '')) ?? null, [filteredCategoryTree, metadata.rootCategoryId] ) const subCategories = selectedRoot?.children || [] const selectedSubCategory = useMemo( () => subCategories.find((item) => String(item.id) === String(metadata.subCategoryId || '')) ?? null, [subCategories, metadata.subCategoryId] ) const sortedFilteredCategories = useMemo(() => { const sorted = [...filteredCategoryTree].sort((a, b) => a.name.localeCompare(b.name)) const q = categorySearch.trim().toLowerCase() return q ? sorted.filter((c) => c.name.toLowerCase().includes(q)) : sorted }, [filteredCategoryTree, categorySearch]) const sortedFilteredSubCategories = useMemo(() => { const sorted = [...subCategories].sort((a, b) => a.name.localeCompare(b.name)) const q = subCategorySearch.trim().toLowerCase() return q ? sorted.filter((s) => s.name.toLowerCase().includes(q)) : sorted }, [subCategories, subCategorySearch]) useEffect(() => { if (!metadata.contentType) { setIsContentTypeChooserOpen(true) } }, [metadata.contentType]) useEffect(() => { if (!metadata.rootCategoryId) { setIsCategoryChooserOpen(true) } }, [metadata.rootCategoryId]) useEffect(() => { if (!metadata.subCategoryId) { setIsSubCategoryChooserOpen(true) } }, [metadata.subCategoryId]) return (
{/* Step header */}

Artwork details

Complete required metadata and rights confirmation before publishing.

{/* Uploaded asset summary */}

Uploaded asset

{/* Thumbnail / Archive icon */} {primaryPreviewUrl && !isArchive ? (
Uploaded artwork thumbnail
) : (
)} {/* File metadata */}

{primaryFile?.name || 'Primary file'}

{isArchive ? `Archive · ${screenshots.length} screenshot${screenshots.length !== 1 ? 's' : ''}` : fileMetadata.resolution !== '—' ? `${fileMetadata.resolution} · ${fileMetadata.size}` : fileMetadata.size}

{isArchive ? 'Archive' : 'Image'}

Content type

Choose the main content family first.

Step 2a
{contentTypeOptions.length === 0 && (
No content types are available right now.
)} {selectedContentType && !isContentTypeChooserOpen && (
Selected content type
{selectedContentType.name}
{filteredCategoryTree.length > 0 ? `Continue by choosing one of the ${filteredCategoryTree.length} matching categories below.` : 'This content type does not have categories yet.'}
)} {(!selectedContentType || isContentTypeChooserOpen) && (
{contentTypeOptions.map((ct) => { const typeValue = String(getContentTypeValue(ct)) const isActive = typeValue === String(metadata.contentType || '') const visualKey = getContentTypeVisualKey(ct) const categoryCount = Array.isArray(ct.categories) ? ct.categories.length : 0 return ( ) })}
)} {metadataErrors.contentType &&

{metadataErrors.contentType}

}

Category path

Choose the main branch first, then refine with a subcategory when needed.

Step 2b
{!selectedContentType && (
Select a content type first

Once you choose the content type, the matching category tree will appear here.

)} {selectedContentType && (
{selectedContentType.name} contains {filteredCategoryTree.length} top-level {filteredCategoryTree.length === 1 ? 'category' : 'categories'}
{selectedRoot && !isCategoryChooserOpen && (
Selected category
{selectedRoot.name}
{subCategories.length > 0 ? `Next step: choose one of the ${subCategories.length} subcategories below.` : 'This category is complete. No subcategory is required.'}
)} {(!selectedRoot || isCategoryChooserOpen) && (
setCategorySearch(e.target.value)} placeholder="Search categories…" className="w-full rounded-xl border border-white/10 bg-white/[0.04] py-2.5 pl-9 pr-4 text-sm text-white placeholder:text-slate-500 focus:border-purple-400/40 focus:outline-none focus:ring-1 focus:ring-purple-400/30" />
{sortedFilteredCategories.length === 0 && (

No categories match “{categorySearch}”

)}
{sortedFilteredCategories.map((cat) => { const isActive = String(metadata.rootCategoryId || '') === String(cat.id) const childCount = cat.children?.length || 0 return ( ) })}
)} {selectedRoot && subCategories.length > 0 && (
Subcategories

Refine {selectedRoot.name} with one more level.

{subCategories.length}
{!metadata.subCategoryId && requiresSubCategory && (
Subcategory still needs to be selected.
)} {selectedSubCategory && !isSubCategoryChooserOpen && (
Selected subcategory
{selectedSubCategory.name}
Final category path: {selectedRoot.name} / {selectedSubCategory.name}
)} {(!selectedSubCategory || isSubCategoryChooserOpen) && (
setSubCategorySearch(e.target.value)} placeholder="Search subcategories…" className="w-full rounded-xl border border-white/10 bg-white/[0.04] py-2.5 pl-9 pr-4 text-sm text-white placeholder:text-slate-500 focus:border-cyan-400/40 focus:outline-none focus:ring-1 focus:ring-cyan-400/30" />
{sortedFilteredSubCategories.length === 0 && (

No subcategories match “{subCategorySearch}”

)}
{sortedFilteredSubCategories.map((sub) => { const isActive = String(metadata.subCategoryId || '') === String(sub.id) return ( ) })}
)}
)} {selectedRoot && subCategories.length === 0 && (
{selectedRoot.name} does not have subcategories. Selecting it is enough.
)}
)} {metadataErrors.category &&

{metadataErrors.category}

}
{/* Title, tags, description, rights */}
) }