Skip to content
Draft
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
2 changes: 1 addition & 1 deletion plugins/hubspot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"dependencies": {
"@tanstack/react-query": "^5.87.4",
"classnames": "^2.5.1",
"framer-plugin": "^3.6.0",
"framer-plugin": "^3.10.2",
"motion": "^12.23.12",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Expand Down
9 changes: 9 additions & 0 deletions plugins/hubspot/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
AccountPage,
CanvasMenuPage,
ChatPage,
CMSPage,
FormsInstallationPage,
FormsPage,
LearnMoreTrackingPage,
Expand Down Expand Up @@ -83,6 +84,14 @@ const routes: Route[] = [
},
],
},
{
path: "/cms",
element: CMSPage,
title: "CMS",
size: {
height: 345,
},
},
],
},
{
Expand Down
20 changes: 10 additions & 10 deletions plugins/hubspot/src/components/Icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,6 @@ export const MessageIcon = () => (
</svg>
)

export const LightningIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18">
<path
d="M 0 4 C 0 1.791 1.791 0 4 0 L 14 0 C 16.209 0 18 1.791 18 4 L 18 14 C 18 16.209 16.209 18 14 18 L 4 18 C 1.791 18 0 16.209 0 14 Z M 5.021 10.099 C 4.79 10.499 5.078 11 5.541 11 L 8.401 11 C 8.713 11 8.949 11.283 8.893 11.589 L 8.545 13.5 C 8.439 14.086 9.229 14.377 9.528 13.861 L 12.979 7.901 C 13.21 7.501 12.922 7 12.459 7 L 9.599 7 C 9.287 7 9.051 6.717 9.107 6.411 L 9.455 4.5 C 9.561 3.914 8.771 3.623 8.472 4.139 Z"
fill="#999999"
></path>
</svg>
)

export const CaretLeftIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12">
<path
Expand All @@ -76,7 +67,16 @@ export const DatabaseIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18">
<path
d="M 0 4 C 0 1.791 1.791 0 4 0 L 14 0 C 16.209 0 18 1.791 18 4 L 18 14 C 18 16.209 16.209 18 14 18 L 4 18 C 1.791 18 0 16.209 0 14 Z M 4.5 5.75 C 4.5 6.993 6.515 8 9 8 C 11.485 8 13.5 6.993 13.5 5.75 C 13.5 4.507 11.485 3.5 9 3.5 C 6.515 3.5 4.5 4.507 4.5 5.75 Z M 13.5 7.1 C 13.5 8.343 11.485 9.35 9 9.35 C 6.515 9.35 4.5 8.343 4.5 7.1 C 4.5 7.1 4.5 8.279 4.5 8.9 C 4.5 10.143 6.515 11.15 9 11.15 C 11.485 11.15 13.5 10.143 13.5 8.9 C 13.5 8.279 13.5 7.1 13.5 7.1 Z M 13.5 10.25 C 13.5 11.493 11.485 12.5 9 12.5 C 6.515 12.5 4.5 11.493 4.5 10.25 C 4.5 10.25 4.5 11.429 4.5 12.05 C 4.5 13.293 6.515 14.3 9 14.3 C 11.485 14.3 13.5 13.293 13.5 12.05 C 13.5 11.429 13.5 10.25 13.5 10.25 Z"
fill="#888888"
fill="#999999"
></path>
</svg>
)

export const CMSCollectionIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 13 15" fill="none">
<path
d="M 6.5 0 C 10.09 0 13 1.38 13 3.083 C 13 4.785 10.09 6.165 6.5 6.165 C 2.91 6.165 0 4.785 0 3.083 C 0 1.38 2.91 0 6.5 0 Z M 13 7.398 C 13 9.101 10.09 10.481 6.5 10.481 C 2.91 10.481 0 9.101 0 7.398 C 0 6.547 0 4.932 0 4.932 C 0 6.635 2.91 8.015 6.5 8.015 C 10.09 8.015 13 6.635 13 4.932 C 13 4.95 13 6.552 13 7.398 Z M 13 11.714 C 13 13.417 10.09 14.797 6.5 14.797 C 2.91 14.797 0 13.417 0 11.714 C 0 10.863 0 9.248 0 9.248 C 0 10.95 2.91 12.331 6.5 12.331 C 10.09 12.331 13 10.95 13 9.248 C 13 9.266 13 10.868 13 11.714 Z"
fill="currentColor"
></path>
</svg>
)
Expand Down
126 changes: 126 additions & 0 deletions plugins/hubspot/src/pages/canvas/CMS.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { framer, useIsAllowedTo } from "framer-plugin"
import { useEffect, useState } from "react"
import { Button } from "../../components/Button"
import { CenteredSpinner } from "../../components/CenteredSpinner"
import { CMSCollectionIcon } from "../../components/Icons"
import { ScrollFadeContainer } from "../../components/ScrollFadeContainer"

export default function CMSPage() {
const [collections, setCollections] = useState<{ id: string; name: string }[]>([])
const [isLoading, setIsLoading] = useState(true)
const [isCreating, setIsCreating] = useState(false)
const isAllowedToCreateCollection = useIsAllowedTo("createManagedCollection")

useEffect(() => {
const loadCollections = async () => {
try {
const allCollections = await framer.getCollections()
const thisPluginCollections = allCollections
.filter(collection => collection.managedBy === "thisPlugin")
.map(collection => ({
id: collection.id,
name: collection.name,
}))
setCollections(thisPluginCollections)
} catch (error) {
console.error("Failed to load collections:", error)
framer.notify("Failed to load collections", { variant: "error" })
} finally {
setIsLoading(false)
}
}

void loadCollections()
}, [])

const handleCollectionClick = async (collectionId: string) => {
try {
await framer.navigateTo(collectionId)
} catch (error) {
console.error("Failed to navigate to collection:", error)
framer.notify("Failed to open collection", { variant: "error" })
}
}

const handleCreateCollection = async () => {
if (!isAllowedToCreateCollection) {
framer.notify("You are not allowed to create collections", { variant: "error" })
return
}

try {
setIsCreating(true)
const name = await findAvailableCollectionName("HubSpot")
const collection = await framer.createManagedCollection(name)
framer.notify("Created a new collection. Click Sync to sync data from HubSpot.")
await framer.navigateTo(collection.id)
} catch (error) {
console.error("Failed to create collection:", error)
framer.notify(`Failed to create collection: ${error instanceof Error ? error.message : "Unknown error"}`, {
variant: "error",
})
} finally {
setIsCreating(false)
}
}

if (isLoading) return <CenteredSpinner />

return (
<div className="flex flex-col gap-0 h-full p-[15px]">
{collections.length > 0 ? (
<ScrollFadeContainer className="col flex-1 gap-0" height={226}>
{collections.map(collection => (
<div
key={collection.id}
className="h-[30px] text-secondary hover:text-primary cursor-pointer px-[10px] flex flex-row items-center hover:bg-tertiary rounded-lg gap-2"
onClick={() => void handleCollectionClick(collection.id)}
>
<CMSCollectionIcon />
<span>{collection.name}</span>
</div>
))}
</ScrollFadeContainer>
) : (
<div className="flex justify-center items-center flex-1">
<p className="text-tertiary text-center max-w-[200px]">
No HubSpot collections yet. Create one to get started.
</p>
</div>
)}

<div className="col-lg sticky top-0 left-0">
<hr />
<Button
className="w-full"
onClick={() => void handleCreateCollection()}
isLoading={isCreating}
disabled={!isAllowedToCreateCollection}
title={isAllowedToCreateCollection ? undefined : "Insufficient permissions"}
>
Create New Collection
</Button>
</div>
</div>
)
}

async function findAvailableCollectionName(baseName: string): Promise<string> {
const collections = await framer.getCollections()
const existingNames = new Set(collections.map(c => c.name))

// Check if base name is available
if (!existingNames.has(baseName)) {
return baseName
}

// Try numbered variants: "HubSpot 2", "HubSpot 3", etc.
let counter = 2
let candidateName = `${baseName} ${counter}`
while (existingNames.has(candidateName)) {
counter++
candidateName = `${baseName} ${counter}`
}

return candidateName
}
10 changes: 2 additions & 8 deletions plugins/hubspot/src/pages/canvas/Menu.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import cx from "classnames"
import { framer } from "framer-plugin"
import { useEffect } from "react"
import { useLocation } from "wouter"
import { useAccountQuery, useFormsQuery, useInboxesQuery, useMeetingsQuery, useUserQuery } from "../../api"
import { ChartIcon, FormsIcon, LightningIcon, MeetingsIcon, MessageIcon, PersonIcon } from "../../components/Icons"
import { ChartIcon, DatabaseIcon, FormsIcon, MeetingsIcon, MessageIcon, PersonIcon } from "../../components/Icons"
import { Logo } from "../../components/Logo"

const queryHooks = {
Expand Down Expand Up @@ -76,12 +75,7 @@ export default function MenuPage() {
<MenuOption title="Tracking" to="/canvas/tracking" icon={<ChartIcon />} />
<MenuOption title="Meetings" to="/canvas/meetings" icon={<MeetingsIcon />} />
<MenuOption title="Chats" to="/canvas/chat" icon={<MessageIcon />} className="gap-[7px]" />
<MenuOption
title="Events"
to="/canvas/events"
icon={<LightningIcon />}
onClick={() => framer.notify("The events feature will be out soon", { variant: "info" })}
/>
<MenuOption title="CMS" to="/canvas/cms" icon={<DatabaseIcon />} />
<MenuOption title="Account" to="/canvas/account" icon={<PersonIcon />} />
</div>
</main>
Expand Down
1 change: 1 addition & 0 deletions plugins/hubspot/src/pages/canvas/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { default as AccountPage } from "./Account"
export { default as ChatPage } from "./Chat"
export { default as CMSPage } from "./CMS"
export { default as FormsPage } from "./forms"
export { default as FormsInstallationPage } from "./forms/installation"
export { default as MeetingsPage } from "./Meetings"
Expand Down
4 changes: 2 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4573,7 +4573,7 @@ __metadata:
languageName: node
linkType: hard

"framer-plugin@npm:3.10.2":
"framer-plugin@npm:3.10.2, framer-plugin@npm:^3.10.2":
version: 3.10.2
resolution: "framer-plugin@npm:3.10.2"
peerDependencies:
Expand Down Expand Up @@ -4899,7 +4899,7 @@ __metadata:
"@types/react": "npm:^18.3.24"
"@types/react-dom": "npm:^18.3.7"
classnames: "npm:^2.5.1"
framer-plugin: "npm:^3.6.0"
framer-plugin: "npm:^3.10.2"
motion: "npm:^12.23.12"
react: "npm:^18.3.1"
react-dom: "npm:^18.3.1"
Expand Down
Loading