deckhearth/components/AdminCatalogView.js

154 lines
5.1 KiB
JavaScript
Raw Normal View History

import { useState, useEffect } from 'react';
import { GlassSurface, Button, Input, SearchBar, StatCard, Modal } from './ui';
import AdminCatalogFilters from './AdminCatalogFilters';
import AdminCatalogTable from './AdminCatalogTable';
import AdminCatalogPagination from './AdminCatalogPagination';
export default function AdminCatalogView(props) {
const {
cards,
loading,
loadingMore,
searchQuery,
selectedGame,
selectedRarity,
selectedSet,
minPrice,
maxPrice,
verifiedOnly,
sortColumn,
sortDirection,
pagination,
filters,
hasMore,
error,
handleSearchChange,
handleGameChange,
handleRarityChange,
handleSetChange,
handleMinPriceChange,
handleMaxPriceChange,
handleVerifiedChange,
handleSort,
handleLoadMore,
handleRefresh,
handleLimitChange
} = props;
const [showFilters, setShowFilters] = useState(true);
const cardCount = cards.length;
const totalCount = pagination.total;
useEffect(() => {
const handleScroll = () => {
if (
window.innerHeight + document.documentElement.scrollTop >=
document.documentElement.offsetHeight - 1000
) {
handleLoadMore();
}
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, [handleLoadMore, hasMore, loadingMore]);
return (
<div className="min-h-screen" style={{ background: 'var(--bg-primary)' }}>
{/* Header */}
<div className="px-4 sm:px-6 lg:px-8 pt-6 pb-4">
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<div>
<h1 className="text-2xl sm:text-3xl font-bold mb-2" style={{ color: 'var(--text-primary)' }}>
Card Catalog
</h1>
<p className="text-base sm:text-lg" style={{ color: 'var(--text-secondary)' }}>
Admin view: {totalCount.toLocaleString()} cards total &middot; showing {cardCount.toLocaleString()}
</p>
</div>
<div className="flex flex-wrap gap-3">
<Button
variant="secondary"
size="sm"
onClick={handleRefresh}
disabled={loading}
leadingIcon={
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
}
>
Refresh
</Button>
<Button
variant="ghost"
size="sm"
onClick={() => setShowFilters(!showFilters)}
leadingIcon={
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
</svg>
}
>
{showFilters ? 'Hide Filters' : 'Show Filters'}
</Button>
</div>
</div>
{/* Filters Panel */}
{showFilters && (
<AdminCatalogFilters
searchQuery={searchQuery}
selectedGame={selectedGame}
selectedRarity={selectedRarity}
selectedSet={selectedSet}
minPrice={minPrice}
maxPrice={maxPrice}
verifiedOnly={verifiedOnly}
filters={filters}
onSearchChange={handleSearchChange}
onGameChange={handleGameChange}
onRarityChange={handleRarityChange}
onSetChange={handleSetChange}
onMinPriceChange={handleMinPriceChange}
onMaxPriceChange={handleMaxPriceChange}
onVerifiedChange={handleVerifiedChange}
loading={loading}
/>
)}
{/* Error Banner */}
{error && (
<GlassSurface tint="low" rim="ember-subtle" className="mt-4 p-4" style={{ background: 'rgba(220, 38, 38, 0.1)' }}>
<div className="flex items-center justify-between gap-4">
<span style={{ color: '#dc2626' }}>Error: {error}</span>
<Button variant="ghost" size="sm" onClick={handleRefresh}>Retry</Button>
</div>
</GlassSurface>
)}
</div>
{/* Table */}
<div className="px-4 sm:px-6 lg:px-8 pb-8">
<AdminCatalogTable
cards={cards}
loading={loading || loadingMore}
sortColumn={sortColumn}
sortDirection={sortDirection}
onSort={handleSort}
hasMore={hasMore}
loadingMore={loadingMore}
/>
{/* Pagination */}
<AdminCatalogPagination
pagination={pagination}
hasMore={hasMore}
loadingMore={loadingMore}
onLoadMore={handleLoadMore}
onLimitChange={handleLimitChange}
/>
</div>
</div>
);
}