Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions apps/web/components/brain-home/brain-home-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { ArrowRight, Check, FileText, Loader2, UserPlus } from "lucide-react"
import { useQueryState } from "nuqs"
import { useSettingsModal } from "@/components/settings/settings-modal"
import { useBrainTrial } from "@/hooks/use-brain-trial"
import { TrialSetupBanner } from "@/components/trial-setup-banner"
import { useTrialStatus } from "@/hooks/use-trial-status"
import { dmSans125ClassName } from "@/lib/fonts"
import { useViewMode } from "@/lib/view-mode-context"
import {
Expand Down Expand Up @@ -170,6 +172,7 @@ export function BrainHomeView() {
const o = useBrainOverview()
const trial = useBrainTrial()
const board = useConnectionsBoard()
const { needsSetup } = useTrialStatus()
// Rows with no reported state (older orgs, pre-Slack) don't count or render.
const milestones = [
...(o.researchStatus != null ? [o.researchStatus === "done"] : []),
Expand All @@ -186,6 +189,7 @@ export function BrainHomeView() {

return (
<div className="mx-auto max-w-[1080px] space-y-6">
<TrialSetupBanner />
<StatsRow
memories={o.memoriesCount}
connected={o.connectedCount}
Expand All @@ -195,7 +199,7 @@ export function BrainHomeView() {
setupTotal={milestonesTotal}
lastUpdatedAt={o.lastUpdatedAt}
/>
{board.slack && !board.slack.connected && <SlackBanner />}
{board.slack && !board.slack.connected && !needsSetup && <SlackBanner />}
<div className="grid items-start gap-6 lg:grid-cols-[minmax(0,1fr)_340px]">
<div className="min-w-0 space-y-6">
{board.showBoard && <ConnectToolsCard board={board} />}
Expand Down Expand Up @@ -486,7 +490,7 @@ function BrainTimeline({
canInvite: boolean
toolsCardVisible: boolean
}) {
const trial = useBrainTrial()
const { needsSetup } = useTrialStatus()
const { openSettings } = useSettingsModal()
const { setViewMode } = useViewMode()
const [, setInvite] = useQueryState("invite")
Expand Down Expand Up @@ -532,12 +536,13 @@ function BrainTimeline({
title: slackConnected ? "Slack connected" : "Connect Slack",
hint: slackConnected
? undefined
: trial.state === "trialing"
? "Ask your brain from any channel."
: "Starts your 14-day free trial. No credit card needed.",
action: slackConnected
? undefined
: { label: "Add", href: `${BACKEND}/brain/slack/oauth/install` },
: needsSetup
? "Starts with your trial."
: "Ask your brain from any channel.",
action:
slackConnected || needsSetup
? undefined
: { label: "Add", href: `${BACKEND}/brain/slack/oauth/install` },
},
...(rollout != null
? [
Expand Down
47 changes: 37 additions & 10 deletions apps/web/components/brain-home/connections-board.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { cn } from "@lib/utils"
import { ArrowRight, Loader2 } from "lucide-react"
import { useCallback, useEffect, useState } from "react"
import { toast } from "sonner"
import { useTrialStatus } from "@/hooks/use-trial-status"
import { dmSans125ClassName } from "@/lib/fonts"
import { useViewMode } from "@/lib/view-mode-context"
import { brainConnectorIcon, SlackMark } from "../brain-connector-icons"
Expand Down Expand Up @@ -192,6 +193,7 @@ export const CONNECT_TOOLS_CARD_ID = "connect-tools"
export function ConnectToolsCard({ board }: { board: ConnectionsBoardState }) {
const { setViewMode } = useViewMode()
const { loading, featured, overflow, busy, isConnected, connect } = board
const { needsSetup } = useTrialStatus()

return (
<section
Expand All @@ -209,11 +211,19 @@ export function ConnectToolsCard({ board }: { board: ConnectionsBoardState }) {
Connect your tools
</p>
<p className="mt-0.5 text-[12px] font-medium text-[#737373]">
Give your Slack agent live access to the apps your team already uses.
{needsSetup
? "Starts with your trial."
: "Give your Slack agent live access to the apps your team already uses."}
</p>
</div>

<div className="overflow-hidden rounded-[12px] bg-[#14161A]">
<div
className={cn(
"overflow-hidden rounded-[12px] bg-[#14161A]",
needsSetup && "pointer-events-none opacity-40 select-none",
)}
aria-disabled={needsSetup || undefined}
>
{loading ? (
Array.from({ length: 3 }).map((_, i) => (
<TileSkeleton key={i} showDivider={i < 2} />
Expand Down Expand Up @@ -248,6 +258,7 @@ export function ConnectToolsCard({ board }: { board: ConnectionsBoardState }) {

export function AskInSlackCard({ board }: { board: ConnectionsBoardState }) {
const { previewApps, isConnected, connectedCount } = board
const { needsSetup } = useTrialStatus()
const prompts = previewApps
.filter((a) => AGENT_PROMPTS[a.slug])
.slice(0, 6)
Expand Down Expand Up @@ -275,12 +286,19 @@ export function AskInSlackCard({ board }: { board: ConnectionsBoardState }) {
</p>
</div>
<p className="text-[12px] font-medium leading-[1.5] text-[#737373]">
{connectedCount > 0
? "Things your agent can answer now:"
: "Connect a tool and your agent can answer:"}
{needsSetup
? "Starts with your trial."
: connectedCount > 0
? "Things your agent can answer now:"
: "Connect a tool and your agent can answer:"}
</p>

<div className="overflow-hidden rounded-[12px] bg-[#14161A]">
<div
className={cn(
"overflow-hidden rounded-[12px] bg-[#14161A]",
needsSetup && "opacity-40 select-none",
)}
>
{prompts.map((p, i) => (
<div
key={p.slug}
Expand Down Expand Up @@ -426,6 +444,7 @@ function TileSkeleton({ showDivider = false }: { showDivider?: boolean }) {
}

export function SlackBanner() {
const { needsSetup } = useTrialStatus()
return (
<section
className="relative overflow-hidden rounded-[18px] bg-[#1B1F24] p-3.5 sm:p-5"
Expand Down Expand Up @@ -472,12 +491,20 @@ export function SlackBanner() {
</div>

<a
href={`${BACKEND}/brain/slack/oauth/install`}
href={
needsSetup ? "/onboarding" : `${BACKEND}/brain/slack/oauth/install`
}
className="inline-flex shrink-0 items-center justify-center rounded-lg bg-white px-3 py-1.5 text-[13px] font-semibold text-[#1D1C1D] transition-transform hover:scale-[1.02] sm:gap-2 sm:px-4 sm:py-2.5 sm:text-[14px]"
>
<SlackMark className="hidden sm:block sm:size-[18px]" />
<span className="sm:hidden">Add</span>
<span className="hidden sm:inline">Add to Slack</span>
{needsSetup ? (
<span>Start trial</span>
) : (
<>
<SlackMark className="hidden sm:block sm:size-[18px]" />
<span className="sm:hidden">Add</span>
<span className="hidden sm:inline">Add to Slack</span>
</>
)}
</a>
</div>
</section>
Expand Down
2 changes: 2 additions & 0 deletions apps/web/components/dashboard-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { StaticGraphPreview } from "@/components/memory-graph/graph-card"
import { Tooltip, TooltipContent, TooltipTrigger } from "@ui/components/tooltip"
import { ChromeIcon, RaycastIcon } from "@/components/integration-icons"
import { SlackConnectCard } from "@/components/slack-connect-card"
import { TrialSetupBanner } from "@/components/trial-setup-banner"
import { GoogleDrive, Notion, MCPIcon } from "@ui/assets/icons"
import { analytics } from "@/lib/analytics"
import type { IntegrationParamValue } from "@/lib/search-params"
Expand Down Expand Up @@ -1344,6 +1345,7 @@ export function DashboardView({
)}
>
<div className="mx-auto w-full max-w-4xl space-y-4 md:space-y-5">
<TrialSetupBanner />
<SlackConnectCard />
{headerNotice ? <div className="space-y-2">{headerNotice}</div> : null}

Expand Down
51 changes: 42 additions & 9 deletions apps/web/components/onboarding-brain/company-brain-onboarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ import {
UserAvatar,
} from "./step-about"
import { ResearchActionRail } from "./research-action-rail"
import { CHECKOUT_RETURN_PARAM, StepTrial } from "./step-trial"
import { useTrialStatus } from "@/hooks/use-trial-status"
import { analytics } from "@/lib/analytics"
import {
type CompanyBrainConfirmResult,
type CompanyBrainOrganizationChoice,
Expand All @@ -52,7 +55,7 @@ interface CompanyBrainOnboardingProps {
const BACKEND =
process.env.NEXT_PUBLIC_BACKEND_URL ?? "https://api.supermemory.ai"

type Phase = "confirm" | "research"
type Phase = "confirm" | "trial" | "research"

function normalizeDomain(input: string): string {
const host = input
Expand Down Expand Up @@ -82,6 +85,23 @@ export function CompanyBrainOnboarding({
onUsePersonal,
}: CompanyBrainOnboardingProps) {
const [phase, setPhase] = useState<Phase>("confirm")
const { needsSetup } = useTrialStatus()
const resumedRef = useRef(false)
useEffect(() => {
if (resumedRef.current) return
const url = new URL(window.location.href)
if (url.searchParams.get(CHECKOUT_RETURN_PARAM) !== "complete") return
resumedRef.current = true
url.searchParams.delete(CHECKOUT_RETURN_PARAM)
window.history.replaceState({}, "", `${url.pathname}${url.search}`)
setPhase("research")
}, [])
useEffect(() => {
if (resumedRef.current || !needsSetup || phase !== "confirm") return
resumedRef.current = true
setPhase("trial")
analytics.brainTrialCardViewed()
}, [needsSetup, phase])
const [domain, setDomain] = useState(initialDomain)
const [organizationChoices, setOrganizationChoices] = useState<
CompanyBrainOrganizationChoice[] | null
Expand All @@ -107,7 +127,8 @@ export function CompanyBrainOnboarding({
}
setOrganizationChoices(null)
setServerSchedulesResearch(result.serverSchedulesResearch)
setPhase("research")
setPhase("trial")
analytics.brainTrialCardViewed()
}

// New-org signup schedules research after provisioning; if that hook is slow
Expand Down Expand Up @@ -203,9 +224,9 @@ export function CompanyBrainOnboarding({
<main
className={cn(
"relative z-10 flex-1 flex flex-col min-h-0",
phase === "confirm"
? "justify-center items-center px-4 md:px-10"
: "justify-start items-stretch pt-2 px-4 md:px-8 xl:px-14",
phase === "research"
? "justify-start items-stretch pt-2 px-4 md:px-8 xl:px-14"
: "justify-center items-center px-4 md:px-10",
)}
>
{/* Persistent card: full confirm card, then morphs into a slim docked header. */}
Expand All @@ -215,13 +236,25 @@ export function CompanyBrainOnboarding({
style={cardSurfaceStyle}
className={cn(
"w-full mx-auto rounded-[22px] bg-[#1B1F24]",
phase === "confirm"
? "max-w-xl p-6 md:p-8"
: "max-w-7xl px-5 py-3 xl:max-w-[1360px]",
phase === "research"
? "max-w-7xl px-5 py-3 xl:max-w-[1360px]"
: phase === "trial"
? "max-w-4xl p-6 md:p-7"
: "max-w-xl p-6 md:p-8",
)}
>
<AnimatePresence mode="wait" initial={false}>
{phase === "confirm" ? (
{phase === "trial" ? (
<motion.div
key="trial"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.15 }}
>
<StepTrial onActive={() => setPhase("research")} />
</motion.div>
) : phase === "confirm" ? (
<motion.div
key="confirm"
initial={{ opacity: 0 }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ function SlackStepBody({
Add to Slack
</a>
<p className="mt-2 text-center text-[11px] font-medium leading-[1.5] text-[#525D6E]">
Starts your 14-day free trial. No credit card needed.
Included in your 14-day trial.
</p>
</div>
)
Expand Down
Loading
Loading