Skip to content

Commit e6f2dcc

Browse files
committed
updated card design
1 parent 6ff3395 commit e6f2dcc

File tree

2 files changed

+106
-82
lines changed

2 files changed

+106
-82
lines changed

site/src/modules/templates/TemplateCard/TemplateCard.tsx

Lines changed: 105 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
import type { Interpolation, Theme } from "@emotion/react";
2-
import ArrowForwardOutlined from "@mui/icons-material/ArrowForwardOutlined";
2+
import AddCircleOutlineIcon from "@mui/icons-material/AddCircleOutline";
33
import Button from "@mui/material/Button";
4+
import Divider from "@mui/material/Divider";
45
import Link from "@mui/material/Link";
56
import Tooltip from "@mui/material/Tooltip";
67
import { visuallyHidden } from "@mui/utils";
78
import type { FC, HTMLAttributes } from "react";
89
import { Link as RouterLink, useNavigate } from "react-router-dom";
910
import type { Template } from "api/typesGenerated";
10-
import { ExternalAvatar, Avatar } from "components/Avatar/Avatar";
11+
import { Avatar } from "components/Avatar/Avatar";
1112
import { AvatarData } from "components/AvatarData/AvatarData";
1213
import { DeprecatedBadge } from "components/Badges/Badges";
13-
import { ExternalImage } from "components/ExternalImage/ExternalImage";
1414
import { Pill } from "components/Pill/Pill";
15+
import { Stack } from "components/Stack/Stack";
16+
import { createDayString } from "utils/createDayString";
1517
import { formatTemplateBuildTime } from "utils/templates";
1618

1719
type TemplateCardProps = HTMLAttributes<HTMLDivElement> & {
@@ -28,54 +30,51 @@ export const TemplateCard: FC<TemplateCardProps> = ({
2830
}) => {
2931
const navigate = useNavigate();
3032
const templatePageLink = `/templates/${template.name}`;
31-
const hasIcon = template.icon && template.icon !== "";
3233

3334
const handleKeyDown = (e: React.KeyboardEvent) => {
3435
if (e.key === "Enter" && e.currentTarget === e.target) {
3536
navigate(templatePageLink);
3637
}
3738
};
39+
40+
const truncatedDescription =
41+
template.description.length >= 60
42+
? template.description.substring(0, 60) + "..."
43+
: template.description;
44+
3845
return (
3946
<div
4047
css={styles.card}
41-
{...divProps}
4248
role="button"
43-
tabIndex={0}
4449
onClick={() => navigate(templatePageLink)}
4550
onKeyDown={handleKeyDown}
51+
tabIndex={0}
52+
{...divProps}
4653
>
47-
<div css={styles.header}>
48-
<div css={{ display: "flex", alignItems: "center" }}>
49-
<AvatarData
50-
displayTitle={false}
51-
subtitle=""
52-
title={
53-
template.display_name.length > 0
54-
? template.display_name
55-
: template.name
56-
}
57-
avatar={hasIcon && <Avatar src={template.icon} size="xl" />}
58-
/>
59-
<p
60-
css={(theme) => ({
61-
fontSize: 13,
62-
margin: "0 0 0 auto",
63-
color: theme.palette.text.secondary,
64-
})}
65-
>
66-
<span css={{ ...visuallyHidden }}>Build time: </span>
67-
<Tooltip title="Build time" placement="bottom-start">
68-
<span>
69-
{formatTemplateBuildTime(template.build_time_stats.start.P50)}
70-
</span>
71-
</Tooltip>
72-
</p>
73-
</div>
54+
<Stack
55+
alignItems="center"
56+
justifyContent="space-between"
57+
direction="row"
58+
css={{ marginBottom: 24 }}
59+
>
60+
<AvatarData
61+
displayTitle={false}
62+
subtitle=""
63+
title={
64+
template.display_name.length > 0
65+
? template.display_name
66+
: template.name
67+
}
68+
avatar={
69+
template.icon &&
70+
template.icon !== "" && <Avatar src={template.icon} size="md" />
71+
}
72+
/>
7473

7574
{hasMultipleOrgs && (
7675
<div css={styles.orgs}>
7776
<RouterLink
78-
to={`/organizations/${template.organization_name}`}
77+
to={`/templates?org=${template.organization_id}`}
7978
onClick={(e) => e.stopPropagation()}
8079
>
8180
<Pill
@@ -89,39 +88,75 @@ export const TemplateCard: FC<TemplateCardProps> = ({
8988
</RouterLink>
9089
</div>
9190
)}
92-
</div>
93-
94-
<div>
95-
<h4 css={{ fontSize: 14, fontWeight: 600, margin: 0, marginBottom: 4 }}>
96-
{template.display_name}
97-
</h4>
98-
<span css={styles.description}>
99-
{template.description}{" "}
100-
<Link
101-
component={RouterLink}
102-
onClick={(e) => e.stopPropagation()}
103-
to={`/templates/${template.name}/docs`}
104-
css={{ display: "inline-block", fontSize: 13, marginTop: 4 }}
105-
>
106-
Read more
107-
</Link>
108-
</span>
109-
</div>
110-
111-
<div css={styles.useButtonContainer}>
112-
{template.deprecated ? (
113-
<DeprecatedBadge />
114-
) : (
115-
<Button
116-
component={RouterLink}
117-
onClick={(e) => e.stopPropagation()}
118-
fullWidth
119-
to={`/templates/${template.name}/workspace`}
91+
</Stack>
92+
93+
<Stack justifyContent="space-between" css={{ height: "100%" }}>
94+
<Stack direction="column" spacing={0}>
95+
<h4
96+
css={{ fontSize: 14, fontWeight: 600, margin: 0, marginBottom: 4 }}
12097
>
121-
Use template
122-
</Button>
123-
)}
124-
</div>
98+
{template.display_name}
99+
</h4>
100+
101+
{template.description && (
102+
<div css={styles.description}>
103+
{truncatedDescription}{" "}
104+
<Link
105+
component={RouterLink}
106+
onClick={(e) => e.stopPropagation()}
107+
to={`${templatePageLink}/docs`}
108+
css={{ display: "inline-block", fontSize: 13, marginTop: 4 }}
109+
>
110+
Read more
111+
</Link>
112+
</div>
113+
)}
114+
</Stack>
115+
116+
<Stack direction="column" alignItems="flex-start" spacing={1}>
117+
<Stack direction="row">
118+
<span css={{ ...visuallyHidden }}>Used by</span>
119+
<Tooltip title="Used by" placement="bottom-start">
120+
<span css={styles.templateStat}>
121+
{`${template.active_user_count} ${
122+
template.active_user_count === 1 ? "developer" : "developers"
123+
}`}
124+
</span>
125+
</Tooltip>
126+
<Divider orientation="vertical" variant="middle" flexItem />
127+
<span css={{ ...visuallyHidden }}>Build time</span>
128+
<Tooltip title="Build time" placement="bottom-start">
129+
<span css={styles.templateStat}>
130+
{`${formatTemplateBuildTime(
131+
template.build_time_stats.start.P50,
132+
)}`}
133+
</span>
134+
</Tooltip>
135+
<Divider orientation="vertical" variant="middle" flexItem />
136+
<span css={{ ...visuallyHidden }}>Last updated</span>
137+
<Tooltip title="Last updated" placement="bottom-start">
138+
<span css={styles.templateStat}>
139+
{`${createDayString(template.updated_at)}`}
140+
</span>
141+
</Tooltip>
142+
</Stack>
143+
{template.deprecated ? (
144+
<DeprecatedBadge />
145+
) : (
146+
<Button
147+
component={RouterLink}
148+
onClick={(e) => e.stopPropagation()}
149+
fullWidth
150+
size="small"
151+
startIcon={<AddCircleOutlineIcon />}
152+
title={`Create a workspace using the ${template.display_name} template`}
153+
to={`${templatePageLink}/workspace`}
154+
>
155+
Create workspace
156+
</Button>
157+
)}
158+
</Stack>
159+
</Stack>
125160
</div>
126161
);
127162
};
@@ -143,13 +178,6 @@ const styles = {
143178
},
144179
}),
145180

146-
header: {
147-
display: "flex",
148-
alignItems: "center",
149-
justifyContent: "space-between",
150-
marginBottom: 24,
151-
},
152-
153181
icon: {
154182
flexShrink: 0,
155183
paddingTop: 4,
@@ -164,14 +192,10 @@ const styles = {
164192
display: "block",
165193
}),
166194

167-
useButtonContainer: {
168-
display: "flex",
169-
gap: 12,
170-
flexDirection: "column",
171-
paddingTop: 24,
172-
marginTop: "auto",
173-
alignItems: "center",
174-
},
195+
templateStat: (theme) => ({
196+
fontSize: 13,
197+
color: theme.palette.text.secondary,
198+
}),
175199

176200
actionButton: (theme) => ({
177201
transition: "none",

site/src/pages/TemplatesPage/MultiOrgTemplatePage/TemplatesPageView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ export const TemplatesPageView: FC<TemplatesPageViewProps> = ({
197197
visibleTemplates.map((template) => (
198198
<TemplateCard
199199
css={(theme) => ({
200-
height: 320,
200+
height: 300,
201201
backgroundColor: theme.palette.background.paper,
202202
})}
203203
template={template}

0 commit comments

Comments
 (0)