Skip to content

feat: add premium license behavior for create organization page #14650

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Sep 13, 2024
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
23 changes: 20 additions & 3 deletions site/src/components/Badges/Badges.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,15 +106,32 @@ export const DisabledBadge: FC = forwardRef<
);
});

export const EnterpriseBadge: FC = () => {
return (
<span
css={[
styles.badge,
(theme) => ({
backgroundColor: theme.branding.enterprise.background,
border: `1px solid ${theme.branding.enterprise.border}`,
color: theme.branding.enterprise.text,
}),
]}
>
Enterprise
</span>
);
};

export const PremiumBadge: FC = () => {
return (
<span
css={[
styles.badge,
(theme) => ({
backgroundColor: theme.branding.background,
border: `1px solid ${theme.branding.border}`,
color: theme.roles.notice.text,
backgroundColor: theme.branding.premium.background,
border: `1px solid ${theme.branding.premium.border}`,
color: theme.branding.premium.text,
}),
]}
>
Expand Down
45 changes: 22 additions & 23 deletions site/src/components/Paywall/Paywall.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,7 @@ export const Paywall: FC<PaywallProps> = ({
documentationLink,
}) => {
return (
<div
css={[
styles.root,
(theme) => ({
backgroundImage: `linear-gradient(160deg, transparent, ${theme.branding.background})`,
border: `1px solid ${theme.branding.border}`,
}),
]}
>
<div css={styles.root}>
<div>
<Stack direction="row" alignItems="center" css={{ marginBottom: 24 }}>
<h5 css={styles.title}>{message}</h5>
Expand All @@ -45,7 +37,7 @@ export const Paywall: FC<PaywallProps> = ({
</Link>
</div>
<div css={styles.separator} />
<Stack direction="column" alignItems="center" spacing={3}>
<Stack direction="column" alignItems="left" spacing={3}>
<ul css={styles.featureList}>
<li css={styles.feature}>
<FeatureIcon />
Expand All @@ -64,16 +56,18 @@ export const Paywall: FC<PaywallProps> = ({
Unlimited Git & external auth integrations
</li>
</ul>
<Button
href={docs("/enterprise")}
target="_blank"
rel="noreferrer"
startIcon={<span css={{ fontSize: 22 }}>&rarr;</span>}
variant="outlined"
color="neutral"
>
Learn about Premium
</Button>
<div css={styles.learnButton}>
<Button
href={docs("/enterprise")}
target="_blank"
rel="noreferrer"
startIcon={<span css={{ fontSize: 22 }}>&rarr;</span>}
variant="outlined"
color="neutral"
>
Learn about Premium
</Button>
</div>
</Stack>
</div>
);
Expand All @@ -84,15 +78,15 @@ const FeatureIcon: FC = () => {
<TaskAltIcon
css={[
(theme) => ({
color: theme.branding.border,
color: theme.branding.premium.border,
}),
]}
/>
);
};

const styles = {
root: () => ({
root: (theme) => ({
display: "flex",
flexDirection: "row",
justifyContent: "center",
Expand All @@ -101,6 +95,8 @@ const styles = {
padding: 24,
borderRadius: 8,
gap: 32,
backgroundImage: `linear-gradient(160deg, transparent, ${theme.branding.premium.background})`,
border: `1px solid ${theme.branding.premium.border}`,
}),
title: {
fontWeight: 600,
Expand All @@ -116,9 +112,12 @@ const styles = {
separator: (theme) => ({
width: 1,
height: 220,
backgroundColor: theme.branding.divider,
backgroundColor: theme.branding.premium.divider,
marginLeft: 8,
}),
learnButton: {
padding: "0 28px",
},
featureList: {
listStyle: "none",
margin: 0,
Expand Down
39 changes: 22 additions & 17 deletions site/src/components/Paywall/PopoverPaywall.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ export const PopoverPaywall: FC<PopoverPaywallProps> = ({
css={[
styles.root,
(theme) => ({
backgroundImage: `linear-gradient(160deg, transparent, ${theme.branding.background})`,
border: `1px solid ${theme.branding.border}`,
backgroundImage: `linear-gradient(160deg, transparent, ${theme.branding.premium.background})`,
border: `1px solid ${theme.branding.premium.border}`,
}),
]}
>
Expand All @@ -45,7 +45,7 @@ export const PopoverPaywall: FC<PopoverPaywallProps> = ({
</Link>
</div>
<div css={styles.separator} />
<Stack direction="column" alignItems="center" spacing={2}>
<Stack direction="column" alignItems="left" spacing={2}>
<ul css={styles.featureList}>
<li css={styles.feature}>
<FeatureIcon /> High availability & workspace proxies
Expand All @@ -60,16 +60,18 @@ export const PopoverPaywall: FC<PopoverPaywallProps> = ({
<FeatureIcon /> Unlimited Git & external auth integrations
</li>
</ul>
<Button
href={docs("/enterprise")}
target="_blank"
rel="noreferrer"
startIcon={<span css={{ fontSize: 22 }}>&rarr;</span>}
variant="outlined"
color="neutral"
>
Learn about Premium
</Button>
<div css={styles.learnButton}>
<Button
href={docs("/enterprise")}
target="_blank"
rel="noreferrer"
startIcon={<span css={{ fontSize: 22 }}>&rarr;</span>}
variant="outlined"
color="neutral"
>
Learn about Premium
</Button>
</div>
</Stack>
</div>
);
Expand All @@ -80,7 +82,7 @@ const FeatureIcon: FC = () => {
<TaskAltIcon
css={[
(theme) => ({
color: theme.branding.border,
color: theme.branding.premium.border,
}),
]}
/>
Expand All @@ -92,7 +94,7 @@ const styles = {
display: "flex",
flexDirection: "row",
alignItems: "center",
maxWidth: 600,
maxWidth: 770,
padding: "24px 36px",
borderRadius: 8,
gap: 18,
Expand All @@ -106,7 +108,7 @@ const styles = {
description: (theme) => ({
marginTop: 8,
fontFamily: "inherit",
maxWidth: 420,
maxWidth: 360,
lineHeight: "160%",
color: theme.palette.text.secondary,
fontSize: 14,
Expand All @@ -121,10 +123,13 @@ const styles = {
listStyle: "none",
margin: 0,
marginRight: 8,
padding: "0 12px",
padding: "0 0 0 24px",
fontSize: 13,
fontWeight: 500,
},
learnButton: {
padding: "0 28px",
},
feature: {
display: "flex",
alignItems: "center",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { appearanceConfigKey, updateAppearance } from "api/queries/appearance";
import type { UpdateAppearanceConfig } from "api/typesGenerated";
import { displayError, displaySuccess } from "components/GlobalSnackbar/utils";
import { useDashboard } from "modules/dashboard/useDashboard";
import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility";
import type { FC } from "react";
import { Helmet } from "react-helmet-async";
import { useMutation, useQueryClient } from "react-query";
Expand All @@ -15,6 +16,7 @@ import { AppearanceSettingsPageView } from "./AppearanceSettingsPageView";
// the command line would be a significantly worse user experience.
const AppearanceSettingsPage: FC = () => {
const { appearance, entitlements } = useDashboard();
const { multiple_organizations: hasPremiumLicense } = useFeatureVisibility();
const queryClient = useQueryClient();
const updateAppearanceMutation = useMutation(updateAppearance(queryClient));

Expand Down Expand Up @@ -46,6 +48,7 @@ const AppearanceSettingsPage: FC = () => {
isEntitled={
entitlements.features.appearance.entitlement !== "not_entitled"
}
isPremium={hasPremiumLicense}
/>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import TextField from "@mui/material/TextField";
import type { UpdateAppearanceConfig } from "api/typesGenerated";
import {
Badges,
DisabledBadge,
EntitledBadge,
EnterpriseBadge,
PremiumBadge,
} from "components/Badges/Badges";
import { PopoverPaywall } from "components/Paywall/PopoverPaywall";
Expand All @@ -24,14 +23,15 @@ import { AnnouncementBannerSettings } from "./AnnouncementBannerSettings";
export type AppearanceSettingsPageViewProps = {
appearance: UpdateAppearanceConfig;
isEntitled: boolean;
isPremium: boolean;
onSaveAppearance: (
newConfig: Partial<UpdateAppearanceConfig>,
) => Promise<void>;
};

export const AppearanceSettingsPageView: FC<
AppearanceSettingsPageViewProps
> = ({ appearance, isEntitled, onSaveAppearance }) => {
> = ({ appearance, isEntitled, isPremium, onSaveAppearance }) => {
const applicationNameForm = useFormik<{
application_name: string;
}>({
Expand Down Expand Up @@ -60,13 +60,17 @@ export const AppearanceSettingsPageView: FC<
/>

<Badges>
{isEntitled ? <EntitledBadge /> : <DisabledBadge />}
<Popover mode="hover">
<PopoverTrigger>
<span>
<PremiumBadge />
</span>
</PopoverTrigger>
{isEntitled && !isPremium ? (
<EnterpriseBadge />
) : (
<PopoverTrigger>
<span>
<PremiumBadge />
</span>
</PopoverTrigger>
)}

<PopoverContent css={{ transform: "translateY(-28px)" }}>
<PopoverPaywall
message="Appearance"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Loader } from "components/Loader/Loader";
import { useDashboard } from "modules/dashboard/useDashboard";
import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility";
import type { FC } from "react";
import { Helmet } from "react-helmet-async";
import { pageTitle } from "utils/page";
Expand All @@ -9,6 +10,7 @@ import { ObservabilitySettingsPageView } from "./ObservabilitySettingsPageView";
const ObservabilitySettingsPage: FC = () => {
const { deploymentValues } = useDeploySettings();
const { entitlements } = useDashboard();
const { multiple_organizations: hasPremiumLicense } = useFeatureVisibility();

return (
<>
Expand All @@ -20,6 +22,7 @@ const ObservabilitySettingsPage: FC = () => {
<ObservabilitySettingsPageView
options={deploymentValues.options}
featureAuditLogEnabled={entitlements.features.audit_log.enabled}
isPremium={hasPremiumLicense}
/>
) : (
<Loader />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import type { SerpentOption } from "api/typesGenerated";
import {
Badges,
DisabledBadge,
EnabledBadge,
EnterpriseBadge,
PremiumBadge,
} from "components/Badges/Badges";
import { PopoverPaywall } from "components/Paywall/PopoverPaywall";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "components/Popover/Popover";
import { SettingsHeader } from "components/SettingsHeader/SettingsHeader";
import { Stack } from "components/Stack/Stack";
import type { FC } from "react";
Expand All @@ -15,11 +20,12 @@ import OptionsTable from "../OptionsTable";
export type ObservabilitySettingsPageViewProps = {
options: SerpentOption[];
featureAuditLogEnabled: boolean;
isPremium: boolean;
};

export const ObservabilitySettingsPageView: FC<
ObservabilitySettingsPageViewProps
> = ({ options, featureAuditLogEnabled }) => {
> = ({ options, featureAuditLogEnabled, isPremium }) => {
return (
<>
<Stack direction="column" spacing={6}>
Expand All @@ -33,8 +39,25 @@ export const ObservabilitySettingsPageView: FC<
/>

<Badges>
{featureAuditLogEnabled ? <EnabledBadge /> : <DisabledBadge />}
<PremiumBadge />
<Popover mode="hover">
{featureAuditLogEnabled && !isPremium ? (
<EnterpriseBadge />
) : (
<PopoverTrigger>
<span>
<PremiumBadge />
</span>
</PopoverTrigger>
)}

<PopoverContent css={{ transform: "translateY(-28px)" }}>
<PopoverPaywall
message="Observability"
description="With a Premium license, you can monitor your application with logs and metrics."
documentationLink="https://coder.com/docs/admin/appearance"
/>
</PopoverContent>
</Popover>
</Badges>
</div>

Expand Down
Loading
Loading