refactor(site): improve templates empty state (#10518)

This commit is contained in:
Bruno Quaresma
2023-11-06 12:24:45 -05:00
committed by GitHub
parent e976f50415
commit a9797fa391
6 changed files with 147 additions and 125 deletions
@@ -1,86 +1,123 @@
import { type Interpolation, type Theme } from "@emotion/react";
import Button from "@mui/material/Button";
import Link from "@mui/material/Link";
import type { TemplateExample } from "api/typesGenerated";
import { type FC } from "react";
import { Link } from "react-router-dom";
import { Pill } from "components/Pill/Pill";
import { HTMLProps } from "react";
import { Link as RouterLink } from "react-router-dom";
export interface TemplateExampleCardProps {
type TemplateExampleCardProps = {
example: TemplateExample;
className?: string;
}
activeTag?: string;
} & HTMLProps<HTMLDivElement>;
export const TemplateExampleCard: FC<TemplateExampleCardProps> = ({
example,
className,
}) => {
export const TemplateExampleCard = (props: TemplateExampleCardProps) => {
const { example, activeTag, ...divProps } = props;
return (
<Link
to={`/starter-templates/${example.id}`}
css={styles.template}
className={className}
key={example.id}
<div
css={(theme) => ({
width: "320px",
padding: 24,
borderRadius: 6,
border: `1px solid ${theme.palette.divider}`,
textAlign: "left",
textDecoration: "none",
color: "inherit",
display: "flex",
flexDirection: "column",
})}
{...divProps}
>
<div css={styles.templateIcon}>
<img src={example.icon} alt="" />
<div
css={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
marginBottom: 24,
}}
>
<div
css={{
flexShrink: 0,
paddingTop: 4,
width: 32,
height: 32,
}}
>
<img
src={example.icon}
alt=""
css={{ width: "100%", height: "100%", objectFit: "contain" }}
/>
</div>
<div css={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
{example.tags.map((tag) => {
const isActive = activeTag === tag;
return (
<RouterLink key={tag} to={`/starter-templates?tag=${tag}`}>
<Pill
text={tag}
css={(theme) => ({
borderColor: isActive
? theme.palette.primary.main
: theme.palette.divider,
cursor: "pointer",
backgroundColor: isActive
? theme.palette.primary.dark
: undefined,
"&: hover": {
borderColor: theme.palette.primary.main,
},
})}
/>
</RouterLink>
);
})}
</div>
</div>
<div css={styles.templateInfo}>
<span css={styles.templateName}>{example.name}</span>
<span css={styles.templateDescription}>{example.description}</span>
<div>
<h4 css={{ fontSize: 14, fontWeight: 600, margin: 0, marginBottom: 4 }}>
{example.name}
</h4>
<span
css={(theme) => ({
fontSize: 13,
color: theme.palette.text.secondary,
lineHeight: "1.6",
display: "block",
})}
>
{example.description}{" "}
<Link
component={RouterLink}
to={`/starter-templates/${example.id}`}
css={{ display: "inline-block", fontSize: 13, marginTop: 4 }}
>
Read more
</Link>
</span>
</div>
</Link>
<div
css={{
display: "flex",
gap: 12,
flexDirection: "column",
paddingTop: 24,
marginTop: "auto",
alignItems: "center",
}}
>
<Button
component={RouterLink}
fullWidth
to={`/templates/new?exampleId=${example.id}`}
>
Use template
</Button>
</div>
</div>
);
};
const styles = {
template: (theme) => ({
border: `1px solid ${theme.palette.divider}`,
borderRadius: 8,
background: theme.palette.background.paper,
textDecoration: "none",
textAlign: "left",
color: "inherit",
display: "flex",
alignItems: "center",
height: "fit-content",
"&:hover": {
backgroundColor: theme.palette.background.paperLight,
},
}),
templateIcon: {
width: 96,
height: 96,
display: "flex",
alignItems: "center",
justifyContent: "center",
flexShrink: 0,
"& img": {
height: 32,
},
},
templateInfo: {
padding: "16px 16px 16px 0",
display: "flex",
flexDirection: "column",
overflow: "hidden",
},
templateName: {
fontSize: 16,
textOverflow: "ellipsis",
width: "100%",
overflow: "hidden",
whiteSpace: "nowrap",
},
templateDescription: (theme) => ({
fontSize: 14,
color: theme.palette.text.secondary,
textOverflow: "ellipsis",
width: "100%",
overflow: "hidden",
whiteSpace: "nowrap",
}),
} satisfies Record<string, Interpolation<Theme>>;
+3 -3
View File
@@ -87,8 +87,8 @@ describe("Setup Page", () => {
element: <SetupPage />,
},
{
path: "/",
element: <h1>Workspaces</h1>,
path: "/templates",
element: <h1>Templates</h1>,
},
],
{ initialEntries: ["/setup"] },
@@ -96,6 +96,6 @@ describe("Setup Page", () => {
);
await waitForLoaderToBeRemoved();
await fillForm();
await waitFor(() => screen.findByText("Workspaces"));
await waitFor(() => screen.findByText("Templates"));
});
});
+1 -1
View File
@@ -35,7 +35,7 @@ export const SetupPage: FC = () => {
onSubmit={async (firstUser) => {
await createFirstUserMutation.mutateAsync(firstUser);
await signIn(firstUser.email, firstUser.password);
navigate("/");
navigate("/templates");
}}
/>
</>
@@ -60,9 +60,11 @@ export const StarterTemplatesPageView: FC<StarterTemplatesPageViewProps> = ({
{Boolean(!starterTemplatesByTag) && <Loader />}
<Stack direction="row" spacing={4}>
<Stack direction="row" spacing={4} alignItems="flex-start">
{starterTemplatesByTag && tags && (
<Stack css={styles.filter}>
<Stack
css={{ width: 208, flexShrink: 0, position: "sticky", top: 48 }}
>
<span css={styles.filterCaption}>Filter</span>
{tags.map((tag) => (
<Link
@@ -79,10 +81,24 @@ export const StarterTemplatesPageView: FC<StarterTemplatesPageViewProps> = ({
</Stack>
)}
<div css={styles.templates}>
<div
css={{
display: "flex",
flexWrap: "wrap",
gap: 32,
height: "max-content",
}}
>
{visibleTemplates &&
visibleTemplates.map((example) => (
<TemplateExampleCard example={example} key={example.id} />
<TemplateExampleCard
css={(theme) => ({
backgroundColor: theme.palette.background.paper,
})}
example={example}
key={example.id}
activeTag={activeTag}
/>
))}
</div>
</Stack>
@@ -91,11 +107,6 @@ export const StarterTemplatesPageView: FC<StarterTemplatesPageViewProps> = ({
};
const styles = {
filter: {
width: 208,
flexShrink: 0,
},
filterCaption: (theme) => ({
textTransform: "uppercase",
fontWeight: 600,
@@ -119,12 +130,4 @@ const styles = {
color: theme.palette.text.primary,
fontWeight: 600,
}),
templates: {
flex: "1",
display: "grid",
gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
gap: 16,
gridAutoRows: "min-content",
},
} satisfies Record<string, Interpolation<Theme>>;
@@ -44,12 +44,11 @@ export const EmptyTemplates: FC<{
if (canCreateTemplates) {
return (
<TableEmpty
message="Create a Template"
message="Create your first template"
description={
<>
Templates are written in Terraform and describe the infrastructure
for workspaces (e.g., docker_container, aws_instance,
kubernetes_pod). Select a starter template below or
for workspaces. You can start using a starter template below or{" "}
<Link
href={docs("/templates/tutorial")}
target="_blank"
@@ -64,11 +63,7 @@ export const EmptyTemplates: FC<{
<Stack alignItems="center" spacing={4}>
<div css={styles.featuredExamples}>
{featuredExamples.map((example) => (
<TemplateExampleCard
example={example}
key={example.id}
css={styles.template}
/>
<TemplateExampleCard example={example} key={example.id} />
))}
</div>
@@ -76,7 +71,7 @@ export const EmptyTemplates: FC<{
size="small"
component={RouterLink}
to="/starter-templates"
css={styles.viewAllButton}
css={{ borderRadius: 9999 }}
>
View all starter templates
</Button>
@@ -117,23 +112,10 @@ const styles = {
},
},
featuredExamples: {
maxWidth: 800,
display: "grid",
gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
gap: 16,
gridAutoRows: "min-content",
},
template: (theme) => ({
backgroundColor: theme.palette.background.paperLight,
"&:hover": {
backgroundColor: theme.palette.divider,
},
featuredExamples: (theme) => ({
display: "flex",
flexWrap: "wrap",
justifyContent: "center",
gap: theme.spacing(2),
}),
viewAllButton: {
borderRadius: 9999,
},
} satisfies Record<string, Interpolation<Theme>>;
@@ -122,7 +122,7 @@ export const WorkspacesEmpty = (props: {
</div>
<div>
<h4 css={{ fontSize: 14, fontWeight: 600, margin: 0 }}>
{t.display_name}
{t.display_name.length > 0 ? t.display_name : t.name}
</h4>
<span
css={(theme) => ({