Skip to content

Commit e03cc62

Browse files
committed
card styling
1 parent 60b586f commit e03cc62

File tree

3 files changed

+113
-39
lines changed

3 files changed

+113
-39
lines changed

site/src/components/AvatarData/AvatarData.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ export interface AvatarDataProps {
88
subtitle?: ReactNode;
99
src?: string;
1010
avatar?: React.ReactNode;
11+
displayTitle?: boolean;
1112
}
1213

1314
export const AvatarData: FC<AvatarDataProps> = ({
1415
title,
1516
subtitle,
1617
src,
1718
avatar,
19+
displayTitle = true,
1820
}) => {
1921
const theme = useTheme();
2022

@@ -47,9 +49,9 @@ export const AvatarData: FC<AvatarDataProps> = ({
4749
fontWeight: 600,
4850
}}
4951
>
50-
{title}
52+
{displayTitle && title}
5153
</span>
52-
{subtitle && (
54+
{subtitle && displayTitle && (
5355
<span
5456
css={{
5557
fontSize: 13,

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

Lines changed: 88 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,29 @@
11
import type { Interpolation, Theme } from "@emotion/react";
22
import ArrowForwardOutlined from "@mui/icons-material/ArrowForwardOutlined";
33
import Button from "@mui/material/Button";
4+
import Link from "@mui/material/Link";
5+
import Tooltip from "@mui/material/Tooltip";
6+
import { visuallyHidden } from "@mui/utils";
47
import type { FC, HTMLAttributes } from "react";
58
import { Link as RouterLink, useNavigate } from "react-router-dom";
69
import type { Template } from "api/typesGenerated";
7-
import { ExternalAvatar } from "components/Avatar/Avatar";
10+
import { ExternalAvatar, Avatar } from "components/Avatar/Avatar";
811
import { AvatarData } from "components/AvatarData/AvatarData";
912
import { DeprecatedBadge } from "components/Badges/Badges";
13+
import { ExternalImage } from "components/ExternalImage/ExternalImage";
14+
import { Pill } from "components/Pill/Pill";
15+
import { formatTemplateBuildTime } from "utils/templates";
1016

1117
type TemplateCardProps = HTMLAttributes<HTMLDivElement> & {
1218
template: Template;
19+
activeOrg?: string;
20+
hasMultipleOrgs: boolean;
1321
};
1422

1523
export const TemplateCard: FC<TemplateCardProps> = ({
1624
template,
25+
activeOrg,
26+
hasMultipleOrgs,
1727
...divProps
1828
}) => {
1929
const navigate = useNavigate();
@@ -25,7 +35,6 @@ export const TemplateCard: FC<TemplateCardProps> = ({
2535
navigate(templatePageLink);
2636
}
2737
};
28-
2938
return (
3039
<div
3140
css={styles.card}
@@ -36,52 +45,78 @@ export const TemplateCard: FC<TemplateCardProps> = ({
3645
onKeyDown={handleKeyDown}
3746
>
3847
<div css={styles.header}>
39-
<div>
48+
<div css={{ display: "flex", alignItems: "center" }}>
4049
<AvatarData
50+
displayTitle={false}
51+
subtitle=""
4152
title={
4253
template.display_name.length > 0
4354
? template.display_name
4455
: template.name
4556
}
46-
subtitle={template.organization_display_name}
47-
avatar={
48-
hasIcon && (
49-
<ExternalAvatar variant="square" fitImage src={template.icon} />
50-
)
51-
}
57+
avatar={hasIcon && <Avatar src={template.icon} size="xl" />}
5258
/>
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>
5373
</div>
54-
<div>
55-
{template.active_user_count}{" "}
56-
{template.active_user_count === 1 ? "user" : "users"}
57-
</div>
74+
75+
{hasMultipleOrgs && (
76+
<div css={styles.orgs}>
77+
<RouterLink
78+
to={`/organizations/${template.organization_name}`}
79+
onClick={(e) => e.stopPropagation()}
80+
>
81+
<Pill
82+
css={[
83+
styles.org,
84+
activeOrg === template.organization_id && styles.activeOrg,
85+
]}
86+
>
87+
{template.organization_display_name}
88+
</Pill>
89+
</RouterLink>
90+
</div>
91+
)}
5892
</div>
5993

6094
<div>
95+
<h4 css={{ fontSize: 14, fontWeight: 600, margin: 0, marginBottom: 4 }}>
96+
{template.display_name}
97+
</h4>
6198
<span css={styles.description}>
62-
<p>{template.description}</p>
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>
63108
</span>
64109
</div>
65110

66111
<div css={styles.useButtonContainer}>
67-
{template.deprecated ? (
68-
<DeprecatedBadge />
69-
) : (
70-
<Button
71-
component={RouterLink}
72-
css={styles.actionButton}
73-
className="actionButton"
74-
fullWidth
75-
startIcon={<ArrowForwardOutlined />}
76-
title={`Create a workspace using the ${template.display_name} template`}
77-
to={`/templates/${template.name}/workspace`}
78-
onClick={(e) => {
79-
e.stopPropagation();
80-
}}
81-
>
82-
Create Workspace
83-
</Button>
84-
)}
112+
<Button
113+
component={RouterLink}
114+
onClick={(e) => e.stopPropagation()}
115+
fullWidth
116+
to={`/templates/${template.name}/workspace`}
117+
>
118+
Use template
119+
</Button>
85120
</div>
86121
</div>
87122
);
@@ -141,4 +176,25 @@ const styles = {
141176
borderColor: theme.palette.text.primary,
142177
},
143178
}),
179+
180+
orgs: {
181+
display: "flex",
182+
flexWrap: "wrap",
183+
gap: 8,
184+
justifyContent: "end",
185+
},
186+
187+
org: (theme) => ({
188+
borderColor: theme.palette.divider,
189+
textDecoration: "none",
190+
cursor: "pointer",
191+
"&: hover": {
192+
borderColor: theme.palette.primary.main,
193+
},
194+
}),
195+
196+
activeOrg: (theme) => ({
197+
borderColor: theme.roles.active.outline,
198+
backgroundColor: theme.roles.active.background,
199+
}),
144200
} satisfies Record<string, Interpolation<Theme>>;

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

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,21 @@ export const TemplatesPageView: FC<TemplatesPageViewProps> = ({
137137
width: "100%",
138138
}}
139139
>
140-
<div css={{ display: "flex" }}>
141-
{templatesByOrg && Object.keys(templatesByOrg).length > 2 && (
140+
{templatesByOrg && Object.keys(templatesByOrg).length > 2 && (
141+
<div css={{ display: "flex" }}>
142142
<Stack
143-
css={{ width: 208, flexShrink: 0, position: "sticky", top: 48 }}
143+
css={(theme) => ({
144+
width: 208,
145+
flexShrink: 0,
146+
position: "sticky",
147+
top: 48,
148+
149+
// increase sidebar width on large screens
150+
// so gap between template cards isn't too large
151+
[theme.breakpoints.up(1440)]: {
152+
width: 308,
153+
},
154+
})}
144155
>
145156
<span css={styles.filterCaption}>ORGANIZATION</span>
146157
{Object.entries(templatesByOrg).map((org) => (
@@ -159,8 +170,8 @@ export const TemplatesPageView: FC<TemplatesPageViewProps> = ({
159170
</Link>
160171
))}
161172
</Stack>
162-
)}
163-
</div>
173+
</div>
174+
)}
164175

165176
<div css={{ display: "flex" }}>
166177
<div
@@ -186,9 +197,14 @@ export const TemplatesPageView: FC<TemplatesPageViewProps> = ({
186197
visibleTemplates.map((template) => (
187198
<TemplateCard
188199
css={(theme) => ({
200+
height: 320,
189201
backgroundColor: theme.palette.background.paper,
190202
})}
191203
template={template}
204+
activeOrg={activeOrg}
205+
hasMultipleOrgs={Boolean(
206+
templatesByOrg && Object.keys(templatesByOrg).length > 2,
207+
)}
192208
key={template.id}
193209
/>
194210
))

0 commit comments

Comments
 (0)