import React from 'react' import { router, usePage } from '@inertiajs/react' import StudioLayout from '../../Layouts/StudioLayout' import NovaSelect from '../../components/ui/NovaSelect' function formatDate(value) { if (!value) return 'Draft' const date = new Date(value) if (Number.isNaN(date.getTime())) return 'Draft' return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', }) } function statusTone(status) { switch (status) { case 'published': return 'border-emerald-300/20 bg-emerald-400/10 text-emerald-100' case 'scheduled': return 'border-sky-300/20 bg-sky-400/10 text-sky-100' case 'in_review': return 'border-amber-300/20 bg-amber-400/10 text-amber-100' case 'archived': return 'border-white/10 bg-white/[0.05] text-slate-300' default: return 'border-white/10 bg-white/[0.05] text-slate-300' } } function buildPaginationPages(current, last) { if (last <= 1) return [1] if (last <= 7) { return Array.from({ length: last }, (_, index) => index + 1) } const pages = new Set([1, 2, current - 1, current, current + 1, last - 1, last]) const sorted = [...pages] .filter((page) => page >= 1 && page <= last) .sort((left, right) => left - right) const result = [] for (let index = 0; index < sorted.length; index += 1) { if (index > 0 && sorted[index] - sorted[index - 1] > 1) { result.push('ellipsis') } result.push(sorted[index]) } return result } export default function StudioNewsIndex() { const { props } = usePage() const items = Array.isArray(props.listing?.items) ? props.listing.items : [] const filters = props.listing?.filters || {} const meta = props.listing?.meta || {} const currentPage = Number(meta.current_page || 1) const lastPage = Number(meta.last_page || 1) const from = Number(meta.from || 0) const to = Number(meta.to || 0) const paginationPages = buildPaginationPages(currentPage, lastPage) const deleteItem = (item) => { if (!item?.delete_url) return if (!window.confirm(`Move "${item.title}" to trash?`)) return router.delete(item.delete_url, { preserveScroll: true, }) } const updateFilter = (next, resetPage = true) => { const hasExplicitPage = Object.prototype.hasOwnProperty.call(next, 'page') router.get('/studio/news', { ...filters, ...next, page: hasExplicitPage ? next.page : (resetPage ? 1 : currentPage), }, { preserveState: true, preserveScroll: true, }) } return (

Editorial surface

Run a first-party newsroom for launches, tutorials, and community stories.

Pinned stories drive the hero, featured pieces strengthen discovery, and related entity links keep News wired into Groups, releases, collections, and profiles.

New article Taxonomies
Status updateFilter({ status: value, q: filters.q || '', type: filters.type || '', category_id: filters.category_id || '', order: filters.order || '', direction: filters.direction || '' })} placeholder="All statuses" options={(Array.isArray(props.statusOptions) ? props.statusOptions : []).map((option) => ({ value: option.value, label: option.label }))} searchable={false} />
Type updateFilter({ type: value, q: filters.q || '', status: filters.status || '', category_id: filters.category_id || '', order: filters.order || '', direction: filters.direction || '' })} placeholder="All types" options={(Array.isArray(props.typeOptions) ? props.typeOptions : []).map((option) => ({ value: option.value, label: option.label }))} searchable={false} />
Category updateFilter({ category_id: value, q: filters.q || '', status: filters.status || '', type: filters.type || '', order: filters.order || '', direction: filters.direction || '' })} placeholder="All categories" options={(Array.isArray(props.categoryOptions) ? props.categoryOptions : []).map((option) => ({ value: String(option.id), label: option.name }))} searchable={false} />
Order updateFilter({ order: value, q: filters.q || '', status: filters.status || '', type: filters.type || '', category_id: filters.category_id || '', direction: filters.direction || '' })} placeholder="Order by" options={[ { value: 'date', label: 'Date' }, { value: 'title', label: 'Title' }, { value: 'views', label: 'Views' }, ]} searchable={false} />
Direction updateFilter({ direction: value, q: filters.q || '', status: filters.status || '', type: filters.type || '', category_id: filters.category_id || '', order: filters.order || '' })} placeholder="Asc / Desc" options={[ { value: 'desc', label: 'Desc' }, { value: 'asc', label: 'Asc' }, ]} searchable={false} />
Per page updateFilter({ per_page: value })} placeholder="Per page" options={[ { value: '15', label: '15 articles' }, { value: '30', label: '30 articles' }, { value: '50', label: '50 articles' }, ]} searchable={false} />
{Number(meta.total || 0).toLocaleString()} articles
{items.length > 0 ? items.map((item) => (
{item.cover_url ? {item.title} :
}
{item.type_label} {item.editorial_status.replaceAll('_', ' ')} {item.is_pinned ? Pinned : null} {item.is_featured ? Featured : null}

{item.title}

{item.category_name ? {item.category_name} : null} {item.author_name} {formatDate(item.published_at)}
Edit {item.editorial_status === 'published' ? 'View' : 'Preview'}
)) :
No News articles match the current filters.
}
{lastPage > 1 ? (
Showing {from.toLocaleString()}-{to.toLocaleString()} of {Number(meta.total || 0).toLocaleString()} articles
{paginationPages.map((page, index) => ( page === 'ellipsis' ? ( ... ) : ( ) ))} Page {currentPage} of {lastPage}
) : null}
) }