diff --git a/app/(app)/dashboard/page.tsx b/app/(app)/dashboard/page.tsx index b072e01..7f64da0 100644 --- a/app/(app)/dashboard/page.tsx +++ b/app/(app)/dashboard/page.tsx @@ -110,7 +110,7 @@ export default async function DashboardPage() { if (isFirstRun) { const steps = [ - { href: '/members', Ico: IcoUsers, title: 'Invite or add members', desc: 'Add people directly, or share an invite code from Customize.' }, + { href: '/members', Ico: IcoUsers, title: 'Invite or add members', desc: 'Add people directly, or generate a shareable join link right there.' }, { href: '/customize', Ico: IcoPlus, title: 'Set up onboarding', desc: 'Decide what new members see and agree to when they join.' }, { href: '/tasks', Ico: IcoTask, title: 'Create your first task or chore', desc: 'Capture the recurring upkeep your space depends on.' }, { href: '/settings', Ico: IcoPay, title: 'Connect payments', desc: 'Link a payment platform to track dues and reconcile income.' }, diff --git a/app/(app)/members/members-client.tsx b/app/(app)/members/members-client.tsx index e4e1dc5..0b8d6af 100644 --- a/app/(app)/members/members-client.tsx +++ b/app/(app)/members/members-client.tsx @@ -1,6 +1,6 @@ 'use client' -import { useState } from 'react' +import { useState, type ReactNode } from 'react' import { Plus, ChevronDown, Users } from 'lucide-react' import { Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyDescription } from '@/components/ui/empty' import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog' @@ -18,6 +18,10 @@ interface Props { members: Member[] currentRole: string areaLeadRoles?: AreaLeadRole[] + // Composed by the server page for admin/board: the existing InvitesPanel, + // so the members page is a real place to create and share a join link + // (the first-run "Invite or add members" step lands here). + inviteSlot?: ReactNode } const TIER_COLORS: Record = { @@ -29,7 +33,7 @@ const TIER_COLORS: Record = { const isAdmin = (role: string) => role === 'admin' || role === 'board' -export function MembersClient({ members: initialMembers, currentRole, areaLeadRoles = [] }: Props) { +export function MembersClient({ members: initialMembers, currentRole, areaLeadRoles = [], inviteSlot }: Props) { const router = useRouter() const confirm = useConfirm() const [members, setMembers] = useState(initialMembers) @@ -230,6 +234,7 @@ export function MembersClient({ members: initialMembers, currentRole, areaLeadRo
+ {inviteSlot &&
{inviteSlot}
}
diff --git a/app/(app)/members/page.tsx b/app/(app)/members/page.tsx index c477746..4c5fa7a 100644 --- a/app/(app)/members/page.tsx +++ b/app/(app)/members/page.tsx @@ -1,6 +1,7 @@ import { createClient } from '@/lib/supabase/server' import { PageHeader, PageTitle } from '@/components/ui/page-title' import { MembersClient } from './members-client' +import { InvitesPanel } from '../customize/panels/invites-panel' // member_directory_visibility values come from space settings: // admin_only — only admins see the directory @@ -15,7 +16,7 @@ export default async function MembersPage() { const { data: self } = await supabase .from('space_members') - .select('space_id, role') + .select('space_id, role, spaces(slug)') .eq('user_id', user.id) .in('status', ['current', 'unverified', 'late']) .single() @@ -73,11 +74,34 @@ export default async function MembersPage() { .order('area_name') : { data: [] } + // Admin/board can create and share join links straight from this page (the + // same InvitesPanel used in Customize). This is where the first-run + // "Invite or add members" step sends people, so the UI has to be here. + const canInvite = self.role === 'admin' || self.role === 'board' + const spaceSlug = (self.spaces as { slug?: string } | null)?.slug ?? '' + const { data: invites } = canInvite + ? await supabase + .from('space_invites') + .select('id, code, label, expires_at, max_uses, uses_count, is_enabled, role, created_at') + .eq('space_id', self.space_id) + .order('created_at', { ascending: false }) + : { data: [] } + return ( } + inviteSlot={ + canInvite ? ( + } + /> + ) : undefined + } /> ) }