From 90c68a367ca0b99baf231df1691484631597690a Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Mon, 27 Nov 2023 23:13:46 +0000 Subject: [PATCH 1/9] Add base template editor behavior --- site/src/AppRouter.tsx | 10 +- .../FileTreeView.tsx | 3 +- .../MonacoEditor.tsx | 25 +- .../TemplateVersionEditor.tsx | 420 +++++++++++------- 4 files changed, 258 insertions(+), 200 deletions(-) diff --git a/site/src/AppRouter.tsx b/site/src/AppRouter.tsx index ab61681679033..c9e3ac20679b4 100644 --- a/site/src/AppRouter.tsx +++ b/site/src/AppRouter.tsx @@ -259,10 +259,6 @@ export const AppRouter: FC = () => { } /> - } - /> @@ -346,13 +342,17 @@ export const AppRouter: FC = () => { - {/* Terminal and CLI auth pages don't have the dashboard layout */} + {/* Pages don't have the dashboard layout */} } /> } /> } /> + } + /> {/* Using path="*"" means "match anything", so this route diff --git a/site/src/pages/TemplateVersionEditorPage/FileTreeView.tsx b/site/src/pages/TemplateVersionEditorPage/FileTreeView.tsx index eba1649f1dc72..1922eb9cdb2c2 100644 --- a/site/src/pages/TemplateVersionEditorPage/FileTreeView.tsx +++ b/site/src/pages/TemplateVersionEditorPage/FileTreeView.tsx @@ -62,6 +62,7 @@ export const FileTreeView: FC<{ css={(theme) => css` overflow: hidden; user-select: none; + height: 32px; &:focus:not(.active) > .MuiTreeItem-content { background: ${theme.palette.action.hover}; @@ -92,7 +93,7 @@ export const FileTreeView: FC<{ &.active { & > .MuiTreeItem-content { color: ${theme.palette.text.primary}; - background: ${colors.gray[13]}; + background: ${colors.gray[14]}; pointer-events: none; } } diff --git a/site/src/pages/TemplateVersionEditorPage/MonacoEditor.tsx b/site/src/pages/TemplateVersionEditorPage/MonacoEditor.tsx index cff5950f408ba..7b3ed9ab29d2a 100644 --- a/site/src/pages/TemplateVersionEditorPage/MonacoEditor.tsx +++ b/site/src/pages/TemplateVersionEditorPage/MonacoEditor.tsx @@ -1,9 +1,8 @@ import { useTheme } from "@emotion/react"; import Editor, { loader } from "@monaco-editor/react"; import * as monaco from "monaco-editor"; -import { FC, useLayoutEffect, useMemo, useState } from "react"; +import { FC, useMemo } from "react"; import { MONOSPACE_FONT_FAMILY } from "theme/constants"; -import type { editor } from "monaco-editor"; loader.config({ monaco }); @@ -13,22 +12,6 @@ export const MonacoEditor: FC<{ onChange?: (value: string) => void; }> = ({ onChange, value, path }) => { const theme = useTheme(); - const [editor, setEditor] = useState(); - useLayoutEffect(() => { - if (!editor) { - return; - } - const resizeListener = () => { - editor.layout({ - height: 0, - width: 0, - }); - }; - window.addEventListener("resize", resizeListener); - return () => { - window.removeEventListener("resize", resizeListener); - }; - }, [editor]); const language = useMemo(() => { if (path?.endsWith(".tf")) { @@ -56,7 +39,7 @@ export const MonacoEditor: FC<{ options={{ automaticLayout: true, fontFamily: MONOSPACE_FONT_FAMILY, - fontSize: 16, + fontSize: 14, wordWrap: "on", padding: { top: 16, @@ -81,8 +64,6 @@ export const MonacoEditor: FC<{ }, ); - setEditor(editor); - document.fonts.ready .then(() => { // Ensures that all text is measured properly. @@ -124,7 +105,7 @@ export const MonacoEditor: FC<{ ], colors: { "editor.foreground": theme.palette.text.primary, - "editor.background": theme.palette.background.default, + "editor.background": theme.palette.background.paper, }, }); editor.updateOptions({ diff --git a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx index b07578fe63bfb..04bfbaace34c5 100644 --- a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx +++ b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx @@ -1,10 +1,7 @@ -import Button from "@mui/material/Button"; +import Button, { ButtonProps } from "@mui/material/Button"; import IconButton from "@mui/material/IconButton"; -import Link from "@mui/material/Link"; import Tooltip from "@mui/material/Tooltip"; import CreateIcon from "@mui/icons-material/AddOutlined"; -import BuildIcon from "@mui/icons-material/BuildOutlined"; -import PreviewIcon from "@mui/icons-material/VisibilityOutlined"; import { ProvisionerJobLog, Template, @@ -16,11 +13,11 @@ import { import { Link as RouterLink } from "react-router-dom"; import { Alert, AlertDetail } from "components/Alert/Alert"; import { Avatar } from "components/Avatar/Avatar"; -import { AvatarData } from "components/AvatarData/AvatarData"; import { TemplateResourcesTable } from "components/TemplateResourcesTable/TemplateResourcesTable"; import { WorkspaceBuildLogs } from "components/WorkspaceBuildLogs/WorkspaceBuildLogs"; import { PublishVersionData } from "pages/TemplateVersionEditorPage/types"; import { type FC, useCallback, useEffect, useRef, useState } from "react"; +import PlayArrowOutlined from "@mui/icons-material/PlayArrowOutlined"; import { createFile, existsFile, @@ -41,13 +38,11 @@ import { FileTreeView } from "./FileTreeView"; import { MissingTemplateVariablesDialog } from "./MissingTemplateVariablesDialog"; import { MonacoEditor } from "./MonacoEditor"; import { PublishTemplateVersionDialog } from "./PublishTemplateVersionDialog"; -import { - getStatus, - TemplateVersionStatusBadge, -} from "./TemplateVersionStatusBadge"; +import { TemplateVersionStatusBadge } from "./TemplateVersionStatusBadge"; import AlertTitle from "@mui/material/AlertTitle"; -import { DashboardFullPage } from "components/Dashboard/DashboardLayout"; import { type Interpolation, type Theme, useTheme } from "@emotion/react"; +import ArrowBackOutlined from "@mui/icons-material/ArrowBackOutlined"; +import CloseOutlined from "@mui/icons-material/CloseOutlined"; type Tab = "logs" | "resources" | undefined; // Undefined is to hide the tab export interface TemplateVersionEditorProps { @@ -74,8 +69,6 @@ export interface TemplateVersionEditorProps { defaultTab?: Tab; } -const topbarHeight = 80; - const findInitialFile = (fileTree: FileTree): string | undefined => { let initialFile: string | undefined; @@ -168,84 +161,172 @@ export const TemplateVersionEditor: FC = ({ previousVersion.current = templateVersion; }, [templateVersion]); - const hasIcon = template.icon && template.icon !== ""; - const showBuildLogs = Boolean(buildLogs); const editorValue = getFileContent(activePath ?? "", fileTree) as string; useEffect(() => { window.dispatchEvent(new Event("resize")); - }, [showBuildLogs]); + }, [selectedTab]); return ( <> - -
-
- - - ) - } - /> - +
+
+
+ + + + +
- {publishedVersion && ( - - Create a workspace - - } - > - Successfully published {publishedVersion.name}! - - )} +
+ + {template.display_name || template.name} + / + + {templateVersion.name} + +
-
+
{buildLogs && ( )} - + Build + - + Publish +
-
-
-
- Template files -
+
+ {publishedVersion && ( +
+ + Create a workspace + + } + > + Successfully published {publishedVersion.name}! + +
+ )} + +
+
+ + Files + + +
= ({ event.currentTarget.blur(); }} > - +
@@ -326,14 +407,14 @@ export const TemplateVersionEditor: FC = ({
-
+
{activePath ? ( = ({ )}
-
-
- - - {!disableUpdate && ( + + - )} +
+ + { + setSelectedTab(undefined); + }} + css={{ + marginLeft: "auto", + width: 36, + height: 36, + borderRadius: 0, + }} + > + +
{templateVersion.job.error && (
@@ -414,7 +531,10 @@ export const TemplateVersionEditor: FC = ({ {buildLogs && buildLogs.length > 0 && ( @@ -422,13 +542,25 @@ export const TemplateVersionEditor: FC = ({
{resources && ( = ({
- +
= ({ ); }; -const styles = { - topbar: (theme) => ({ - padding: 16, - borderBottom: `1px solid ${theme.palette.divider}`, - display: "flex", - alignItems: "center", - justifyContent: "space-between", - height: topbarHeight, - }), - topbarSides: { - display: "flex", - alignItems: "center", - gap: 16, - }, - sidebarAndEditor: { - display: "flex", - flex: 1, - flexBasis: 0, - overflow: "hidden", - }, - sidebar: (theme) => ({ - minWidth: 256, - borderRight: `1px solid ${theme.palette.divider}`, - }), - sidebarTitle: (theme) => ({ - fontSize: 10, - textTransform: "uppercase", - padding: "8px 16px", - color: theme.palette.text.primary, - fontWeight: 500, - letterSpacing: "0.5px", - display: "flex", - alignItems: "center", - }), - sidebarActions: (theme) => ({ - marginLeft: "auto", - "& svg": { - fill: theme.palette.text.primary, - }, - }), - editor: { - flex: 1, - }, - panelWrapper: (theme) => ({ - flex: 1, - borderLeft: `1px solid ${theme.palette.divider}`, - overflow: "hidden", - display: "flex", - flexDirection: "column", - }), - panel: { - overflowY: "auto", - height: "100%", - - // Hack to access customize resource-card from here - "& .resource-card": { - border: 0, - }, - }, - tabs: (theme) => ({ - borderBottom: `1px solid ${theme.palette.divider}`, - display: "flex", - boxShadow: "#000000 0 6px 6px -6px inset", +const TopbarButton = (props: ButtonProps) => { + return ( +
- { - setSelectedTab(undefined); - }} - css={{ - marginLeft: "auto", - width: 36, - height: 36, - borderRadius: 0, - }} - > - - + {selectedTab && ( + { + setSelectedTab(undefined); + }} + css={{ + marginLeft: "auto", + width: 36, + height: 36, + borderRadius: 0, + }} + > + + + )}
= ({
)} + {buildLogs && buildLogs.length === 0 && ( + + )} + {buildLogs && buildLogs.length > 0 && ( { const styles = { tab: (theme) => ({ - cursor: "pointer", + "&:not(:disabled)": { + cursor: "pointer", + }, padding: 12, fontSize: 10, textTransform: "uppercase", @@ -646,9 +683,13 @@ const styles = { }, }, - "&:hover": { + "&:not(:disabled):hover": { color: theme.palette.text.primary, }, + + "&:disabled": { + color: theme.palette.text.disabled, + }, }), tabBar: (theme) => ({ padding: "8px 16px", From ec90ce19aa9574333398d3679b14e0018f0a3974 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Mon, 27 Nov 2023 23:32:45 +0000 Subject: [PATCH 3/9] Fix test --- .../TemplateVersionEditorPage.test.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.test.tsx b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.test.tsx index 5c8e5461e64eb..67ec4c92b1e21 100644 --- a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.test.tsx +++ b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.test.tsx @@ -57,7 +57,7 @@ test("Use custom name, message and set it as active when publishing", async () = return jest.fn() as never; }); const buildButton = within(topbar).getByRole("button", { - name: "Build template", + name: "Build", }); await user.click(buildButton); @@ -70,7 +70,7 @@ test("Use custom name, message and set it as active when publishing", async () = .mockResolvedValue({ message: "" }); await within(topbar).findByText("Success"); const publishButton = within(topbar).getByRole("button", { - name: "Publish version", + name: "Publish", }); await user.click(publishButton); const publishDialog = await screen.findByTestId("dialog"); @@ -120,7 +120,7 @@ test("Do not mark as active if promote is not checked", async () => { return jest.fn() as never; }); const buildButton = within(topbar).getByRole("button", { - name: "Build template", + name: "Build", }); await user.click(buildButton); @@ -133,7 +133,7 @@ test("Do not mark as active if promote is not checked", async () => { .mockResolvedValue({ message: "" }); await within(topbar).findByText("Success"); const publishButton = within(topbar).getByRole("button", { - name: "Publish version", + name: "Publish", }); await user.click(publishButton); const publishDialog = await screen.findByTestId("dialog"); @@ -185,7 +185,7 @@ test("Patch request is not send when there are no changes", async () => { return jest.fn() as never; }); const buildButton = within(topbar).getByRole("button", { - name: "Build template", + name: "Build", }); await user.click(buildButton); @@ -195,11 +195,11 @@ test("Patch request is not send when there are no changes", async () => { .mockResolvedValue(MockTemplateVersionWithEmptyMessage); await within(topbar).findByText("Success"); const publishButton = within(topbar).getByRole("button", { - name: "Publish version", + name: "Publish", }); await user.click(publishButton); const publishDialog = await screen.findByTestId("dialog"); - // It is using the name from the template version + // It is using the name from the template const nameField = within(publishDialog).getByLabelText("Version name"); expect(nameField).toHaveValue(MockTemplateVersionWithEmptyMessage.name); // Publish From b2f0054f67df5d2738c9e8ea9e0a2ab2b21558f0 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Tue, 28 Nov 2023 00:00:46 +0000 Subject: [PATCH 4/9] Minor fixes --- .../TemplateVersionEditor.tsx | 3 +++ .../TemplateVersionEditorPage.tsx | 20 ++++++++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx index 2d09b870dc29c..491c972f5d343 100644 --- a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx +++ b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx @@ -273,6 +273,9 @@ export const TemplateVersionEditor: FC = ({ > {publishedVersion && (
{ tarFile, newFileTree, ); - const serverFile = - await uploadFileMutation.mutateAsync(newVersionFile); + const serverFile = await uploadFileMutation.mutateAsync( + newVersionFile, + ); const newVersion = await createTemplateVersionMutation.mutateAsync({ provisioner: "terraform", storage_method: "file", @@ -144,8 +145,21 @@ export const TemplateVersionEditorPage: FC = () => { data, version: templateVersionQuery.data, }); + const publishedVersion = { + ...templateVersionQuery.data, + ...data, + }; + setCurrentVersionName(publishedVersion.name); setIsPublishingDialogOpen(false); - setLastSuccessfulPublishedVersion(templateVersionQuery.data); + setLastSuccessfulPublishedVersion(publishedVersion); + queryClient.setQueryData( + templateVersionOptions.queryKey, + publishedVersion, + ); + navigate( + `/templates/${templateName}/versions/${publishedVersion.name}/edit`, + { replace: true }, + ); }} isAskingPublishParameters={isPublishingDialogOpen} isPublishing={publishVersionMutation.isLoading} From 3c4c0f5540148b2b59f46bcf86ea3a8797d0fc28 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Tue, 28 Nov 2023 00:03:53 +0000 Subject: [PATCH 5/9] Allow submit publish form using keyboard --- .../PublishTemplateVersionDialog.tsx | 73 ++++++++++--------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/site/src/pages/TemplateVersionEditorPage/PublishTemplateVersionDialog.tsx b/site/src/pages/TemplateVersionEditorPage/PublishTemplateVersionDialog.tsx index 66013982f5382..d572bcdef2be9 100644 --- a/site/src/pages/TemplateVersionEditorPage/PublishTemplateVersionDialog.tsx +++ b/site/src/pages/TemplateVersionEditorPage/PublishTemplateVersionDialog.tsx @@ -68,44 +68,45 @@ export const PublishTemplateVersionDialog: FC< confirmText="Publish" title="Publish new version" description={ - -

You are about to publish a new version of this template.

- - +
+ +

You are about to publish a new version of this template.

+ + - + - { - await form.setFieldValue( - "isActiveVersion", - e.target.checked, - ); - }} - name="isActiveVersion" - /> - } - /> - -
+ { + await form.setFieldValue( + "isActiveVersion", + e.target.checked, + ); + }} + name="isActiveVersion" + /> + } + /> + + + } /> ); From 2735d25f5da310a7f09227959d542b11397c6ae5 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Tue, 28 Nov 2023 00:06:21 +0000 Subject: [PATCH 6/9] Add link to the header --- .../TemplateSettingsForm.tsx | 1 - .../TemplateVersionEditor.tsx | 14 +++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsForm.tsx b/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsForm.tsx index 2a6dd37a6661c..6e5878eba5490 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsForm.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsForm.tsx @@ -233,7 +233,6 @@ export const TemplateSettingsForm: FC = ({ diff --git a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx index 491c972f5d343..2703ae7eb0b26 100644 --- a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx +++ b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx @@ -217,7 +217,19 @@ export const TemplateVersionEditor: FC = ({ fitImage css={{ width: 16, height: 16 }} /> - {template.display_name || template.name} + + {template.display_name || template.name} + / {templateVersion.name} From bbe78946725ffc45a91d2577cbf2f47abac0e971 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Tue, 28 Nov 2023 00:07:55 +0000 Subject: [PATCH 7/9] Add better loading --- .../TemplateVersionEditorPage/TemplateVersionEditorPage.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx index 004b77a12b0ae..e20dfeb836786 100644 --- a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx +++ b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx @@ -30,6 +30,7 @@ import { TemplateVersion, } from "api/typesGenerated"; import { displayError } from "components/GlobalSnackbar/utils"; +import { FullScreenLoader } from "components/Loader/FullScreenLoader"; type Params = { version: string; @@ -107,7 +108,7 @@ export const TemplateVersionEditorPage: FC = () => { Codestin Search App - {templateQuery.data && templateVersionQuery.data && fileTree && ( + {templateQuery.data && templateVersionQuery.data && fileTree ? ( { setIsMissingVariablesDialogOpen(false); }} /> + ) : ( + )} ); From 9ed1474ba515c69950afcc1341787abd848a18ae Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Tue, 28 Nov 2023 00:15:11 +0000 Subject: [PATCH 8/9] Fix fmt --- .../TemplateVersionEditorPage/TemplateVersionEditorPage.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx index e20dfeb836786..f7ca854ed5fa3 100644 --- a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx +++ b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx @@ -122,9 +122,8 @@ export const TemplateVersionEditorPage: FC = () => { tarFile, newFileTree, ); - const serverFile = await uploadFileMutation.mutateAsync( - newVersionFile, - ); + const serverFile = + await uploadFileMutation.mutateAsync(newVersionFile); const newVersion = await createTemplateVersionMutation.mutateAsync({ provisioner: "terraform", storage_method: "file", From 46a1944764966edfdef105ed494146d4ed73430d Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Tue, 28 Nov 2023 16:32:33 -0300 Subject: [PATCH 9/9] Update site/src/AppRouter.tsx Co-authored-by: Asher --- site/src/AppRouter.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/AppRouter.tsx b/site/src/AppRouter.tsx index c9e3ac20679b4..8fa55bd9efee0 100644 --- a/site/src/AppRouter.tsx +++ b/site/src/AppRouter.tsx @@ -342,7 +342,7 @@ export const AppRouter: FC = () => { - {/* Pages don't have the dashboard layout */} + {/* Pages that don't have the dashboard layout */} }