Add org-scoped data isolation across all server actions to prevent cross-org data leakage. Add read-only demo mode with mutation guards on all write endpoints. Multi-tenancy: - org filter on executeDashboardQueries (all query types) - org boundary checks on getChannel, joinChannel - searchMentionableUsers derives org from session - getConversationUsage scoped to user, not org-wide for admins - organizations table, members, org switcher component Demo mode: - /demo route sets strict sameSite cookie - isDemoUser guards on all mutation server actions - demo banner, CTA dialog, and gate components - seed script for demo org data Also: exclude scripts/ from tsconfig (fixes build), add multi-tenancy architecture documentation. Co-authored-by: Nicholai <nicholaivogelfilms@gmail.com>
46 lines
1.2 KiB
TypeScript
46 lines
1.2 KiB
TypeScript
"use client"
|
|
|
|
import Link from "next/link"
|
|
import { X } from "lucide-react"
|
|
import { Button } from "@/components/ui/button"
|
|
import { useState } from "react"
|
|
import { cn } from "@/lib/utils"
|
|
|
|
interface DemoBannerProps {
|
|
readonly isDemo: boolean
|
|
}
|
|
|
|
export function DemoBanner({ isDemo }: DemoBannerProps) {
|
|
const [dismissed, setDismissed] = useState(false)
|
|
|
|
if (!isDemo || dismissed) return null
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"relative flex items-center justify-center gap-3",
|
|
"border-b bg-muted/30 px-4 py-2 text-sm"
|
|
)}
|
|
>
|
|
<span className="text-muted-foreground">
|
|
You’re exploring a demo workspace
|
|
</span>
|
|
<div className="flex items-center gap-2">
|
|
<Button asChild variant="outline" size="sm">
|
|
<Link href="/login">Log in</Link>
|
|
</Button>
|
|
<Button asChild size="sm">
|
|
<Link href="/signup">Sign up</Link>
|
|
</Button>
|
|
</div>
|
|
<button
|
|
onClick={() => setDismissed(true)}
|
|
className="absolute right-4 p-1 text-muted-foreground hover:text-foreground"
|
|
aria-label="Dismiss"
|
|
>
|
|
<X className="h-4 w-4" />
|
|
</button>
|
|
</div>
|
|
)
|
|
}
|