Skip to content
Merged
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
3 changes: 1 addition & 2 deletions apps/web/ce/components/license/modal/upgrade-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { FC } from "react";
import { observer } from "mobx-react";
// plane imports
import {
Expand All @@ -19,7 +18,7 @@ import type { TCheckoutParams } from "@/components/license/modal/card/checkout-b

// Constants
const COMMON_CARD_CLASSNAME = "flex flex-col w-full h-full justify-end col-span-12 sm:col-span-6 xl:col-span-3";
const COMMON_EXTRA_FEATURES_CLASSNAME = "pt-2 text-center text-11 text-accent-secondary font-medium hover:underline";
const COMMON_EXTRA_FEATURES_CLASSNAME = "pt-2 text-center text-caption-md-medium text-accent-primary hover:underline";

export type PaidPlanUpgradeModalProps = {
isOpen: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export function PageNavigationPaneOutlineTabEmptyState() {
<div className="flex flex-col items-center gap-y-6 text-center">
<img
src={resolvedPath}
className="w-[160px] h-[160px] object-contain"
className="w-[160px] h-[160px]"
alt="An image depicting the outline of a page"
/>
<div className="space-y-2.5">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import type { FC } from "react";
// plane imports
import { observer } from "mobx-react";
import type { EProductSubscriptionEnum, TBillingFrequency } from "@plane/types";
import { getSubscriptionBackgroundColor, getDiscountPillStyle } from "@plane/ui";
import { calculateYearlyDiscount, cn } from "@plane/utils";

type TPlanFrequencyToggleProps = {
Expand All @@ -14,26 +12,21 @@ type TPlanFrequencyToggleProps = {
};

export const PlanFrequencyToggle = observer(function PlanFrequencyToggle(props: TPlanFrequencyToggleProps) {
const { subscriptionType, monthlyPrice, yearlyPrice, selectedFrequency, setSelectedFrequency } = props;
const { monthlyPrice, yearlyPrice, selectedFrequency, setSelectedFrequency } = props;
// derived values
const yearlyDiscount = calculateYearlyDiscount(monthlyPrice, yearlyPrice);

return (
<div className="flex w-full items-center cursor-pointer py-1 animate-slide-up">
<div
className={cn(
"flex space-x-1 rounded-md bg-accent-primary/80/10 p-0.5 w-full",
getSubscriptionBackgroundColor(subscriptionType, "50")
)}
>
<div className="flex w-full items-center cursor-pointer py-1">
<div className="flex space-x-1 rounded-md bg-layer-3 p-0.5 w-full">
<button
type="button"
onClick={() => setSelectedFrequency("month")}
className={cn(
"w-full rounded-sm px-1 py-0.5 text-11 font-medium leading-5 text-center",
"w-full rounded-sm px-1 py-0.5 text-caption-sm-medium leading-5 text-center",
selectedFrequency === "month"
? "bg-layer-transparent-selected text-primary shadow"
: "hover:bg-layer-transparent-hover text-tertiary hover:text-secondary"
? "bg-layer-2 text-primary shadow-raised-100 border border-subtle-1"
: "text-tertiary hover:text-secondary"
)}
>
Monthly
Expand All @@ -42,15 +35,15 @@ export const PlanFrequencyToggle = observer(function PlanFrequencyToggle(props:
type="button"
onClick={() => setSelectedFrequency("year")}
className={cn(
"w-full rounded-sm px-1 py-0.5 text-11 font-medium leading-5 text-center",
"w-full rounded-sm px-1 py-0.5 text-caption-sm-medium leading-5 text-center",
selectedFrequency === "year"
? "bg-layer-transparent-selected text-primary shadow"
: "hover:bg-layer-transparent-hover text-tertiary hover:text-secondary"
? "bg-layer-2 text-primary shadow-raised-100 border border-subtle-1"
: "text-tertiary hover:text-secondary"
)}
>
Yearly
{yearlyDiscount > 0 && (
<span className={cn(getDiscountPillStyle(subscriptionType), "rounded-full px-1 py-0.5 ml-1 text-9")}>
<span className="bg-accent-primary text-on-color rounded-full px-1 py-0.5 ml-1.5 text-caption-xs-regular">
-{yearlyDiscount}%
</span>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,17 @@ export const PlanDetail = observer(function PlanDetail(props: TPlanDetailProps)
<div className="flex flex-col justify-between col-span-1 p-3 space-y-0.5">
{/* Plan name and pricing section */}
<div className="flex flex-col items-start">
<div className="flex w-full gap-2 items-center text-18 font-medium">
<span className="transition-all duration-300">{subscriptionName}</span>
<div className="flex w-full gap-2 items-center text-h4-semibold">
<span>{subscriptionName}</span>
{subscriptionType === EProductSubscriptionEnum.PRO && (
<span className="px-2 rounded-sm text-on-color bg-accent-primary text-11">Popular</span>
<span className="px-2 py-0.5 rounded-sm text-on-color bg-accent-primary text-caption-sm-medium">
Popular
</span>
)}
</div>
<div className="flex gap-x-2 items-start text-tertiary pb-1 transition-all duration-300 animate-slide-up">
<div className="flex gap-x-2 items-start text-tertiary pb-1">
{isSubscriptionActive && displayPrice !== undefined && (
<div className="flex items-center gap-1 text-20 text-primary font-semibold transition-all duration-300">
<div className="flex items-center gap-1 text-h3-semibold text-primary">
<DiscountInfo
currency="$"
frequency={billingFrequency ?? "month"}
Expand All @@ -78,9 +80,9 @@ export const PlanDetail = observer(function PlanDetail(props: TPlanDetailProps)
</div>
)}
<div className="pt-1">
{pricingDescription && <div className="transition-all duration-300">{pricingDescription}</div>}
{pricingDescription && <div>{pricingDescription}</div>}
{pricingSecondaryDescription && (
<div className="text-11 text-placeholder transition-all duration-300">{pricingSecondaryDescription}</div>
<div className="text-caption-xs text-placeholder">{pricingSecondaryDescription}</div>
)}
</div>
</div>
Expand All @@ -100,12 +102,12 @@ export const PlanDetail = observer(function PlanDetail(props: TPlanDetailProps)
)}

{/* Subscription button */}
<div className="flex flex-col gap-1 py-3 items-start transition-all duration-300">
<div className="flex flex-col gap-1 py-3 items-start">
<Button
variant="primary"
size="lg"
onClick={handleRedirection}
className="animate-slide-up w-full"
className="w-full"
data-ph-element={
isSubscriptionActive
? WORKSPACE_SETTINGS_TRACKER_ELEMENTS.BILLING_UPGRADE_BUTTON(subscriptionType)
Expand Down
18 changes: 6 additions & 12 deletions apps/web/ce/components/workspace/billing/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import { DEFAULT_PRODUCT_BILLING_FREQUENCY, SUBSCRIPTION_WITH_BILLING_FREQUENCY
import { useTranslation } from "@plane/i18n";
import type { TBillingFrequency, TProductBillingFrequency } from "@plane/types";
import { EProductSubscriptionEnum } from "@plane/types";
import { getSubscriptionTextColor } from "@plane/ui";
import { cn } from "@plane/utils";
// components
import { SettingsHeading } from "@/components/settings/heading";
// local imports
Expand Down Expand Up @@ -44,24 +42,20 @@ export const BillingRoot = observer(function BillingRoot() {
title={t("workspace_settings.settings.billing_and_plans.heading")}
description={t("workspace_settings.settings.billing_and_plans.description")}
/>
<div className={cn("transition-all duration-500 ease-in-out will-change-[height,opacity]")}>
<div>
<div className="py-6">
<div className={cn("px-6 py-4 border border-subtle rounded-lg")}>
<div className="flex gap-2 font-medium items-center justify-between">
<div className="px-6 py-4 rounded-lg bg-layer-1">
<div className="flex gap-2 items-center justify-between">
<div className="flex flex-col gap-1">
<h4
className={cn("text-18 leading-6 font-bold", getSubscriptionTextColor(EProductSubscriptionEnum.FREE))}
>
Community
</h4>
<div className="text-13 text-secondary font-medium">
<h4 className="text-h4-bold text-primary">Community</h4>
<div className="text-caption-md-medium text-secondary">
Unlimited projects, issues, cycles, modules, pages, and storage
</div>
</div>
</div>
</div>
</div>
<div className="text-18 font-semibold mt-3">All plans</div>
<div className="text-h4-semibold mt-3">All plans</div>
</div>
<PlansComparison
isCompareAllFeaturesSectionOpen={isCompareAllFeaturesSectionOpen}
Expand Down
10 changes: 5 additions & 5 deletions apps/web/ce/components/workspace/edition-badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { usePlatformOS } from "@/hooks/use-platform-os";
import packageJson from "package.json";
// local components
import { PaidPlanUpgradeModal } from "../license";
import { Button } from "@plane/propel/button";

export const WorkspaceEditionBadge = observer(function WorkspaceEditionBadge() {
// states
Expand All @@ -24,16 +25,15 @@ export const WorkspaceEditionBadge = observer(function WorkspaceEditionBadge() {
handleClose={() => setIsPaidPlanPurchaseModalOpen(false)}
/>
<Tooltip tooltipContent={`Version: v${packageJson.version}`} isMobile={isMobile}>
<button
type="button"
tabIndex={-1}
className="w-fit min-w-24 cursor-pointer rounded-2xl px-2 py-1 text-center text-13 font-medium outline-none bg-plans-neutral-subtle text-plans-neutral-primary"
<Button
variant="tertiary"
size="lg"
onClick={() => setIsPaidPlanPurchaseModalOpen(true)}
aria-haspopup="dialog"
aria-label={t("aria_labels.projects_sidebar.edition_badge")}
>
Community
</button>
</Button>
</Tooltip>
</>
);
Expand Down
23 changes: 10 additions & 13 deletions apps/web/core/components/license/modal/card/base-paid-plan-card.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import type { FC } from "react";
import { useState } from "react";
import { observer } from "mobx-react";
import { CheckCircle } from "lucide-react";
import { Tab } from "@headlessui/react";
// plane imports
// helpers
import type { EProductSubscriptionEnum, TBillingFrequency, TSubscriptionPrice } from "@plane/types";
import { getSubscriptionBackgroundColor, getUpgradeCardVariantStyle } from "@plane/ui";
import { cn, getBaseSubscriptionName, getSubscriptionName } from "@plane/utils";

export type TBasePaidPlanCardProps = {
Expand All @@ -33,24 +31,23 @@ export const BasePaidPlanCard = observer(function BasePaidPlanCard(props: TBaseP
// states
const [selectedPlan, setSelectedPlan] = useState<TBillingFrequency>("month");
const basePlan = getBaseSubscriptionName(planVariant);
const upgradeCardVariantStyle = getUpgradeCardVariantStyle(planVariant);
// Plane details
const planeName = getSubscriptionName(planVariant);

return (
<div className={cn("flex flex-col py-6 px-3", upgradeCardVariantStyle)}>
<div className="flex flex-col py-6 px-3 bg-layer-2 rounded-xl border border-subtle">
<Tab.Group selectedIndex={selectedPlan === "month" ? 0 : 1}>
<div className="flex w-full justify-center h-9">
<Tab.List
className={cn("flex space-x-1 rounded-md p-0.5 w-60", getSubscriptionBackgroundColor(planVariant, "50"))}
>
<Tab.List className="flex space-x-1 rounded-md p-0.5 w-60 bg-layer-3">
{prices.map((price: TSubscriptionPrice) => (
<Tab
key={price.key}
className={({ selected }) =>
cn(
"w-full rounded-sm py-1 text-13 font-medium leading-5",
selected ? "bg-surface-1 text-primary shadow" : "text-tertiary hover:text-secondary"
"w-full rounded-sm py-1 text-caption-md-medium leading-5",
selected
? "bg-layer-2 text-primary shadow-raised-100 border border-subtle-1"
: "text-tertiary hover:text-secondary"
)
}
onClick={() => setSelectedPlan(price.recurring)}
Expand All @@ -64,11 +61,11 @@ export const BasePaidPlanCard = observer(function BasePaidPlanCard(props: TBaseP
{prices.map((price: TSubscriptionPrice) => (
<Tab.Panel key={price.key}>
<div className="pt-6 text-center">
<div className="text-18 font-medium">Plane {planeName}</div>
<div className="text-h4-medium">Plane {planeName}</div>
{renderActionButton(price)}
</div>
<div className="px-2 pt-6 pb-2">
<div className="p-2 text-13 font-semibold">{`Everything in ${basePlan} +`}</div>
<div className="p-2 text-caption-md-semibold">{`Everything in ${basePlan} +`}</div>
<ul className="grid grid-cols-12 gap-x-4">
{features.map((feature) => (
<li
Expand All @@ -77,8 +74,8 @@ export const BasePaidPlanCard = observer(function BasePaidPlanCard(props: TBaseP
"sm:col-span-6": !verticalFeatureList,
})}
>
<p className="w-full text-13 font-medium leading-5 flex items-center line-clamp-1">
<CheckCircle className="h-4 w-4 mr-2 text-tertiary flex-shrink-0" />
<p className="w-full text-caption-md-medium leading-5 flex items-center line-clamp-1">
<CheckCircle className="size-4 mr-2 text-tertiary flex-shrink-0" />
<span className="text-secondary truncate">{feature}</span>
</p>
</li>
Expand Down
28 changes: 12 additions & 16 deletions apps/web/core/components/license/modal/card/checkout-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,34 +43,30 @@ export const PlanCheckoutButton = observer(function PlanCheckoutButton(props: Pr

return (
<>
<div className="pb-4 text-center transition-all duration-700 animate-slide-up">
<div className="text-20 font-semibold h-9 transition-all duration-300">
<div className="pb-4 text-center">
<div className="text-20 font-semibold h-9">
{isLoading ? (
<Loader className="flex flex-col items-center justify-center">
<Loader.Item height="36px" width="4rem" />
</Loader>
) : (
<span className="animate-fade-in">
<DiscountInfo
currency={price.currency}
frequency={price.recurring}
price={price.price}
subscriptionType={planVariant}
className="mr-1.5"
/>
</span>
<DiscountInfo
currency={price.currency}
frequency={price.recurring}
price={price.price}
subscriptionType={planVariant}
className="mr-1.5"
/>
)}
</div>
<div className="text-13 font-medium text-tertiary transition-all duration-300 animate-fade-in">
per user per month
</div>
<div className="text-caption-md-medium text-tertiary">per user per month</div>
</div>
{isLoading ? (
<Loader className="flex flex-col items-center justify-center">
<Loader.Item height="38px" width="14rem" />
</Loader>
) : (
<div className="flex flex-col items-center justify-center w-full space-y-4 transition-all duration-300 animate-fade-in">
<div className="flex flex-col items-center justify-center w-full space-y-4">
<Button
variant="primary"
size="lg"
Expand All @@ -89,7 +85,7 @@ export const PlanCheckoutButton = observer(function PlanCheckoutButton(props: Pr
{upgradeLoaderType === planVariant ? "Redirecting to Stripe" : (upgradeCTA ?? `Upgrade to ${planeName}`)}
</Button>
{isTrialAllowed && !isSelfHosted && (
<div className="mt-4 h-4 transition-all duration-300 animate-fade-in">
<div className="mt-4 h-4">
{renderTrialButton &&
renderTrialButton({
productId: product?.id,
Expand Down
12 changes: 7 additions & 5 deletions apps/web/core/components/license/modal/card/free-plan.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,24 @@ type FreePlanCardProps = {
export const FreePlanCard = observer(function FreePlanCard(props: FreePlanCardProps) {
const { isOnFreePlan } = props;
return (
<div className="py-4 px-2 border border-subtle rounded-xl">
<div className="py-4 px-2 rounded-xl bg-layer-1">
{isOnFreePlan && (
<div className="py-2 px-3">
<span className="px-2 py-1 bg-surface-2 text-13 text-tertiary font-medium rounded-sm">Your plan</span>
<span className="px-2 py-1 bg-layer-2 text-caption-md-medium text-tertiary rounded-sm border border-subtle-1">
Your plan
</span>
</div>
)}
<div className="px-4 py-2 font-semibold">
<div className="text-20">Free</div>
<div className="text-13 text-tertiary">$0 per user per month</div>
<div className="text-caption-md text-tertiary">$0 per user per month</div>
</div>
<div className="px-2 pt-2 pb-3">
<ul className="w-full grid grid-cols-12 gap-x-4">
{FREE_PLAN_UPGRADE_FEATURES.map((feature) => (
<li key={feature} className={cn("col-span-12 relative rounded-md p-2 flex")}>
<p className="w-full text-13 font-medium leading-5 flex items-center">
<CircleX className="h-4 w-4 mr-2 text-red-500 flex-shrink-0" />
<p className="w-full text-caption-md-medium leading-5 flex items-center">
<CircleX className="size-4 mr-2 text-danger-secondary flex-shrink-0" />
<span className="text-secondary truncate">{feature}</span>
</p>
</li>
Expand Down
5 changes: 2 additions & 3 deletions apps/web/core/components/license/modal/card/plan-upgrade.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { observer } from "mobx-react";
// plane imports
import { TALK_TO_SALES_URL } from "@plane/constants";
import type { EProductSubscriptionEnum, IPaymentProduct, TSubscriptionPrice } from "@plane/types";
import { getDiscountPillStyle } from "@plane/ui";
import { calculateYearlyDiscount, cn, getSubscriptionName, getSubscriptionPriceDetails } from "@plane/utils";
import { calculateYearlyDiscount, getSubscriptionName, getSubscriptionPriceDetails } from "@plane/utils";
// components
import { BasePaidPlanCard, TalkToSalesCard } from "@/components/license";
// local components
Expand Down Expand Up @@ -72,7 +71,7 @@ export const PlanUpgradeCard = observer(function PlanUpgradeCard(props: PlanUpgr
<>
Yearly
{yearlyDiscount > 0 && (
<span className={cn(getDiscountPillStyle(planVariant), "rounded-full px-1.5 py-0.5 ml-1 text-11")}>
<span className="bg-accent-primary text-on-color rounded-full px-1.5 py-0.5 ml-1 text-caption-sm">
-{yearlyDiscount}%
</span>
)}
Expand Down
4 changes: 2 additions & 2 deletions apps/web/core/components/license/modal/card/talk-to-sales.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export const TalkToSalesCard = observer(function TalkToSalesCard(props: TalkToSa
<>Quote on request</>
)}
</div>
<div className="text-13 font-medium text-tertiary">per user per month</div>
<div className="text-caption-md-medium text-tertiary">per user per month</div>
</div>
{isLoading ? (
<Loader className="flex flex-col items-center justify-center">
Expand All @@ -70,7 +70,7 @@ export const TalkToSalesCard = observer(function TalkToSalesCard(props: TalkToSa
Talk to Sales
</a>
{isTrialAllowed && !isSelfHosted && (
<div className="mt-4 h-4 transition-all duration-300 animate-fade-in">
<div className="mt-4 h-4">
{renderTrialButton &&
renderTrialButton({
productId: product?.id,
Expand Down
Loading