optimizations

This commit is contained in:
2026-03-28 19:15:39 +01:00
parent 0b25d9570a
commit cab4fbd83e
509 changed files with 1016804 additions and 1605 deletions

View File

@@ -0,0 +1,143 @@
import React from 'react'
import { Head, usePage } from '@inertiajs/react'
function MetricCard({ label, value, delta, icon }) {
return (
<div className="rounded-[26px] border border-white/10 bg-white/[0.04] p-5 backdrop-blur-sm">
<div className="flex items-center justify-between gap-3">
<div className="text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">{label}</div>
<i className={`fa-solid ${icon} text-slate-500`} />
</div>
<div className="mt-3 text-3xl font-semibold tracking-[-0.04em] text-white">{Number(value || 0).toLocaleString()}</div>
<div className="mt-2 text-sm text-slate-300">{Number(delta || 0).toLocaleString()} in the selected range</div>
</div>
)
}
function TimelineChart({ timeline }) {
const safeTimeline = Array.isArray(timeline) ? timeline : []
const maxValue = safeTimeline.reduce((largest, item) => Math.max(largest, item.views || 0, item.likes || 0, item.saves || 0), 0) || 1
return (
<section className="rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7">
<div className="flex items-center justify-between gap-3">
<div>
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80">Timeline</p>
<h2 className="mt-2 text-2xl font-semibold text-white">Engagement trend</h2>
</div>
<span className="rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs font-semibold text-slate-300">{safeTimeline.length} days</span>
</div>
{safeTimeline.length ? (
<div className="mt-6 flex h-64 items-end gap-2 overflow-x-auto rounded-[24px] border border-white/10 bg-slate-950/40 px-4 py-5">
{safeTimeline.map((point) => (
<div key={point.date} className="flex min-w-[32px] flex-1 flex-col items-center justify-end gap-2">
<div className="flex h-full w-full items-end gap-1">
<div className="w-1/3 rounded-t-full bg-sky-300/80" style={{ height: `${Math.max(6, ((point.views || 0) / maxValue) * 100)}%` }} title={`Views: ${point.views || 0}`} />
<div className="w-1/3 rounded-t-full bg-emerald-300/75" style={{ height: `${Math.max(6, ((point.likes || 0) / maxValue) * 100)}%` }} title={`Likes: ${point.likes || 0}`} />
<div className="w-1/3 rounded-t-full bg-amber-300/75" style={{ height: `${Math.max(6, ((point.saves || 0) / maxValue) * 100)}%` }} title={`Saves: ${point.saves || 0}`} />
</div>
<div className="text-[10px] font-semibold uppercase tracking-[0.14em] text-slate-500">{String(point.date || '').slice(5)}</div>
</div>
))}
</div>
) : (
<div className="mt-6 rounded-[24px] border border-dashed border-white/12 bg-white/[0.03] px-6 py-12 text-sm text-slate-300">Analytics are enabled, but there are not enough daily snapshots yet to render a timeline.</div>
)}
<div className="mt-4 flex flex-wrap gap-4 text-xs font-semibold uppercase tracking-[0.16em] text-slate-400">
<span className="inline-flex items-center gap-2"><span className="h-2.5 w-2.5 rounded-full bg-sky-300/80" />Views</span>
<span className="inline-flex items-center gap-2"><span className="h-2.5 w-2.5 rounded-full bg-emerald-300/75" />Likes</span>
<span className="inline-flex items-center gap-2"><span className="h-2.5 w-2.5 rounded-full bg-amber-300/75" />Saves</span>
</div>
</section>
)
}
export default function CollectionAnalytics() {
const { props } = usePage()
const collection = props.collection || {}
const analytics = props.analytics || {}
const totals = analytics.totals || {}
const range = analytics.range || {}
const topArtworks = Array.isArray(analytics.top_artworks) ? analytics.top_artworks : []
const seo = props.seo || {}
return (
<>
<Head>
<title>{seo.title || `${collection.title || 'Collection'} Analytics — Skinbase Nova`}</title>
<meta name="description" content={seo.description || 'Collection analytics overview.'} />
{seo.canonical ? <link rel="canonical" href={seo.canonical} /> : null}
<meta name="robots" content={seo.robots || 'noindex,follow'} />
</Head>
<div className="relative min-h-screen overflow-hidden pb-16">
<div aria-hidden="true" className="pointer-events-none absolute inset-x-0 top-0 -z-10 h-[32rem] opacity-95" style={{ background: 'radial-gradient(circle at 14% 14%, rgba(56,189,248,0.18), transparent 26%), radial-gradient(circle at 86% 18%, rgba(16,185,129,0.16), transparent 24%), linear-gradient(180deg, #07101d 0%, #0a1220 42%, #08111f 100%)' }} />
<div aria-hidden="true" className="pointer-events-none absolute inset-0 -z-10 opacity-[0.05]" style={{ backgroundImage: 'url(/gfx/noise.png)', backgroundSize: '180px' }} />
<div className="mx-auto max-w-7xl px-4 pt-8 md:px-6">
<div className="flex flex-wrap items-center gap-3 text-sm text-slate-300">
{props.dashboardUrl ? <a href={props.dashboardUrl} className="inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 transition hover:bg-white/[0.07] hover:text-white"><i className="fa-solid fa-arrow-left fa-fw text-[11px]" />Dashboard</a> : null}
{props.historyUrl ? <a href={props.historyUrl} className="inline-flex items-center gap-2 rounded-full border border-sky-300/20 bg-sky-400/10 px-4 py-2 font-semibold text-sky-100 transition hover:bg-sky-400/15"><i className="fa-solid fa-timeline fa-fw text-[11px]" />History</a> : null}
{collection.manage_url ? <a href={collection.manage_url} className="inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 transition hover:bg-white/[0.07] hover:text-white"><i className="fa-solid fa-pen-to-square fa-fw text-[11px]" />Manage</a> : null}
</div>
<section className="mt-6 rounded-[34px] border border-white/10 bg-white/[0.04] p-6 shadow-[0_30px_90px_rgba(2,6,23,0.28)] backdrop-blur-sm md:p-8">
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80">Performance</p>
<h1 className="mt-3 text-4xl font-semibold tracking-[-0.05em] text-white md:text-5xl">{collection.title || 'Collection analytics'}</h1>
<p className="mt-4 max-w-3xl text-sm leading-relaxed text-slate-300 md:text-[15px]">
Review activity velocity, audience response, and the artworks carrying the most discovery value over the last {range.days || 30} days.
</p>
</section>
<section className="mt-8 grid gap-5 md:grid-cols-2 xl:grid-cols-3">
<MetricCard label="Views" value={totals.views} delta={range.views_delta} icon="fa-eye" />
<MetricCard label="Likes" value={totals.likes} delta={range.likes_delta} icon="fa-heart" />
<MetricCard label="Follows" value={totals.follows} delta={range.follows_delta} icon="fa-bell" />
<MetricCard label="Saves" value={totals.saves} delta={range.saves_delta} icon="fa-bookmark" />
<MetricCard label="Comments" value={totals.comments} delta={range.comments_delta} icon="fa-comments" />
<MetricCard label="Submissions" value={totals.submissions} delta={totals.submissions} icon="fa-inbox" />
</section>
<div className="mt-8 space-y-6">
<TimelineChart timeline={analytics.timeline} />
<section className="rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7">
<div className="flex items-center justify-between gap-3">
<div>
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80">Artworks</p>
<h2 className="mt-2 text-2xl font-semibold text-white">Top artwork drivers</h2>
</div>
<span className="rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs font-semibold text-slate-300">{topArtworks.length}</span>
</div>
{topArtworks.length ? (
<div className="mt-6 grid gap-4 md:grid-cols-2 xl:grid-cols-4">
{topArtworks.map((artwork) => (
<div key={artwork.id} className="overflow-hidden rounded-[24px] border border-white/10 bg-slate-950/40">
<div className="aspect-square bg-slate-950/60">
{artwork.thumb ? <img src={artwork.thumb} alt={artwork.title} className="h-full w-full object-cover" /> : <div className="flex h-full w-full items-center justify-center text-slate-500"><i className="fa-solid fa-image text-3xl" /></div>}
</div>
<div className="space-y-2 p-4">
<div className="truncate text-sm font-semibold text-white">{artwork.title}</div>
<div className="grid grid-cols-2 gap-2 text-xs text-slate-300">
<div className="rounded-2xl border border-white/10 bg-white/[0.04] px-3 py-2">Views: {Number(artwork.views || 0).toLocaleString()}</div>
<div className="rounded-2xl border border-white/10 bg-white/[0.04] px-3 py-2">Favs: {Number(artwork.favourites || 0).toLocaleString()}</div>
<div className="rounded-2xl border border-white/10 bg-white/[0.04] px-3 py-2">Shares: {Number(artwork.shares || 0).toLocaleString()}</div>
<div className="rounded-2xl border border-white/10 bg-white/[0.04] px-3 py-2">Rank: {Number(artwork.ranking_score || 0).toFixed(1)}</div>
</div>
</div>
</div>
))}
</div>
) : (
<div className="mt-6 rounded-[24px] border border-dashed border-white/12 bg-white/[0.03] px-6 py-12 text-sm text-slate-300">Attach or publish more artworks before artwork-level ranking can be surfaced here.</div>
)}
</section>
</div>
</div>
</div>
</>
)
}