From 0938cb425072ce166e3b546cef2bfef3ba07e2cf Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Fri, 31 Mar 2023 14:33:15 +0000 Subject: [PATCH 1/7] Display active version --- site/src/AppRouter.tsx | 6 +++- .../TemplateLayout/TemplateLayout.tsx | 11 +++++++ .../components/VersionsTable/VersionRow.tsx | 29 +++++++++++++++---- .../VersionsTable/VersionsTable.tsx | 8 ++++- .../TemplateFilesPage/TemplateFilesPage.tsx | 4 +-- .../TemplateSummaryPage.tsx | 12 ++------ .../TemplateSummaryPageView.tsx | 5 +--- .../TemplatePage/TemplateSummaryPage/data.ts | 10 ++----- .../TemplateVersionsPage.tsx | 28 ++++++++++++++++++ site/src/pages/TemplatePage/utils.ts | 10 +++++++ 10 files changed, 92 insertions(+), 31 deletions(-) create mode 100644 site/src/pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage.tsx create mode 100644 site/src/pages/TemplatePage/utils.ts diff --git a/site/src/AppRouter.tsx b/site/src/AppRouter.tsx index 1910440f0ad08..ce88dfc6c6e32 100644 --- a/site/src/AppRouter.tsx +++ b/site/src/AppRouter.tsx @@ -135,6 +135,10 @@ const CreateTokenPage = lazy( const TemplateFilesPage = lazy( () => import("./pages/TemplatePage/TemplateFilesPage/TemplateFilesPage"), ) +const TemplateVersionsPage = lazy( + () => + import("./pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage"), +) const TemplateSchedulePage = lazy( () => import( @@ -170,8 +174,8 @@ export const AppRouter: FC = () => { }> } /> - } /> + } /> } /> diff --git a/site/src/components/TemplateLayout/TemplateLayout.tsx b/site/src/components/TemplateLayout/TemplateLayout.tsx index c44d3ac5a9b6b..f9b91da40556d 100644 --- a/site/src/components/TemplateLayout/TemplateLayout.tsx +++ b/site/src/components/TemplateLayout/TemplateLayout.tsx @@ -123,6 +123,17 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({ Source Code )} + + combineClasses([ + styles.tabItem, + isActive ? styles.tabItemActive : undefined, + ]) + } + > + Versions + diff --git a/site/src/components/VersionsTable/VersionRow.tsx b/site/src/components/VersionsTable/VersionRow.tsx index e1cb777f7f979..ec8278925e708 100644 --- a/site/src/components/VersionsTable/VersionRow.tsx +++ b/site/src/components/VersionsTable/VersionRow.tsx @@ -1,32 +1,46 @@ import { makeStyles } from "@material-ui/core/styles" import TableCell from "@material-ui/core/TableCell" import { TemplateVersion } from "api/typesGenerated" +import { Pill } from "components/Pill/Pill" import { Stack } from "components/Stack/Stack" import { TimelineEntry } from "components/Timeline/TimelineEntry" import { UserAvatar } from "components/UserAvatar/UserAvatar" -import { useClickable } from "hooks/useClickable" +import { useClickableTableRow } from "hooks/useClickableTableRow" import { useTranslation } from "react-i18next" import { useNavigate } from "react-router-dom" +import { combineClasses } from "util/combineClasses" export interface VersionRowProps { version: TemplateVersion + isActive: boolean } -export const VersionRow: React.FC = ({ version }) => { +export const VersionRow: React.FC = ({ + version, + isActive, +}) => { const styles = useStyles() const { t } = useTranslation("templatePage") const navigate = useNavigate() - const clickableProps = useClickable(() => { - navigate(`versions/${version.name}`) + const clickableProps = useClickableTableRow(() => { + navigate(version.name) }) return ( - + = ({ version }) => { + {isActive && } @@ -60,6 +75,10 @@ const useStyles = makeStyles((theme) => ({ padding: theme.spacing(2, 4), }, + active: { + backgroundColor: theme.palette.background.paperLight, + }, + versionCell: { padding: "0 !important", position: "relative", diff --git a/site/src/components/VersionsTable/VersionsTable.tsx b/site/src/components/VersionsTable/VersionsTable.tsx index 9aabf87522de3..199381d8223df 100644 --- a/site/src/components/VersionsTable/VersionsTable.tsx +++ b/site/src/components/VersionsTable/VersionsTable.tsx @@ -19,11 +19,13 @@ export const Language = { } export interface VersionsTableProps { + activeVersionId: string versions?: TypesGen.TemplateVersion[] } export const VersionsTable: FC> = ({ versions, + activeVersionId, }) => { return ( @@ -34,7 +36,11 @@ export const VersionsTable: FC> = ({ items={versions.slice().reverse()} getDate={(version) => new Date(version.created_at)} row={(version) => ( - + )} /> ) : ( diff --git a/site/src/pages/TemplatePage/TemplateFilesPage/TemplateFilesPage.tsx b/site/src/pages/TemplatePage/TemplateFilesPage/TemplateFilesPage.tsx index 88b0729aa5a03..bd00c166b7371 100644 --- a/site/src/pages/TemplatePage/TemplateFilesPage/TemplateFilesPage.tsx +++ b/site/src/pages/TemplatePage/TemplateFilesPage/TemplateFilesPage.tsx @@ -8,11 +8,11 @@ import { useOrganizationId } from "hooks/useOrganizationId" import { useTab } from "hooks/useTab" import { FC, useEffect } from "react" import { Helmet } from "react-helmet-async" -import { pageTitle } from "util/page" import { getTemplateVersionFiles, TemplateVersionFiles, } from "util/templateVersion" +import { getTemplatePageTitle } from "../utils" const fetchTemplateFiles = async ( organizationId: string, @@ -80,7 +80,7 @@ const TemplateFilesPage: FC = () => { return ( <> - {pageTitle(`${template?.name} · Source Code`)} + {getTemplatePageTitle("Source Code", template)} {templateFiles && tab.isLoaded ? ( diff --git a/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPage.tsx b/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPage.tsx index ef9359e618533..34faa71dd05fd 100644 --- a/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPage.tsx +++ b/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPage.tsx @@ -1,7 +1,7 @@ import { useTemplateLayoutContext } from "components/TemplateLayout/TemplateLayout" import { FC } from "react" import { Helmet } from "react-helmet-async" -import { pageTitle } from "util/page" +import { getTemplatePageTitle } from "../utils" import { useTemplateSummaryData } from "./data" import { TemplateSummaryPageView } from "./TemplateSummaryPageView" @@ -15,15 +15,7 @@ export const TemplateSummaryPage: FC = () => { return ( <> - - {pageTitle( - `${ - template.display_name.length > 0 - ? template.display_name - : template.name - } · Template`, - )} - + {getTemplatePageTitle("Template", template)} = ({ return } - const { daus, resources, versions } = data + const { daus, resources } = data const readme = frontMatter(activeVersion.readme) const getStartedResources = (resources: WorkspaceResource[]) => { @@ -53,8 +52,6 @@ export const TemplateSummaryPageView: FC = ({ {readme.body} - - ) } diff --git a/site/src/pages/TemplatePage/TemplateSummaryPage/data.ts b/site/src/pages/TemplatePage/TemplateSummaryPage/data.ts index 6470f402693b9..f9457d7549c6c 100644 --- a/site/src/pages/TemplatePage/TemplateSummaryPage/data.ts +++ b/site/src/pages/TemplatePage/TemplateSummaryPage/data.ts @@ -1,23 +1,17 @@ import { useQuery } from "@tanstack/react-query" -import { - getTemplateVersionResources, - getTemplateVersions, - getTemplateDAUs, -} from "api/api" +import { getTemplateVersionResources, getTemplateDAUs } from "api/api" const fetchTemplateSummary = async ( templateId: string, activeVersionId: string, ) => { - const [resources, versions, daus] = await Promise.all([ + const [resources, daus] = await Promise.all([ getTemplateVersionResources(activeVersionId), - getTemplateVersions(templateId), getTemplateDAUs(templateId), ]) return { resources, - versions, daus, } } diff --git a/site/src/pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage.tsx b/site/src/pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage.tsx new file mode 100644 index 0000000000000..4fcdb0e17df38 --- /dev/null +++ b/site/src/pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage.tsx @@ -0,0 +1,28 @@ +import { useQuery } from "@tanstack/react-query" +import { getTemplateVersions } from "api/api" +import { useTemplateLayoutContext } from "components/TemplateLayout/TemplateLayout" +import { VersionsTable } from "components/VersionsTable/VersionsTable" +import { Helmet } from "react-helmet-async" +import { getTemplatePageTitle } from "../utils" + +const TemplateVersionsPage = () => { + const { template } = useTemplateLayoutContext() + const { data } = useQuery({ + queryKey: ["template", "versions", template.id], + queryFn: () => getTemplateVersions(template.id), + }) + + return ( + <> + + {getTemplatePageTitle("Versions", template)} + + + + ) +} + +export default TemplateVersionsPage diff --git a/site/src/pages/TemplatePage/utils.ts b/site/src/pages/TemplatePage/utils.ts new file mode 100644 index 0000000000000..89e7a3321bbdd --- /dev/null +++ b/site/src/pages/TemplatePage/utils.ts @@ -0,0 +1,10 @@ +import { Template } from "api/typesGenerated" +import { pageTitle } from "util/page" + +export const getTemplatePageTitle = (title: string, template: Template) => { + return pageTitle( + `${ + template.display_name.length > 0 ? template.display_name : template.name + } · ${title}`, + ) +} From 9a6b85214945c76e308aec1ef8608eb70f86daf3 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Fri, 31 Mar 2023 14:40:14 +0000 Subject: [PATCH 2/7] Display promote button on the row --- .../components/VersionsTable/VersionRow.tsx | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/site/src/components/VersionsTable/VersionRow.tsx b/site/src/components/VersionsTable/VersionRow.tsx index ec8278925e708..d0ebed9eca0c6 100644 --- a/site/src/components/VersionsTable/VersionRow.tsx +++ b/site/src/components/VersionsTable/VersionRow.tsx @@ -1,3 +1,4 @@ +import Button from "@material-ui/core/Button" import { makeStyles } from "@material-ui/core/styles" import TableCell from "@material-ui/core/TableCell" import { TemplateVersion } from "api/typesGenerated" @@ -8,6 +9,7 @@ import { UserAvatar } from "components/UserAvatar/UserAvatar" import { useClickableTableRow } from "hooks/useClickableTableRow" import { useTranslation } from "react-i18next" import { useNavigate } from "react-router-dom" +import { colors } from "theme/colors" import { combineClasses } from "util/combineClasses" export interface VersionRowProps { @@ -32,6 +34,7 @@ export const VersionRow: React.FC = ({ {...clickableProps} className={combineClasses({ [clickableProps.className]: true, + [styles.row]: true, [styles.active]: isActive, })} > @@ -63,7 +66,17 @@ export const VersionRow: React.FC = ({ - {isActive && } + {isActive ? ( + + ) : ( + + )} @@ -71,6 +84,21 @@ export const VersionRow: React.FC = ({ } const useStyles = makeStyles((theme) => ({ + row: { + "&:hover $promoteButton": { + color: theme.palette.text.primary, + borderColor: colors.gray[11], + "&:hover": { + borderColor: theme.palette.text.primary, + }, + }, + }, + + promoteButton: { + color: theme.palette.text.secondary, + transition: "none", + }, + versionWrapper: { padding: theme.spacing(2, 4), }, From 83cdf19406cac58853232d344eed8d45694b680b Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Fri, 31 Mar 2023 14:51:59 +0000 Subject: [PATCH 3/7] Display confirmation --- .../components/VersionsTable/VersionRow.tsx | 23 +++++++++++++------ .../VersionsTable/VersionsTable.tsx | 3 +++ .../TemplateVersionsPage.tsx | 23 ++++++++++++++++++- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/site/src/components/VersionsTable/VersionRow.tsx b/site/src/components/VersionsTable/VersionRow.tsx index d0ebed9eca0c6..b113eda4be488 100644 --- a/site/src/components/VersionsTable/VersionRow.tsx +++ b/site/src/components/VersionsTable/VersionRow.tsx @@ -15,11 +15,13 @@ import { combineClasses } from "util/combineClasses" export interface VersionRowProps { version: TemplateVersion isActive: boolean + onPromoteClick?: () => void } export const VersionRow: React.FC = ({ version, isActive, + onPromoteClick, }) => { const styles = useStyles() const { t } = useTranslation("templatePage") @@ -69,13 +71,20 @@ export const VersionRow: React.FC = ({ {isActive ? ( ) : ( - + onPromoteClick && ( + + ) )} diff --git a/site/src/components/VersionsTable/VersionsTable.tsx b/site/src/components/VersionsTable/VersionsTable.tsx index 199381d8223df..c89b36becb85b 100644 --- a/site/src/components/VersionsTable/VersionsTable.tsx +++ b/site/src/components/VersionsTable/VersionsTable.tsx @@ -20,11 +20,13 @@ export const Language = { export interface VersionsTableProps { activeVersionId: string + onPromoteClick?: () => void versions?: TypesGen.TemplateVersion[] } export const VersionsTable: FC> = ({ versions, + onPromoteClick, activeVersionId, }) => { return ( @@ -37,6 +39,7 @@ export const VersionsTable: FC> = ({ getDate={(version) => new Date(version.created_at)} row={(version) => ( { - const { template } = useTemplateLayoutContext() + const { template, permissions } = useTemplateLayoutContext() const { data } = useQuery({ queryKey: ["template", "versions", template.id], queryFn: () => getTemplateVersions(template.id), }) + const [promoteState, setPromoteState] = useState< + "idle" | "confirming" | "promoting" + >("idle") return ( <> @@ -19,8 +24,24 @@ const TemplateVersionsPage = () => { setPromoteState("confirming") + : undefined + } activeVersionId={template.active_version_id} /> + {}} + onClose={() => setPromoteState("idle")} + title="Promote version" + confirmLoading={promoteState === "promoting"} + confirmText="Promote" + description="Are you sure you want to promote this version?" + /> ) } From d58a4652c0d75aa4033c2499d29da46740925760 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Fri, 31 Mar 2023 15:29:57 +0000 Subject: [PATCH 4/7] Finish promote work --- .../components/VersionsTable/VersionRow.tsx | 4 +- .../VersionsTable/VersionsTable.tsx | 2 +- .../TemplateVersionsPage.tsx | 45 ++++++++++++++----- 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/site/src/components/VersionsTable/VersionRow.tsx b/site/src/components/VersionsTable/VersionRow.tsx index b113eda4be488..fd5f1bb2ddd76 100644 --- a/site/src/components/VersionsTable/VersionRow.tsx +++ b/site/src/components/VersionsTable/VersionRow.tsx @@ -15,7 +15,7 @@ import { combineClasses } from "util/combineClasses" export interface VersionRowProps { version: TemplateVersion isActive: boolean - onPromoteClick?: () => void + onPromoteClick?: (templateVersionId: string) => void } export const VersionRow: React.FC = ({ @@ -79,7 +79,7 @@ export const VersionRow: React.FC = ({ onClick={(e) => { e.preventDefault() e.stopPropagation() - onPromoteClick() + onPromoteClick(version.id) }} > Promote version diff --git a/site/src/components/VersionsTable/VersionsTable.tsx b/site/src/components/VersionsTable/VersionsTable.tsx index c89b36becb85b..1854a109e977d 100644 --- a/site/src/components/VersionsTable/VersionsTable.tsx +++ b/site/src/components/VersionsTable/VersionsTable.tsx @@ -20,7 +20,7 @@ export const Language = { export interface VersionsTableProps { activeVersionId: string - onPromoteClick?: () => void + onPromoteClick?: (templateVersionId: string) => void versions?: TypesGen.TemplateVersion[] } diff --git a/site/src/pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage.tsx b/site/src/pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage.tsx index 59e6e094f9acd..6e36e1a90d1b4 100644 --- a/site/src/pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage.tsx +++ b/site/src/pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage.tsx @@ -1,6 +1,8 @@ -import { useQuery } from "@tanstack/react-query" -import { getTemplateVersions } from "api/api" +import { useMutation, useQuery } from "@tanstack/react-query" +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 { VersionsTable } from "components/VersionsTable/VersionsTable" import { useState } from "react" @@ -13,9 +15,28 @@ const TemplateVersionsPage = () => { queryKey: ["template", "versions", template.id], queryFn: () => getTemplateVersions(template.id), }) - const [promoteState, setPromoteState] = useState< - "idle" | "confirming" | "promoting" - >("idle") + // We use this to update the active version in the UI without having to refetch the template + const [latestActiveVersion, setLatestActiveVersion] = useState( + template.active_version_id, + ) + const { mutate: promoteVersion, isLoading: isPromoting } = useMutation({ + mutationFn: (templateVersionId: string) => { + return updateActiveTemplateVersion(template.id, { + id: templateVersionId, + }) + }, + onSuccess: async () => { + setLatestActiveVersion(selectedVersionIdToPromote as string) + setSelectedVersionIdToPromote(undefined) + displaySuccess("Version promoted successfully") + }, + onError: (error) => { + displayError(getErrorMessage(error, "Failed to promote version")) + }, + }) + const [selectedVersionIdToPromote, setSelectedVersionIdToPromote] = useState< + string | undefined + >() return ( <> @@ -26,19 +47,21 @@ const TemplateVersionsPage = () => { versions={data} onPromoteClick={ permissions.canUpdateTemplate - ? () => setPromoteState("confirming") + ? setSelectedVersionIdToPromote : undefined } - activeVersionId={template.active_version_id} + activeVersionId={latestActiveVersion} /> {}} - onClose={() => setPromoteState("idle")} + open={selectedVersionIdToPromote !== undefined} + onConfirm={() => { + promoteVersion(selectedVersionIdToPromote as string) + }} + onClose={() => setSelectedVersionIdToPromote(undefined)} title="Promote version" - confirmLoading={promoteState === "promoting"} + confirmLoading={isPromoting} confirmText="Promote" description="Are you sure you want to promote this version?" /> From d8c669ada51206f52fd01ed5b78578ec22a39f17 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Fri, 31 Mar 2023 15:33:07 +0000 Subject: [PATCH 5/7] Add storybook --- .../VersionsTable/VersionsTable.stories.tsx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/site/src/components/VersionsTable/VersionsTable.stories.tsx b/site/src/components/VersionsTable/VersionsTable.stories.tsx index 9956d7d150784..7196bdbaa137e 100644 --- a/site/src/components/VersionsTable/VersionsTable.stories.tsx +++ b/site/src/components/VersionsTable/VersionsTable.stories.tsx @@ -1,3 +1,4 @@ +import { action } from "@storybook/addon-actions" import { ComponentMeta, Story } from "@storybook/react" import { MockTemplateVersion } from "../../testHelpers/entities" import { VersionsTable, VersionsTableProps } from "./VersionsTable" @@ -13,13 +14,30 @@ const Template: Story = (args) => ( export const Example = Template.bind({}) Example.args = { + activeVersionId: MockTemplateVersion.id, versions: [ + { + ...MockTemplateVersion, + id: "2", + name: "test-template-version-2", + created_at: "2022-05-18T18:39:01.382927298Z", + }, MockTemplateVersion, + ], +} + +export const CanPromote = Template.bind({}) +CanPromote.args = { + activeVersionId: MockTemplateVersion.id, + onPromoteClick: action("onPromoteClick"), + versions: [ { ...MockTemplateVersion, + id: "2", name: "test-template-version-2", created_at: "2022-05-18T18:39:01.382927298Z", }, + MockTemplateVersion, ], } From 9068d15d0e66b307580f8ae2b5b8f41d37fe73d9 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Fri, 31 Mar 2023 15:57:12 +0000 Subject: [PATCH 6/7] Update copy --- .../TemplatePage/TemplateVersionsPage/TemplateVersionsPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage.tsx b/site/src/pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage.tsx index 6e36e1a90d1b4..d3c7780c305e3 100644 --- a/site/src/pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage.tsx +++ b/site/src/pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage.tsx @@ -63,7 +63,7 @@ const TemplateVersionsPage = () => { title="Promote version" confirmLoading={isPromoting} confirmText="Promote" - description="Are you sure you want to promote this version?" + description="Are you sure you want to promote this version? Workspaces will be prompted to “Update” to this version once promoted." /> ) From 4dc67decbf85482af09fb79a0fb49dca5b73c920 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Mon, 3 Apr 2023 12:54:36 +0000 Subject: [PATCH 7/7] Add promote version begind template editor --- .../TemplateVersionsPage/TemplateVersionsPage.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/site/src/pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage.tsx b/site/src/pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage.tsx index d3c7780c305e3..d5b820e6e3cdd 100644 --- a/site/src/pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage.tsx +++ b/site/src/pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage.tsx @@ -8,8 +8,10 @@ import { VersionsTable } from "components/VersionsTable/VersionsTable" import { useState } from "react" import { Helmet } from "react-helmet-async" import { getTemplatePageTitle } from "../utils" +import { useDashboard } from "components/Dashboard/DashboardProvider" const TemplateVersionsPage = () => { + const dashboard = useDashboard() const { template, permissions } = useTemplateLayoutContext() const { data } = useQuery({ queryKey: ["template", "versions", template.id], @@ -37,6 +39,9 @@ const TemplateVersionsPage = () => { const [selectedVersionIdToPromote, setSelectedVersionIdToPromote] = useState< string | undefined >() + const canPromoteVersion = + dashboard.experiments.includes("template_editor") && + permissions.canUpdateTemplate return ( <> @@ -46,9 +51,7 @@ const TemplateVersionsPage = () => {