mirror of
https://github.com/makeplane/plane.git
synced 2025-12-28 16:06:33 +01:00
fix: sperating out plane one and cloud badges
This commit is contained in:
@@ -14,10 +14,10 @@ from .base import BaseAPIView
|
||||
from plane.app.permissions.workspace import WorkSpaceAdminPermission
|
||||
from plane.db.models import WorkspaceMember, Workspace
|
||||
from plane.authentication.utils.host import base_host
|
||||
from plane.utils.exception_logger import log_exception
|
||||
|
||||
|
||||
class PaymentLinkEndpoint(BaseAPIView):
|
||||
|
||||
permission_classes = [
|
||||
WorkSpaceAdminPermission,
|
||||
]
|
||||
@@ -50,6 +50,10 @@ class PaymentLinkEndpoint(BaseAPIView):
|
||||
if settings.PAYMENT_SERVER_BASE_URL:
|
||||
response = requests.post(
|
||||
f"{settings.PAYMENT_SERVER_BASE_URL}/api/payment-link/",
|
||||
headers={
|
||||
"content-type": "application/json",
|
||||
"x-api-key": settings.PAYMENT_SERVER_AUTH_TOKEN,
|
||||
},
|
||||
json={
|
||||
"workspace_id": str(workspace.id),
|
||||
"slug": slug,
|
||||
@@ -59,8 +63,8 @@ class PaymentLinkEndpoint(BaseAPIView):
|
||||
"members_list": list(workspace_members),
|
||||
"host": base_host(request=request, is_app=True),
|
||||
},
|
||||
headers={"content-type": "application/json"},
|
||||
)
|
||||
response.raise_for_status()
|
||||
response = response.json()
|
||||
return Response(response, status=status.HTTP_200_OK)
|
||||
else:
|
||||
@@ -68,7 +72,8 @@ class PaymentLinkEndpoint(BaseAPIView):
|
||||
{"error": "error fetching payment link"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
except requests.exceptions.RequestException:
|
||||
except requests.exceptions.RequestException as e:
|
||||
log_exception(e)
|
||||
return Response(
|
||||
{"error": "error fetching payment link"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
|
||||
@@ -15,10 +15,10 @@ from plane.app.permissions.workspace import (
|
||||
WorkspaceUserPermission,
|
||||
)
|
||||
from plane.db.models import WorkspaceMember, Workspace
|
||||
from plane.utils.exception_logger import log_exception
|
||||
|
||||
|
||||
class ProductEndpoint(BaseAPIView):
|
||||
|
||||
permission_classes = [
|
||||
WorkSpaceAdminPermission,
|
||||
]
|
||||
@@ -31,8 +31,12 @@ class ProductEndpoint(BaseAPIView):
|
||||
).count()
|
||||
response = requests.get(
|
||||
f"{settings.PAYMENT_SERVER_BASE_URL}/api/products/?quantity={count}",
|
||||
headers={"content-type": "application/json"},
|
||||
headers={
|
||||
"content-type": "application/json",
|
||||
"x-api-key": settings.PAYMENT_SERVER_AUTH_TOKEN,
|
||||
},
|
||||
)
|
||||
response.raise_for_status()
|
||||
response = response.json()
|
||||
return Response(response, status=status.HTTP_200_OK)
|
||||
else:
|
||||
@@ -40,7 +44,8 @@ class ProductEndpoint(BaseAPIView):
|
||||
{"error": "error fetching product details"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
except requests.exceptions.RequestException:
|
||||
except requests.exceptions.RequestException as e:
|
||||
log_exception(e)
|
||||
return Response(
|
||||
{"error": "error fetching product details"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
@@ -48,7 +53,6 @@ class ProductEndpoint(BaseAPIView):
|
||||
|
||||
|
||||
class WorkspaceProductEndpoint(BaseAPIView):
|
||||
|
||||
permission_classes = [
|
||||
WorkspaceUserPermission,
|
||||
]
|
||||
@@ -59,8 +63,12 @@ class WorkspaceProductEndpoint(BaseAPIView):
|
||||
workspace = Workspace.objects.get(slug=slug)
|
||||
response = requests.get(
|
||||
f"{settings.PAYMENT_SERVER_BASE_URL}/api/products/workspace-products/{str(workspace.id)}/",
|
||||
headers={"content-type": "application/json"},
|
||||
headers={
|
||||
"content-type": "application/json",
|
||||
"x-api-key": settings.PAYMENT_SERVER_AUTH_TOKEN,
|
||||
},
|
||||
)
|
||||
response.raise_for_status()
|
||||
response = response.json()
|
||||
return Response(response, status=status.HTTP_200_OK)
|
||||
else:
|
||||
@@ -68,7 +76,8 @@ class WorkspaceProductEndpoint(BaseAPIView):
|
||||
{"error": "error fetching product details"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
except requests.exceptions.RequestException:
|
||||
except requests.exceptions.RequestException as e:
|
||||
log_exception(e)
|
||||
return Response(
|
||||
{"error": "error fetching product details"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
|
||||
@@ -361,3 +361,4 @@ APP_BASE_URL = os.environ.get("APP_BASE_URL")
|
||||
|
||||
# Cloud server base url
|
||||
PAYMENT_SERVER_BASE_URL = os.environ.get("PAYMENT_SERVER_BASE_URL", False)
|
||||
PAYMENT_SERVER_AUTH_TOKEN = os.environ.get("PAYMENT_SERVER_AUTH_TOKEN", "")
|
||||
2
packages/types/src/payment.d.ts
vendored
2
packages/types/src/payment.d.ts
vendored
@@ -16,6 +16,6 @@ export type IPaymentProduct = {
|
||||
};
|
||||
|
||||
export type IWorkspaceProductSubscription = {
|
||||
product: FREE | PRO | ULTIMATE;
|
||||
product: "FREE" | "PRO" | "ULTIMATE";
|
||||
expiry_date: string | null;
|
||||
};
|
||||
|
||||
23
turbo.json
23
turbo.json
@@ -26,36 +26,25 @@
|
||||
"SENTRY_MONITORING_ENABLED",
|
||||
"NEXT_PUBLIC_PRO_PLAN_MONTHLY_REDIRECT_URL",
|
||||
"NEXT_PUBLIC_PRO_PLAN_YEARLY_REDIRECT_URL",
|
||||
"NEXT_PUBLIC_IS_MULTI_TENANT"
|
||||
"NEXT_PUBLIC_DISCO_BASE_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": {
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import Image from "next/image";
|
||||
import { useParams } from "next/navigation";
|
||||
import useSWR from "swr";
|
||||
// ui
|
||||
import { Tooltip, Button, getButtonStyling } from "@plane/ui";
|
||||
// hooks
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { useEventTracker, useInstance } from "@/hooks/store";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
// plane web components
|
||||
import { PlaneOneModal, CloudProductsModal, ProPlanDetailsModal } from "@/plane-web/components/license";
|
||||
// assets
|
||||
import PlaneOneLogo from "@/public/plane-logos/plane-one.svg";
|
||||
// services
|
||||
import { DiscoService } from "@/services/disco.service";
|
||||
|
||||
import packageJson from "package.json";
|
||||
|
||||
const discoService = new DiscoService();
|
||||
|
||||
export const PlaneBadge: React.FC = observer(() => {
|
||||
// params
|
||||
const { workspaceSlug } = useParams();
|
||||
// states
|
||||
const [isProPlanModalOpen, setIsProPlanModalOpen] = useState(false);
|
||||
const [isProPlanDetailsModalOpen, setProPlanDetailsModalOpen] = useState(false);
|
||||
const [isPlaneOneModalOpen, setIsPlaneOneModalOpen] = useState(false);
|
||||
// hooks
|
||||
const { captureEvent } = useEventTracker();
|
||||
const { isMobile } = usePlatformOS();
|
||||
const { instance } = useInstance();
|
||||
|
||||
// fetch workspace current plane information
|
||||
const { data } = useSWR(
|
||||
workspaceSlug ? "WORKSPACE_CURRENT_PLANE" : null,
|
||||
workspaceSlug ? () => discoService.getWorkspaceCurrentPlane(workspaceSlug.toString()) : null
|
||||
);
|
||||
|
||||
console.log("data", data);
|
||||
|
||||
const handleProPlanPurchaseModalOpen = () => {
|
||||
setIsProPlanModalOpen(true);
|
||||
captureEvent("pro_plan_modal_opened", {});
|
||||
};
|
||||
|
||||
const handlePlaneOneModalOpen = () => {
|
||||
setIsPlaneOneModalOpen(true);
|
||||
captureEvent("plane_one_modal_opened", {});
|
||||
};
|
||||
|
||||
// const handleProPlanDetailsModalOpen = () => {
|
||||
// setProPlanDetailsModalOpen(true);
|
||||
// };
|
||||
|
||||
if (process.env.NEXT_PUBLIC_IS_MULTI_TENANT === "1") {
|
||||
return (
|
||||
<>
|
||||
<CloudProductsModal isOpen={isProPlanModalOpen} handleClose={() => setIsProPlanModalOpen(false)} />
|
||||
<ProPlanDetailsModal isOpen={isProPlanDetailsModalOpen} handleClose={() => setProPlanDetailsModalOpen(false)} />
|
||||
{data && data.product === "s" && (
|
||||
<Button
|
||||
variant="outline-primary"
|
||||
className="w-1/2 cursor-pointer rounded-2xl px-3 py-1.5 text-center text-sm font-medium outline-none"
|
||||
onClick={handleProPlanPurchaseModalOpen}
|
||||
>
|
||||
Upgrade to Pro
|
||||
</Button>
|
||||
)}
|
||||
{data && data.product === "FREE" && (
|
||||
<div className="w-1/2 flex justify-start">
|
||||
<span className="items-center justify-center px-3.5 py-0.5 text-xs leading-4 rounded-xl bg-custom-primary-100/10 text-custom-primary-100">
|
||||
Pro
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (instance?.product === "plane-one") {
|
||||
return (
|
||||
<>
|
||||
<PlaneOneModal isOpen={isPlaneOneModalOpen} handleClose={() => setIsPlaneOneModalOpen(false)} />
|
||||
<Tooltip tooltipContent={`Version: ${instance.current_version}`} isMobile={isMobile}>
|
||||
<button
|
||||
tabIndex={-1}
|
||||
className={cn(
|
||||
getButtonStyling("accent-primary", "md"),
|
||||
"w-fit cursor-pointer rounded-2xl px-3 py-1.5 text-center text-sm font-medium outline-none"
|
||||
)}
|
||||
onClick={handlePlaneOneModalOpen}
|
||||
>
|
||||
<Image src={PlaneOneLogo} alt="Plane One" width={24} height={24} />
|
||||
{"Plane One"}
|
||||
</button>
|
||||
</Tooltip>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip tooltipContent={`Version: v${packageJson.version}`} isMobile={isMobile}>
|
||||
<div className="w-1/2 cursor-default rounded-md bg-green-500/10 px-2 py-1 text-center text-xs font-medium text-green-500 outline-none leading-6">
|
||||
Enterprise Edition
|
||||
</div>
|
||||
</Tooltip>
|
||||
</>
|
||||
);
|
||||
});
|
||||
60
web/ee/components/license/cloud-badge.tsx
Normal file
60
web/ee/components/license/cloud-badge.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
import useSWR from "swr";
|
||||
// ui
|
||||
import { Button } from "@plane/ui";
|
||||
// hooks
|
||||
import { useEventTracker } from "@/hooks/store";
|
||||
// enterprise imports
|
||||
import { CloudProductsModal, ProPlanDetailsModal } from "@/plane-web/components/license";
|
||||
import { useWorkspaceSubscription } from "@/plane-web/hooks/store";
|
||||
|
||||
export const CloudEditionBadge = observer(() => {
|
||||
// params
|
||||
const { workspaceSlug } = useParams();
|
||||
// states
|
||||
const [isProPlanModalOpen, setIsProPlanModalOpen] = useState(false);
|
||||
const [isProPlanDetailsModalOpen, setProPlanDetailsModalOpen] = useState(false);
|
||||
// hooks
|
||||
const { captureEvent } = useEventTracker();
|
||||
const { fetchWorkspaceSubscribedPlan, subscribedPlan } = useWorkspaceSubscription();
|
||||
// fetch workspace current plane information
|
||||
useSWR(
|
||||
workspaceSlug && process.env.NEXT_PUBLIC_DISCO_BASE_URL ? `WORKSPACE_CURRENT_PLAN_${workspaceSlug}` : null,
|
||||
workspaceSlug && process.env.NEXT_PUBLIC_DISCO_BASE_URL
|
||||
? () => fetchWorkspaceSubscribedPlan(workspaceSlug.toString())
|
||||
: null,
|
||||
{
|
||||
errorRetryCount: 2,
|
||||
}
|
||||
);
|
||||
|
||||
const handleProPlanPurchaseModalOpen = () => {
|
||||
setIsProPlanModalOpen(true);
|
||||
captureEvent("pro_plan_modal_opened", {});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<CloudProductsModal isOpen={isProPlanModalOpen} handleClose={() => setIsProPlanModalOpen(false)} />
|
||||
<ProPlanDetailsModal isOpen={isProPlanDetailsModalOpen} handleClose={() => setProPlanDetailsModalOpen(false)} />
|
||||
{subscribedPlan === "FREE" && (
|
||||
<Button
|
||||
variant="outline-primary"
|
||||
className="w-full cursor-pointer rounded-2xl px-3 py-1.5 text-center text-sm font-medium outline-none"
|
||||
onClick={handleProPlanPurchaseModalOpen}
|
||||
>
|
||||
Upgrade to Pro
|
||||
</Button>
|
||||
)}
|
||||
{subscribedPlan === "PRO" && (
|
||||
<div className="w-full flex justify-start">
|
||||
<span className="items-center justify-center px-3.5 py-0.5 text-xs leading-4 rounded-xl bg-custom-primary-100/10 text-custom-primary-100">
|
||||
Pro
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
@@ -11,9 +11,9 @@ import { setToast, TOAST_TYPE } from "@plane/ui";
|
||||
// store
|
||||
import { useEventTracker } from "@/hooks/store";
|
||||
// services
|
||||
import { DiscoService } from "@/services/disco.service";
|
||||
import { PaymentService } from "@/plane-web/services/payment.service";
|
||||
|
||||
const discoService = new DiscoService();
|
||||
const paymentService = new PaymentService();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function classNames(...classes: any[]) {
|
||||
@@ -51,12 +51,17 @@ export const CloudProductsModal: FC<CloudProductsModalProps> = (props) => {
|
||||
const { captureEvent } = useEventTracker();
|
||||
// fetch products
|
||||
const { data } = useSWR(
|
||||
workspaceSlug ? "CLOUD_PAYMENT_PRODUCTS" : null,
|
||||
workspaceSlug ? () => discoService.listProducts(workspaceSlug.toString()) : null
|
||||
workspaceSlug && process.env.NEXT_PUBLIC_DISCO_BASE_URL ? "CLOUD_PAYMENT_PRODUCTS" : null,
|
||||
workspaceSlug && process.env.NEXT_PUBLIC_DISCO_BASE_URL
|
||||
? () => paymentService.listProducts(workspaceSlug.toString())
|
||||
: null,
|
||||
{
|
||||
errorRetryCount: 2,
|
||||
}
|
||||
);
|
||||
const proProduct = data?.find((product: IPaymentProduct) => product?.type === "PRO");
|
||||
const proProduct = (data || [])?.find((product: IPaymentProduct) => product?.type === "PRO");
|
||||
const proProductPrices = orderBy(proProduct?.prices || [], ["recurring"], ["asc"]);
|
||||
console.log("data", data);
|
||||
console.log("proProductPrices", proProductPrices);
|
||||
// states
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [tabIndex, setTabIndex] = useState(0);
|
||||
@@ -66,7 +71,7 @@ export const CloudProductsModal: FC<CloudProductsModalProps> = (props) => {
|
||||
if (!workspaceSlug) return;
|
||||
setLoading(true);
|
||||
captureEvent("pro_plan_payment_link_clicked", { workspaceSlug });
|
||||
discoService
|
||||
paymentService
|
||||
.getPaymentLink(workspaceSlug.toString(), {
|
||||
price_id: priceId,
|
||||
product_id: proProduct?.id,
|
||||
|
||||
@@ -3,3 +3,5 @@ export * from "./plane-one-modal";
|
||||
export * from "./plane-one-billing";
|
||||
export * from "./plane-cloud-billing";
|
||||
export * from "./pro-plan-details-modal";
|
||||
export * from "./plane-one-badge";
|
||||
export * from "./cloud-badge";
|
||||
|
||||
48
web/ee/components/license/plane-one-badge.tsx
Normal file
48
web/ee/components/license/plane-one-badge.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import Image from "next/image";
|
||||
// ui
|
||||
import { Tooltip, getButtonStyling } from "@plane/ui";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
// hooks
|
||||
import { useInstance, useEventTracker } from "@/hooks/store";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
// plane web components
|
||||
import { PlaneOneModal } from "@/plane-web/components/license";
|
||||
// assets
|
||||
import PlaneOneLogo from "@/public/plane-logos/plane-one.svg";
|
||||
|
||||
export const PlaneOneEditionBadge = observer(() => {
|
||||
// states
|
||||
const [isPlaneOneModalOpen, setIsPlaneOneModalOpen] = useState(false);
|
||||
// hooks
|
||||
const { isMobile } = usePlatformOS();
|
||||
// store hooks
|
||||
const { instance } = useInstance();
|
||||
const { captureEvent } = useEventTracker();
|
||||
|
||||
const handlePlaneOneModalOpen = () => {
|
||||
setIsPlaneOneModalOpen(true);
|
||||
captureEvent("plane_one_modal_opened", {});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<PlaneOneModal isOpen={isPlaneOneModalOpen} handleClose={() => setIsPlaneOneModalOpen(false)} />
|
||||
<Tooltip tooltipContent={`Version: ${instance?.current_version}`} isMobile={isMobile}>
|
||||
<button
|
||||
tabIndex={-1}
|
||||
className={cn(
|
||||
getButtonStyling("accent-primary", "md"),
|
||||
"w-fit cursor-pointer rounded-2xl px-3 py-1.5 text-center text-sm font-medium outline-none"
|
||||
)}
|
||||
onClick={handlePlaneOneModalOpen}
|
||||
>
|
||||
<Image src={PlaneOneLogo} alt="Plane One" width={24} height={24} />
|
||||
{"Plane One"}
|
||||
</button>
|
||||
</Tooltip>
|
||||
</>
|
||||
);
|
||||
});
|
||||
@@ -1,100 +1,27 @@
|
||||
import { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import Image from "next/image";
|
||||
import { useParams } from "next/navigation";
|
||||
import useSWR from "swr";
|
||||
// ui
|
||||
import { Tooltip, Button, getButtonStyling } from "@plane/ui";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { Tooltip } from "@plane/ui";
|
||||
// hooks
|
||||
import { useInstance, useEventTracker } from "@/hooks/store";
|
||||
import { useInstance } from "@/hooks/store";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
// plane web components
|
||||
import { PlaneOneModal, CloudProductsModal, ProPlanDetailsModal } from "@/plane-web/components/license";
|
||||
import { useWorkspaceSubscription } from "@/plane-web/hooks/store";
|
||||
// assets
|
||||
import PlaneOneLogo from "@/public/plane-logos/plane-one.svg";
|
||||
import { PlaneOneEditionBadge, CloudEditionBadge } from "@/plane-web/components/license";
|
||||
// assets
|
||||
import packageJson from "package.json";
|
||||
|
||||
export const WorkspaceEditionBadge = observer(() => {
|
||||
// params
|
||||
const { workspaceSlug } = useParams();
|
||||
// states
|
||||
const [isProPlanModalOpen, setIsProPlanModalOpen] = useState(false);
|
||||
const [isProPlanDetailsModalOpen, setProPlanDetailsModalOpen] = useState(false);
|
||||
const [isPlaneOneModalOpen, setIsPlaneOneModalOpen] = useState(false);
|
||||
// hooks
|
||||
const { captureEvent } = useEventTracker();
|
||||
const { isMobile } = usePlatformOS();
|
||||
const { instance } = useInstance();
|
||||
const { fetchWorkspaceSubscribedPlan, subscribedPlan } = useWorkspaceSubscription();
|
||||
// fetch workspace current plane information
|
||||
useSWR(
|
||||
workspaceSlug && process.env.NEXT_PUBLIC_IS_MULTI_TENANT === "1" ? `WORKSPACE_CURRENT_PLAN_${workspaceSlug}` : null,
|
||||
workspaceSlug && process.env.NEXT_PUBLIC_IS_MULTI_TENANT === "1"
|
||||
? () => fetchWorkspaceSubscribedPlan(workspaceSlug.toString())
|
||||
: null,
|
||||
{
|
||||
errorRetryCount: 2,
|
||||
}
|
||||
);
|
||||
|
||||
const handleProPlanPurchaseModalOpen = () => {
|
||||
setIsProPlanModalOpen(true);
|
||||
captureEvent("pro_plan_modal_opened", {});
|
||||
};
|
||||
const DISCO_BASE_URL = process.env.NEXT_PUBLIC_DISCO_BASE_URL || "";
|
||||
|
||||
const handlePlaneOneModalOpen = () => {
|
||||
setIsPlaneOneModalOpen(true);
|
||||
captureEvent("plane_one_modal_opened", {});
|
||||
};
|
||||
|
||||
if (process.env.NEXT_PUBLIC_IS_MULTI_TENANT === "1") {
|
||||
return (
|
||||
<>
|
||||
<CloudProductsModal isOpen={isProPlanModalOpen} handleClose={() => setIsProPlanModalOpen(false)} />
|
||||
<ProPlanDetailsModal isOpen={isProPlanDetailsModalOpen} handleClose={() => setProPlanDetailsModalOpen(false)} />
|
||||
{subscribedPlan === "FREE" && (
|
||||
<Button
|
||||
variant="outline-primary"
|
||||
className="w-full cursor-pointer rounded-2xl px-3 py-1.5 text-center text-sm font-medium outline-none"
|
||||
onClick={handleProPlanPurchaseModalOpen}
|
||||
>
|
||||
Upgrade to Pro
|
||||
</Button>
|
||||
)}
|
||||
{subscribedPlan === "PRO" && (
|
||||
<div className="w-full flex justify-start">
|
||||
<span className="items-center justify-center px-3.5 py-0.5 text-xs leading-4 rounded-xl bg-custom-primary-100/10 text-custom-primary-100">
|
||||
Pro
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
if (DISCO_BASE_URL.length > 0) {
|
||||
return <CloudEditionBadge />;
|
||||
}
|
||||
|
||||
if (instance?.product === "plane-one") {
|
||||
return (
|
||||
<>
|
||||
<PlaneOneModal isOpen={isPlaneOneModalOpen} handleClose={() => setIsPlaneOneModalOpen(false)} />
|
||||
<Tooltip tooltipContent={`Version: ${instance.current_version}`} isMobile={isMobile}>
|
||||
<button
|
||||
tabIndex={-1}
|
||||
className={cn(
|
||||
getButtonStyling("accent-primary", "md"),
|
||||
"w-fit cursor-pointer rounded-2xl px-3 py-1.5 text-center text-sm font-medium outline-none"
|
||||
)}
|
||||
onClick={handlePlaneOneModalOpen}
|
||||
>
|
||||
<Image src={PlaneOneLogo} alt="Plane One" width={24} height={24} />
|
||||
{"Plane One"}
|
||||
</button>
|
||||
</Tooltip>
|
||||
</>
|
||||
);
|
||||
return <PlaneOneEditionBadge />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -4,7 +4,7 @@ import { API_BASE_URL } from "@/helpers/common.helper";
|
||||
// services
|
||||
import { APIService } from "@/services/api.service";
|
||||
|
||||
export class DiscoService extends APIService {
|
||||
export class PaymentService extends APIService {
|
||||
constructor() {
|
||||
super(API_BASE_URL);
|
||||
}
|
||||
@@ -2,17 +2,17 @@ import { action, makeObservable, observable, runInAction } from "mobx";
|
||||
// types
|
||||
import { IWorkspaceProductSubscription } from "@plane/types";
|
||||
// services
|
||||
import { DiscoService } from "@/services/disco.service";
|
||||
import { PaymentService } from "@/plane-web/services/payment.service";
|
||||
|
||||
const discoService = new DiscoService();
|
||||
const paymentService = new PaymentService();
|
||||
|
||||
export interface IWorkspaceSubscriptionStore {
|
||||
subscribedPlan: "FREE" | "PRO";
|
||||
subscribedPlan: "FREE" | "PRO" | "ULTIMATE";
|
||||
fetchWorkspaceSubscribedPlan: (workspaceSlug: string) => Promise<IWorkspaceProductSubscription>;
|
||||
}
|
||||
|
||||
export class WorkspaceSubscriptionStore implements IWorkspaceSubscriptionStore {
|
||||
subscribedPlan: "FREE" | "PRO" = "FREE";
|
||||
subscribedPlan: "FREE" | "PRO" | "ULTIMATE" = "FREE";
|
||||
|
||||
constructor() {
|
||||
makeObservable(this, {
|
||||
@@ -23,9 +23,9 @@ export class WorkspaceSubscriptionStore implements IWorkspaceSubscriptionStore {
|
||||
|
||||
fetchWorkspaceSubscribedPlan = async (workspaceSlug: string) => {
|
||||
try {
|
||||
const response = await discoService.getWorkspaceCurrentPlane(workspaceSlug);
|
||||
const response = await paymentService.getWorkspaceCurrentPlane(workspaceSlug);
|
||||
runInAction(() => {
|
||||
this.subscribedPlan = response.product;
|
||||
this.subscribedPlan = response?.product || "FREE";
|
||||
});
|
||||
return response;
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user