mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-07-09 17:18:51 +00:00
Move Granola connector to Pro plan (#1132)
## Summary - move Granola from Max-gated to Pro-gated in Nova integrations and add-connection flows - add Granola to Pro plan card connector copy in billing - update Granola connector docs to say Pro Plan or higher ## Testing - bunx biome check apps/web/components/settings/billing.tsx apps/web/components/integrations-view.tsx apps/web/components/add-document/connections.tsx apps/docs/connectors/granola.mdx - git diff --check Note: onboarding-brain was intentionally left unchanged.
This commit is contained in:
parent
def847af5f
commit
00d481e8c0
4 changed files with 35 additions and 33 deletions
|
|
@ -7,7 +7,7 @@ icon: "/images/granola.svg"
|
|||
Connect Granola to sync AI meeting notes and transcripts into your Supermemory knowledge base. The connector uses a Granola API key, so there is no OAuth redirect flow.
|
||||
|
||||
<Note>
|
||||
The Granola connector requires a **Max Plan** or higher in Supermemory and a Granola plan that can create API keys. In Granola, create one from **Settings > Connectors > API keys**.
|
||||
The Granola connector requires a **Pro Plan** or higher in Supermemory and a Granola plan that can create API keys. In Granola, create one from **Settings > Connectors > API keys**.
|
||||
</Note>
|
||||
|
||||
## Quick Setup
|
||||
|
|
|
|||
|
|
@ -310,9 +310,6 @@ export function ConnectContent({ selectedProject }: ConnectContentProps) {
|
|||
const queryClient = useQueryClient()
|
||||
const autumn = useCustomer()
|
||||
const isProUser = hasActivePlan(autumn.data?.subscriptions, "api_pro")
|
||||
// Granola is a Max-tier (and above) connector, like the Gmail connector.
|
||||
// Lower plans see it but can't connect — the API-key modal stays gated.
|
||||
const isMaxUser = hasActivePlan(autumn.data?.subscriptions, "api_max")
|
||||
const [connectingProvider, setConnectingProvider] =
|
||||
useState<ConnectorProvider | null>(null)
|
||||
const [granolaModalOpen, setGranolaModalOpen] = useState(false)
|
||||
|
|
@ -608,15 +605,15 @@ export function ConnectContent({ selectedProject }: ConnectContentProps) {
|
|||
</div>
|
||||
) : provider === "granola" ? (
|
||||
<>
|
||||
{!isMaxUser && (
|
||||
{!isProUser && (
|
||||
<span className="bg-[#0054AD] text-[#FAFAFA] text-[10px] font-bold px-1.5 py-[2px] rounded-[3px] uppercase tracking-wide">
|
||||
Max
|
||||
Pro
|
||||
</span>
|
||||
)}
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (!isMaxUser) {
|
||||
handleUpgrade("api_max")
|
||||
if (!isProUser) {
|
||||
handleUpgrade("api_pro")
|
||||
return
|
||||
}
|
||||
setGranolaModalOpen(true)
|
||||
|
|
@ -624,7 +621,7 @@ export function ConnectContent({ selectedProject }: ConnectContentProps) {
|
|||
disabled={isUpgrading || autumn.isLoading}
|
||||
className="bg-[#4BA0FA] text-black hover:bg-[#4BA0FA]/90 text-[14px] font-medium px-3 py-1.5 h-8"
|
||||
>
|
||||
{!isMaxUser
|
||||
{!isProUser
|
||||
? isUpgrading || autumn.isLoading
|
||||
? "Upgrading..."
|
||||
: "Upgrade"
|
||||
|
|
@ -795,8 +792,8 @@ export function ConnectContent({ selectedProject }: ConnectContentProps) {
|
|||
<DropdownMenuItem
|
||||
disabled={isUpgrading || autumn.isLoading}
|
||||
onClick={() => {
|
||||
if (!isMaxUser) {
|
||||
handleUpgrade("api_max")
|
||||
if (!isProUser) {
|
||||
handleUpgrade("api_pro")
|
||||
return
|
||||
}
|
||||
setGranolaModalOpen(true)
|
||||
|
|
@ -807,16 +804,16 @@ export function ConnectContent({ selectedProject }: ConnectContentProps) {
|
|||
<div className="flex flex-col gap-0.5 min-w-0">
|
||||
<span className="flex items-center gap-1.5 text-[14px] font-medium text-[#FAFAFA] leading-tight">
|
||||
Granola
|
||||
{!isMaxUser && (
|
||||
{!isProUser && (
|
||||
<span className="bg-[#0054AD] text-[#FAFAFA] text-[9px] font-bold px-1 py-px rounded-[3px] uppercase tracking-wide">
|
||||
Max
|
||||
Pro
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
<span className="text-[11px] text-[#737373] leading-tight">
|
||||
{isMaxUser
|
||||
{isProUser
|
||||
? "Meeting notes & transcripts"
|
||||
: "Upgrade to Max"}
|
||||
: "Upgrade to Pro"}
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
|
|
@ -881,7 +878,7 @@ export function ConnectContent({ selectedProject }: ConnectContentProps) {
|
|||
<>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleUpgrade}
|
||||
onClick={() => handleUpgrade()}
|
||||
className="underline text-[#737373] hover:text-white transition-colors cursor-pointer"
|
||||
>
|
||||
Upgrade to Pro
|
||||
|
|
@ -948,8 +945,8 @@ export function ConnectContent({ selectedProject }: ConnectContentProps) {
|
|||
/>
|
||||
|
||||
<GranolaConnectModal
|
||||
open={isMaxUser && granolaModalOpen}
|
||||
onOpenChange={(open) => setGranolaModalOpen(open && isMaxUser)}
|
||||
open={isProUser && granolaModalOpen}
|
||||
onOpenChange={(open) => setGranolaModalOpen(open && isProUser)}
|
||||
containerTags={[selectedProject]}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -457,7 +457,8 @@ const SECTIONS: Array<{
|
|||
tagline: "Sync AI meeting notes into your memory",
|
||||
simpleTitle: "Your meeting notes, ready to recall",
|
||||
icon: <Granola className="size-6" />,
|
||||
max: true,
|
||||
pro: true,
|
||||
docsUrl: "https://supermemory.ai/docs/connectors/granola",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -1077,6 +1078,11 @@ const CONNECTOR_META: Record<
|
|||
icon: <OneDrive className="size-6" />,
|
||||
documentLabel: "documents",
|
||||
},
|
||||
granola: {
|
||||
name: "Granola",
|
||||
icon: <Granola className="size-6" />,
|
||||
documentLabel: "notes",
|
||||
},
|
||||
}
|
||||
|
||||
interface PluginEntry {
|
||||
|
|
@ -1527,6 +1533,7 @@ const CONNECTOR_SMALL_ICON: Record<ConnectorProvider, ReactNode> = {
|
|||
"google-drive": <GoogleDrive className="size-3.5" />,
|
||||
notion: <Notion className="size-3.5" />,
|
||||
onedrive: <OneDrive className="size-3.5" />,
|
||||
granola: <Granola className="size-3.5" />,
|
||||
}
|
||||
|
||||
function pluginIconNode(iconSrc: string | null): ReactNode {
|
||||
|
|
@ -1589,6 +1596,9 @@ function resolveDocSource(
|
|||
if (type.includes("onedrive") || type.includes("microsoft")) {
|
||||
return { label: "OneDrive", icon: <OneDrive className="size-3.5" /> }
|
||||
}
|
||||
if (type.includes("granola")) {
|
||||
return { label: "Granola", icon: <Granola className="size-3.5" /> }
|
||||
}
|
||||
const host = hostnameOf(doc.url)
|
||||
if (host) {
|
||||
return { label: host, icon: <Globe className="size-3.5 text-[#737373]" /> }
|
||||
|
|
@ -2386,8 +2396,6 @@ export function IntegrationsView({
|
|||
const autumn = useCustomer({ queryOptions: { enabled: !publicMode } })
|
||||
const hasProProduct =
|
||||
!publicMode && hasActivePlan(autumn.data?.subscriptions, "api_pro")
|
||||
const hasMaxProduct =
|
||||
!publicMode && hasActivePlan(autumn.data?.subscriptions, "api_max")
|
||||
const isAutumnLoading = !publicMode && autumn.isLoading
|
||||
|
||||
const [connectingPlugin, setConnectingPlugin] = useState<string | null>(null)
|
||||
|
|
@ -3066,9 +3074,7 @@ export function IntegrationsView({
|
|||
case "connector": {
|
||||
const count = connectionsByProvider[item.provider].length
|
||||
const isGranola = item.provider === "granola"
|
||||
const needsPlanUpgrade = isGranola
|
||||
? !hasMaxProduct
|
||||
: !isAutumnLoading && !hasProProduct
|
||||
const needsPlanUpgrade = !isAutumnLoading && !hasProProduct
|
||||
if (count > 0) {
|
||||
return (
|
||||
<div className="flex w-full items-center justify-between gap-2">
|
||||
|
|
@ -3080,8 +3086,8 @@ export function IntegrationsView({
|
|||
onClick={() => {
|
||||
trackCard(item)
|
||||
if (isGranola) {
|
||||
if (!hasMaxProduct) {
|
||||
handleUpgrade("api_max")
|
||||
if (!hasProProduct) {
|
||||
handleUpgrade("api_pro")
|
||||
return
|
||||
}
|
||||
setGranolaModalOpen(true)
|
||||
|
|
@ -3101,9 +3107,7 @@ export function IntegrationsView({
|
|||
}
|
||||
if (needsPlanUpgrade) {
|
||||
return (
|
||||
<PillButton
|
||||
onClick={() => handleUpgrade(isGranola ? "api_max" : "api_pro")}
|
||||
>
|
||||
<PillButton onClick={() => handleUpgrade("api_pro")}>
|
||||
<Zap className="size-3.5 text-[#4BA0FA]" /> Upgrade
|
||||
</PillButton>
|
||||
)
|
||||
|
|
@ -3114,8 +3118,8 @@ export function IntegrationsView({
|
|||
onClick={() => {
|
||||
trackCard(item)
|
||||
if (isGranola) {
|
||||
if (!hasMaxProduct) {
|
||||
handleUpgrade("api_max")
|
||||
if (!hasProProduct) {
|
||||
handleUpgrade("api_pro")
|
||||
return
|
||||
}
|
||||
setGranolaModalOpen(true)
|
||||
|
|
@ -3895,8 +3899,8 @@ export function IntegrationsView({
|
|||
</Dialog>
|
||||
|
||||
<GranolaConnectModal
|
||||
open={hasMaxProduct && granolaModalOpen}
|
||||
onOpenChange={(open) => setGranolaModalOpen(open && hasMaxProduct)}
|
||||
open={hasProProduct && granolaModalOpen}
|
||||
onOpenChange={(open) => setGranolaModalOpen(open && hasProProduct)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -148,6 +148,7 @@ const PLAN_CARDS: PlanCardDefinition[] = [
|
|||
description: "For people building with AI memory",
|
||||
features: [
|
||||
"Auto top-up when balance runs low",
|
||||
"Google Drive, Notion, OneDrive & Granola connectors",
|
||||
"All plugins (Claude Code, Cursor, Hermes...)",
|
||||
"Priority support",
|
||||
],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue