compassmock/src/components/demo/demo-banner.tsx
Nicholai ad2f0c0b9c
feat(security): add multi-tenancy isolation and demo mode (#90)
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>
2026-02-15 22:05:12 -07:00

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&rsquo;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>
)
}