mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-10-08 03:08:21 +00:00
feat(web): company brain entitlement helper and hook
Add hasCompanyBrain helper + useHasCompanyBrain hook reading the company_brain add-on from org metadata to gate Company Brain UI.
This commit is contained in:
parent
d4a3a57a42
commit
c829fd3811
3 changed files with 58 additions and 0 deletions
7
apps/web/hooks/use-company-brain.ts
Normal file
7
apps/web/hooks/use-company-brain.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { useAuth } from "@lib/auth-context"
|
||||
import { hasCompanyBrain } from "@/lib/billing-utils"
|
||||
|
||||
export function useHasCompanyBrain(): boolean {
|
||||
const { org } = useAuth()
|
||||
return hasCompanyBrain(org?.metadata as Record<string, unknown> | undefined)
|
||||
}
|
||||
|
|
@ -1,3 +1,21 @@
|
|||
const COMPANY_BRAIN_PRODUCT_ID = "company_brain"
|
||||
|
||||
// Add-on resolved by product presence, not tier.
|
||||
export function hasCompanyBrain(
|
||||
metadata: Record<string, unknown> | null | undefined,
|
||||
): boolean {
|
||||
if (!metadata) return false
|
||||
const overrides = metadata.featureOverrides as
|
||||
| Record<string, { allow?: boolean }>
|
||||
| undefined
|
||||
const override = overrides?.[COMPANY_BRAIN_PRODUCT_ID]
|
||||
if (override) return Boolean(override.allow)
|
||||
const activeProducts = Array.isArray(metadata.activeProducts)
|
||||
? (metadata.activeProducts as string[])
|
||||
: []
|
||||
return activeProducts.includes(COMPANY_BRAIN_PRODUCT_ID)
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a number with K/M suffix for display
|
||||
* @example formatUsageNumber(1500000) => "1.5M"
|
||||
|
|
|
|||
|
|
@ -18,6 +18,23 @@ type OrganizationListItem = NonNullable<
|
|||
|
||||
const STORAGE_KEY = "supermemory-consumer-last-org-slug"
|
||||
|
||||
// Reads ?org=<slug> from the URL once and removes it, so a deep link that
|
||||
// selects an org doesn't re-fire on refresh or back-navigation.
|
||||
function consumeRequestedOrgSlug(): string | null {
|
||||
if (typeof window === "undefined") return null
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
const slug = params.get("org")
|
||||
if (!slug) return null
|
||||
params.delete("org")
|
||||
const qs = params.toString()
|
||||
window.history.replaceState(
|
||||
null,
|
||||
"",
|
||||
`${window.location.pathname}${qs ? `?${qs}` : ""}${window.location.hash}`,
|
||||
)
|
||||
return slug
|
||||
}
|
||||
|
||||
interface AuthContextType {
|
||||
session: SessionData["session"] | null
|
||||
user: SessionData["user"] | null
|
||||
|
|
@ -123,6 +140,22 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|||
|
||||
const activeOrgId = session.session.activeOrganizationId
|
||||
|
||||
// Deep link (?org=<slug>) takes priority — used when arriving from
|
||||
// the console. Strip the param so refresh/back doesn't re-trigger.
|
||||
const requestedSlug = consumeRequestedOrgSlug()
|
||||
if (requestedSlug) {
|
||||
const match = orgs.find((o) => o.slug === requestedSlug)
|
||||
if (match) {
|
||||
if (activeOrgId === match.id) {
|
||||
const full = await authClient.organization.getFullOrganization()
|
||||
if (!cancelled) setOrg(full?.data ?? null)
|
||||
} else {
|
||||
await setActiveOrg(requestedSlug)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (orgs.length === 1) {
|
||||
const one = orgs[0]
|
||||
if (!one) return
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue