mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
refactor: redesign Paywall component (#11907)
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import { Paywall } from "./Paywall";
|
||||
|
||||
const meta: Meta<typeof Paywall> = {
|
||||
title: "components/Paywall",
|
||||
component: Paywall,
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Paywall>;
|
||||
|
||||
const Example: Story = {
|
||||
args: {
|
||||
message: "Black Lotus",
|
||||
description:
|
||||
"Adds 3 mana of any single color of your choice to your mana pool, then is discarded. Tapping this artifact can be played as an interrupt.",
|
||||
},
|
||||
};
|
||||
|
||||
export { Example as Paywall };
|
||||
@@ -1,48 +1,95 @@
|
||||
import { type FC, type ReactNode } from "react";
|
||||
import { type Interpolation, type Theme } from "@emotion/react";
|
||||
import Button from "@mui/material/Button";
|
||||
import Link from "@mui/material/Link";
|
||||
import TaskAltIcon from "@mui/icons-material/TaskAlt";
|
||||
import { type FC, type ReactNode } from "react";
|
||||
import { EnterpriseBadge } from "components/Badges/Badges";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import { docs } from "utils/docs";
|
||||
|
||||
export interface PaywallProps {
|
||||
children?: ReactNode;
|
||||
message: string;
|
||||
description?: string | ReactNode;
|
||||
cta?: ReactNode;
|
||||
description?: ReactNode;
|
||||
documentationLink?: string;
|
||||
}
|
||||
|
||||
export const Paywall: FC<PaywallProps> = ({ message, description, cta }) => {
|
||||
export const Paywall: FC<PaywallProps> = ({
|
||||
message,
|
||||
description,
|
||||
documentationLink,
|
||||
}) => {
|
||||
return (
|
||||
<div css={styles.root}>
|
||||
<div css={{ marginBottom: 24 }}>
|
||||
<Stack direction="row" alignItems="center" justifyContent="center">
|
||||
<div>
|
||||
<Stack direction="row" alignItems="center" css={{ marginBottom: 24 }}>
|
||||
<h5 css={styles.title}>{message}</h5>
|
||||
<EnterpriseBadge />
|
||||
</Stack>
|
||||
|
||||
{description && <p css={styles.description}>{description}</p>}
|
||||
<Link
|
||||
href={documentationLink}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
css={{ fontWeight: 600 }}
|
||||
>
|
||||
Read the documentation
|
||||
</Link>
|
||||
</div>
|
||||
{cta}
|
||||
<div css={styles.separator}></div>
|
||||
<Stack direction="column" alignItems="center" spacing={3}>
|
||||
<ul css={styles.featureList}>
|
||||
<li css={styles.feature}>
|
||||
<FeatureIcon /> Template access control
|
||||
</li>
|
||||
<li css={styles.feature}>
|
||||
<FeatureIcon /> User groups
|
||||
</li>
|
||||
<li css={styles.feature}>
|
||||
<FeatureIcon /> 24 hour support
|
||||
</li>
|
||||
<li css={styles.feature}>
|
||||
<FeatureIcon /> Audit logs
|
||||
</li>
|
||||
</ul>
|
||||
<Button
|
||||
href={docs("/enterprise")}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
startIcon={<span css={{ fontSize: 22 }}>→</span>}
|
||||
variant="outlined"
|
||||
color="neutral"
|
||||
>
|
||||
Learn about Enterprise
|
||||
</Button>
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const FeatureIcon: FC = () => {
|
||||
return <TaskAltIcon css={styles.featureIcon} />;
|
||||
};
|
||||
|
||||
const styles = {
|
||||
root: (theme) => ({
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
textAlign: "center",
|
||||
minHeight: 300,
|
||||
maxWidth: 920,
|
||||
margin: "auto",
|
||||
padding: 24,
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
backgroundImage: `linear-gradient(160deg, transparent, ${theme.experimental.roles.active.background})`,
|
||||
border: `1px solid ${theme.experimental.roles.active.fill.outline}`,
|
||||
borderRadius: 8,
|
||||
gap: 32,
|
||||
}),
|
||||
title: {
|
||||
fontWeight: 600,
|
||||
fontFamily: "inherit",
|
||||
fontSize: 24,
|
||||
fontSize: 22,
|
||||
margin: 0,
|
||||
},
|
||||
description: (theme) => ({
|
||||
@@ -51,6 +98,29 @@ const styles = {
|
||||
maxWidth: 420,
|
||||
lineHeight: "160%",
|
||||
color: theme.palette.text.secondary,
|
||||
fontSize: 14,
|
||||
fontSize: 16,
|
||||
}),
|
||||
separator: (theme) => ({
|
||||
width: 1,
|
||||
height: 220,
|
||||
backgroundColor: theme.palette.divider,
|
||||
marginLeft: 8,
|
||||
}),
|
||||
featureList: {
|
||||
listStyle: "none",
|
||||
margin: 0,
|
||||
marginRight: 8,
|
||||
padding: "0 12px",
|
||||
fontSize: 14,
|
||||
fontWeight: 500,
|
||||
},
|
||||
featureIcon: (theme) => ({
|
||||
color: theme.experimental.roles.active.fill.outline,
|
||||
}),
|
||||
feature: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
padding: 3,
|
||||
gap: 8,
|
||||
},
|
||||
} satisfies Record<string, Interpolation<Theme>>;
|
||||
|
||||
@@ -3,8 +3,8 @@ import TableBody from "@mui/material/TableBody";
|
||||
import TableCell from "@mui/material/TableCell";
|
||||
import TableContainer from "@mui/material/TableContainer";
|
||||
import TableRow from "@mui/material/TableRow";
|
||||
import { AuditLog } from "api/typesGenerated";
|
||||
import { AuditLogRow } from "./AuditLogRow/AuditLogRow";
|
||||
import { type ComponentProps, type FC } from "react";
|
||||
import type { AuditLog } from "api/typesGenerated";
|
||||
import { ChooseOne, Cond } from "components/Conditionals/ChooseOne";
|
||||
import { EmptyState } from "components/EmptyState/EmptyState";
|
||||
import { Margins } from "components/Margins/Margins";
|
||||
@@ -16,15 +16,15 @@ import {
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import { TableLoader } from "components/TableLoader/TableLoader";
|
||||
import { Timeline } from "components/Timeline/Timeline";
|
||||
import { AuditHelpTooltip } from "./AuditHelpTooltip";
|
||||
import { ComponentProps, FC } from "react";
|
||||
import { AuditPaywall } from "./AuditPaywall";
|
||||
import { AuditFilter } from "./AuditFilter";
|
||||
|
||||
import { Paywall } from "components/Paywall/Paywall";
|
||||
import {
|
||||
type PaginationResult,
|
||||
PaginationContainer,
|
||||
} from "components/PaginationWidget/PaginationContainer";
|
||||
import { docs } from "utils/docs";
|
||||
import { AuditHelpTooltip } from "./AuditHelpTooltip";
|
||||
import { AuditFilter } from "./AuditFilter";
|
||||
import { AuditLogRow } from "./AuditLogRow/AuditLogRow";
|
||||
|
||||
export const Language = {
|
||||
title: "Audit",
|
||||
@@ -130,7 +130,11 @@ export const AuditPageView: FC<AuditPageViewProps> = ({
|
||||
</Cond>
|
||||
|
||||
<Cond>
|
||||
<AuditPaywall />
|
||||
<Paywall
|
||||
message="Audit logs"
|
||||
description="Audit logs allow you to monitor user operations on your deployment. You need an Enterprise license to use this feature."
|
||||
documentationLink={docs("/admin/audit-logs")}
|
||||
/>
|
||||
</Cond>
|
||||
</ChooseOne>
|
||||
</Margins>
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import Button from "@mui/material/Button";
|
||||
import Link from "@mui/material/Link";
|
||||
import ArrowRightAltOutlined from "@mui/icons-material/ArrowRightAltOutlined";
|
||||
import { Paywall } from "components/Paywall/Paywall";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import { FC } from "react";
|
||||
import { docs } from "utils/docs";
|
||||
|
||||
export const AuditPaywall: FC = () => {
|
||||
return (
|
||||
<Paywall
|
||||
message="Audit logs"
|
||||
description="Audit Logs allows Auditors to monitor user operations in their deployment. To use this feature, you need an Enterprise license."
|
||||
cta={
|
||||
<Stack direction="row" alignItems="center">
|
||||
<Link target="_blank" rel="noreferrer">
|
||||
<Button
|
||||
href={docs("/admin/upgrade")}
|
||||
size="small"
|
||||
startIcon={<ArrowRightAltOutlined />}
|
||||
>
|
||||
See how to upgrade
|
||||
</Button>
|
||||
</Link>
|
||||
<Link
|
||||
href={docs("/admin/audit-logs")}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Read the documentation
|
||||
</Link>
|
||||
</Stack>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,20 +1,18 @@
|
||||
import { type Interpolation, type Theme } from "@emotion/react";
|
||||
import Button from "@mui/material/Button";
|
||||
import Link from "@mui/material/Link";
|
||||
import Table from "@mui/material/Table";
|
||||
import TableBody from "@mui/material/TableBody";
|
||||
import TableCell from "@mui/material/TableCell";
|
||||
import TableContainer from "@mui/material/TableContainer";
|
||||
import TableHead from "@mui/material/TableHead";
|
||||
import TableRow from "@mui/material/TableRow";
|
||||
import ArrowRightAltOutlined from "@mui/icons-material/ArrowRightAltOutlined";
|
||||
import Skeleton from "@mui/material/Skeleton";
|
||||
import AddOutlined from "@mui/icons-material/AddOutlined";
|
||||
import KeyboardArrowRight from "@mui/icons-material/KeyboardArrowRight";
|
||||
import AvatarGroup from "@mui/material/AvatarGroup";
|
||||
import { AvatarData } from "components/AvatarData/AvatarData";
|
||||
import { ChooseOne, Cond } from "components/Conditionals/ChooseOne";
|
||||
import { EmptyState } from "components/EmptyState/EmptyState";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import {
|
||||
TableLoaderSkeleton,
|
||||
TableRowSkeleton,
|
||||
@@ -26,7 +24,6 @@ import { Paywall } from "components/Paywall/Paywall";
|
||||
import type { Group } from "api/typesGenerated";
|
||||
import { GroupAvatar } from "components/GroupAvatar/GroupAvatar";
|
||||
import { docs } from "utils/docs";
|
||||
import Skeleton from "@mui/material/Skeleton";
|
||||
import { AvatarDataSkeleton } from "components/AvatarData/AvatarDataSkeleton";
|
||||
|
||||
export type GroupsPageViewProps = {
|
||||
@@ -51,28 +48,7 @@ export const GroupsPageView: FC<GroupsPageViewProps> = ({
|
||||
<Paywall
|
||||
message="Groups"
|
||||
description="Organize users into groups with restricted access to templates. You need an Enterprise license to use this feature."
|
||||
cta={
|
||||
<Stack direction="row" alignItems="center">
|
||||
<Button
|
||||
href={docs("/enterprise")}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
startIcon={<ArrowRightAltOutlined />}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
>
|
||||
Learn about Enterprise
|
||||
</Button>
|
||||
|
||||
<Link
|
||||
href={docs("/admin/groups")}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Read the documentation
|
||||
</Link>
|
||||
</Stack>
|
||||
}
|
||||
documentationLink={docs("/admin/groups")}
|
||||
/>
|
||||
</Cond>
|
||||
<Cond>
|
||||
|
||||
+2
-24
@@ -1,12 +1,8 @@
|
||||
import Button from "@mui/material/Button";
|
||||
import Link from "@mui/material/Link";
|
||||
import ArrowRightAltOutlined from "@mui/icons-material/ArrowRightAltOutlined";
|
||||
import { type FC } from "react";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { useMutation, useQuery, useQueryClient } from "react-query";
|
||||
import { setGroupRole, setUserRole, templateACL } from "api/queries/templates";
|
||||
import { Paywall } from "components/Paywall/Paywall";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import { displaySuccess } from "components/GlobalSnackbar/utils";
|
||||
import { useOrganizationId } from "contexts/auth/useOrganizationId";
|
||||
import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility";
|
||||
@@ -38,26 +34,8 @@ export const TemplatePermissionsPage: FC = () => {
|
||||
{!isTemplateRBACEnabled ? (
|
||||
<Paywall
|
||||
message="Template permissions"
|
||||
description="Manage your template permissions to allow users or groups to view or admin the template. To use this feature, you have to upgrade your account."
|
||||
cta={
|
||||
<Stack direction="row" alignItems="center">
|
||||
<Link
|
||||
href={docs("/admin/upgrade")}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<Button
|
||||
startIcon={<ArrowRightAltOutlined />}
|
||||
variant="contained"
|
||||
>
|
||||
See how to upgrade
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href={docs("/admin/rbac")} target="_blank" rel="noreferrer">
|
||||
Read the documentation
|
||||
</Link>
|
||||
</Stack>
|
||||
}
|
||||
description="Control access of templates for users and groups to templates. You need an Enterprise license to use this feature."
|
||||
documentationLink={docs("/admin/rbac")}
|
||||
/>
|
||||
) : (
|
||||
<TemplatePermissionsPageView
|
||||
|
||||
@@ -151,7 +151,7 @@ export default {
|
||||
text: colors.sky[50],
|
||||
fill: {
|
||||
solid: colors.sky[600],
|
||||
outline: colors.sky[600],
|
||||
outline: colors.sky[400],
|
||||
text: colors.white,
|
||||
},
|
||||
disabled: {
|
||||
|
||||
Reference in New Issue
Block a user