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,
publishMode,
scheduledAt,
timezone,
onPublishModeChange,
onScheduleAt,
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 ? (
) : (
)}
{/* 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'}
{/* ── Combined: Content type → Category → Subcategory ─────────────────── */}
{/* Section header */}
Content type & category
Choose the content family, then narrow down to a category and subcategory.
Step 2
{/* ── Content type ── */}
Content type
{contentTypeOptions.length === 0 && (
No content types are available right now.
)}
{selectedContentType && !isContentTypeChooserOpen && (
}.webp`})
{ e.currentTarget.style.display = 'none' }}
/>
Selected
{selectedContentType.name}
)}
{(!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 ── */}
{selectedContentType && (
<>
Category
{filteredCategoryTree.length} available
{selectedRoot && !isCategoryChooserOpen && (
Selected
{selectedRoot.name}
{subCategories.length > 0
? `${subCategories.length} subcategories available`
: 'No subcategory required'}
)}
{(!selectedRoot || isCategoryChooserOpen) && (
{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 (
)
})}
)}
{metadataErrors.category &&
{metadataErrors.category}
}
>
)}
{/* ── Subcategory ── */}
{selectedRoot && subCategories.length > 0 && (
<>
Subcategory
{subCategories.length} available
{!metadata.subCategoryId && requiresSubCategory && (
Subcategory still needs to be selected.
)}
{selectedSubCategory && !isSubCategoryChooserOpen && (
Selected
{selectedSubCategory.name}
Path: {selectedRoot.name} / {selectedSubCategory.name}
)}
{(!selectedSubCategory || isSubCategoryChooserOpen) && (
{sortedFilteredSubCategories.length === 0 && (
No subcategories match “{subCategorySearch}”
)}
{sortedFilteredSubCategories.map((sub) => {
const isActive = String(metadata.subCategoryId || '') === String(sub.id)
return (
)
})}
)}
>
)}
{selectedRoot && subCategories.length === 0 && selectedRoot && (
{selectedRoot.name} has no subcategories — selecting it is enough.
)}
{/* Title, tags, description, rights */}
)
}