91 lines
6.2 KiB
JavaScript
91 lines
6.2 KiB
JavaScript
import React from 'react'
|
|
import { Link, router, usePage } from '@inertiajs/react'
|
|
import StudioLayout from '../../Layouts/StudioLayout'
|
|
import GroupStudioPromoCard from '../../components/groups/GroupStudioPromoCard'
|
|
|
|
function GroupCard({ group }) {
|
|
return (
|
|
<article className="rounded-[28px] border border-white/10 bg-white/[0.03] p-5 shadow-[0_18px_50px_rgba(3,7,18,0.22)]">
|
|
<div className="flex items-start gap-4">
|
|
<div className="flex h-14 w-14 items-center justify-center overflow-hidden rounded-2xl border border-white/10 bg-slate-900/70 text-slate-300">
|
|
{group.avatar_url ? <img src={group.avatar_url} alt={group.name} className="h-full w-full object-cover" /> : <i className="fa-solid fa-people-group" />}
|
|
</div>
|
|
<div className="min-w-0 flex-1">
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
<h2 className="truncate text-lg font-semibold text-white">{group.name}</h2>
|
|
{group.viewer?.role ? <span className="rounded-full border border-sky-300/20 bg-sky-300/10 px-2.5 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-sky-100">{group.viewer.role}</span> : null}
|
|
{Number(group.pending_invites_count || 0) > 0 ? <span className="rounded-full border border-amber-300/20 bg-amber-400/10 px-2.5 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-amber-100">{Number(group.pending_invites_count)} pending invite{Number(group.pending_invites_count) === 1 ? '' : 's'}</span> : null}
|
|
</div>
|
|
{group.headline ? <p className="mt-2 text-sm text-slate-300">{group.headline}</p> : null}
|
|
<div className="mt-4 flex flex-wrap gap-4 text-xs text-slate-400">
|
|
<span>{Number(group.counts?.artworks || 0).toLocaleString()} artworks</span>
|
|
<span>{Number(group.counts?.collections || 0).toLocaleString()} collections</span>
|
|
<span>{Number(group.counts?.followers || 0).toLocaleString()} followers</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="mt-5 flex flex-wrap gap-2">
|
|
<a href={group.urls?.studio} className="rounded-full border border-sky-300/20 bg-sky-300/10 px-4 py-2 text-sm font-semibold text-sky-100 transition hover:border-sky-300/35 hover:bg-sky-300/15">Open Studio</a>
|
|
<a href={group.urls?.studio_invitations} className="rounded-full border border-white/10 bg-white/[0.03] px-4 py-2 text-sm font-semibold text-white transition hover:border-white/20 hover:bg-white/[0.06]">Invitations</a>
|
|
<a href={group.urls?.public} className="rounded-full border border-white/10 bg-white/[0.03] px-4 py-2 text-sm font-semibold text-white transition hover:border-white/20 hover:bg-white/[0.06]">Public page</a>
|
|
</div>
|
|
</article>
|
|
)
|
|
}
|
|
|
|
export default function StudioGroupsIndex() {
|
|
const { props } = usePage()
|
|
const groups = Array.isArray(props.groups) ? props.groups : []
|
|
const pendingInvites = Array.isArray(props.pendingInvites) ? props.pendingInvites : []
|
|
|
|
return (
|
|
<StudioLayout title={props.title} subtitle={props.description}>
|
|
<GroupStudioPromoCard
|
|
title="Publish as a team, not just an individual"
|
|
description="Groups let you share ownership across artworks, releases, collections, reviews, and recruiting while keeping one public identity for the whole collective."
|
|
bullets={[
|
|
{ title: 'Shared publishing', body: 'Release under one name while keeping credited contributors visible across the artwork and group pages.' },
|
|
{ title: 'Team workflow', body: 'Invite reviewers, managers, and contributors into one studio space with role-based permissions.' },
|
|
{ title: 'Public discovery', body: 'Groups now appear across search, homepage modules, leaderboards, and public browse surfaces.' },
|
|
]}
|
|
primaryLabel="Create a group"
|
|
primaryHref={props.endpoints?.create}
|
|
secondaryLabel="Browse public groups"
|
|
secondaryHref="/groups"
|
|
/>
|
|
|
|
<div className="mb-6 flex items-center justify-between gap-3 rounded-[28px] border border-white/10 bg-white/[0.03] p-5">
|
|
<div>
|
|
<p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-sky-200/80">Collective publishing</p>
|
|
<h2 className="mt-2 text-2xl font-semibold text-white">Launch and manage shared identities</h2>
|
|
</div>
|
|
<Link href={props.endpoints?.create} className="rounded-full border border-sky-300/20 bg-sky-300/10 px-4 py-2 text-sm font-semibold text-sky-100 transition hover:border-sky-300/35 hover:bg-sky-300/15">Create group</Link>
|
|
</div>
|
|
|
|
{pendingInvites.length > 0 ? (
|
|
<section className="mb-6 rounded-[28px] border border-amber-300/20 bg-amber-400/10 p-5">
|
|
<h2 className="text-lg font-semibold text-amber-50">Pending invites</h2>
|
|
<div className="mt-4 grid gap-3 md:grid-cols-2">
|
|
{pendingInvites.map((invite) => (
|
|
<article key={invite.id} className="rounded-2xl border border-white/10 bg-black/20 p-4 text-white">
|
|
<h3 className="text-base font-semibold">{invite.group?.name}</h3>
|
|
<p className="mt-2 text-sm text-amber-50/80">Role: {invite.role}</p>
|
|
{invite.invited_by ? <p className="mt-1 text-sm text-amber-50/70">Invited by {invite.invited_by.name || invite.invited_by.username}</p> : null}
|
|
<div className="mt-4 flex gap-2">
|
|
<button type="button" onClick={() => router.post(invite.accept_url)} className="rounded-full border border-emerald-300/20 bg-emerald-400/10 px-3 py-2 text-sm font-semibold text-emerald-100">Accept</button>
|
|
<button type="button" onClick={() => router.post(invite.decline_url)} className="rounded-full border border-white/10 bg-white/[0.04] px-3 py-2 text-sm font-semibold text-white">Decline</button>
|
|
</div>
|
|
</article>
|
|
))}
|
|
</div>
|
|
</section>
|
|
) : null}
|
|
|
|
<div className="grid gap-4 xl:grid-cols-2">
|
|
{groups.length > 0 ? groups.map((group) => <GroupCard key={group.slug} group={group} />) : (
|
|
<div className="rounded-[28px] border border-dashed border-white/10 px-6 py-16 text-center text-slate-400">No groups yet. Create one to start publishing collaboratively.</div>
|
|
)}
|
|
</div>
|
|
</StudioLayout>
|
|
)
|
|
} |