Studio: make grid checkbox rectangular and commit table changes

This commit is contained in:
2026-03-01 08:43:48 +01:00
parent 211dc58884
commit e3ca845a6d
89 changed files with 7323 additions and 475 deletions

View File

@@ -0,0 +1,101 @@
import React from 'react'
import StatusBadge from '../Badges/StatusBadge'
import RisingBadge from '../Badges/RisingBadge'
function getStatus(art) {
if (art.deleted_at) return 'archived'
if (!art.is_public) return 'draft'
return 'published'
}
function statItem(icon, value) {
return (
<span className="flex items-center gap-1 text-xs text-slate-400">
<span>{icon}</span>
<span>{typeof value === 'number' ? value.toLocaleString() : value}</span>
</span>
)
}
export default function StudioGridCard({ artwork, selected, onSelect, onAction }) {
const status = getStatus(artwork)
return (
<div
className={`group relative bg-nova-900/60 border rounded-2xl overflow-hidden transition-all duration-300 hover:-translate-y-0.5 hover:shadow-xl hover:shadow-accent/5 ${
selected ? 'border-accent/60 ring-2 ring-accent/20' : 'border-white/10 hover:border-white/20'
}`}
>
{/* Selection checkbox */}
<label className="absolute top-3 left-3 z-10 cursor-pointer">
<input
type="checkbox"
checked={selected}
onChange={() => onSelect(artwork.id)}
className="w-4 h-4 rounded-sm bg-transparent border border-white/20 accent-accent focus:ring-accent/50 cursor-pointer"
/>
</label>
{/* Thumbnail */}
<div className="relative aspect-[4/3] bg-nova-800 overflow-hidden">
<img
src={artwork.thumb_url}
alt={artwork.title}
className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"
loading="lazy"
/>
{/* Hover actions */}
<div className="absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300">
<div className="absolute bottom-3 right-3 flex gap-1.5">
<ActionBtn icon="fa-eye" title="View public" onClick={() => window.open(`/artworks/${artwork.slug}`, '_blank')} />
<ActionBtn icon="fa-pen" title="Edit" onClick={() => onAction('edit', artwork)} />
{status !== 'archived' ? (
<ActionBtn icon="fa-box-archive" title="Archive" onClick={() => onAction('archive', artwork)} />
) : (
<ActionBtn icon="fa-rotate-left" title="Unarchive" onClick={() => onAction('unarchive', artwork)} />
)}
<ActionBtn icon="fa-trash" title="Delete" onClick={() => onAction('delete', artwork)} danger />
</div>
</div>
</div>
{/* Info */}
<div className="p-3 space-y-2">
<h3 className="text-sm font-semibold text-white truncate" title={artwork.title}>
{artwork.title}
</h3>
<div className="flex flex-wrap items-center gap-1.5">
<StatusBadge status={status} />
<RisingBadge heatScore={artwork.heat_score} rankingScore={artwork.ranking_score} />
</div>
<div className="flex flex-wrap items-center gap-3">
{statItem('👁', artwork.views)}
{statItem('❤️', artwork.favourites)}
{statItem('🔗', artwork.shares)}
{statItem('💬', artwork.comments)}
{statItem('⬇', artwork.downloads)}
</div>
</div>
</div>
)
}
function ActionBtn({ icon, title, onClick, danger }) {
return (
<button
onClick={(e) => { e.stopPropagation(); onClick() }}
title={title}
className={`w-8 h-8 rounded-lg flex items-center justify-center text-sm transition-all backdrop-blur-sm ${
danger
? 'bg-red-500/20 text-red-400 hover:bg-red-500/40'
: 'bg-white/10 text-white hover:bg-white/20'
}`}
aria-label={title}
>
<i className={`fa-solid ${icon}`} />
</button>
)
}