import React, { useState } from 'react' import { router, usePage } from '@inertiajs/react' import StudioLayout from '../../Layouts/StudioLayout' import NovaSelect from '../../components/ui/NovaSelect' function overrideMap(member) { const entries = Array.isArray(member.permission_overrides) ? member.permission_overrides : [] return entries.reduce((carry, item) => { if (!item?.key) return carry carry[item.key] = item.is_allowed === true ? 'allow' : 'deny' return carry }, {}) } function prettifyPermission(value) { return String(value || '').replaceAll('_', ' ') } export default function StudioGroupMembers() { const { props } = usePage() const canManageMembers = Boolean(props.canManageMembers) const [invite, setInvite] = useState({ username: '', role: 'contributor', note: '' }) const [search, setSearch] = useState('') const [editingMemberId, setEditingMemberId] = useState(null) const [permissionDrafts, setPermissionDrafts] = useState({}) const members = Array.isArray(props.members) ? props.members : [] const permissionOptions = Array.isArray(props.permissionOverrideOptions) ? props.permissionOverrideOptions : [] const filteredMembers = members.filter((member) => { const haystack = `${member.user?.name || ''} ${member.user?.username || ''} ${member.role_label || member.role || ''}`.toLowerCase() return haystack.includes(search.trim().toLowerCase()) }) const confirmTransfer = (member) => { if (!window.confirm(`Transfer ownership of this group to ${member.user?.name || member.user?.username}? This removes your owner privileges immediately.`)) { return } router.post(props.endpoints?.transferPattern.replace('__MEMBER__', String(member.id))) } const confirmRemoval = (member) => { if (!window.confirm(`Remove ${member.user?.name || member.user?.username} from this group?`)) { return } router.delete(props.endpoints?.deletePattern.replace('__MEMBER__', String(member.id))) } const openPermissionEditor = (member) => { setEditingMemberId(member.id) setPermissionDrafts((current) => ({ ...current, [member.id]: overrideMap(member) })) } const setPermissionState = (memberId, key, value) => { setPermissionDrafts((current) => ({ ...current, [memberId]: { ...(current[memberId] || {}), [key]: value, }, })) } const savePermissions = (member) => { const state = permissionDrafts[member.id] || {} const payload = permissionOptions .filter((option) => state[option.value] === 'allow' || state[option.value] === 'deny') .map((option) => ({ key: option.value, is_allowed: state[option.value] === 'allow' })) router.patch(props.endpoints?.permissionsPattern.replace('__MEMBER__', String(member.id)), { permission_overrides: payload, }, { onSuccess: () => setEditingMemberId(null), }) } return ( {canManageMembers ? (

Invite member

{props.endpoints?.invitations ? Manage invitations : null}
setInvite((current) => ({ ...current, username: event.target.value }))} placeholder="Username" className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" /> setInvite((current) => ({ ...current, role: val }))} searchable={false} options={[{ value: 'contributor', label: 'Contributor' }, { value: 'editor', label: 'Editor' }, { value: 'admin', label: 'Admin' }]} /> setInvite((current) => ({ ...current, note: event.target.value }))} placeholder="Optional note" className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
) : null}

Member directory

Search the current roster, then adjust roles or membership status.

Member Role Status Actions
{filteredMembers.map((member) => (
{member.user?.avatar_url ? {member.user.name :
}
{member.user?.name || member.user?.username}
@{member.user?.username || 'member'}
{canManageMembers && member.role !== 'owner' ? ( router.patch(props.endpoints?.updatePattern.replace('__MEMBER__', String(member.id)), { role: val })} searchable={false} options={[{ value: 'contributor', label: 'Contributor' }, { value: 'editor', label: 'Editor' }, { value: 'admin', label: 'Admin' }]} /> ) : {member.role === 'owner' ? 'Owner' : (member.role_label || member.role)}} {Array.isArray(member.permission_overrides) && member.permission_overrides.length > 0 ? (
{member.permission_overrides.map((permission) => ( {permission.is_allowed ? 'Allow' : 'Deny'} {prettifyPermission(permission.key)} ))}
) : null}
{member.status}
{member.can_manage_permissions ? : null} {canManageMembers && member.can_transfer ? : null} {canManageMembers && member.can_revoke ? : null}
{editingMemberId === member.id ? (

Permission overrides

Set each advanced capability to inherit, allow, or deny.

{permissionOptions.map((option) => { const current = permissionDrafts[member.id]?.[option.value] || 'inherit' return (
{option.label}
) })}
) : null}
))} {filteredMembers.length === 0 ?
No members match the current search.
: null}
) }