Skip to content

Commit ff3fc09

Browse files
authored
feat: create a workspace from any template version (#9861)
1 parent 4adbf24 commit ff3fc09

18 files changed

+66
-28
lines changed

site/src/AppRouter.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { FullScreenLoader } from "components/Loader/FullScreenLoader";
2-
import { TemplateLayout } from "components/TemplateLayout/TemplateLayout";
32
import { UsersLayout } from "components/UsersLayout/UsersLayout";
43
import AuditPage from "pages/AuditPage/AuditPage";
54
import LoginPage from "pages/LoginPage/LoginPage";
65
import { SetupPage } from "pages/SetupPage/SetupPage";
6+
import { TemplateLayout } from "pages/TemplatePage/TemplateLayout";
77
import TemplatesPage from "pages/TemplatesPage/TemplatesPage";
88
import UsersPage from "pages/UsersPage/UsersPage";
99
import WorkspacesPage from "pages/WorkspacesPage/WorkspacesPage";

site/src/pages/TemplatePage/TemplateDocsPage/TemplateDocsPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { makeStyles } from "@mui/styles";
22
import { MemoizedMarkdown } from "components/Markdown/Markdown";
3-
import { useTemplateLayoutContext } from "components/TemplateLayout/TemplateLayout";
3+
import { useTemplateLayoutContext } from "pages/TemplatePage/TemplateLayout";
44
import frontMatter from "front-matter";
55
import { Helmet } from "react-helmet-async";
66
import { pageTitle } from "utils/page";

site/src/pages/TemplatePage/TemplateEmbedPage/TemplateEmbedPage.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
waitForLoaderToBeRemoved,
44
} from "testHelpers/renderHelpers";
55
import TemplateEmbedPage from "./TemplateEmbedPage";
6-
import { TemplateLayout } from "components/TemplateLayout/TemplateLayout";
6+
import { TemplateLayout } from "pages/TemplatePage/TemplateLayout";
77
import {
88
MockTemplate,
99
MockTemplateVersionParameter1 as parameter1,

site/src/pages/TemplatePage/TemplateEmbedPage/TemplateEmbedPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { getTemplateVersionRichParameters } from "api/api";
1010
import { Template, TemplateVersionParameter } from "api/typesGenerated";
1111
import { FormSection, VerticalForm } from "components/Form/Form";
1212
import { Loader } from "components/Loader/Loader";
13-
import { useTemplateLayoutContext } from "components/TemplateLayout/TemplateLayout";
13+
import { useTemplateLayoutContext } from "pages/TemplatePage/TemplateLayout";
1414
import {
1515
ImmutableTemplateParametersSection,
1616
MutableTemplateParametersSection,

site/src/pages/TemplatePage/TemplateFilesPage/TemplateFilesPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { getPreviousTemplateVersionByName } from "api/api";
33
import { TemplateVersion } from "api/typesGenerated";
44
import { Loader } from "components/Loader/Loader";
55
import { TemplateFiles } from "components/TemplateFiles/TemplateFiles";
6-
import { useTemplateLayoutContext } from "components/TemplateLayout/TemplateLayout";
6+
import { useTemplateLayoutContext } from "pages/TemplatePage/TemplateLayout";
77
import { useOrganizationId } from "hooks/useOrganizationId";
88
import { useTab } from "hooks/useTab";
99
import { FC, useEffect } from "react";

site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { BoxProps } from "@mui/system";
55
import { useQuery } from "@tanstack/react-query";
66
import { getInsightsTemplate, getInsightsUserLatency } from "api/api";
77
import { DAUChart, DAUTitle } from "components/DAUChart/DAUChart";
8-
import { useTemplateLayoutContext } from "components/TemplateLayout/TemplateLayout";
8+
import { useTemplateLayoutContext } from "pages/TemplatePage/TemplateLayout";
99
import {
1010
HelpTooltip,
1111
HelpTooltipTitle,

site/src/components/TemplateLayout/TemplatePageHeader.stories.tsx renamed to site/src/pages/TemplatePage/TemplatePageHeader.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { TemplatePageHeader } from "./TemplatePageHeader";
33
import type { Meta, StoryObj } from "@storybook/react";
44

55
const meta: Meta<typeof TemplatePageHeader> = {
6-
title: "components/TemplatePageHeader",
6+
title: "pages/TemplatePage/TemplatePageHeader",
77
component: TemplatePageHeader,
88
args: {
99
template: MockTemplate,

site/src/components/TemplateLayout/TemplatePageHeader.tsx renamed to site/src/pages/TemplatePage/TemplatePageHeader.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,6 @@ const TemplateMenu: FC<{
6969
<SettingsOutlined />
7070
Settings
7171
</MenuItem>
72-
<MenuItem
73-
onClick={onMenuItemClick(() =>
74-
navigate(`/templates/new?fromTemplate=${templateName}`),
75-
)}
76-
>
77-
<FileCopyOutlined />
78-
Duplicate
79-
</MenuItem>
8072
<MenuItem
8173
onClick={onMenuItemClick(() =>
8274
navigate(
@@ -87,9 +79,17 @@ const TemplateMenu: FC<{
8779
<EditOutlined />
8880
Edit files
8981
</MenuItem>
82+
<MenuItem
83+
onClick={onMenuItemClick(() =>
84+
navigate(`/templates/new?fromTemplate=${templateName}`),
85+
)}
86+
>
87+
<FileCopyOutlined />
88+
Duplicate&hellip;
89+
</MenuItem>
9090
<MenuItem onClick={onMenuItemClick(onDelete)}>
9191
<DeleteOutlined />
92-
Delete
92+
Delete&hellip;
9393
</MenuItem>
9494
</Menu>
9595
</div>

site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useTemplateLayoutContext } from "components/TemplateLayout/TemplateLayout";
1+
import { useTemplateLayoutContext } from "pages/TemplatePage/TemplateLayout";
22
import { FC } from "react";
33
import { Helmet } from "react-helmet-async";
44
import { getTemplatePageTitle } from "../utils";

site/src/pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { getTemplateVersions, updateActiveTemplateVersion } from "api/api";
33
import { getErrorMessage } from "api/errors";
44
import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog";
55
import { displayError, displaySuccess } from "components/GlobalSnackbar/utils";
6-
import { useTemplateLayoutContext } from "components/TemplateLayout/TemplateLayout";
6+
import { useTemplateLayoutContext } from "pages/TemplatePage/TemplateLayout";
77
import { VersionsTable } from "./VersionsTable";
88
import { useState } from "react";
99
import { Helmet } from "react-helmet-async";

site/src/pages/TemplatePage/TemplateVersionsPage/VersionRow.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ export const VersionRow: React.FC<VersionRowProps> = ({
8888
onPromoteClick(version.id);
8989
}}
9090
>
91-
Promote
91+
Promote&hellip;
9292
</Button>
9393
)}
9494
</Stack>

site/src/pages/TemplateVersionPage/TemplateVersionPage.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { useMachine } from "@xstate/react";
2+
import { usePermissions } from "hooks/usePermissions";
23
import { useOrganizationId } from "hooks/useOrganizationId";
34
import { useTab } from "hooks/useTab";
4-
import { FC } from "react";
5+
import { type FC, useMemo } from "react";
56
import { Helmet } from "react-helmet-async";
67
import { useParams } from "react-router-dom";
78
import { pageTitle } from "utils/page";
@@ -21,6 +22,17 @@ export const TemplateVersionPage: FC = () => {
2122
context: { templateName, versionName, orgId },
2223
});
2324
const tab = useTab("file", "0");
25+
const permissions = usePermissions();
26+
27+
const versionId = state.context.currentVersion?.id;
28+
const createWorkspaceUrl = useMemo(() => {
29+
const params = new URLSearchParams();
30+
if (versionId) {
31+
params.set("version", versionId);
32+
return `/templates/${templateName}/workspace?${params.toString()}`;
33+
}
34+
return undefined;
35+
}, [templateName, versionId]);
2436

2537
return (
2638
<>
@@ -33,6 +45,9 @@ export const TemplateVersionPage: FC = () => {
3345
versionName={versionName}
3446
templateName={templateName}
3547
tab={tab}
48+
createWorkspaceUrl={
49+
permissions.updateTemplates ? createWorkspaceUrl : undefined
50+
}
3651
/>
3752
</>
3853
);

site/src/pages/TemplateVersionPage/TemplateVersionPageView.tsx

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import Button from "@mui/material/Button";
2-
import Link from "@mui/material/Link";
2+
import AddIcon from "@mui/icons-material/Add";
33
import EditIcon from "@mui/icons-material/Edit";
44
import { Loader } from "components/Loader/Loader";
55
import { Margins } from "components/Margins/Margins";
@@ -13,7 +13,7 @@ import { Stack } from "components/Stack/Stack";
1313
import { Stats, StatsItem } from "components/Stats/Stats";
1414
import { TemplateFiles } from "components/TemplateFiles/TemplateFiles";
1515
import { UseTabResult } from "hooks/useTab";
16-
import { FC } from "react";
16+
import { type FC } from "react";
1717
import { Link as RouterLink } from "react-router-dom";
1818
import { createDayString } from "utils/createDayString";
1919
import { TemplateVersionMachineContext } from "xServices/templateVersion/templateVersionXService";
@@ -27,26 +27,41 @@ export interface TemplateVersionPageViewProps {
2727
templateName: string;
2828
tab: UseTabResult;
2929
context: TemplateVersionMachineContext;
30+
createWorkspaceUrl?: string;
3031
}
3132

3233
export const TemplateVersionPageView: FC<TemplateVersionPageViewProps> = ({
3334
context,
3435
tab,
3536
versionName,
3637
templateName,
38+
createWorkspaceUrl,
3739
}) => {
3840
const { currentFiles, error, currentVersion, previousFiles } = context;
3941

4042
return (
4143
<Margins>
4244
<PageHeader
4345
actions={
44-
<Link
45-
component={RouterLink}
46-
to={`/templates/${templateName}/versions/${versionName}/edit`}
47-
>
48-
<Button startIcon={<EditIcon />}>Edit</Button>
49-
</Link>
46+
<>
47+
{createWorkspaceUrl && (
48+
<Button
49+
variant="contained"
50+
startIcon={<AddIcon />}
51+
component={RouterLink}
52+
to={createWorkspaceUrl}
53+
>
54+
Create workspace
55+
</Button>
56+
)}
57+
<Button
58+
startIcon={<EditIcon />}
59+
component={RouterLink}
60+
to={`/templates/${templateName}/versions/${versionName}/edit`}
61+
>
62+
Edit
63+
</Button>
64+
</>
5065
}
5166
>
5267
<PageHeaderCaption>Version</PageHeaderCaption>

site/src/testHelpers/entities.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2109,6 +2109,7 @@ export const MockPermissions: Permissions = {
21092109
createTemplates: true,
21102110
createUser: true,
21112111
deleteTemplates: true,
2112+
updateTemplates: true,
21122113
readAllUsers: true,
21132114
updateUsers: true,
21142115
viewAuditLog: true,

site/src/xServices/auth/authXService.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export const checks = {
1212
updateUsers: "updateUsers",
1313
createUser: "createUser",
1414
createTemplates: "createTemplates",
15+
updateTemplates: "updateTemplates",
1516
deleteTemplates: "deleteTemplates",
1617
viewAuditLog: "viewAuditLog",
1718
viewDeploymentValues: "viewDeploymentValues",
@@ -47,6 +48,12 @@ export const permissionsToCheck = {
4748
},
4849
action: "update",
4950
},
51+
[checks.updateTemplates]: {
52+
object: {
53+
resource_type: "template",
54+
},
55+
action: "update",
56+
},
5057
[checks.deleteTemplates]: {
5158
object: {
5259
resource_type: "template",

0 commit comments

Comments
 (0)