diff --git a/turbo.json b/turbo.json index 48f0c04223..936b6c398d 100644 --- a/turbo.json +++ b/turbo.json @@ -17,17 +17,14 @@ "NEXT_PUBLIC_POSTHOG_KEY", "NEXT_PUBLIC_POSTHOG_HOST", "JITSU_TRACKER_ACCESS_KEY", - "JITSU_TRACKER_HOST" + "JITSU_TRACKER_HOST", + "PRO_PLAN_MONTHLY_REDIRECT_URL", + "PRO_PLAN_YEARLY_REDIRECT_URL" ], "pipeline": { "build": { - "dependsOn": [ - "^build" - ], - "outputs": [ - ".next/**", - "dist/**" - ] + "dependsOn": ["^build"], + "outputs": [".next/**", "dist/**"] }, "web#develop": { "cache": false, @@ -89,9 +86,7 @@ ] }, "test": { - "dependsOn": [ - "^build" - ], + "dependsOn": ["^build"], "outputs": [] }, "lint": { diff --git a/web/components/license/index.ts b/web/components/license/index.ts new file mode 100644 index 0000000000..ddc06de25f --- /dev/null +++ b/web/components/license/index.ts @@ -0,0 +1 @@ +export * from "./pro-plan-modal"; diff --git a/web/components/license/pro-plan-modal.tsx b/web/components/license/pro-plan-modal.tsx new file mode 100644 index 0000000000..b75eaddc15 --- /dev/null +++ b/web/components/license/pro-plan-modal.tsx @@ -0,0 +1,193 @@ +import { FC, Fragment, useState } from "react"; +import { Dialog, Transition, Tab } from "@headlessui/react"; +import { CheckCircle } from "lucide-react"; +import { useMobxStore } from "lib/mobx/store-provider"; + +function classNames(...classes: any[]) { + return classes.filter(Boolean).join(" "); +} + +const PRICING_CATEGORIES = ["Monthly", "Yearly"]; + +const MONTHLY_PLAN_ITEMS = [ + "White-glove onboarding for your use-cases", + "Bespoke implementation", + "Priority integrations", + "Priority Support and SLAs", + "Early access to all paid features", + "Locked-in discount for a whole year", +]; + +const YEARLY_PLAN_ITEMS = [ + "White-glove onboarding for your use-cases", + "Bespoke implementation", + "Priority integrations", + "Priority Support and SLAs", + "Early access to all paid features", + "Tiered discounts for the second and third years", +]; + +export type ProPlanModalProps = { + isOpen: boolean; + handleClose: () => void; +}; + +export const ProPlanModal: FC = (props) => { + const { isOpen, handleClose } = props; + // store + const { + trackEvent: { captureEvent }, + } = useMobxStore(); + // states + const [tabIndex, setTabIndex] = useState(0); + + const handleProPlaneMonthRedirection = () => { + if (process.env.PRO_PLAN_MONTHLY_REDIRECT_URL) { + window.open(process.env.PRO_PLAN_MONTHLY_REDIRECT_URL, "_blank"); + captureEvent("pro_plan_modal_month_redirection"); + } + }; + + const handleProPlanYearlyRedirection = () => { + if (process.env.PRO_PLAN_YEARLY_REDIRECT_URL) { + window.open(process.env.PRO_PLAN_YEARLY_REDIRECT_URL, "_blank"); + captureEvent("pro_plan_modal_yearly_redirection"); + } + }; + + return ( + + + +
+ + +
+
+ + + + Early-adopter pricing for believers + +
+

+ Build Plane to your specs. You decide what we prioritize and build for everyone. Also get tailored + onboarding + implementation and priority support. +

+ +
+ + {PRICING_CATEGORIES.map((category, index) => ( + + classNames( + "w-full rounded-lg py-2 text-sm font-medium leading-5 text-slate-800", + "ring-white/60 ring-offset-2 ring-offset-blue-400 focus:outline-none", + selected + ? "bg-white text-blue-700 shadow" + : "text-blue-100 hover:bg-white/[0.12] hover:text-slate-600" + ) + } + onClick={() => setTabIndex(index)} + > + <> + {category} + {category === "Yearly" && ( + + -28% + + )} + + + ))} + +
+ + + +

+ $7 + /user/month +

+
    + {MONTHLY_PLAN_ITEMS.map((item) => ( +
  • +

    + + {item} +

    +
  • + ))} +
+
+
+
+ +
+
+ + +

+ $5 + /user/month +

+
    + {YEARLY_PLAN_ITEMS.map((item) => ( +
  • +

    + + {item} +

    +
  • + ))} +
+
+
+
+ +
+
+ + + +
+ + +
+
+
+
+ ); +}; diff --git a/web/components/workspace/help-section.tsx b/web/components/workspace/help-section.tsx index 23663afcf1..37789dbb17 100644 --- a/web/components/workspace/help-section.tsx +++ b/web/components/workspace/help-section.tsx @@ -11,6 +11,8 @@ import { FileText, HelpCircle, MessagesSquare, MoveLeft, Zap } from "lucide-reac import { DiscordIcon, GithubIcon } from "@plane/ui"; // assets import packageJson from "package.json"; +// components +import { ProPlanModal } from "components/license"; const helpOptions = [ { @@ -41,10 +43,13 @@ export interface WorkspaceHelpSectionProps { } export const WorkspaceHelpSection: React.FC = observer(() => { + // states + const [isProPlanModalOpen, setIsProPlanModalOpen] = useState(false); // store const { theme: { sidebarCollapsed, toggleSidebar }, commandPalette: { toggleShortcutModal }, + trackEvent: { captureEvent }, } = useMobxStore(); // states const [isNeedHelpOpen, setIsNeedHelpOpen] = useState(false); @@ -55,16 +60,25 @@ export const WorkspaceHelpSection: React.FC = observe const isCollapsed = sidebarCollapsed || false; + const handleProPlanModalOpen = () => { + setIsProPlanModalOpen(true); + captureEvent("pro_plan_modal_opened"); + }; + return ( <> + setIsProPlanModalOpen(false)} />
{!isCollapsed && ( -
- Free Plan +
+ Plan Pro
)}
diff --git a/web/store/event-tracker.store.ts b/web/store/event-tracker.store.ts index ff507177f9..0beaa2fc08 100644 --- a/web/store/event-tracker.store.ts +++ b/web/store/event-tracker.store.ts @@ -10,6 +10,7 @@ export interface ITrackEventStore { payload: object | [] | null, group?: { isGrouping: boolean | null; groupType: string | null; gorupId: string | null } | null ) => void; + captureEvent: (eventName: string, payload?: any) => void; } export class TrackEventStore implements ITrackEventStore { @@ -74,4 +75,12 @@ export class TrackEventStore implements ITrackEventStore { } this.setTrackElement(""); }; + + captureEvent = (eventName: string, payload?: any) => { + try { + posthog?.capture(eventName, payload); + } catch (error) { + console.log(error); + } + }; }