Skip to content

Commit a9797fa

Browse files
refactor(site): improve templates empty state (#10518)
1 parent e976f50 commit a9797fa

File tree

6 files changed

+147
-125
lines changed

6 files changed

+147
-125
lines changed
Lines changed: 113 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,123 @@
1-
import { type Interpolation, type Theme } from "@emotion/react";
1+
import Button from "@mui/material/Button";
2+
import Link from "@mui/material/Link";
23
import type { TemplateExample } from "api/typesGenerated";
3-
import { type FC } from "react";
4-
import { Link } from "react-router-dom";
4+
import { Pill } from "components/Pill/Pill";
5+
import { HTMLProps } from "react";
6+
import { Link as RouterLink } from "react-router-dom";
57

6-
export interface TemplateExampleCardProps {
8+
type TemplateExampleCardProps = {
79
example: TemplateExample;
8-
className?: string;
9-
}
10+
activeTag?: string;
11+
} & HTMLProps<HTMLDivElement>;
1012

11-
export const TemplateExampleCard: FC<TemplateExampleCardProps> = ({
12-
example,
13-
className,
14-
}) => {
13+
export const TemplateExampleCard = (props: TemplateExampleCardProps) => {
14+
const { example, activeTag, ...divProps } = props;
1515
return (
16-
<Link
17-
to={`/starter-templates/${example.id}`}
18-
css={styles.template}
19-
className={className}
20-
key={example.id}
16+
<div
17+
css={(theme) => ({
18+
width: "320px",
19+
padding: 24,
20+
borderRadius: 6,
21+
border: `1px solid ${theme.palette.divider}`,
22+
textAlign: "left",
23+
textDecoration: "none",
24+
color: "inherit",
25+
display: "flex",
26+
flexDirection: "column",
27+
})}
28+
{...divProps}
2129
>
22-
<div css={styles.templateIcon}>
23-
<img src={example.icon} alt="" />
24-
</div>
25-
<div css={styles.templateInfo}>
26-
<span css={styles.templateName}>{example.name}</span>
27-
<span css={styles.templateDescription}>{example.description}</span>
28-
</div>
29-
</Link>
30-
);
31-
};
32-
33-
const styles = {
34-
template: (theme) => ({
35-
border: `1px solid ${theme.palette.divider}`,
36-
borderRadius: 8,
37-
background: theme.palette.background.paper,
38-
textDecoration: "none",
39-
textAlign: "left",
40-
color: "inherit",
41-
display: "flex",
42-
alignItems: "center",
43-
height: "fit-content",
44-
45-
"&:hover": {
46-
backgroundColor: theme.palette.background.paperLight,
47-
},
48-
}),
30+
<div
31+
css={{
32+
display: "flex",
33+
alignItems: "center",
34+
justifyContent: "space-between",
35+
marginBottom: 24,
36+
}}
37+
>
38+
<div
39+
css={{
40+
flexShrink: 0,
41+
paddingTop: 4,
42+
width: 32,
43+
height: 32,
44+
}}
45+
>
46+
<img
47+
src={example.icon}
48+
alt=""
49+
css={{ width: "100%", height: "100%", objectFit: "contain" }}
50+
/>
51+
</div>
4952

50-
templateIcon: {
51-
width: 96,
52-
height: 96,
53-
display: "flex",
54-
alignItems: "center",
55-
justifyContent: "center",
56-
flexShrink: 0,
53+
<div css={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
54+
{example.tags.map((tag) => {
55+
const isActive = activeTag === tag;
5756

58-
"& img": {
59-
height: 32,
60-
},
61-
},
62-
63-
templateInfo: {
64-
padding: "16px 16px 16px 0",
65-
display: "flex",
66-
flexDirection: "column",
67-
overflow: "hidden",
68-
},
57+
return (
58+
<RouterLink key={tag} to={`/starter-templates?tag=${tag}`}>
59+
<Pill
60+
text={tag}
61+
css={(theme) => ({
62+
borderColor: isActive
63+
? theme.palette.primary.main
64+
: theme.palette.divider,
65+
cursor: "pointer",
66+
backgroundColor: isActive
67+
? theme.palette.primary.dark
68+
: undefined,
69+
"&: hover": {
70+
borderColor: theme.palette.primary.main,
71+
},
72+
})}
73+
/>
74+
</RouterLink>
75+
);
76+
})}
77+
</div>
78+
</div>
6979

70-
templateName: {
71-
fontSize: 16,
72-
textOverflow: "ellipsis",
73-
width: "100%",
74-
overflow: "hidden",
75-
whiteSpace: "nowrap",
76-
},
80+
<div>
81+
<h4 css={{ fontSize: 14, fontWeight: 600, margin: 0, marginBottom: 4 }}>
82+
{example.name}
83+
</h4>
84+
<span
85+
css={(theme) => ({
86+
fontSize: 13,
87+
color: theme.palette.text.secondary,
88+
lineHeight: "1.6",
89+
display: "block",
90+
})}
91+
>
92+
{example.description}{" "}
93+
<Link
94+
component={RouterLink}
95+
to={`/starter-templates/${example.id}`}
96+
css={{ display: "inline-block", fontSize: 13, marginTop: 4 }}
97+
>
98+
Read more
99+
</Link>
100+
</span>
101+
</div>
77102

78-
templateDescription: (theme) => ({
79-
fontSize: 14,
80-
color: theme.palette.text.secondary,
81-
textOverflow: "ellipsis",
82-
width: "100%",
83-
overflow: "hidden",
84-
whiteSpace: "nowrap",
85-
}),
86-
} satisfies Record<string, Interpolation<Theme>>;
103+
<div
104+
css={{
105+
display: "flex",
106+
gap: 12,
107+
flexDirection: "column",
108+
paddingTop: 24,
109+
marginTop: "auto",
110+
alignItems: "center",
111+
}}
112+
>
113+
<Button
114+
component={RouterLink}
115+
fullWidth
116+
to={`/templates/new?exampleId=${example.id}`}
117+
>
118+
Use template
119+
</Button>
120+
</div>
121+
</div>
122+
);
123+
};

site/src/pages/SetupPage/SetupPage.test.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,15 @@ describe("Setup Page", () => {
8787
element: <SetupPage />,
8888
},
8989
{
90-
path: "/",
91-
element: <h1>Workspaces</h1>,
90+
path: "/templates",
91+
element: <h1>Templates</h1>,
9292
},
9393
],
9494
{ initialEntries: ["/setup"] },
9595
),
9696
);
9797
await waitForLoaderToBeRemoved();
9898
await fillForm();
99-
await waitFor(() => screen.findByText("Workspaces"));
99+
await waitFor(() => screen.findByText("Templates"));
100100
});
101101
});

site/src/pages/SetupPage/SetupPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export const SetupPage: FC = () => {
3535
onSubmit={async (firstUser) => {
3636
await createFirstUserMutation.mutateAsync(firstUser);
3737
await signIn(firstUser.email, firstUser.password);
38-
navigate("/");
38+
navigate("/templates");
3939
}}
4040
/>
4141
</>

site/src/pages/StarterTemplatesPage/StarterTemplatesPageView.tsx

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,11 @@ export const StarterTemplatesPageView: FC<StarterTemplatesPageViewProps> = ({
6060

6161
{Boolean(!starterTemplatesByTag) && <Loader />}
6262

63-
<Stack direction="row" spacing={4}>
63+
<Stack direction="row" spacing={4} alignItems="flex-start">
6464
{starterTemplatesByTag && tags && (
65-
<Stack css={styles.filter}>
65+
<Stack
66+
css={{ width: 208, flexShrink: 0, position: "sticky", top: 48 }}
67+
>
6668
<span css={styles.filterCaption}>Filter</span>
6769
{tags.map((tag) => (
6870
<Link
@@ -79,10 +81,24 @@ export const StarterTemplatesPageView: FC<StarterTemplatesPageViewProps> = ({
7981
</Stack>
8082
)}
8183

82-
<div css={styles.templates}>
84+
<div
85+
css={{
86+
display: "flex",
87+
flexWrap: "wrap",
88+
gap: 32,
89+
height: "max-content",
90+
}}
91+
>
8392
{visibleTemplates &&
8493
visibleTemplates.map((example) => (
85-
<TemplateExampleCard example={example} key={example.id} />
94+
<TemplateExampleCard
95+
css={(theme) => ({
96+
backgroundColor: theme.palette.background.paper,
97+
})}
98+
example={example}
99+
key={example.id}
100+
activeTag={activeTag}
101+
/>
86102
))}
87103
</div>
88104
</Stack>
@@ -91,11 +107,6 @@ export const StarterTemplatesPageView: FC<StarterTemplatesPageViewProps> = ({
91107
};
92108

93109
const styles = {
94-
filter: {
95-
width: 208,
96-
flexShrink: 0,
97-
},
98-
99110
filterCaption: (theme) => ({
100111
textTransform: "uppercase",
101112
fontWeight: 600,
@@ -119,12 +130,4 @@ const styles = {
119130
color: theme.palette.text.primary,
120131
fontWeight: 600,
121132
}),
122-
123-
templates: {
124-
flex: "1",
125-
display: "grid",
126-
gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
127-
gap: 16,
128-
gridAutoRows: "min-content",
129-
},
130133
} satisfies Record<string, Interpolation<Theme>>;

site/src/pages/TemplatesPage/EmptyTemplates.tsx

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,11 @@ export const EmptyTemplates: FC<{
4444
if (canCreateTemplates) {
4545
return (
4646
<TableEmpty
47-
message="Create a Template"
47+
message="Create your first template"
4848
description={
4949
<>
5050
Templates are written in Terraform and describe the infrastructure
51-
for workspaces (e.g., docker_container, aws_instance,
52-
kubernetes_pod). Select a starter template below or
51+
for workspaces. You can start using a starter template below or{" "}
5352
<Link
5453
href={docs("/templates/tutorial")}
5554
target="_blank"
@@ -64,19 +63,15 @@ export const EmptyTemplates: FC<{
6463
<Stack alignItems="center" spacing={4}>
6564
<div css={styles.featuredExamples}>
6665
{featuredExamples.map((example) => (
67-
<TemplateExampleCard
68-
example={example}
69-
key={example.id}
70-
css={styles.template}
71-
/>
66+
<TemplateExampleCard example={example} key={example.id} />
7267
))}
7368
</div>
7469

7570
<Button
7671
size="small"
7772
component={RouterLink}
7873
to="/starter-templates"
79-
css={styles.viewAllButton}
74+
css={{ borderRadius: 9999 }}
8075
>
8176
View all starter templates
8277
</Button>
@@ -117,23 +112,10 @@ const styles = {
117112
},
118113
},
119114

120-
featuredExamples: {
121-
maxWidth: 800,
122-
display: "grid",
123-
gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
124-
gap: 16,
125-
gridAutoRows: "min-content",
126-
},
127-
128-
template: (theme) => ({
129-
backgroundColor: theme.palette.background.paperLight,
130-
131-
"&:hover": {
132-
backgroundColor: theme.palette.divider,
133-
},
115+
featuredExamples: (theme) => ({
116+
display: "flex",
117+
flexWrap: "wrap",
118+
justifyContent: "center",
119+
gap: theme.spacing(2),
134120
}),
135-
136-
viewAllButton: {
137-
borderRadius: 9999,
138-
},
139121
} satisfies Record<string, Interpolation<Theme>>;

site/src/pages/WorkspacesPage/WorkspacesEmpty.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ export const WorkspacesEmpty = (props: {
122122
</div>
123123
<div>
124124
<h4 css={{ fontSize: 14, fontWeight: 600, margin: 0 }}>
125-
{t.display_name}
125+
{t.display_name.length > 0 ? t.display_name : t.name}
126126
</h4>
127127
<span
128128
css={(theme) => ({

0 commit comments

Comments
 (0)