From e93976b4517b5f0f38fbd2a7c4fcabc760b56870 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Mon, 25 Sep 2023 18:02:52 +0000 Subject: [PATCH] feat: create-a-workspace-from-any-template-version --- site/src/AppRouter.tsx | 2 +- .../TemplateDocsPage/TemplateDocsPage.tsx | 2 +- .../TemplateEmbedPage.test.tsx | 2 +- .../TemplateEmbedPage/TemplateEmbedPage.tsx | 2 +- .../TemplateFilesPage/TemplateFilesPage.tsx | 2 +- .../TemplateInsightsPage.tsx | 2 +- .../TemplatePage}/TemplateLayout.tsx | 0 .../TemplatePageHeader.stories.tsx | 2 +- .../TemplatePage}/TemplatePageHeader.tsx | 18 +++++------ .../TemplateSummaryPage.tsx | 2 +- .../TemplateVersionsPage.tsx | 2 +- .../TemplateVersionsPage/VersionRow.tsx | 2 +- .../TemplatePage}/deleteTemplate.test.ts | 0 .../TemplatePage}/deleteTemplate.ts | 0 .../TemplateVersionPage.tsx | 17 +++++++++- .../TemplateVersionPageView.tsx | 31 ++++++++++++++----- site/src/testHelpers/entities.ts | 1 + site/src/xServices/auth/authXService.ts | 7 +++++ 18 files changed, 66 insertions(+), 28 deletions(-) rename site/src/{components/TemplateLayout => pages/TemplatePage}/TemplateLayout.tsx (100%) rename site/src/{components/TemplateLayout => pages/TemplatePage}/TemplatePageHeader.stories.tsx (92%) rename site/src/{components/TemplateLayout => pages/TemplatePage}/TemplatePageHeader.tsx (98%) rename site/src/{components/TemplateLayout => pages/TemplatePage}/deleteTemplate.test.ts (100%) rename site/src/{components/TemplateLayout => pages/TemplatePage}/deleteTemplate.ts (100%) diff --git a/site/src/AppRouter.tsx b/site/src/AppRouter.tsx index ec23a17ada74d..badbbc0aa6496 100644 --- a/site/src/AppRouter.tsx +++ b/site/src/AppRouter.tsx @@ -1,9 +1,9 @@ import { FullScreenLoader } from "components/Loader/FullScreenLoader"; -import { TemplateLayout } from "components/TemplateLayout/TemplateLayout"; import { UsersLayout } from "components/UsersLayout/UsersLayout"; import AuditPage from "pages/AuditPage/AuditPage"; import LoginPage from "pages/LoginPage/LoginPage"; import { SetupPage } from "pages/SetupPage/SetupPage"; +import { TemplateLayout } from "pages/TemplatePage/TemplateLayout"; import TemplatesPage from "pages/TemplatesPage/TemplatesPage"; import UsersPage from "pages/UsersPage/UsersPage"; import WorkspacesPage from "pages/WorkspacesPage/WorkspacesPage"; diff --git a/site/src/pages/TemplatePage/TemplateDocsPage/TemplateDocsPage.tsx b/site/src/pages/TemplatePage/TemplateDocsPage/TemplateDocsPage.tsx index 5e1827861b536..a04052e95db11 100644 --- a/site/src/pages/TemplatePage/TemplateDocsPage/TemplateDocsPage.tsx +++ b/site/src/pages/TemplatePage/TemplateDocsPage/TemplateDocsPage.tsx @@ -1,6 +1,6 @@ import { makeStyles } from "@mui/styles"; import { MemoizedMarkdown } from "components/Markdown/Markdown"; -import { useTemplateLayoutContext } from "components/TemplateLayout/TemplateLayout"; +import { useTemplateLayoutContext } from "pages/TemplatePage/TemplateLayout"; import frontMatter from "front-matter"; import { Helmet } from "react-helmet-async"; import { pageTitle } from "utils/page"; diff --git a/site/src/pages/TemplatePage/TemplateEmbedPage/TemplateEmbedPage.test.tsx b/site/src/pages/TemplatePage/TemplateEmbedPage/TemplateEmbedPage.test.tsx index 2bfed5e94d497..d77181f9590b1 100644 --- a/site/src/pages/TemplatePage/TemplateEmbedPage/TemplateEmbedPage.test.tsx +++ b/site/src/pages/TemplatePage/TemplateEmbedPage/TemplateEmbedPage.test.tsx @@ -3,7 +3,7 @@ import { waitForLoaderToBeRemoved, } from "testHelpers/renderHelpers"; import TemplateEmbedPage from "./TemplateEmbedPage"; -import { TemplateLayout } from "components/TemplateLayout/TemplateLayout"; +import { TemplateLayout } from "pages/TemplatePage/TemplateLayout"; import { MockTemplate, MockTemplateVersionParameter1 as parameter1, diff --git a/site/src/pages/TemplatePage/TemplateEmbedPage/TemplateEmbedPage.tsx b/site/src/pages/TemplatePage/TemplateEmbedPage/TemplateEmbedPage.tsx index 25ed4b51580cb..ad4c920828052 100644 --- a/site/src/pages/TemplatePage/TemplateEmbedPage/TemplateEmbedPage.tsx +++ b/site/src/pages/TemplatePage/TemplateEmbedPage/TemplateEmbedPage.tsx @@ -10,7 +10,7 @@ import { getTemplateVersionRichParameters } from "api/api"; import { Template, TemplateVersionParameter } from "api/typesGenerated"; import { FormSection, VerticalForm } from "components/Form/Form"; import { Loader } from "components/Loader/Loader"; -import { useTemplateLayoutContext } from "components/TemplateLayout/TemplateLayout"; +import { useTemplateLayoutContext } from "pages/TemplatePage/TemplateLayout"; import { ImmutableTemplateParametersSection, MutableTemplateParametersSection, diff --git a/site/src/pages/TemplatePage/TemplateFilesPage/TemplateFilesPage.tsx b/site/src/pages/TemplatePage/TemplateFilesPage/TemplateFilesPage.tsx index e361f923a992f..e52e2e5d7c673 100644 --- a/site/src/pages/TemplatePage/TemplateFilesPage/TemplateFilesPage.tsx +++ b/site/src/pages/TemplatePage/TemplateFilesPage/TemplateFilesPage.tsx @@ -3,7 +3,7 @@ import { getPreviousTemplateVersionByName } from "api/api"; import { TemplateVersion } from "api/typesGenerated"; import { Loader } from "components/Loader/Loader"; import { TemplateFiles } from "components/TemplateFiles/TemplateFiles"; -import { useTemplateLayoutContext } from "components/TemplateLayout/TemplateLayout"; +import { useTemplateLayoutContext } from "pages/TemplatePage/TemplateLayout"; import { useOrganizationId } from "hooks/useOrganizationId"; import { useTab } from "hooks/useTab"; import { FC, useEffect } from "react"; diff --git a/site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.tsx b/site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.tsx index ee4bccf93250d..e6ae5e642e903 100644 --- a/site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.tsx +++ b/site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.tsx @@ -5,7 +5,7 @@ import { BoxProps } from "@mui/system"; import { useQuery } from "@tanstack/react-query"; import { getInsightsTemplate, getInsightsUserLatency } from "api/api"; import { DAUChart, DAUTitle } from "components/DAUChart/DAUChart"; -import { useTemplateLayoutContext } from "components/TemplateLayout/TemplateLayout"; +import { useTemplateLayoutContext } from "pages/TemplatePage/TemplateLayout"; import { HelpTooltip, HelpTooltipTitle, diff --git a/site/src/components/TemplateLayout/TemplateLayout.tsx b/site/src/pages/TemplatePage/TemplateLayout.tsx similarity index 100% rename from site/src/components/TemplateLayout/TemplateLayout.tsx rename to site/src/pages/TemplatePage/TemplateLayout.tsx diff --git a/site/src/components/TemplateLayout/TemplatePageHeader.stories.tsx b/site/src/pages/TemplatePage/TemplatePageHeader.stories.tsx similarity index 92% rename from site/src/components/TemplateLayout/TemplatePageHeader.stories.tsx rename to site/src/pages/TemplatePage/TemplatePageHeader.stories.tsx index 369650f875d30..e5e837145ae06 100644 --- a/site/src/components/TemplateLayout/TemplatePageHeader.stories.tsx +++ b/site/src/pages/TemplatePage/TemplatePageHeader.stories.tsx @@ -3,7 +3,7 @@ import { TemplatePageHeader } from "./TemplatePageHeader"; import type { Meta, StoryObj } from "@storybook/react"; const meta: Meta = { - title: "components/TemplatePageHeader", + title: "pages/TemplatePage/TemplatePageHeader", component: TemplatePageHeader, args: { template: MockTemplate, diff --git a/site/src/components/TemplateLayout/TemplatePageHeader.tsx b/site/src/pages/TemplatePage/TemplatePageHeader.tsx similarity index 98% rename from site/src/components/TemplateLayout/TemplatePageHeader.tsx rename to site/src/pages/TemplatePage/TemplatePageHeader.tsx index beca9aecb15eb..ace6fb9764df6 100644 --- a/site/src/components/TemplateLayout/TemplatePageHeader.tsx +++ b/site/src/pages/TemplatePage/TemplatePageHeader.tsx @@ -69,14 +69,6 @@ const TemplateMenu: FC<{ Settings - - navigate(`/templates/new?fromTemplate=${templateName}`), - )} - > - - Duplicate - navigate( @@ -87,9 +79,17 @@ const TemplateMenu: FC<{ Edit files + + navigate(`/templates/new?fromTemplate=${templateName}`), + )} + > + + Duplicate… + - Delete + Delete… diff --git a/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPage.tsx b/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPage.tsx index 180fd0007848c..7231443a16cc3 100644 --- a/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPage.tsx +++ b/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPage.tsx @@ -1,4 +1,4 @@ -import { useTemplateLayoutContext } from "components/TemplateLayout/TemplateLayout"; +import { useTemplateLayoutContext } from "pages/TemplatePage/TemplateLayout"; import { FC } from "react"; import { Helmet } from "react-helmet-async"; import { getTemplatePageTitle } from "../utils"; diff --git a/site/src/pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage.tsx b/site/src/pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage.tsx index b34690b7203ef..458fc305bb24a 100644 --- a/site/src/pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage.tsx +++ b/site/src/pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage.tsx @@ -3,7 +3,7 @@ import { getTemplateVersions, updateActiveTemplateVersion } from "api/api"; import { getErrorMessage } from "api/errors"; import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog"; import { displayError, displaySuccess } from "components/GlobalSnackbar/utils"; -import { useTemplateLayoutContext } from "components/TemplateLayout/TemplateLayout"; +import { useTemplateLayoutContext } from "pages/TemplatePage/TemplateLayout"; import { VersionsTable } from "./VersionsTable"; import { useState } from "react"; import { Helmet } from "react-helmet-async"; diff --git a/site/src/pages/TemplatePage/TemplateVersionsPage/VersionRow.tsx b/site/src/pages/TemplatePage/TemplateVersionsPage/VersionRow.tsx index 11462e8141b1d..8b8675a8e62d1 100644 --- a/site/src/pages/TemplatePage/TemplateVersionsPage/VersionRow.tsx +++ b/site/src/pages/TemplatePage/TemplateVersionsPage/VersionRow.tsx @@ -88,7 +88,7 @@ export const VersionRow: React.FC = ({ onPromoteClick(version.id); }} > - Promote + Promote… )} diff --git a/site/src/components/TemplateLayout/deleteTemplate.test.ts b/site/src/pages/TemplatePage/deleteTemplate.test.ts similarity index 100% rename from site/src/components/TemplateLayout/deleteTemplate.test.ts rename to site/src/pages/TemplatePage/deleteTemplate.test.ts diff --git a/site/src/components/TemplateLayout/deleteTemplate.ts b/site/src/pages/TemplatePage/deleteTemplate.ts similarity index 100% rename from site/src/components/TemplateLayout/deleteTemplate.ts rename to site/src/pages/TemplatePage/deleteTemplate.ts diff --git a/site/src/pages/TemplateVersionPage/TemplateVersionPage.tsx b/site/src/pages/TemplateVersionPage/TemplateVersionPage.tsx index f3a5162543248..7567efd502e04 100644 --- a/site/src/pages/TemplateVersionPage/TemplateVersionPage.tsx +++ b/site/src/pages/TemplateVersionPage/TemplateVersionPage.tsx @@ -1,7 +1,8 @@ import { useMachine } from "@xstate/react"; +import { usePermissions } from "hooks/usePermissions"; import { useOrganizationId } from "hooks/useOrganizationId"; import { useTab } from "hooks/useTab"; -import { FC } from "react"; +import { type FC, useMemo } from "react"; import { Helmet } from "react-helmet-async"; import { useParams } from "react-router-dom"; import { pageTitle } from "utils/page"; @@ -21,6 +22,17 @@ export const TemplateVersionPage: FC = () => { context: { templateName, versionName, orgId }, }); const tab = useTab("file", "0"); + const permissions = usePermissions(); + + const versionId = state.context.currentVersion?.id; + const createWorkspaceUrl = useMemo(() => { + const params = new URLSearchParams(); + if (versionId) { + params.set("version", versionId); + return `/templates/${templateName}/workspace?${params.toString()}`; + } + return undefined; + }, [templateName, versionId]); return ( <> @@ -33,6 +45,9 @@ export const TemplateVersionPage: FC = () => { versionName={versionName} templateName={templateName} tab={tab} + createWorkspaceUrl={ + permissions.updateTemplates ? createWorkspaceUrl : undefined + } /> ); diff --git a/site/src/pages/TemplateVersionPage/TemplateVersionPageView.tsx b/site/src/pages/TemplateVersionPage/TemplateVersionPageView.tsx index ac470db3f633a..2238f9ccddfec 100644 --- a/site/src/pages/TemplateVersionPage/TemplateVersionPageView.tsx +++ b/site/src/pages/TemplateVersionPage/TemplateVersionPageView.tsx @@ -1,5 +1,5 @@ import Button from "@mui/material/Button"; -import Link from "@mui/material/Link"; +import AddIcon from "@mui/icons-material/Add"; import EditIcon from "@mui/icons-material/Edit"; import { Loader } from "components/Loader/Loader"; import { Margins } from "components/Margins/Margins"; @@ -13,7 +13,7 @@ import { Stack } from "components/Stack/Stack"; import { Stats, StatsItem } from "components/Stats/Stats"; import { TemplateFiles } from "components/TemplateFiles/TemplateFiles"; import { UseTabResult } from "hooks/useTab"; -import { FC } from "react"; +import { type FC } from "react"; import { Link as RouterLink } from "react-router-dom"; import { createDayString } from "utils/createDayString"; import { TemplateVersionMachineContext } from "xServices/templateVersion/templateVersionXService"; @@ -27,6 +27,7 @@ export interface TemplateVersionPageViewProps { templateName: string; tab: UseTabResult; context: TemplateVersionMachineContext; + createWorkspaceUrl?: string; } export const TemplateVersionPageView: FC = ({ @@ -34,6 +35,7 @@ export const TemplateVersionPageView: FC = ({ tab, versionName, templateName, + createWorkspaceUrl, }) => { const { currentFiles, error, currentVersion, previousFiles } = context; @@ -41,12 +43,25 @@ export const TemplateVersionPageView: FC = ({ - - + <> + {createWorkspaceUrl && ( + + )} + + } > Version diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 6e048e22bd735..7933f10c2a1bf 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -2109,6 +2109,7 @@ export const MockPermissions: Permissions = { createTemplates: true, createUser: true, deleteTemplates: true, + updateTemplates: true, readAllUsers: true, updateUsers: true, viewAuditLog: true, diff --git a/site/src/xServices/auth/authXService.ts b/site/src/xServices/auth/authXService.ts index ac52bd23ba724..eb83fe3be56b5 100644 --- a/site/src/xServices/auth/authXService.ts +++ b/site/src/xServices/auth/authXService.ts @@ -12,6 +12,7 @@ export const checks = { updateUsers: "updateUsers", createUser: "createUser", createTemplates: "createTemplates", + updateTemplates: "updateTemplates", deleteTemplates: "deleteTemplates", viewAuditLog: "viewAuditLog", viewDeploymentValues: "viewDeploymentValues", @@ -47,6 +48,12 @@ export const permissionsToCheck = { }, action: "update", }, + [checks.updateTemplates]: { + object: { + resource_type: "template", + }, + action: "update", + }, [checks.deleteTemplates]: { object: { resource_type: "template",