import React, { useState, useCallback, useEffect, useRef } from 'react' import { usePage } from '@inertiajs/react' import StudioLayout from '../../Layouts/StudioLayout' import StudioToolbar from '../../Components/Studio/StudioToolbar' import StudioFilters from '../../Components/Studio/StudioFilters' import StudioGridCard from '../../Components/Studio/StudioGridCard' import StudioTable from '../../Components/Studio/StudioTable' import BulkActionsBar from '../../Components/Studio/BulkActionsBar' import BulkTagModal from '../../Components/Studio/BulkTagModal' import BulkCategoryModal from '../../Components/Studio/BulkCategoryModal' import ConfirmDangerModal from '../../Components/Studio/ConfirmDangerModal' const VIEW_MODE_KEY = 'studio_view_mode' function getCsrfToken() { return document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '' } export default function StudioArtworks() { const { props } = usePage() const { categories } = props // State const [viewMode, setViewMode] = useState(() => localStorage.getItem(VIEW_MODE_KEY) || 'grid') const [artworks, setArtworks] = useState([]) const [meta, setMeta] = useState({ current_page: 1, last_page: 1, per_page: 24, total: 0 }) const [loading, setLoading] = useState(true) const [search, setSearch] = useState('') const [sort, setSort] = useState('created_at:desc') const [filtersOpen, setFiltersOpen] = useState(false) const [filters, setFilters] = useState({ status: '', category: '', performance: '', date_from: '', date_to: '', tags: [] }) const [selectedIds, setSelectedIds] = useState([]) const [deleteModal, setDeleteModal] = useState({ open: false, ids: [] }) const [tagModal, setTagModal] = useState({ open: false, mode: 'add' }) const [categoryModal, setCategoryModal] = useState({ open: false }) const searchTimer = useRef(null) const perPage = viewMode === 'list' ? 50 : 24 // Fetch artworks from API const fetchArtworks = useCallback(async (page = 1) => { setLoading(true) try { const params = new URLSearchParams() params.set('page', page) params.set('per_page', perPage) params.set('sort', sort) if (search) params.set('q', search) if (filters.status) params.set('status', filters.status) if (filters.category) params.set('category', filters.category) if (filters.performance) params.set('performance', filters.performance) if (filters.date_from) params.set('date_from', filters.date_from) if (filters.date_to) params.set('date_to', filters.date_to) const res = await fetch(`/api/studio/artworks?${params.toString()}`, { headers: { 'Accept': 'application/json', 'X-CSRF-TOKEN': getCsrfToken() }, credentials: 'same-origin', }) const data = await res.json() setArtworks(data.data || []) setMeta(data.meta || meta) } catch (err) { console.error('Failed to fetch artworks:', err) } finally { setLoading(false) } }, [search, sort, filters, perPage]) // Debounced search useEffect(() => { clearTimeout(searchTimer.current) searchTimer.current = setTimeout(() => fetchArtworks(1), 300) return () => clearTimeout(searchTimer.current) }, [fetchArtworks]) // Persist view mode const handleViewModeChange = (mode) => { setViewMode(mode) localStorage.setItem(VIEW_MODE_KEY, mode) } // Selection const toggleSelect = (id) => { setSelectedIds((prev) => prev.includes(id) ? prev.filter((i) => i !== id) : [...prev, id]) } const selectAll = () => { const allIds = artworks.map((a) => a.id) const allSelected = allIds.every((id) => selectedIds.includes(id)) setSelectedIds(allSelected ? [] : allIds) } const clearSelection = () => setSelectedIds([]) // Actions const handleAction = async (action, artwork) => { if (action === 'edit') { window.location.href = `/studio/artworks/${artwork.id}/edit` return } if (action === 'delete') { setDeleteModal({ open: true, ids: [artwork.id] }) return } // Toggle actions try { await fetch(`/api/studio/artworks/${artwork.id}/toggle`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'X-CSRF-TOKEN': getCsrfToken() }, credentials: 'same-origin', body: JSON.stringify({ action }), }) fetchArtworks(meta.current_page) } catch (err) { console.error('Action failed:', err) } } // Bulk action execution const executeBulk = async (action) => { if (action === 'delete') { setDeleteModal({ open: true, ids: [...selectedIds] }) return } if (action === 'add_tags') { setTagModal({ open: true, mode: 'add' }); return } if (action === 'remove_tags') { setTagModal({ open: true, mode: 'remove' }); return } if (action === 'change_category') { setCategoryModal({ open: true }); return } try { await fetch('/api/studio/artworks/bulk', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'X-CSRF-TOKEN': getCsrfToken() }, credentials: 'same-origin', body: JSON.stringify({ action, artwork_ids: selectedIds, params: {} }), }) clearSelection() fetchArtworks(meta.current_page) } catch (err) { console.error('Bulk action failed:', err) } } // Confirm bulk tag action const confirmBulkTags = async (tagIds) => { const action = tagModal.mode === 'add' ? 'add_tags' : 'remove_tags' setTagModal({ open: false, mode: 'add' }) try { await fetch('/api/studio/artworks/bulk', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'X-CSRF-TOKEN': getCsrfToken() }, credentials: 'same-origin', body: JSON.stringify({ action, artwork_ids: selectedIds, params: { tag_ids: tagIds } }), }) clearSelection() fetchArtworks(meta.current_page) } catch (err) { console.error('Bulk tag action failed:', err) } } // Confirm bulk category change const confirmBulkCategory = async (categoryId) => { setCategoryModal({ open: false }) try { await fetch('/api/studio/artworks/bulk', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'X-CSRF-TOKEN': getCsrfToken() }, credentials: 'same-origin', body: JSON.stringify({ action: 'change_category', artwork_ids: selectedIds, params: { category_id: categoryId } }), }) clearSelection() fetchArtworks(meta.current_page) } catch (err) { console.error('Bulk category action failed:', err) } } // Confirm delete const confirmDelete = async () => { try { await fetch('/api/studio/artworks/bulk', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'X-CSRF-TOKEN': getCsrfToken() }, credentials: 'same-origin', body: JSON.stringify({ action: 'delete', artwork_ids: deleteModal.ids, confirm: 'DELETE' }), }) setDeleteModal({ open: false, ids: [] }) setSelectedIds((prev) => prev.filter((id) => !deleteModal.ids.includes(id))) fetchArtworks(meta.current_page) } catch (err) { console.error('Delete failed:', err) } } return ( {/* Toolbar */} setFiltersOpen(!filtersOpen)} selectedCount={selectedIds.length} />
{/* Filters sidebar (desktop) */}
setFiltersOpen(false)} filters={filters} onFilterChange={setFilters} categories={categories} />
{/* Mobile filter drawer */}
setFiltersOpen(false)} filters={filters} onFilterChange={setFilters} categories={categories} />
{/* Content */}
{/* Loading */} {loading && (
)} {/* Grid view */} {!loading && viewMode === 'grid' && (
{artworks.map((art) => ( ))}
)} {/* List view */} {!loading && viewMode === 'list' && ( )} {/* Empty state */} {!loading && artworks.length === 0 && (

No artworks match your criteria

)} {/* Pagination */} {meta.last_page > 1 && (
{Array.from({ length: meta.last_page }, (_, i) => i + 1) .filter((p) => p === 1 || p === meta.last_page || Math.abs(p - meta.current_page) <= 2) .map((page, idx, arr) => ( {idx > 0 && arr[idx - 1] !== page - 1 && ( )} ))}
)} {/* Total count */} {!loading && meta.total > 0 && (

{meta.total.toLocaleString()} artwork{meta.total !== 1 ? 's' : ''} total

)}
{/* Bulk actions bar */} {/* Delete confirmation modal */} setDeleteModal({ open: false, ids: [] })} onConfirm={confirmDelete} title="Permanently delete artworks?" message={`This will permanently delete ${deleteModal.ids.length} artwork${deleteModal.ids.length !== 1 ? 's' : ''}. This action cannot be undone.`} /> {/* Bulk tag modal */} setTagModal({ open: false, mode: 'add' })} onConfirm={confirmBulkTags} /> {/* Bulk category modal */} setCategoryModal({ open: false })} onConfirm={confirmBulkCategory} /> ) }