diff --git a/app/(app)/forms/forms-client.tsx b/app/(app)/forms/forms-client.tsx
index edb8e0c..c507754 100644
--- a/app/(app)/forms/forms-client.tsx
+++ b/app/(app)/forms/forms-client.tsx
@@ -6,6 +6,7 @@ import { PageHeader, PageTitle } from '@/components/ui/page-title'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyDescription } from '@/components/ui/empty'
+import { CopyLinkButton } from '@/components/forms/copy-link-button'
type FormRow = {
id: string
@@ -76,6 +77,9 @@ export function FormsClient({ forms, spaceSlug }: { forms: FormRow[]; spaceSlug:
{f.status}
+ {f.visibility !== 'members' && f.status === 'published' && (
+
+ )}
diff --git a/app/(app)/my-forms/page.tsx b/app/(app)/my-forms/page.tsx
index 2481a64..8d5b775 100644
--- a/app/(app)/my-forms/page.tsx
+++ b/app/(app)/my-forms/page.tsx
@@ -5,6 +5,7 @@ import { PageHeader, PageTitle } from '@/components/ui/page-title'
import { Badge } from '@/components/ui/badge'
import { Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyDescription } from '@/components/ui/empty'
import { ClipboardList } from 'lucide-react'
+import { CopyLinkButton } from '@/components/forms/copy-link-button'
export const dynamic = 'force-dynamic'
@@ -20,15 +21,17 @@ export default async function MyFormsPage() {
const { data: member } = await supabase
.from('space_members')
- .select('space_id')
+ .select('space_id, spaces(slug)')
.eq('user_id', user.id)
.in('status', ['current', 'unverified', 'late'])
.single()
if (!member) redirect('/login')
+ const spaceSlug = (member.spaces as { slug?: string } | null)?.slug ?? ''
+
const { data: forms } = await supabase
.from('forms')
- .select('id, title, description, kind, status')
+ .select('id, title, description, kind, status, slug, visibility')
.eq('space_id', member.space_id)
.eq('status', 'published')
.order('created_at', { ascending: false })
@@ -55,24 +58,36 @@ export default async function MyFormsPage() {
) : (
- {list.map(f => (
-
-
-
-
{f.title}
- {f.kind === 'waiver' &&
Waiver}
+ {list.map(f => {
+ const isPublic = f.visibility !== 'members'
+ return (
+
+
+
+
+ {f.title}
+
+ {f.kind === 'waiver' && Waiver}
+
+ {f.description && (
+
{f.description}
+ )}
+ {isPublic && (
+
+ /f/{spaceSlug}/{f.slug}
+
+ )}
- {f.description && (
-
{f.description}
- )}
+ {isPublic &&
}
+
+ Open →
+
-
Open →
-
- ))}
+ )
+ })}
)}
diff --git a/components/forms/copy-link-button.tsx b/components/forms/copy-link-button.tsx
new file mode 100644
index 0000000..0783b39
--- /dev/null
+++ b/components/forms/copy-link-button.tsx
@@ -0,0 +1,33 @@
+'use client'
+
+import { useState } from 'react'
+import { Check, Copy } from 'lucide-react'
+import { toast } from 'sonner'
+import { Button } from '@/components/ui/button'
+
+// One reusable copy-to-clipboard control for a form's public link. Shared by
+// the admin forms list and the member forms list so the behaviour and styling
+// never drift.
+export function CopyLinkButton({ path, label = 'Copy link' }: { path: string; label?: string }) {
+ const [copied, setCopied] = useState(false)
+
+ async function copy() {
+ const url =
+ typeof window !== 'undefined' ? `${window.location.origin}${path}` : path
+ try {
+ await navigator.clipboard?.writeText(url)
+ setCopied(true)
+ toast.success('Public link copied')
+ setTimeout(() => setCopied(false), 2000)
+ } catch {
+ toast.error('Could not copy — copy it manually')
+ }
+ }
+
+ return (
+
+ )
+}