Skip to content

Commit 5b9e26a

Browse files
refactor(site): handle edge cases for non-admin users with no workspaces and templates (#10517)
1 parent 55fb6b6 commit 5b9e26a

File tree

5 files changed

+100
-8
lines changed

5 files changed

+100
-8
lines changed

site/src/pages/WorkspacesPage/WorkspacesEmpty.tsx

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,76 @@ import { Link } from "react-router-dom";
88
export const WorkspacesEmpty = (props: {
99
isUsingFilter: boolean;
1010
templates?: Template[];
11+
canCreateTemplate: boolean;
1112
}) => {
12-
const { isUsingFilter, templates } = props;
13+
const { isUsingFilter, templates, canCreateTemplate } = props;
1314
const totalFeaturedTemplates = 6;
1415
const featuredTemplates = templates?.slice(0, totalFeaturedTemplates);
16+
const defaultTitle = "Create a workspace";
17+
const defaultMessage =
18+
"A workspace is your personal, customizable development environment.";
19+
const defaultImage = (
20+
<div
21+
css={{
22+
maxWidth: "50%",
23+
height: 272,
24+
overflow: "hidden",
25+
marginTop: 48,
26+
opacity: 0.85,
27+
28+
"& img": {
29+
maxWidth: "100%",
30+
},
31+
}}
32+
>
33+
<img src="/featured/workspaces.webp" alt="" />
34+
</div>
35+
);
1536

1637
if (isUsingFilter) {
1738
return <TableEmpty message="No results matched your search" />;
1839
}
1940

41+
if (templates && templates.length === 0 && canCreateTemplate) {
42+
return (
43+
<TableEmpty
44+
message={defaultTitle}
45+
description={`${defaultMessage} To create a workspace, you first need to create a template.`}
46+
cta={
47+
<Button
48+
component={Link}
49+
to="/templates"
50+
variant="contained"
51+
startIcon={<ArrowForwardOutlined />}
52+
>
53+
Go to templates
54+
</Button>
55+
}
56+
css={{
57+
paddingBottom: 0,
58+
}}
59+
image={defaultImage}
60+
/>
61+
);
62+
}
63+
64+
if (templates && templates.length === 0 && !canCreateTemplate) {
65+
return (
66+
<TableEmpty
67+
message={defaultTitle}
68+
description={`${defaultMessage} There are no templates available, but you will see them here once your admin adds them.`}
69+
css={{
70+
paddingBottom: 0,
71+
}}
72+
image={defaultImage}
73+
/>
74+
);
75+
}
76+
2077
return (
2178
<TableEmpty
22-
message="Create a workspace"
23-
description="A workspace is your personal, customizable development environment in the cloud. Select one template below to start."
79+
message={defaultTitle}
80+
description={`${defaultMessage} Select one template below to start.`}
2481
cta={
2582
<div>
2683
<div
@@ -80,6 +137,7 @@ export const WorkspacesEmpty = (props: {
80137
</Link>
81138
))}
82139
</div>
140+
83141
{templates && templates.length > totalFeaturedTemplates && (
84142
<Button
85143
component={Link}

site/src/pages/WorkspacesPage/WorkspacesPage.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ const WorkspacesPage: FC = () => {
9797
const { entitlements } = useDashboard();
9898
const canCheckWorkspaces =
9999
entitlements.features["workspace_batch_actions"].enabled;
100+
const permissions = usePermissions();
100101

101102
// We want to uncheck the selected workspaces always when the url changes
102103
// because of filtering or pagination
@@ -111,6 +112,7 @@ const WorkspacesPage: FC = () => {
111112
</Helmet>
112113

113114
<WorkspacesPageView
115+
canCreateTemplate={permissions.createTemplates}
114116
checkedWorkspaces={checkedWorkspaces}
115117
onCheckChange={setCheckedWorkspaces}
116118
canCheckWorkspaces={canCheckWorkspaces}

site/src/pages/WorkspacesPage/WorkspacesPageView.stories.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,33 @@ export const OwnerHasNoWorkspaces: Story = {
147147
args: {
148148
workspaces: [],
149149
count: 0,
150+
canCreateTemplate: true,
151+
},
152+
};
153+
154+
export const OwnerHasNoWorkspacesAndNoTemplates: Story = {
155+
args: {
156+
workspaces: [],
157+
templates: [],
158+
count: 0,
159+
canCreateTemplate: true,
160+
},
161+
};
162+
163+
export const UserHasNoWorkspaces: Story = {
164+
args: {
165+
workspaces: [],
166+
count: 0,
167+
canCreateTemplate: false,
168+
},
169+
};
170+
171+
export const UserHasNoWorkspacesAndNoTemplates: Story = {
172+
args: {
173+
workspaces: [],
174+
templates: [],
175+
count: 0,
176+
canCreateTemplate: false,
150177
},
151178
};
152179

site/src/pages/WorkspacesPage/WorkspacesPageView.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Template, Workspace } from "api/typesGenerated";
22
import { PaginationWidgetBase } from "components/PaginationWidget/PaginationWidgetBase";
3-
import { ComponentProps, FC } from "react";
3+
import { ComponentProps } from "react";
44
import { Margins } from "components/Margins/Margins";
55
import { PageHeader, PageHeaderTitle } from "components/PageHeader/PageHeader";
66
import { Stack } from "components/Stack/Stack";
@@ -49,11 +49,10 @@ export interface WorkspacesPageViewProps {
4949
canCheckWorkspaces: boolean;
5050
templatesFetchStatus: TemplateQuery["status"];
5151
templates: TemplateQuery["data"];
52+
canCreateTemplate: boolean;
5253
}
5354

54-
export const WorkspacesPageView: FC<
55-
React.PropsWithChildren<WorkspacesPageViewProps>
56-
> = ({
55+
export const WorkspacesPageView = ({
5756
workspaces,
5857
dormantWorkspaces,
5958
error,
@@ -69,7 +68,8 @@ export const WorkspacesPageView: FC<
6968
canCheckWorkspaces,
7069
templates,
7170
templatesFetchStatus,
72-
}) => {
71+
canCreateTemplate,
72+
}: WorkspacesPageViewProps) => {
7373
const { saveLocal } = useLocalStorage();
7474

7575
const workspacesDeletionScheduled = dormantWorkspaces
@@ -149,6 +149,7 @@ export const WorkspacesPageView: FC<
149149
</TableToolbar>
150150

151151
<WorkspacesTable
152+
canCreateTemplate={canCreateTemplate}
152153
workspaces={workspaces}
153154
isUsingFilter={filterProps.filter.used}
154155
onUpdateWorkspace={onUpdateWorkspace}
@@ -157,6 +158,7 @@ export const WorkspacesPageView: FC<
157158
canCheckWorkspaces={canCheckWorkspaces}
158159
templates={templates}
159160
/>
161+
160162
{count !== undefined && (
161163
<PaginationWidgetBase
162164
count={count}

site/src/pages/WorkspacesPage/WorkspacesTable.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export interface WorkspacesTableProps {
3636
onCheckChange: (checkedWorkspaces: Workspace[]) => void;
3737
canCheckWorkspaces: boolean;
3838
templates?: Template[];
39+
canCreateTemplate: boolean;
3940
}
4041

4142
export const WorkspacesTable: FC<WorkspacesTableProps> = ({
@@ -46,6 +47,7 @@ export const WorkspacesTable: FC<WorkspacesTableProps> = ({
4647
onCheckChange,
4748
canCheckWorkspaces,
4849
templates,
50+
canCreateTemplate,
4951
}) => {
5052
return (
5153
<TableContainer>
@@ -92,6 +94,7 @@ export const WorkspacesTable: FC<WorkspacesTableProps> = ({
9294
<WorkspacesEmpty
9395
templates={templates}
9496
isUsingFilter={isUsingFilter}
97+
canCreateTemplate={canCreateTemplate}
9598
/>
9699
)}
97100
{workspaces &&

0 commit comments

Comments
 (0)