Files
SkinbaseNova/resources/js/Pages/Home/HomeCreators.jsx
Gregor Klevze eee7df1f8c feat: artwork page carousels, recommendations, avatars & fixes
- Infinite loop carousels for Similar Artworks & Trending rails
- Mouse wheel horizontal scrolling on both carousels
- Author avatar shown on hover in RailCard (similar + trending)
- Removed "View" badge from RailCard hover overlay
- Added `id` to Meilisearch filterable attributes
- Auto-prepend Scout prefix in meilisearch:configure-index command
- Added author name + avatar to Similar Artworks API response
- Added avatar_url to ArtworkListResource author object
- Added direct /art/{id}/{slug} URL to ArtworkListResource
- Fixed race condition: Similar Artworks no longer briefly shows trending items
- Fixed user_profiles eager load (user_id primary key, not id)
- Bumped /api/art/{id}/similar rate limit to 300/min
- Removed decorative heart icons from tag pills
- Moved ReactionBar under artwork description
2026-02-28 14:05:39 +01:00

73 lines
2.7 KiB
JavaScript

import React from 'react'
const AVATAR_FALLBACK = 'https://files.skinbase.org/default/avatar_default.webp'
function CreatorCard({ creator }) {
return (
<article className="group relative flex flex-col items-center gap-3 overflow-hidden rounded-xl bg-panel p-5 shadow-sm text-center transition hover:ring-1 hover:ring-nova-500">
{/* Background artwork thumbnail */}
{creator.bg_thumb && (
<>
<img
src={creator.bg_thumb}
alt=""
aria-hidden="true"
className="pointer-events-none absolute inset-0 h-full w-full object-cover opacity-50 transition duration-500 group-hover:opacity-20 group-hover:scale-105"
loading="lazy"
decoding="async"
/>
<div className="pointer-events-none absolute inset-0 bg-gradient-to-t from-panel via-panel/80 to-panel/60" />
</>
)}
{/* Content */}
<a href={creator.url} className="relative block">
<img
src={creator.avatar}
alt={creator.name}
className="mx-auto h-16 w-16 rounded-full object-cover ring-4 bg-nova-800/80 ring-nova-800"
loading="lazy"
decoding="async"
onError={(e) => { e.currentTarget.src = AVATAR_FALLBACK }}
/>
<h3 className="mt-2 text-sm font-semibold text-white">{creator.name}</h3>
</a>
<div className="relative flex flex-wrap justify-center gap-3 text-xs text-soft">
<span title="Total uploads">📁 {creator.uploads}</span>
{creator.weekly_uploads > 0 && (
<span title="Uploads this week" className="text-accent font-semibold">{creator.weekly_uploads} this week</span>
)}
<span title="Views">👁 {creator.views.toLocaleString()}</span>
{creator.awards > 0 && <span title="Awards">🏆 {creator.awards}</span>}
</div>
<a
href={creator.url}
className="relative mt-1 rounded-lg bg-nova-700 px-4 py-1.5 text-xs font-semibold text-white transition hover:bg-nova-600"
>
View Profile
</a>
</article>
)
}
export default function HomeCreators({ creators }) {
if (!Array.isArray(creators) || creators.length === 0) return null
return (
<section className="mt-14 px-4 sm:px-6 lg:px-8">
<div className="mb-5 flex items-center justify-between">
<h2 className="text-xl font-bold text-white">👤 Creator Spotlight</h2>
<a href="/members" className="text-sm text-nova-300 hover:text-white transition">
All creators
</a>
</div>
<div className="grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-6">
{creators.map((c) => (
<CreatorCard key={c.id} creator={c} />
))}
</div>
</section>
)
}