import React, { useEffect, useMemo, useState } from 'react' import ArtworkGallery from '../../artwork/ArtworkGallery' function slugify(value) { return String(value ?? '') .toLowerCase() .replace(/[^a-z0-9]+/g, '-') .replace(/^-|-$/g, '') } function formatNumber(value) { return Number(value ?? 0).toLocaleString() } function sortByPublishedAt(items) { return [...items].sort((left, right) => { const leftTime = left?.published_at ? new Date(left.published_at).getTime() : 0 const rightTime = right?.published_at ? new Date(right.published_at).getTime() : 0 return rightTime - leftTime }) } function isWallpaperArtwork(item) { const contentType = String(item?.content_type_slug || item?.content_type || '').toLowerCase() const category = String(item?.category_slug || item?.category || '').toLowerCase() return contentType.includes('wallpaper') || category.includes('wallpaper') } function useArtworkPreview(username, sort) { const [items, setItems] = useState([]) useEffect(() => { let active = true async function load() { try { const response = await fetch(`/api/profile/${encodeURIComponent(username)}/artworks?sort=${encodeURIComponent(sort)}`, { headers: { Accept: 'application/json' }, }) if (!response.ok) return const data = await response.json() if (active) { setItems(Array.isArray(data?.data) ? data.data : []) } } catch (_) {} } load() return () => { active = false } }, [sort, username]) return items } function SectionHeader({ eyebrow, title, description, action }) { return (
{eyebrow}
{description}
: null}A standout first impression for the artwork landing page, built to pull attention before visitors move into trending picks and the full archive.
Featured
These picks create a cleaner visual entry point and give the artwork page more personality than a simple list of thumbnails.
Full archive
The curated sections above are a friendlier starting point. The full gallery has the infinite-scroll archive with everything published by @{username}.