Save workspace changes

This commit is contained in:
2026-04-18 17:02:56 +02:00
parent f02ea9a711
commit 87d60af5a9
4220 changed files with 1388603 additions and 1554 deletions

View File

@@ -7,6 +7,22 @@ function normalizeText(value) {
return String(value || '').trim().toLowerCase()
}
const MEMBER_ROLE_COLORS = {
owner: { badge: 'border-amber-300/25 bg-amber-400/10 text-amber-100', icon: 'fa-crown', iconColor: 'text-amber-300' },
admin: { badge: 'border-sky-300/25 bg-sky-400/10 text-sky-100', icon: 'fa-shield-halved', iconColor: 'text-sky-300' },
editor: { badge: 'border-violet-300/25 bg-violet-400/10 text-violet-100', icon: 'fa-pen-nib', iconColor: 'text-violet-300' },
contributor: { badge: 'border-emerald-300/25 bg-emerald-400/10 text-emerald-100', icon: 'fa-star', iconColor: 'text-emerald-300' },
}
const POST_TYPE_ICONS = {
announcement: { icon: 'fa-bullhorn', bar: 'from-sky-400/80 to-sky-300/30', bg: 'bg-sky-400/10', border: 'border-sky-300/20', text: 'text-sky-200' },
update: { icon: 'fa-rotate', bar: 'from-emerald-400/80 to-emerald-300/30', bg: 'bg-emerald-400/10', border: 'border-emerald-300/20', text: 'text-emerald-200' },
event: { icon: 'fa-calendar-days', bar: 'from-violet-400/80 to-violet-300/30', bg: 'bg-violet-400/10', border: 'border-violet-300/20', text: 'text-violet-200' },
news: { icon: 'fa-newspaper', bar: 'from-amber-400/80 to-amber-300/30', bg: 'bg-amber-400/10', border: 'border-amber-300/20', text: 'text-amber-200' },
discussion: { icon: 'fa-comments', bar: 'from-rose-400/80 to-rose-300/30', bg: 'bg-rose-400/10', border: 'border-rose-300/20', text: 'text-rose-200' },
tutorial: { icon: 'fa-graduation-cap', bar: 'from-teal-400/80 to-teal-300/30', bg: 'bg-teal-400/10', border: 'border-teal-300/20', text: 'text-teal-200' },
}
function formatCompactNumber(value) {
return Number(value ?? 0).toLocaleString()
}
@@ -323,17 +339,37 @@ function GroupHero({
function ArtworkGrid({ artworks, emptyLabel = 'No artworks yet.' }) {
if (!Array.isArray(artworks) || artworks.length === 0) {
return <p className="mt-5 text-sm text-slate-400">{emptyLabel}</p>
return (
<div className="mt-5 flex flex-col items-center gap-3 rounded-[24px] border border-white/8 bg-white/[0.02] py-10 text-center">
<span className="inline-flex h-14 w-14 items-center justify-center rounded-2xl border border-white/10 bg-white/[0.04] text-slate-500">
<i className="fa-solid fa-images text-2xl" />
</span>
<p className="text-sm text-slate-400">{emptyLabel}</p>
</div>
)
}
return (
<div className="mt-5 grid gap-4 sm:grid-cols-2 xl:grid-cols-3">
{artworks.map((artwork) => (
<a key={artwork.id} href={artwork.url} className="overflow-hidden rounded-[24px] border border-white/10 bg-black/20 transition hover:border-white/20">
{artwork.thumb ? <img src={artwork.thumb} alt={artwork.title} className="aspect-[4/3] w-full object-cover" /> : null}
<a key={artwork.id} href={artwork.url} className="group relative overflow-hidden rounded-[24px] border border-white/10 bg-black/20 transition hover:border-sky-300/30 hover:shadow-[0_8px_32px_rgba(56,189,248,0.08)]">
{artwork.thumb ? (
<div className="relative overflow-hidden aspect-[4/3]">
<img src={artwork.thumb} alt={artwork.title} className="h-full w-full object-cover transition duration-300 group-hover:scale-[1.03]" />
<div className="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent opacity-0 transition group-hover:opacity-100" />
</div>
) : (
<div className="flex aspect-[4/3] items-center justify-center bg-white/[0.03] text-slate-500">
<i className="fa-solid fa-image text-3xl" />
</div>
)}
<div className="p-4">
<h3 className="text-base font-semibold text-white">{artwork.title}</h3>
<p className="mt-1 text-sm text-slate-400">{artwork.author}</p>
{artwork.author ? (
<span className="mt-2 inline-flex items-center gap-1.5 rounded-full border border-white/8 bg-white/[0.04] px-2.5 py-1 text-[11px] font-medium text-slate-300">
<i className="fa-solid fa-user-pen fa-fw text-slate-500" />{artwork.author}
</span>
) : null}
</div>
</a>
))}
@@ -343,19 +379,37 @@ function ArtworkGrid({ artworks, emptyLabel = 'No artworks yet.' }) {
function CollectionGrid({ collections, emptyLabel = 'No collections yet.' }) {
if (!Array.isArray(collections) || collections.length === 0) {
return <p className="mt-5 text-sm text-slate-400">{emptyLabel}</p>
return (
<div className="mt-5 flex flex-col items-center gap-3 rounded-[24px] border border-white/8 bg-white/[0.02] py-10 text-center">
<span className="inline-flex h-14 w-14 items-center justify-center rounded-2xl border border-white/10 bg-white/[0.04] text-slate-500">
<i className="fa-solid fa-layer-group text-2xl" />
</span>
<p className="text-sm text-slate-400">{emptyLabel}</p>
</div>
)
}
return (
<div className="mt-5 grid gap-4 md:grid-cols-2">
{collections.map((collection) => (
<a key={collection.id} href={collection.url} className="rounded-[24px] border border-white/10 bg-black/20 p-4 transition hover:border-white/20">
<div className="flex items-start justify-between gap-3">
<div>
<h3 className="text-base font-semibold text-white">{collection.title}</h3>
<p className="mt-2 text-sm text-slate-300">{collection.summary || collection.description_excerpt || 'Collection'}</p>
<a key={collection.id} href={collection.url} className="group relative overflow-hidden rounded-[24px] border border-white/10 bg-black/20 p-5 transition hover:border-sky-300/25 hover:shadow-[0_6px_24px_rgba(56,189,248,0.07)]">
<div className="absolute inset-x-0 top-0 h-[3px] rounded-t-[24px] bg-gradient-to-r from-sky-400/70 via-cyan-300/50 to-transparent" />
<div className="flex items-start gap-3">
<span className="mt-0.5 inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-[14px] border border-sky-300/20 bg-sky-400/10 text-sky-200">
<i className="fa-solid fa-layer-group fa-fw text-sm" />
</span>
<div className="min-w-0 flex-1">
<div className="flex items-start justify-between gap-2">
<h3 className="text-base font-semibold text-white">{collection.title}</h3>
{collection.is_featured ? (
<span className="shrink-0 rounded-full border border-amber-300/20 bg-amber-300/10 px-2.5 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-amber-100">Featured</span>
) : null}
</div>
<p className="mt-1.5 text-sm leading-6 text-slate-300">{collection.summary || collection.description_excerpt || 'Open to explore this collection.'}</p>
</div>
{collection.is_featured ? <span className="rounded-full border border-amber-300/20 bg-amber-300/10 px-2.5 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-amber-100">Featured</span> : null}
</div>
<div className="mt-2 flex justify-end">
<span className="text-xs font-semibold text-sky-300 opacity-0 transition group-hover:opacity-100">Browse <i className="fa-solid fa-arrow-right ml-0.5" /></span>
</div>
</a>
))}
@@ -365,18 +419,31 @@ function CollectionGrid({ collections, emptyLabel = 'No collections yet.' }) {
function CompactCardGrid({ items, emptyLabel, badgeKey = 'status' }) {
if (!Array.isArray(items) || items.length === 0) {
return <p className="mt-5 text-sm text-slate-400">{emptyLabel}</p>
return (
<div className="mt-5 flex flex-col items-center gap-3 rounded-[24px] border border-white/8 bg-white/[0.02] py-10 text-center">
<span className="inline-flex h-14 w-14 items-center justify-center rounded-2xl border border-white/10 bg-white/[0.04] text-slate-500">
<i className="fa-solid fa-folder-open text-2xl" />
</span>
<p className="text-sm text-slate-400">{emptyLabel}</p>
</div>
)
}
return (
<div className="mt-5 grid gap-4 md:grid-cols-2 xl:grid-cols-3">
{items.map((item) => (
<a key={item.id} href={item.url} className="rounded-[24px] border border-white/10 bg-black/20 p-4 transition hover:border-white/20">
<a key={item.id} href={item.url} className="group relative overflow-hidden rounded-[24px] border border-white/10 bg-black/20 p-5 transition hover:border-white/20 hover:shadow-[0_6px_24px_rgba(2,6,23,0.4)]">
<div className="absolute inset-x-0 top-0 h-[3px] rounded-t-[24px] bg-gradient-to-r from-violet-400/60 via-sky-300/40 to-transparent" />
<div className="flex items-center justify-between gap-3">
<h3 className="text-base font-semibold text-white">{item.title}</h3>
{item[badgeKey] ? <span className="rounded-full border border-white/10 bg-white/[0.04] px-2 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-300">{item[badgeKey]}</span> : null}
{item[badgeKey] ? (
<span className="shrink-0 rounded-full border border-white/10 bg-white/[0.06] px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-300">{item[badgeKey]}</span>
) : null}
</div>
<p className="mt-2 text-sm leading-6 text-slate-300">{item.summary || 'Open for more details.'}</p>
<div className="mt-3 flex justify-end">
<span className="text-xs font-semibold text-sky-300 opacity-0 transition group-hover:opacity-100">Open <i className="fa-solid fa-arrow-right ml-0.5" /></span>
</div>
</a>
))}
</div>
@@ -385,22 +452,46 @@ function CompactCardGrid({ items, emptyLabel, badgeKey = 'status' }) {
function ReleaseGrid({ releases, emptyLabel = 'No public releases yet.' }) {
if (!Array.isArray(releases) || releases.length === 0) {
return <p className="mt-5 text-sm text-slate-400">{emptyLabel}</p>
return (
<div className="mt-5 flex flex-col items-center gap-3 rounded-[24px] border border-white/8 bg-white/[0.02] py-10 text-center">
<span className="inline-flex h-14 w-14 items-center justify-center rounded-2xl border border-white/10 bg-white/[0.04] text-slate-500">
<i className="fa-solid fa-rocket text-2xl" />
</span>
<p className="text-sm text-slate-400">{emptyLabel}</p>
</div>
)
}
return (
<div className="mt-5 grid gap-4 md:grid-cols-2 xl:grid-cols-3">
<div className="mt-5 grid gap-5 md:grid-cols-2 xl:grid-cols-3">
{releases.map((release) => (
<a key={release.id} href={release.url} className="overflow-hidden rounded-[24px] border border-white/10 bg-black/20 transition hover:border-white/20">
{release.cover_url ? <img src={release.cover_url} alt={release.title} className="aspect-[4/3] w-full object-cover" /> : <div className="flex aspect-[4/3] items-center justify-center bg-white/[0.03] text-slate-500"><i className="fa-solid fa-rocket text-2xl" /></div>}
<div className="p-4">
<div className="flex flex-wrap items-center gap-2">
<span className="rounded-full border border-white/10 bg-white/[0.04] px-2 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-300">{release.status}</span>
<span className="rounded-full border border-white/10 bg-white/[0.04] px-2 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-300">{release.current_stage}</span>
<a key={release.id} href={release.url} className="group relative overflow-hidden rounded-[24px] border border-white/10 bg-black/20 transition hover:border-sky-300/25 hover:shadow-[0_8px_32px_rgba(56,189,248,0.08)]">
{release.cover_url ? (
<div className="relative overflow-hidden aspect-[4/3]">
<img src={release.cover_url} alt={release.title} className="h-full w-full object-cover transition duration-300 group-hover:scale-[1.03]" />
<div className="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent" />
<div className="absolute bottom-3 left-3 flex flex-wrap gap-1.5">
{release.status ? <span className="rounded-full border border-white/15 bg-black/50 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-white backdrop-blur-sm">{release.status}</span> : null}
{release.current_stage ? <span className="rounded-full border border-sky-300/25 bg-sky-400/20 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-sky-100 backdrop-blur-sm">{release.current_stage}</span> : null}
</div>
</div>
) : (
<div className="relative flex aspect-[4/3] items-center justify-center bg-gradient-to-br from-sky-900/30 to-slate-900/40">
<i className="fa-solid fa-rocket text-3xl text-slate-400" />
<div className="absolute bottom-3 left-3 flex flex-wrap gap-1.5">
{release.status ? <span className="rounded-full border border-white/15 bg-black/50 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-white backdrop-blur-sm">{release.status}</span> : null}
{release.current_stage ? <span className="rounded-full border border-sky-300/25 bg-sky-400/20 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-sky-100 backdrop-blur-sm">{release.current_stage}</span> : null}
</div>
</div>
)}
<div className="p-5">
<h3 className="text-base font-semibold text-white">{release.title}</h3>
<p className="mt-1.5 text-sm leading-6 text-slate-300">{release.summary || 'Release overview and linked artworks.'}</p>
<div className="mt-3 flex items-center gap-4 text-xs text-slate-500">
<span><i className="fa-solid fa-images mr-1.5" />{release.counts?.artworks || 0}</span>
<span><i className="fa-solid fa-users mr-1.5" />{release.counts?.contributors || 0}</span>
<span><i className="fa-solid fa-flag-checkered mr-1.5" />{release.counts?.milestones || 0}</span>
</div>
<h3 className="mt-3 text-base font-semibold text-white">{release.title}</h3>
<p className="mt-2 text-sm leading-6 text-slate-300">{release.summary || 'Release overview and linked artworks.'}</p>
<div className="mt-3 text-xs text-slate-500">{release.counts?.artworks || 0} artworks {release.counts?.contributors || 0} contributors {release.counts?.milestones || 0} milestones</div>
</div>
</a>
))}
@@ -454,23 +545,37 @@ function LeadershipPreview({ leadership }) {
}
return (
<section className="rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<div className="flex items-center justify-between gap-3">
<section className="relative overflow-hidden rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<div className="absolute inset-x-0 top-0 h-[3px] rounded-t-[30px] bg-gradient-to-r from-amber-400/70 via-yellow-300/40 to-transparent" />
<div className="flex items-center gap-3">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-[14px] border border-amber-300/20 bg-amber-400/10 text-amber-200">
<i className="fa-solid fa-crown fa-fw" />
</span>
<div>
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/70">Leadership</p>
<h2 className="mt-2 text-2xl font-semibold text-white">Owner and admins</h2>
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-amber-200/70">Leadership</p>
<h2 className="text-xl font-semibold text-white">Owner and admins</h2>
</div>
</div>
<div className="mt-5 grid gap-3 sm:grid-cols-2">
{leadership.map((member) => (
<a key={member.id} href={member.profile_url || '#'} className="flex items-center gap-3 rounded-[24px] border border-white/10 bg-black/20 px-4 py-4 transition hover:border-white/20">
{member.avatar_url ? <img src={member.avatar_url} alt={member.name || member.username} className="h-12 w-12 rounded-2xl object-cover" /> : <div className="flex h-12 w-12 items-center justify-center rounded-2xl border border-white/10 bg-white/[0.03] text-slate-400"><i className="fa-solid fa-user" /></div>}
<div className="min-w-0">
<div className="truncate font-semibold text-white">{member.name || member.username}</div>
<div className="text-xs uppercase tracking-[0.16em] text-slate-400">{member.role_label || member.role}</div>
</div>
</a>
))}
{leadership.map((member) => {
const roleKey = String(member.role || '').toLowerCase()
const roleStyle = MEMBER_ROLE_COLORS[roleKey] || MEMBER_ROLE_COLORS.contributor
return (
<a key={member.id} href={member.profile_url || '#'} className="group flex items-center gap-3 rounded-[24px] border border-white/10 bg-black/20 px-4 py-4 transition hover:border-white/20 hover:bg-white/[0.04]">
{member.avatar_url
? <img src={member.avatar_url} alt={member.name || member.username} className="h-12 w-12 shrink-0 rounded-2xl object-cover ring-1 ring-white/10 transition group-hover:ring-white/20" />
: <div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-2xl border border-white/10 bg-white/[0.03] text-slate-400"><i className="fa-solid fa-user" /></div>
}
<div className="min-w-0 flex-1">
<div className="truncate font-semibold text-white">{member.name || member.username}</div>
<div className={`mt-1 inline-flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 text-[10px] font-semibold uppercase tracking-[0.14em] ${roleStyle.badge}`}>
<i className={`fa-solid ${roleStyle.icon} fa-fw text-[9px]`} />
{member.role_label || member.role}
</div>
</div>
</a>
)
})}
</div>
</section>
)
@@ -499,22 +604,46 @@ function TrustSignalPanel({ signals }) {
return null
}
const toneClasses = {
sky: 'border-sky-300/20 bg-sky-300/10 text-sky-100',
emerald: 'border-emerald-300/20 bg-emerald-300/10 text-emerald-100',
amber: 'border-amber-300/20 bg-amber-300/10 text-amber-100',
violet: 'border-violet-300/20 bg-violet-300/10 text-violet-100',
const TONE_STYLES = {
sky: { badge: 'border-sky-300/20 bg-sky-300/10 text-sky-100', dot: 'bg-sky-400', bar: 'from-sky-400/70 to-transparent' },
emerald: { badge: 'border-emerald-300/20 bg-emerald-300/10 text-emerald-100', dot: 'bg-emerald-400', bar: 'from-emerald-400/70 to-transparent' },
amber: { badge: 'border-amber-300/20 bg-amber-300/10 text-amber-100', dot: 'bg-amber-400', bar: 'from-amber-400/70 to-transparent' },
violet: { badge: 'border-violet-300/20 bg-violet-300/10 text-violet-100', dot: 'bg-violet-400', bar: 'from-violet-400/70 to-transparent' },
}
return (
<section className="rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/70">Trust signals</p>
<h2 className="mt-2 text-2xl font-semibold text-white">How this group shows up</h2>
<section className="relative overflow-hidden rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<div className="absolute inset-x-0 top-0 h-[3px] rounded-t-[30px] bg-gradient-to-r from-sky-400/70 via-cyan-300/40 to-transparent" />
<div className="flex items-center gap-3">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-[14px] border border-sky-300/20 bg-sky-400/10 text-sky-200">
<i className="fa-solid fa-shield-check fa-fw" />
</span>
<div>
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/70">Trust signals</p>
<h2 className="text-xl font-semibold text-white">How this group shows up</h2>
</div>
</div>
<div className="mt-4 flex flex-wrap gap-2">
{signals.map((signal) => <span key={signal.key} className={`rounded-full border px-3 py-2 text-sm font-semibold ${toneClasses[signal.tone] || 'border-white/10 bg-white/[0.04] text-white'}`}>{signal.label}</span>)}
{signals.map((signal) => {
const ts = TONE_STYLES[signal.tone] || { badge: 'border-white/10 bg-white/[0.04] text-white', dot: 'bg-slate-400' }
return (
<span key={signal.key} className={`inline-flex items-center gap-2 rounded-full border px-3 py-1.5 text-[11px] font-semibold ${ts.badge}`}>
<span className={`h-1.5 w-1.5 rounded-full ${ts.dot}`} />{signal.label}
</span>
)
})}
</div>
<div className="mt-5 space-y-3">
{signals.map((signal) => <div key={`${signal.key}-reason`} className="rounded-2xl border border-white/10 bg-black/20 px-4 py-4"><div className="font-semibold text-white">{signal.label}</div><p className="mt-2 text-sm leading-6 text-slate-400">{signal.reason}</p></div>)}
{signals.map((signal) => {
const ts = TONE_STYLES[signal.tone] || { badge: 'border-white/10 bg-white/[0.02] text-white', bar: 'from-slate-400/40 to-transparent' }
return (
<div key={`${signal.key}-reason`} className="relative overflow-hidden rounded-2xl border border-white/10 bg-black/20 px-4 py-4">
<div className={`absolute inset-x-0 top-0 h-[2px] rounded-t-2xl bg-gradient-to-r ${ts.bar}`} />
<div className={`text-sm font-semibold ${ts.badge.includes('text-') ? ts.badge.split(' ').find((c) => c.startsWith('text-')) : 'text-white'}`}>{signal.label}</div>
<p className="mt-1.5 text-sm leading-6 text-slate-400">{signal.reason}</p>
</div>
)
})}
</div>
</section>
)
@@ -526,17 +655,29 @@ function BadgeShowcase({ badges }) {
}
return (
<section className="rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-amber-100/80">Badges</p>
<h2 className="mt-2 text-2xl font-semibold text-white">Earned group signals</h2>
<section className="relative overflow-hidden rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<div className="absolute inset-x-0 top-0 h-[3px] rounded-t-[30px] bg-gradient-to-r from-amber-400/70 via-yellow-300/40 to-transparent" />
<div className="flex items-center gap-3">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-[14px] border border-amber-300/20 bg-amber-400/10 text-amber-200">
<i className="fa-solid fa-medal fa-fw" />
</span>
<div>
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-amber-100/80">Badges</p>
<h2 className="text-xl font-semibold text-white">Earned group signals</h2>
</div>
</div>
<div className="mt-5 grid gap-3">
{badges.map((badge) => (
<div key={badge.key} className="rounded-2xl border border-white/10 bg-black/20 px-4 py-4">
<div key={badge.key} className="relative overflow-hidden rounded-2xl border border-amber-300/10 bg-amber-400/5 px-4 py-4">
<div className="absolute inset-y-0 left-0 w-[3px] rounded-l-2xl bg-gradient-to-b from-amber-400/70 to-amber-300/20" />
<div className="flex items-center justify-between gap-3">
<div className="font-semibold text-white">{badge.label}</div>
<div className="flex items-center gap-2">
<i className="fa-solid fa-certificate text-amber-300/70" />
<div className="font-semibold text-white">{badge.label}</div>
</div>
{badge.awarded_at ? <div className="text-xs text-slate-500">{new Date(badge.awarded_at).toLocaleDateString()}</div> : null}
</div>
<p className="mt-2 text-sm leading-6 text-slate-400">{badge.reason}</p>
<p className="mt-1.5 text-sm leading-6 text-slate-400">{badge.reason}</p>
</div>
))}
</div>
@@ -550,21 +691,46 @@ function ContributorHighlights({ contributors }) {
}
return (
<section className="rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/70">Contributors</p>
<h2 className="mt-2 text-2xl font-semibold text-white">Trusted collaborators</h2>
<section className="relative overflow-hidden rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<div className="absolute inset-x-0 top-0 h-[3px] rounded-t-[30px] bg-gradient-to-r from-emerald-400/70 via-teal-300/40 to-transparent" />
<div className="flex items-center gap-3">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-[14px] border border-emerald-300/20 bg-emerald-400/10 text-emerald-200">
<i className="fa-solid fa-user-star fa-fw" />
</span>
<div>
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-emerald-200/70">Contributors</p>
<h2 className="text-xl font-semibold text-white">Trusted collaborators</h2>
</div>
</div>
<div className="mt-5 space-y-3">
{contributors.map((entry) => (
<a key={entry.user?.id} href={entry.user?.profile_url || '#'} className="flex gap-3 rounded-[24px] border border-white/10 bg-black/20 px-4 py-4 transition hover:border-white/20">
{entry.user?.avatar_url ? <img src={entry.user.avatar_url} alt={entry.user?.name || entry.user?.username} className="h-12 w-12 rounded-2xl object-cover" /> : <div className="flex h-12 w-12 items-center justify-center rounded-2xl border border-white/10 bg-white/[0.03] text-slate-400"><i className="fa-solid fa-user" /></div>}
<a key={entry.user?.id} href={entry.user?.profile_url || '#'} className="group flex gap-3 rounded-[24px] border border-white/10 bg-black/20 px-4 py-4 transition hover:border-emerald-300/20 hover:bg-white/[0.04]">
{entry.user?.avatar_url
? <img src={entry.user.avatar_url} alt={entry.user?.name || entry.user?.username} className="h-12 w-12 shrink-0 rounded-2xl object-cover ring-1 ring-white/10 transition group-hover:ring-emerald-300/20" />
: <div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-2xl border border-white/10 bg-white/[0.03] text-slate-400"><i className="fa-solid fa-user" /></div>
}
<div className="min-w-0 flex-1">
<div className="flex flex-wrap items-center gap-2">
<div className="truncate font-semibold text-white">{entry.user?.name || entry.user?.username}</div>
{entry.trusted_indicator ? <span className="rounded-full border border-emerald-300/20 bg-emerald-300/10 px-2 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-emerald-100">Trusted</span> : null}
{entry.trusted_indicator ? (
<span className="inline-flex items-center gap-1 rounded-full border border-emerald-300/20 bg-emerald-300/10 px-2.5 py-0.5 text-[10px] font-semibold uppercase tracking-[0.16em] text-emerald-100">
<i className="fa-solid fa-circle-check text-[9px]" />Trusted
</span>
) : null}
</div>
{entry.summary ? <p className="mt-1 text-sm text-slate-400">{entry.summary}</p> : null}
<div className="mt-2 text-xs text-slate-500">{entry.counts?.releases || 0} releases {entry.counts?.credited_artworks || 0} artworks {entry.counts?.projects || 0} projects</div>
{Array.isArray(entry.badges) && entry.badges.length > 0 ? <div className="mt-3 flex flex-wrap gap-2">{entry.badges.slice(0, 3).map((badge) => <span key={`${entry.user?.id}-${badge.key}`} className="rounded-full border border-white/10 bg-white/[0.04] px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-300">{badge.label}</span>)}</div> : null}
<div className="mt-2 flex items-center gap-3 text-xs text-slate-500">
<span><i className="fa-solid fa-rocket mr-1" />{entry.counts?.releases || 0}</span>
<span><i className="fa-solid fa-images mr-1" />{entry.counts?.credited_artworks || 0}</span>
<span><i className="fa-solid fa-diagram-project mr-1" />{entry.counts?.projects || 0}</span>
</div>
{Array.isArray(entry.badges) && entry.badges.length > 0 ? (
<div className="mt-2 flex flex-wrap gap-1.5">
{entry.badges.slice(0, 3).map((badge) => (
<span key={`${entry.user?.id}-${badge.key}`} className="rounded-full border border-white/10 bg-white/[0.04] px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-300">{badge.label}</span>
))}
</div>
) : null}
</div>
</a>
))}
@@ -744,58 +910,88 @@ export default function GroupShow() {
{section === 'overview' ? (
<div className="mt-8 grid gap-8">
<section className="rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<section className="relative overflow-hidden rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<div className="absolute inset-x-0 top-0 h-[3px] rounded-t-[30px] bg-gradient-to-r from-amber-400/70 via-yellow-300/40 to-transparent" />
<div className="flex items-center justify-between gap-3">
<div>
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-amber-100/80">Highlights</p>
<h2 className="mt-2 text-2xl font-semibold text-white">Featured artworks</h2>
<div className="flex items-center gap-3">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-[14px] border border-amber-300/20 bg-amber-400/10 text-amber-200">
<i className="fa-solid fa-star fa-fw" />
</span>
<div>
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-amber-200/70">Highlights</p>
<h2 className="text-xl font-semibold text-white">Featured artworks</h2>
</div>
</div>
<a href={`${group.urls?.public}/artworks`} className="text-sm font-semibold text-sky-200">Browse all</a>
<a href={`${group.urls?.public}/artworks`} className="text-sm font-semibold text-sky-200 transition hover:text-sky-100">Browse all</a>
</div>
<ArtworkGrid artworks={featuredArtworks} emptyLabel="No featured artworks yet." />
</section>
<section className="rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<section className="relative overflow-hidden rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<div className="absolute inset-x-0 top-0 h-[3px] rounded-t-[30px] bg-gradient-to-r from-sky-400/70 via-cyan-300/40 to-transparent" />
<div className="flex items-center justify-between gap-3">
<div>
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/70">Latest work</p>
<h2 className="mt-2 text-2xl font-semibold text-white">Latest artworks</h2>
<div className="flex items-center gap-3">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-[14px] border border-sky-300/20 bg-sky-400/10 text-sky-200">
<i className="fa-solid fa-images fa-fw" />
</span>
<div>
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/70">Latest work</p>
<h2 className="text-xl font-semibold text-white">Latest artworks</h2>
</div>
</div>
<a href={`${group.urls?.public}/artworks`} className="text-sm font-semibold text-sky-200">View archive</a>
<a href={`${group.urls?.public}/artworks`} className="text-sm font-semibold text-sky-200 transition hover:text-sky-100">View archive</a>
</div>
<ArtworkGrid artworks={artworks.slice(0, 6)} emptyLabel="No published artworks yet." />
</section>
<section className="rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<section className="relative overflow-hidden rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<div className="absolute inset-x-0 top-0 h-[3px] rounded-t-[30px] bg-gradient-to-r from-violet-400/70 via-sky-300/40 to-transparent" />
<div className="flex items-center justify-between gap-3">
<div>
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-amber-100/80">Pipeline</p>
<h2 className="mt-2 text-2xl font-semibold text-white">Recent releases</h2>
<div className="flex items-center gap-3">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-[14px] border border-violet-300/20 bg-violet-400/10 text-violet-200">
<i className="fa-solid fa-rocket fa-fw" />
</span>
<div>
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-violet-200/70">Pipeline</p>
<h2 className="text-xl font-semibold text-white">Recent releases</h2>
</div>
</div>
<a href={`${group.urls?.public}/releases`} className="text-sm font-semibold text-sky-200">View releases</a>
<a href={`${group.urls?.public}/releases`} className="text-sm font-semibold text-sky-200 transition hover:text-sky-100">View releases</a>
</div>
<ReleaseGrid releases={releases.slice(0, 3)} emptyLabel="No public releases yet." />
</section>
<div className="grid gap-8 xl:grid-cols-[minmax(0,1.2fr)_minmax(0,0.8fr)]">
<section className="rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<section className="relative overflow-hidden rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<div className="absolute inset-x-0 top-0 h-[3px] rounded-t-[30px] bg-gradient-to-r from-sky-400/70 via-cyan-300/40 to-transparent" />
<div className="flex items-center justify-between gap-3">
<div>
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-amber-100/80">Curated</p>
<h2 className="mt-2 text-2xl font-semibold text-white">Featured collections</h2>
<div className="flex items-center gap-3">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-[14px] border border-sky-300/20 bg-sky-400/10 text-sky-200">
<i className="fa-solid fa-layer-group fa-fw" />
</span>
<div>
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/70">Curated</p>
<h2 className="text-xl font-semibold text-white">Featured collections</h2>
</div>
</div>
<a href={`${group.urls?.public}/collections`} className="text-sm font-semibold text-sky-200">View collections</a>
<a href={`${group.urls?.public}/collections`} className="text-sm font-semibold text-sky-200 transition hover:text-sky-100">View collections</a>
</div>
<CollectionGrid collections={featuredCollections.length > 0 ? featuredCollections : collections.slice(0, 2)} emptyLabel="No featured collections yet." />
</section>
<div className="grid gap-8">
{group.pinned_post ? (
<section className="rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-amber-100/80">Pinned post</p>
<h2 className="mt-2 text-2xl font-semibold text-white">{group.pinned_post.title}</h2>
<p className="mt-4 text-sm leading-7 text-slate-300">{group.pinned_post.excerpt || 'Read the latest pinned update from this group.'}</p>
<a href={group.pinned_post.url} className="mt-4 inline-flex rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white">Read post</a>
<section className="relative overflow-hidden rounded-[30px] border border-amber-300/15 bg-amber-400/5 p-6">
<div className="absolute inset-x-0 top-0 h-[3px] rounded-t-[30px] bg-gradient-to-r from-amber-400/80 to-transparent" />
<div className="flex items-center gap-3">
<span className="inline-flex h-9 w-9 items-center justify-center rounded-[14px] border border-amber-300/20 bg-amber-400/10 text-amber-200">
<i className="fa-solid fa-thumbtack fa-fw text-sm" />
</span>
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-amber-100/80">Pinned post</p>
</div>
<h2 className="mt-3 text-xl font-semibold text-white">{group.pinned_post.title}</h2>
<p className="mt-3 text-sm leading-7 text-amber-50/80">{group.pinned_post.excerpt || 'Read the latest pinned update from this group.'}</p>
<a href={group.pinned_post.url} className="mt-4 inline-flex items-center gap-2 rounded-2xl border border-amber-300/20 bg-amber-400/10 px-4 py-2 text-sm font-semibold text-amber-100 transition hover:bg-amber-400/15"><i className="fa-solid fa-book-open fa-fw" />Read post</a>
</section>
) : null}
@@ -817,26 +1013,44 @@ export default function GroupShow() {
</section>
) : null}
<section className="rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/70">Resources</p>
<h2 className="mt-2 text-2xl font-semibold text-white">Shared downloads</h2>
<section className="relative overflow-hidden rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<div className="absolute inset-x-0 top-0 h-[3px] rounded-t-[30px] bg-gradient-to-r from-teal-400/70 to-transparent" />
<div className="flex items-center gap-3">
<span className="inline-flex h-9 w-9 items-center justify-center rounded-[14px] border border-teal-300/20 bg-teal-400/10 text-teal-200">
<i className="fa-solid fa-download fa-fw text-sm" />
</span>
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-teal-200/70">Resources</p>
</div>
<h2 className="mt-2 text-xl font-semibold text-white">Shared downloads</h2>
<AssetGrid assets={assets.slice(0, 3)} />
</section>
<section className="rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/70">Public feed</p>
<h2 className="mt-2 text-2xl font-semibold text-white">Recent activity</h2>
<section className="relative overflow-hidden rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<div className="absolute inset-x-0 top-0 h-[3px] rounded-t-[30px] bg-gradient-to-r from-sky-400/70 to-transparent" />
<div className="flex items-center gap-3">
<span className="inline-flex h-9 w-9 items-center justify-center rounded-[14px] border border-sky-300/20 bg-sky-400/10 text-sky-200">
<i className="fa-solid fa-bolt fa-fw text-sm" />
</span>
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/70">Public feed</p>
</div>
<h2 className="mt-2 text-xl font-semibold text-white">Recent activity</h2>
<ActivityFeed items={activity.slice(0, 4)} />
</section>
<section className="rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/70">About</p>
<h2 className="mt-2 text-2xl font-semibold text-white">About {group.name}</h2>
<p className="mt-5 text-sm leading-7 text-slate-300">{group.bio || 'No long-form description yet.'}</p>
<div className="mt-5 flex flex-wrap gap-3 text-xs text-slate-400">
{group.founded_at ? <span>Founded {new Date(group.founded_at).toLocaleDateString()}</span> : null}
{group.type ? <span>{group.type}</span> : null}
{group.website_url ? <a href={group.website_url} className="text-sky-200 underline underline-offset-4">Website</a> : null}
<section className="relative overflow-hidden rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<div className="absolute inset-x-0 top-0 h-[3px] rounded-t-[30px] bg-gradient-to-r from-slate-400/50 to-transparent" />
<div className="flex items-center gap-3">
<span className="inline-flex h-9 w-9 items-center justify-center rounded-[14px] border border-white/10 bg-white/[0.05] text-slate-300">
<i className="fa-solid fa-id-card fa-fw text-sm" />
</span>
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400/80">About</p>
</div>
<h2 className="mt-2 text-xl font-semibold text-white">About {group.name}</h2>
<p className="mt-4 text-sm leading-7 text-slate-300">{group.bio || 'No long-form description yet.'}</p>
<div className="mt-4 flex flex-wrap gap-3 text-xs text-slate-400">
{group.founded_at ? <span className="inline-flex items-center gap-1.5"><i className="fa-solid fa-calendar-days text-slate-500" />Founded {new Date(group.founded_at).toLocaleDateString()}</span> : null}
{group.type ? <span className="inline-flex items-center gap-1.5"><i className="fa-solid fa-tag text-slate-500" />{group.type}</span> : null}
{group.website_url ? <a href={group.website_url} className="inline-flex items-center gap-1.5 text-sky-200 underline underline-offset-4 transition hover:text-sky-100"><i className="fa-solid fa-link" />Website</a> : null}
</div>
</section>
</div>
@@ -845,11 +1059,17 @@ export default function GroupShow() {
) : null}
{section === 'artworks' ? (
<section className="mt-8 rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<section className="mt-8 relative overflow-hidden rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<div className="absolute inset-x-0 top-0 h-[3px] rounded-t-[30px] bg-gradient-to-r from-sky-400/70 via-cyan-300/40 to-transparent" />
<div className="flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between">
<div>
<h2 className="text-2xl font-semibold text-white">Artworks</h2>
<p className="mt-2 text-sm text-slate-400">Filter the group archive by title or contributor credit label, then change the sort order.</p>
<div className="flex items-center gap-3">
<span className="inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-[14px] border border-sky-300/20 bg-sky-400/10 text-sky-200">
<i className="fa-solid fa-images fa-fw" />
</span>
<div>
<h2 className="text-2xl font-semibold text-white">Artworks</h2>
<p className="mt-1 text-sm text-slate-400">Filter the group archive by title or contributor credit label, then change the sort order.</p>
</div>
</div>
<div className="grid gap-3 sm:grid-cols-2">
<label className="grid gap-2 text-sm text-slate-300">
@@ -871,113 +1091,234 @@ export default function GroupShow() {
) : null}
{section === 'collections' ? (
<section className="mt-8 rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<h2 className="text-2xl font-semibold text-white">Collections</h2>
<section className="mt-8 relative overflow-hidden rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<div className="absolute inset-x-0 top-0 h-[3px] rounded-t-[30px] bg-gradient-to-r from-sky-400/70 via-cyan-300/40 to-transparent" />
<div className="flex items-center gap-3">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-[14px] border border-sky-300/20 bg-sky-400/10 text-sky-200">
<i className="fa-solid fa-layer-group fa-fw" />
</span>
<h2 className="text-2xl font-semibold text-white">Collections</h2>
</div>
<CollectionGrid collections={collections} emptyLabel="No collections yet." />
</section>
) : null}
{section === 'posts' ? (
<section className="mt-8 rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<h2 className="text-2xl font-semibold text-white">Posts</h2>
<div className="mt-5 grid gap-4 md:grid-cols-2">
{posts.length > 0 ? posts.map((post) => (
<a key={post.id} href={post.url} className="rounded-[24px] border border-white/10 bg-black/20 p-4 transition hover:border-white/20">
<div className="flex items-center justify-between gap-3">
<div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">{post.type}</div>
{post.is_pinned ? <span className="rounded-full border border-amber-300/20 bg-amber-400/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-amber-100">Pinned</span> : null}
</div>
<h3 className="mt-2 text-lg font-semibold text-white">{post.title}</h3>
<p className="mt-2 text-sm leading-6 text-slate-300">{post.excerpt || 'Open the post to read more.'}</p>
</a>
)) : <p className="text-sm text-slate-400">No posts published yet.</p>}
<div className="mt-8">
<div className="mb-6 flex items-center gap-4">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-[14px] border border-sky-300/20 bg-sky-400/10 text-sky-200">
<i className="fa-solid fa-newspaper fa-fw" />
</span>
<div>
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/70">Group updates</p>
<h2 className="text-2xl font-semibold text-white">Posts</h2>
</div>
</div>
</section>
{posts.length > 0 ? (
<div className="grid gap-4 md:grid-cols-2">
{posts.map((post) => {
const typeKey = String(post.type || '').toLowerCase()
const typeStyle = POST_TYPE_ICONS[typeKey] || POST_TYPE_ICONS.announcement
return (
<a key={post.id} href={post.url} className="group relative overflow-hidden rounded-[28px] border border-white/10 bg-black/20 p-5 transition hover:border-white/20 hover:shadow-[0_8px_24px_rgba(2,6,23,0.4)]">
<div className={`absolute inset-x-0 top-0 h-[3px] rounded-t-[28px] bg-gradient-to-r ${typeStyle.bar}`} />
<div className="flex items-start gap-4">
<span className={`shrink-0 inline-flex h-10 w-10 items-center justify-center rounded-[14px] border ${typeStyle.border} ${typeStyle.bg} ${typeStyle.text}`}>
<i className={`fa-solid ${typeStyle.icon} fa-fw`} />
</span>
<div className="min-w-0 flex-1">
<div className="flex flex-wrap items-center gap-2">
<span className={`text-[10px] font-semibold uppercase tracking-[0.18em] ${typeStyle.text}`}>{post.type || 'post'}</span>
{post.is_pinned ? (
<span className="inline-flex items-center gap-1 rounded-full border border-amber-300/20 bg-amber-400/10 px-2.5 py-0.5 text-[10px] font-semibold uppercase tracking-[0.16em] text-amber-100">
<i className="fa-solid fa-thumbtack text-[9px]" />Pinned
</span>
) : null}
</div>
<h3 className="mt-1.5 text-base font-semibold text-white">{post.title}</h3>
<p className="mt-1.5 text-sm leading-6 text-slate-300">{post.excerpt || 'Open the post to read more.'}</p>
</div>
</div>
<div className="mt-3 flex justify-end">
<span className="text-xs font-semibold text-sky-300 opacity-0 transition group-hover:opacity-100">Read post <i className="fa-solid fa-arrow-right ml-0.5" /></span>
</div>
</a>
)
})}
</div>
) : (
<div className="flex flex-col items-center gap-4 rounded-[32px] border border-white/8 bg-white/[0.02] py-16 text-center">
<span className="inline-flex h-16 w-16 items-center justify-center rounded-2xl border border-white/10 bg-white/[0.04] text-slate-500">
<i className="fa-solid fa-newspaper text-3xl" />
</span>
<div>
<p className="text-sm font-semibold text-slate-300">No posts published yet.</p>
<p className="mt-1 text-xs text-slate-500">Check back later for group updates and announcements.</p>
</div>
</div>
)}
</div>
) : null}
{section === 'projects' ? (
<section className="mt-8 rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<h2 className="text-2xl font-semibold text-white">Projects</h2>
<p className="mt-2 text-sm text-slate-400">Structured releases, collaboration hubs, and production pages published by this group.</p>
<section className="mt-8 relative overflow-hidden rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<div className="absolute inset-x-0 top-0 h-[3px] rounded-t-[30px] bg-gradient-to-r from-violet-400/70 via-sky-300/40 to-transparent" />
<div className="flex items-center gap-3">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-[14px] border border-violet-300/20 bg-violet-400/10 text-violet-200">
<i className="fa-solid fa-diagram-project fa-fw" />
</span>
<div>
<h2 className="text-2xl font-semibold text-white">Projects</h2>
<p className="mt-1 text-sm text-slate-400">Structured releases, collaboration hubs, and production pages published by this group.</p>
</div>
</div>
<CompactCardGrid items={projects} emptyLabel="No public projects yet." />
</section>
) : null}
{section === 'releases' ? (
<section className="mt-8 rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<h2 className="text-2xl font-semibold text-white">Releases</h2>
<p className="mt-2 text-sm text-slate-400">Published drops, milestone pipelines, and linked showcases from this group.</p>
<section className="mt-8 relative overflow-hidden rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<div className="absolute inset-x-0 top-0 h-[3px] rounded-t-[30px] bg-gradient-to-r from-sky-400/70 via-cyan-300/40 to-transparent" />
<div className="flex items-center gap-3">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-[14px] border border-sky-300/20 bg-sky-400/10 text-sky-200">
<i className="fa-solid fa-rocket fa-fw" />
</span>
<div>
<h2 className="text-2xl font-semibold text-white">Releases</h2>
<p className="mt-1 text-sm text-slate-400">Published drops, milestone pipelines, and linked showcases from this group.</p>
</div>
</div>
<ReleaseGrid releases={releases} emptyLabel="No public releases yet." />
</section>
) : null}
{section === 'challenges' ? (
<section className="mt-8 rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<h2 className="text-2xl font-semibold text-white">Challenges</h2>
<p className="mt-2 text-sm text-slate-400">Current and past prompts, internal sprints, and public-facing challenge runs.</p>
<section className="mt-8 relative overflow-hidden rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<div className="absolute inset-x-0 top-0 h-[3px] rounded-t-[30px] bg-gradient-to-r from-amber-400/70 via-yellow-300/40 to-transparent" />
<div className="flex items-center gap-3">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-[14px] border border-amber-300/20 bg-amber-400/10 text-amber-200">
<i className="fa-solid fa-trophy fa-fw" />
</span>
<div>
<h2 className="text-2xl font-semibold text-white">Challenges</h2>
<p className="mt-1 text-sm text-slate-400">Current and past prompts, internal sprints, and public-facing challenge runs.</p>
</div>
</div>
<CompactCardGrid items={challenges} emptyLabel="No public challenges yet." />
</section>
) : null}
{section === 'events' ? (
<section className="mt-8 rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<h2 className="text-2xl font-semibold text-white">Events</h2>
<p className="mt-2 text-sm text-slate-400">Launches, milestones, streams, and other moments on the group timeline.</p>
<section className="mt-8 relative overflow-hidden rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<div className="absolute inset-x-0 top-0 h-[3px] rounded-t-[30px] bg-gradient-to-r from-violet-400/70 via-sky-300/40 to-transparent" />
<div className="flex items-center gap-3">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-[14px] border border-violet-300/20 bg-violet-400/10 text-violet-200">
<i className="fa-solid fa-calendar-days fa-fw" />
</span>
<div>
<h2 className="text-2xl font-semibold text-white">Events</h2>
<p className="mt-1 text-sm text-slate-400">Launches, milestones, streams, and other moments on the group timeline.</p>
</div>
</div>
<CompactCardGrid items={events} emptyLabel="No public events yet." badgeKey="event_type" />
</section>
) : null}
{section === 'activity' ? (
<section className="mt-8 rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<h2 className="text-2xl font-semibold text-white">Activity</h2>
<p className="mt-2 text-sm text-slate-400">Public milestones from posts, releases, events, member changes, and challenge highlights.</p>
<section className="mt-8 relative overflow-hidden rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<div className="absolute inset-x-0 top-0 h-[3px] rounded-t-[30px] bg-gradient-to-r from-sky-400/70 via-cyan-300/40 to-transparent" />
<div className="flex items-center gap-3">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-[14px] border border-sky-300/20 bg-sky-400/10 text-sky-200">
<i className="fa-solid fa-bolt fa-fw" />
</span>
<div>
<h2 className="text-2xl font-semibold text-white">Activity</h2>
<p className="mt-1 text-sm text-slate-400">Public milestones from posts, releases, events, member changes, and challenge highlights.</p>
</div>
</div>
<ActivityFeed items={activity} />
</section>
) : null}
{section === 'members' ? (
<section className="mt-8 rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<h2 className="text-2xl font-semibold text-white">Members</h2>
<div className="mt-6 grid gap-8">
<div className="mt-8">
<div className="mb-6 flex items-center gap-4">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-[14px] border border-sky-300/20 bg-sky-400/10 text-sky-200">
<i className="fa-solid fa-users fa-fw" />
</span>
<div>
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/70">Community</p>
<h2 className="text-2xl font-semibold text-white">Members</h2>
</div>
</div>
<div className="grid gap-6">
{[
['Owner', groupedMembers.owner],
['Admins', groupedMembers.admins],
['Editors', groupedMembers.editors],
['Contributors', groupedMembers.contributors],
].map(([label, bucket]) => (
bucket.length > 0 ? (
<section key={label}>
<div className="flex items-center justify-between gap-3">
['Owner', 'owner', groupedMembers.owner],
['Admins', 'admin', groupedMembers.admins],
['Editors', 'editor', groupedMembers.editors],
['Contributors', 'contributor', groupedMembers.contributors],
].map(([label, roleKey, bucket]) => {
if (bucket.length === 0) return null
const roleStyle = MEMBER_ROLE_COLORS[roleKey] || MEMBER_ROLE_COLORS.contributor
return (
<section key={label} className="relative overflow-hidden rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<div className={`absolute inset-x-0 top-0 h-[3px] rounded-t-[30px] bg-gradient-to-r ${roleKey === 'owner' ? 'from-amber-400/70 to-transparent' : roleKey === 'admin' ? 'from-sky-400/70 to-transparent' : roleKey === 'editor' ? 'from-violet-400/70 to-transparent' : 'from-emerald-400/70 to-transparent'}`} />
<div className="flex items-center gap-3">
<span className={`inline-flex h-8 w-8 items-center justify-center rounded-xl border ${roleStyle.badge}`}>
<i className={`fa-solid ${roleStyle.icon} fa-fw text-sm ${roleStyle.iconColor}`} />
</span>
<h3 className="text-lg font-semibold text-white">{label}</h3>
<span className="rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs font-semibold text-slate-300">{bucket.length}</span>
<span className="ml-auto rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs font-semibold text-slate-300">{bucket.length}</span>
</div>
<div className="mt-4 grid gap-3 md:grid-cols-2 xl:grid-cols-3">
{bucket.map((member) => (
<a key={member.id} href={member.user?.profile_url || '#'} className="flex items-center gap-3 rounded-[24px] border border-white/10 bg-black/20 px-4 py-4 transition hover:border-white/20">
{member.user?.avatar_url ? <img src={member.user.avatar_url} alt={member.user.name || member.user.username} className="h-12 w-12 rounded-2xl object-cover" /> : <div className="flex h-12 w-12 items-center justify-center rounded-2xl border border-white/10 bg-white/[0.03] text-slate-400"><i className="fa-solid fa-user" /></div>}
<a key={member.id} href={member.user?.profile_url || '#'} className="group flex items-center gap-3 rounded-[24px] border border-white/10 bg-black/20 px-4 py-4 transition hover:border-white/20 hover:bg-white/[0.04]">
{member.user?.avatar_url
? <img src={member.user.avatar_url} alt={member.user.name || member.user.username} className="h-12 w-12 shrink-0 rounded-2xl object-cover ring-1 ring-white/10 transition group-hover:ring-white/20" />
: <div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-2xl border border-white/10 bg-white/[0.03] text-slate-400"><i className="fa-solid fa-user" /></div>
}
<div className="min-w-0">
<div className="truncate font-semibold text-white">{member.user?.name || member.user?.username}</div>
<div className="text-xs uppercase tracking-[0.16em] text-slate-400">{member.role_label || member.role}</div>
<div className={`mt-1 inline-flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 text-[10px] font-semibold uppercase tracking-[0.14em] ${roleStyle.badge}`}>
<i className={`fa-solid ${roleStyle.icon} fa-fw text-[9px]`} />
{member.role_label || member.role}
</div>
</div>
</a>
))}
</div>
</section>
) : null
))}
)
})}
</div>
</section>
</div>
) : null}
{section === 'about' ? (
<section className="mt-8 rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<h2 className="text-2xl font-semibold text-white">About</h2>
<section className="mt-8 relative overflow-hidden rounded-[30px] border border-white/10 bg-white/[0.03] p-6">
<div className="absolute inset-x-0 top-0 h-[3px] rounded-t-[30px] bg-gradient-to-r from-slate-400/50 to-transparent" />
<div className="flex items-center gap-3">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-[14px] border border-white/10 bg-white/[0.05] text-slate-300">
<i className="fa-solid fa-id-card fa-fw" />
</span>
<h2 className="text-2xl font-semibold text-white">About</h2>
</div>
<div className="mt-5 space-y-4 text-sm leading-7 text-slate-300">
<p>{group.bio || 'No long-form description yet.'}</p>
{group.website_url ? <p><a href={group.website_url} className="text-sky-200 underline underline-offset-4">{group.website_url}</a></p> : null}
{Array.isArray(group.links) && group.links.length > 0 ? <div className="flex flex-wrap gap-3">{group.links.map((link) => <a key={`${link.label}-${link.url}`} href={link.url} className="rounded-full border border-white/10 bg-white/[0.04] px-3 py-1.5 text-xs font-semibold text-white">{link.label}</a>)}</div> : null}
{group.founded_at ? <p>Founded: {new Date(group.founded_at).toLocaleDateString()}</p> : null}
{group.type ? <p>Type: {group.type}</p> : null}
{group.website_url ? <p><a href={group.website_url} className="inline-flex items-center gap-1.5 text-sky-200 underline underline-offset-4 transition hover:text-sky-100"><i className="fa-solid fa-link" />{group.website_url}</a></p> : null}
{Array.isArray(group.links) && group.links.length > 0 ? (
<div className="flex flex-wrap gap-3">
{group.links.map((link) => (
<a key={`${link.label}-${link.url}`} href={link.url} className="inline-flex items-center gap-2 rounded-[14px] border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white transition hover:bg-white/[0.08]">
<i className="fa-solid fa-arrow-up-right-from-square text-slate-400" />{link.label}
</a>
))}
</div>
) : null}
<div className="mt-4 flex flex-wrap gap-4 border-t border-white/8 pt-4 text-xs text-slate-400">
{group.founded_at ? <span className="inline-flex items-center gap-2"><i className="fa-solid fa-calendar-days text-slate-500" />Founded {new Date(group.founded_at).toLocaleDateString()}</span> : null}
{group.type ? <span className="inline-flex items-center gap-2"><i className="fa-solid fa-tag text-slate-500" />{group.type}</span> : null}
</div>
</div>
</section>
) : null}