import React from 'react' import { Head, usePage } from '@inertiajs/react' import ArtworkGallery from '../../components/artwork/ArtworkGallery' import CollectionCard from '../../components/profile/collections/CollectionCard' import CollectionVisibilityBadge from '../../components/profile/collections/CollectionVisibilityBadge' import CommentForm from '../../components/social/CommentForm' import CommentList from '../../components/social/CommentList' import useWebShare from '../../hooks/useWebShare' function getCsrfToken() { if (typeof document === 'undefined') return '' return document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '' } async function requestJson(url, { method = 'POST', body } = {}) { const response = await fetch(url, { method, credentials: 'same-origin', headers: { Accept: 'application/json', 'Content-Type': 'application/json', 'X-CSRF-TOKEN': getCsrfToken(), 'X-Requested-With': 'XMLHttpRequest', }, body: body ? JSON.stringify(body) : undefined, }) const payload = await response.json().catch(() => ({})) if (!response.ok) { throw new Error(payload?.message || 'Request failed.') } return payload } function TypeBadge({ collection }) { const label = collection?.type === 'editorial' ? 'Editorial' : collection?.type === 'community' ? 'Community' : 'Personal' return {label} } function CollaboratorCard({ member }) { return ( {member?.user?.name
{member?.user?.name || member?.user?.username}
{member?.role} {member?.status === 'pending' ? '• invited' : ''}
) } function SubmissionCard({ submission, onApprove, onReject, onWithdraw, onReport }) { return (
{submission?.artwork?.thumb ? {submission.artwork.title} : null}
{submission?.artwork?.title || 'Artwork submission'}
{submission?.status}

Submitted by @{submission?.user?.username}

{submission?.message ?

{submission.message}

: null}
{submission?.can_review ? : null} {submission?.can_review ? : null} {submission?.can_withdraw ? : null} {submission?.can_report ? : null}
) } function MetaRow({ icon, label, value, compact = false }) { const title = `${label}: ${value}` if (compact) { return (
{value}
) } return (
{label}
{value}
) } function getSpotlightClasses(style) { switch (style) { case 'editorial': return 'border-amber-300/20 bg-[linear-gradient(135deg,rgba(120,53,15,0.45),rgba(2,6,23,0.82))] text-amber-50' case 'seasonal': return 'border-emerald-300/20 bg-[linear-gradient(135deg,rgba(6,78,59,0.5),rgba(2,6,23,0.82))] text-emerald-50' case 'challenge': return 'border-fuchsia-300/20 bg-[linear-gradient(135deg,rgba(112,26,117,0.48),rgba(2,6,23,0.82))] text-fuchsia-50' case 'community': return 'border-sky-300/20 bg-[linear-gradient(135deg,rgba(3,105,161,0.45),rgba(2,6,23,0.82))] text-sky-50' default: return 'border-white/10 bg-[linear-gradient(135deg,rgba(15,23,42,0.9),rgba(30,41,59,0.72))] text-white' } } function EmptyCollectionState({ isOwner, smart = false }) { return (

This collection is still taking shape

{isOwner ? (smart ? 'Adjust the smart rules to broaden the match or publish more artworks that fit this set.' : 'Add artworks to start building the visual rhythm, cover image, and sequence for this showcase.') : (smart ? 'This smart collection does not have visible matches right now.' : 'There are no visible artworks in this collection right now.')}

) } function OwnerCard({ owner, collectionType }) { const label = owner?.is_system ? 'Editorial Owner' : collectionType === 'editorial' ? 'Editorial Curator' : 'Curator' const body = ( <> {owner?.avatar_url ? ( {owner?.name ) : (
)}
{label}
{owner?.name || owner?.username || 'Skinbase Curator'}
{owner?.username ?
@{owner.username}
: null}
) if (owner?.profile_url) { return {body} } return
{body}
} function PageSection({ eyebrow, title, count, children }) { return (

{eyebrow}

{title}

{count !== undefined ? {count} : null}
{children}
) } function EntityLinkCard({ item }) { return (
{item?.image_url ? {item?.title} :
}

{item?.title}

{item?.meta ? {item.meta} : null}
{item?.subtitle ?

{item.subtitle}

: null} {item?.relationship_type ?

{item.relationship_type}

: null}
{item?.description ?

{item.description}

: null}
) } function humanizeToken(value) { return String(value || '') .replaceAll('_', ' ') .replaceAll('-', ' ') .replace(/\b\w/g, (match) => match.toUpperCase()) } function groupEntityLinks(items) { return (Array.isArray(items) ? items : []).reduce((groups, item) => { const key = item?.linked_type || 'other' if (!groups[key]) groups[key] = [] groups[key].push(item) return groups }, {}) } function recommendationReasons(currentCollection, candidate) { const reasons = [] if (candidate?.event_key && currentCollection?.event_key && candidate.event_key === currentCollection.event_key) { reasons.push('Same event context') } if (candidate?.campaign_key && currentCollection?.campaign_key && candidate.campaign_key === currentCollection.campaign_key) { reasons.push('Same campaign') } if (candidate?.theme_token && currentCollection?.theme_token && candidate.theme_token === currentCollection.theme_token) { reasons.push('Shared theme') } if (candidate?.type && currentCollection?.type && candidate.type === currentCollection.type) { reasons.push(`${humanizeToken(candidate.type)} collection`) } if (candidate?.owner?.id && currentCollection?.owner?.id && candidate.owner.id === currentCollection.owner.id) { reasons.push('Same curator') } if (candidate?.trust_tier && currentCollection?.trust_tier && candidate.trust_tier === currentCollection.trust_tier) { reasons.push(`${humanizeToken(candidate.trust_tier)} trust tier`) } return reasons.slice(0, 3) } function ContextSignalCard({ item }) { const wrapperClassName = 'flex h-full flex-col gap-3 rounded-[24px] border border-white/10 bg-white/[0.04] p-5 transition hover:bg-white/[0.07]' const body = ( <>
{item.meta} {item.kicker ? {item.kicker} : null}

{item.title}

{item.subtitle ?

{item.subtitle}

: null}
{item.description ?

{item.description}

: null} ) if (item.url) { return {body} } return
{body}
} export default function CollectionShow() { const { props } = usePage() const { collection: initialCollection, artworks, owner, isOwner, manageUrl, editUrl, analyticsUrl, historyUrl, profileCollectionsUrl, featuredCollectionsUrl, engagement, seo, members: initialMembers, comments: initialComments, submissions: initialSubmissions, entityLinks, relatedCollections, commentsEndpoint, submitEndpoint, submissionArtworkOptions, seriesContext, canSubmit, canComment, reportEndpoint, } = props const [collection, setCollection] = React.useState(initialCollection) const [comments, setComments] = React.useState(initialComments || []) const [submissions, setSubmissions] = React.useState(initialSubmissions || []) const [selectedArtworkId, setSelectedArtworkId] = React.useState(submissionArtworkOptions?.[0]?.id || '') const [state, setState] = React.useState({ liked: Boolean(engagement?.liked), following: Boolean(engagement?.following), saved: Boolean(engagement?.saved), notice: '', busy: false, }) const artworkItems = artworks?.data ?? [] const creatorIds = Array.from(new Set(artworkItems.map((artwork) => artwork?.author?.id).filter(Boolean))) const featuringCreatorsCount = creatorIds.length const showArtworkAuthors = collection?.type !== 'personal' || featuringCreatorsCount > 1 const enabledModules = Array.isArray(collection?.layout_modules) ? collection.layout_modules.filter((module) => module?.enabled !== false) : [] const enabledModuleKeys = new Set(enabledModules.map((module) => module?.key).filter(Boolean)) const showIntroBlock = enabledModuleKeys.size === 0 || enabledModuleKeys.has('intro_block') const metaOwnerName = owner?.name || owner?.username || collection?.owner?.name || 'Skinbase Curator' const metaTitle = seo?.title || `${collection?.title} — Skinbase Nova` const metaDescription = seo?.description || collection?.summary || collection?.description || '' const collectionSchema = seo?.canonical ? { '@context': 'https://schema.org', '@type': 'CollectionPage', name: collection?.title, description: metaDescription, url: seo.canonical, image: seo?.og_image || collection?.cover_image || undefined, isPartOf: { '@type': 'WebSite', name: 'Skinbase Nova', url: typeof window !== 'undefined' ? window.location.origin : undefined, }, author: owner ? { '@type': 'Person', name: metaOwnerName, url: owner.profile_url, } : undefined, keywords: [collection?.type, collection?.mode, collection?.badge_label].filter(Boolean).join(', ') || undefined, mainEntity: { '@type': 'ItemList', numberOfItems: collection?.artworks_count || artworkItems.length || 0, itemListElement: artworkItems.slice(0, 12).map((artwork, index) => ({ '@type': 'ListItem', position: index + 1, url: artwork.url, name: artwork.title, })), }, } : null const [members] = React.useState(initialMembers || []) const spotlightClasses = getSpotlightClasses(collection?.spotlight_style) const groupedEntityLinks = groupEntityLinks(entityLinks) const storyLinks = groupedEntityLinks.story || [] const taxonomyLinks = [...(groupedEntityLinks.category || []), ...(groupedEntityLinks.tag || [])] const contributorLinks = [...(groupedEntityLinks.creator || []), ...(groupedEntityLinks.artwork || [])] const linkedContextSignals = [ ...(groupedEntityLinks.campaign || []).map((item) => ({ meta: item.meta || 'Campaign', kicker: item.relationship_type || 'Linked campaign', title: item.title, subtitle: item.subtitle, description: item.description, url: item.url, })), ...(groupedEntityLinks.event || []).map((item) => ({ meta: item.meta || 'Event', kicker: item.relationship_type || 'Linked event', title: item.title, subtitle: item.subtitle, description: item.description, url: item.url, })), ] const contextSignals = [ collection?.campaign_key ? { meta: 'Campaign', kicker: 'Discover surface', title: collection.campaign_label || humanizeToken(collection.campaign_key), subtitle: collection.campaign_key, description: 'This collection is programmed into a campaign surface and can be explored alongside other campaign-ready sets.', url: `/collections/campaigns/${encodeURIComponent(collection.campaign_key)}`, } : null, collection?.program_key ? { meta: 'Program', kicker: 'Partner context', title: humanizeToken(collection.program_key), subtitle: collection.partner_label || collection.partner_key || 'Collection program', description: 'This collection is attached to a program or partner-ready surface, which affects how it is grouped and surfaced.', url: `/collections/program/${encodeURIComponent(collection.program_key)}`, } : null, (collection?.event_label || collection?.event_key) ? { meta: 'Event', kicker: 'Seasonal context', title: collection.event_label || humanizeToken(collection.event_key), subtitle: collection.season_key ? `Season ${humanizeToken(collection.season_key)}` : 'Event-linked collection', description: 'This collection is tied to an event or seasonal programming window, so related recommendations favor matching event context.', url: null, } : null, collection?.theme_token ? { meta: 'Theme', kicker: 'Visual language', title: humanizeToken(collection.theme_token), subtitle: collection.presentation_style ? `Presentation ${humanizeToken(collection.presentation_style)}` : null, description: 'Theme and presentation signals help similar collections cluster together in discovery and recommendation surfaces.', url: `/collections/search?theme=${encodeURIComponent(collection.theme_token)}`, } : null, collection?.trust_tier ? { meta: 'Quality Tier', kicker: 'Placement signal', title: humanizeToken(collection.trust_tier), subtitle: collection?.quality_score != null ? `Quality score ${Number(collection.quality_score).toFixed(1)}` : null, description: 'Trust tier and quality score shape how aggressively this collection can be used in premium or partner-facing placements.', url: `/collections/search?quality_tier=${encodeURIComponent(collection.trust_tier)}`, } : null, ...linkedContextSignals, ].filter(Boolean).filter((item, index, items) => { const key = `${item.meta}:${item.title}:${item.subtitle || ''}` return items.findIndex((candidate) => `${candidate.meta}:${candidate.title}:${candidate.subtitle || ''}` === key) === index }) const { share } = useWebShare({ onFallback: async ({ url }) => { if (navigator?.clipboard?.writeText) { await navigator.clipboard.writeText(url) setState((current) => ({ ...current, notice: 'Collection link copied.' })) } }, }) async function handleLike() { if (!engagement?.can_interact) { if (engagement?.login_url) window.location.assign(engagement.login_url) return } setState((current) => ({ ...current, busy: true, notice: '' })) try { const payload = await requestJson(state.liked ? engagement.unlike_url : engagement.like_url, { method: state.liked ? 'DELETE' : 'POST', }) setState((current) => ({ ...current, liked: Boolean(payload?.liked), busy: false })) setCollection((current) => ({ ...current, likes_count: payload?.likes_count ?? current.likes_count })) } catch (error) { setState((current) => ({ ...current, busy: false, notice: error.message })) } } async function handleFollow() { if (!engagement?.can_interact) { if (engagement?.login_url) window.location.assign(engagement.login_url) return } setState((current) => ({ ...current, busy: true, notice: '' })) try { const payload = await requestJson(state.following ? engagement.unfollow_url : engagement.follow_url, { method: state.following ? 'DELETE' : 'POST', }) setState((current) => ({ ...current, following: Boolean(payload?.following), busy: false })) setCollection((current) => ({ ...current, followers_count: payload?.followers_count ?? current.followers_count })) } catch (error) { setState((current) => ({ ...current, busy: false, notice: error.message })) } } async function handleShare() { try { const payload = await requestJson(engagement?.share_url, { method: 'POST' }) setCollection((current) => ({ ...current, shares_count: payload?.shares_count ?? current.shares_count })) await share({ title: collection?.title, text: collection?.summary || collection?.description || `Explore ${collection?.title} on Skinbase Nova.`, url: collection?.public_url, }) } catch (error) { setState((current) => ({ ...current, notice: error.message })) } } async function handleSave() { if (!engagement?.save_url) { if (engagement?.login_url) window.location.assign(engagement.login_url) return } setState((current) => ({ ...current, busy: true, notice: '' })) try { const payload = await requestJson(state.saved ? engagement.unsave_url : engagement.save_url, { method: state.saved ? 'DELETE' : 'POST', body: state.saved ? undefined : { context: 'collection_detail', context_meta: { collection_type: collection?.type || null, collection_mode: collection?.mode || null, }, }, }) setState((current) => ({ ...current, saved: Boolean(payload?.saved), busy: false })) setCollection((current) => ({ ...current, saves_count: payload?.saves_count ?? current.saves_count })) } catch (error) { setState((current) => ({ ...current, busy: false, notice: error.message })) } } async function handleCommentSubmit(body) { const payload = await requestJson(commentsEndpoint, { method: 'POST', body: { body }, }) setComments(payload?.comments || []) setCollection((current) => ({ ...current, comments_count: payload?.comments_count ?? current.comments_count })) } async function handleDeleteComment(commentId) { const payload = await requestJson(`${commentsEndpoint}/${commentId}`, { method: 'DELETE' }) setComments(payload?.comments || []) setCollection((current) => ({ ...current, comments_count: payload?.comments_count ?? current.comments_count })) } async function handleSubmitArtwork() { if (!submitEndpoint || !selectedArtworkId) return try { const payload = await requestJson(submitEndpoint, { method: 'POST', body: { artwork_id: selectedArtworkId }, }) setSubmissions(payload?.submissions || []) setState((current) => ({ ...current, notice: 'Artwork submitted for review.' })) } catch (error) { setState((current) => ({ ...current, notice: error.message })) } } async function handleSubmissionAction(submission, action) { const url = action === 'approve' ? `/collections/submissions/${submission.id}/approve` : action === 'reject' ? `/collections/submissions/${submission.id}/reject` : `/collections/submissions/${submission.id}` const payload = await requestJson(url, { method: action === 'withdraw' ? 'DELETE' : 'POST', }) setSubmissions(payload?.submissions || []) if (action === 'approve') { setCollection((current) => ({ ...current, artworks_count: (current?.artworks_count ?? 0) + 1 })) } } async function handleReport(targetType, targetId) { if (!reportEndpoint) { if (engagement?.login_url) window.location.assign(engagement.login_url) return } const reason = window.prompt('Why are you reporting this? (required)') if (!reason || !reason.trim()) return try { await requestJson(reportEndpoint, { method: 'POST', body: { target_type: targetType, target_id: targetId, reason: reason.trim(), }, }) setState((current) => ({ ...current, notice: 'Report submitted. Thank you.' })) } catch (error) { setState((current) => ({ ...current, notice: error.message })) } } function renderModule(module) { if (!module?.key) return null if (module.key === 'intro_block') { return null } if (module.key === 'featured_artworks') { if (!artworkItems.length) return null return (

Start with the standout pieces from this collection before diving into the full sequence.

) } if (module.key === 'editorial_note') { if (collection?.type !== 'editorial') return null return (
{collection?.description || 'A staff-curated collection prepared for premium discovery placement.'}
{(collection?.event_label || collection?.badge_label) ? (
{collection?.event_label ? {collection.event_label} : null} {collection?.badge_label ? {collection.badge_label} : null}
) : null}
) } if (module.key === 'artwork_grid') { return (
{artworkItems.length ? : } {(artworks?.links?.prev || artworks?.links?.next) ? ( ) : null}
) } if (module.key === 'discussion') { if (!collection?.allow_comments) return null return ( {canComment ?
: null}
handleReport('collection_comment', comment.id)} emptyMessage="No comments yet." />
) } if (module.key === 'related_collections') { if (!Array.isArray(relatedCollections) || !relatedCollections.length) return null return (
{relatedCollections.map((item) => (
{recommendationReasons(collection, item).map((reason) => ( {reason} ))}
))}
) } if (module.key === 'collaborators') { return (
{members.length ? members.filter((member) => member?.status === 'active').map((member) => ) :

This collection is curated by a single owner right now.

}
) } if (module.key === 'submissions') { if (!collection?.allow_submissions) return null return ( {canSubmit && submissionArtworkOptions?.length ? (
) : (

Sign in with at least one artwork on your account to submit here.

)}
{submissions.length ? submissions.map((submission) => ( handleSubmissionAction(item, 'approve')} onReject={(item) => handleSubmissionAction(item, 'reject')} onWithdraw={(item) => handleSubmissionAction(item, 'withdraw')} onReport={(item) => handleReport('collection_submission', item.id)} /> )) :

No submissions yet.

}
) } return null } const renderedFullModules = enabledModules.filter((module) => module.slot === 'full').map(renderModule).filter(Boolean) const renderedMainModules = enabledModules.filter((module) => module.slot === 'main').map(renderModule).filter(Boolean) const renderedSidebarModules = enabledModules.filter((module) => module.slot === 'sidebar').map(renderModule).filter(Boolean) return ( <> {metaTitle} {metaDescription ? : null} {seo?.canonical ? : null} {seo?.robots ? : null} {metaDescription ? : null} {seo?.og_image ? : null} {seo?.canonical ? : null} {metaDescription ? : null} {seo?.og_image ? : null} {collectionSchema ? : null}