diff --git a/packages/ui/src/modals/constants.ts b/packages/ui/src/modals/constants.ts index fd0c828316..0cb268fc88 100644 --- a/packages/ui/src/modals/constants.ts +++ b/packages/ui/src/modals/constants.ts @@ -8,4 +8,6 @@ export enum EModalWidth { XXL = "sm:max-w-2xl", XXXL = "sm:max-w-3xl", XXXXL = "sm:max-w-4xl", + VXL = "sm:max-w-5xl", + VIXL = "sm:max-w-6xl", } diff --git a/packages/ui/src/modals/modal-core.tsx b/packages/ui/src/modals/modal-core.tsx index 3ef3f801e2..534cb27ce1 100644 --- a/packages/ui/src/modals/modal-core.tsx +++ b/packages/ui/src/modals/modal-core.tsx @@ -11,9 +11,17 @@ type Props = { isOpen: boolean; position?: EModalPosition; width?: EModalWidth; + className?: string; }; export const ModalCore: React.FC = (props) => { - const { children, handleClose, isOpen, position = EModalPosition.CENTER, width = EModalWidth.XXL } = props; + const { + children, + handleClose, + isOpen, + position = EModalPosition.CENTER, + width = EModalWidth.XXL, + className = "", + } = props; return ( @@ -44,7 +52,8 @@ export const ModalCore: React.FC = (props) => { {children} diff --git a/turbo.json b/turbo.json index 0325c3b2b4..64391d04a4 100644 --- a/turbo.json +++ b/turbo.json @@ -22,36 +22,28 @@ "SENTRY_PROJECT_ID", "NEXT_PUBLIC_SENTRY_ENVIRONMENT", "NEXT_PUBLIC_SENTRY_DSN", - "SENTRY_MONITORING_ENABLED" + "SENTRY_MONITORING_ENABLED", + "NEXT_PUBLIC_PRO_PLAN_MONTHLY_PAYMENT_URL", + "NEXT_PUBLIC_PRO_PLAN_YEARLY_PAYMENT_URL", + "NEXT_PUBLIC_PLANE_ONE_PAYMENT_URL" ], "tasks": { "build": { - "dependsOn": [ - "^build" - ], - "outputs": [ - ".next/**", - "dist/**" - ] + "dependsOn": ["^build"], + "outputs": [".next/**", "dist/**"] }, "develop": { "cache": false, "persistent": true, - "dependsOn": [ - "^build" - ] + "dependsOn": ["^build"] }, "dev": { "cache": false, "persistent": true, - "dependsOn": [ - "^build" - ] + "dependsOn": ["^build"] }, "test": { - "dependsOn": [ - "^build" - ], + "dependsOn": ["^build"], "outputs": [] }, "lint": { diff --git a/web/ce/components/workspace/edition-badge.tsx b/web/ce/components/workspace/edition-badge.tsx index 5b81bae785..ce2c46bc2e 100644 --- a/web/ce/components/workspace/edition-badge.tsx +++ b/web/ce/components/workspace/edition-badge.tsx @@ -1,19 +1,35 @@ +import { useState } from "react"; import { observer } from "mobx-react"; // ui -import { Tooltip } from "@plane/ui"; +import { Button, Tooltip } from "@plane/ui"; // hooks import { usePlatformOS } from "@/hooks/use-platform-os"; // assets import packageJson from "package.json"; +// local components +import { PaidPlanUpgradeModal } from "./upgrade"; export const WorkspaceEditionBadge = observer(() => { const { isMobile } = usePlatformOS(); + // states + const [isPaidPlanPurchaseModalOpen, setIsPaidPlanPurchaseModalOpen] = useState(false); return ( - -
- Community -
-
+ <> + setIsPaidPlanPurchaseModalOpen(false)} + /> + + + + ); }); diff --git a/web/ce/components/workspace/upgrade/index.tsx b/web/ce/components/workspace/upgrade/index.tsx new file mode 100644 index 0000000000..25115ef33c --- /dev/null +++ b/web/ce/components/workspace/upgrade/index.tsx @@ -0,0 +1,3 @@ +export * from "./pro-plan-upgrade"; +export * from "./one-plan-upgrade"; +export * from "./paid-plans-upgrade-modal"; diff --git a/web/ce/components/workspace/upgrade/one-plan-upgrade.tsx b/web/ce/components/workspace/upgrade/one-plan-upgrade.tsx new file mode 100644 index 0000000000..5bf05115a8 --- /dev/null +++ b/web/ce/components/workspace/upgrade/one-plan-upgrade.tsx @@ -0,0 +1,55 @@ +import { FC } from "react"; +import { CheckCircle } from "lucide-react"; +// helpers +import { cn } from "@/helpers/common.helper"; + +export type OnePlanUpgradeProps = { + features: string[]; + verticalFeatureList?: boolean; + extraFeatures?: string | React.ReactNode; +}; + +export const OnePlanUpgrade: FC = (props) => { + const { features, verticalFeatureList = false, extraFeatures } = props; + // env + const PLANE_ONE_PAYMENT_URL = process.env.NEXT_PUBLIC_PLANE_ONE_PAYMENT_URL ?? "https://plane.so/one"; + + return ( +
+
+
+
Plane One
+
$799
+
for two years’ support and updates
+
+ +
+
Everything in Free +
+
    + {features.map((feature) => ( +
  • +

    + + {feature} +

    +
  • + ))} +
+ {extraFeatures &&
{extraFeatures}
} +
+
+ ); +}; diff --git a/web/ce/components/workspace/upgrade/paid-plans-upgrade-modal.tsx b/web/ce/components/workspace/upgrade/paid-plans-upgrade-modal.tsx new file mode 100644 index 0000000000..0f6d0681eb --- /dev/null +++ b/web/ce/components/workspace/upgrade/paid-plans-upgrade-modal.tsx @@ -0,0 +1,113 @@ +import { FC } from "react"; +// types +import { CircleX } from "lucide-react"; +// services +import { EModalWidth, ModalCore } from "@plane/ui"; +// plane web components +import { cn } from "@/helpers/common.helper"; +// local components +import { OnePlanUpgrade } from "./one-plan-upgrade"; +import { ProPlanUpgrade } from "./pro-plan-upgrade"; + +const PRO_PLAN_FEATURES = [ + "More Cycles features", + "Full Time Tracking + Bulk Ops", + "Workflow manager", + "Automations", + "Popular integrations", + "Plane AI", +]; + +const ONE_PLAN_FEATURES = [ + "OIDC + SAML for SSO", + "Active Cycles", + "Real-time collab + public views and page", + "Link pages in issues and vice-versa", + "Time-tracking + limited bulk ops", + "Docker, Kubernetes and more", +]; + +const FREE_PLAN_UPGRADE_FEATURES = [ + "OIDC + SAML for SSO", + "Time tracking and bulk ops", + "Integrations", + "Public views and pages", +]; + +export type PaidPlanUpgradeModalProps = { + isOpen: boolean; + handleClose: () => void; +}; + +export const PaidPlanUpgradeModal: FC = (props) => { + const { isOpen, handleClose } = props; + + return ( + +
+
+
+
Upgrade to a paid plan and unlock missing features.
+
+

+ Active Cycles, time tracking, bulk ops, and other features are waiting for you on one of our paid plans. + Upgrade today to unlock features your teams need yesterday. +

+
+ {/* Free plan details */} +
+
+ + Your plan + +
+
+
Free
+
$0 a user per month
+
+
+
    + {FREE_PLAN_UPGRADE_FEATURES.map((feature) => ( +
  • +

    + + {feature} +

    +
  • + ))} +
+
+
+
+ + +
+
+
+ ); +}; diff --git a/web/ce/components/workspace/upgrade/pro-plan-upgrade.tsx b/web/ce/components/workspace/upgrade/pro-plan-upgrade.tsx new file mode 100644 index 0000000000..1f8a64222b --- /dev/null +++ b/web/ce/components/workspace/upgrade/pro-plan-upgrade.tsx @@ -0,0 +1,111 @@ +import { FC, useState } from "react"; +import { CheckCircle } from "lucide-react"; +import { Tab } from "@headlessui/react"; +// helpers +import { cn } from "@/helpers/common.helper"; + +export type ProPlanUpgradeProps = { + basePlan: "Free" | "One"; + features: string[]; + verticalFeatureList?: boolean; + extraFeatures?: string | React.ReactNode; +}; + +type TProPiceFrequency = "month" | "year"; + +type TProPlanPrice = { + key: string; + price: string; + recurring: TProPiceFrequency; +}; + +const PRO_PLAN_PRICES: TProPlanPrice[] = [ + { key: "monthly", price: "$7", recurring: "month" }, + { key: "yearly", price: "$5", recurring: "year" }, +]; + +export const ProPlanUpgrade: FC = (props) => { + const { basePlan, features, verticalFeatureList = false, extraFeatures } = props; + // states + const [selectedPlan, setSelectedPlan] = useState("month"); + // env + const PRO_PLAN_MONTHLY_PAYMENT_URL = process.env.NEXT_PUBLIC_PRO_PLAN_MONTHLY_PAYMENT_URL ?? "https://plane.so/pro"; + const PRO_PLAN_YEARLY_PAYMENT_URL = process.env.NEXT_PUBLIC_PRO_PLAN_YEARLY_PAYMENT_URL ?? "https://plane.so/pro"; + + return ( +
+ +
+ + {PRO_PLAN_PRICES.map((price: TProPlanPrice) => ( + + cn( + "w-full rounded-lg py-1.5 text-sm font-medium leading-5", + selected + ? "bg-custom-background-100 text-custom-primary-300 shadow" + : "hover:bg-custom-primary-100/5 text-custom-text-300 hover:text-custom-text-200" + ) + } + onClick={() => setSelectedPlan(price.recurring)} + > + <> + {price.recurring === "month" && ("Monthly" as string)} + {price.recurring === "year" && ("Yearly" as string)} + {price.recurring === "year" && ( + + -28% + + )} + + + ))} + +
+ + {PRO_PLAN_PRICES.map((price: TProPlanPrice) => ( + +
+
Plane Pro
+
+ {price.recurring === "month" && "$7"} + {price.recurring === "year" && "$5"} +
+
a user per month
+
+ +
+
{`Everything in ${basePlan} +`}
+
    + {features.map((feature) => ( +
  • +

    + + {feature} +

    +
  • + ))} +
+ {extraFeatures &&
{extraFeatures}
} +
+
+ ))} +
+
+
+ ); +}; diff --git a/web/core/hooks/use-app-router.tsx b/web/core/hooks/use-app-router.tsx index 4ea07abd2a..6de0049a65 100644 --- a/web/core/hooks/use-app-router.tsx +++ b/web/core/hooks/use-app-router.tsx @@ -1,6 +1,4 @@ -// type -import { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime"; -// router from next-nprogress-bar +// router from n-progress-bar import { useRouter } from "@/lib/n-progress"; -export const useAppRouter = (): AppRouterInstance => useRouter(); +export const useAppRouter = () => useRouter();