diff --git a/site/src/AppRouter.tsx b/site/src/AppRouter.tsx index ab61681679033..8fa55bd9efee0 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 that don't have the dashboard layout */} } /> } /> } /> + } + /> {/* Using path="*"" means "match anything", so this route diff --git a/site/src/components/WorkspaceBuildLogs/Logs.tsx b/site/src/components/WorkspaceBuildLogs/Logs.tsx index e95999806b0b6..585adf0f9788b 100644 --- a/site/src/components/WorkspaceBuildLogs/Logs.tsx +++ b/site/src/components/WorkspaceBuildLogs/Logs.tsx @@ -24,10 +24,14 @@ export const Logs: FC> = ({ className = "", }) => { return ( -
+
{lines.map((line, idx) => ( -
+
{!hideTimestamps && ( <> diff --git a/site/src/components/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx b/site/src/components/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx index a0fdba3f9bb3f..c41ad894175a5 100644 --- a/site/src/components/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx +++ b/site/src/components/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx @@ -77,7 +77,10 @@ export const WorkspaceBuildLogs: FC = ({ return ( -
+
{stage}
{shouldDisplayDuration && (
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/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/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" + /> + } + /> + + + } /> ); diff --git a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx index b07578fe63bfb..2703ae7eb0b26 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,13 @@ 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"; +import { MONOSPACE_FONT_FAMILY } from "theme/constants"; +import { Loader } from "components/Loader/Loader"; type Tab = "logs" | "resources" | undefined; // Undefined is to hide the tab export interface TemplateVersionEditorProps { @@ -74,8 +71,6 @@ export interface TemplateVersionEditorProps { defaultTab?: Tab; } -const topbarHeight = 80; - const findInitialFile = (fileTree: FileTree): string | undefined => { let initialFile: string | undefined; @@ -162,90 +157,196 @@ export const TemplateVersionEditor: FC = ({ ["running", "pending"].includes(previousVersion.current.job.status) && templateVersion.job.status === "succeeded" ) { - setSelectedTab("resources"); setDirty(false); } previousVersion.current = templateVersion; }, [templateVersion]); - const hasIcon = template.icon && template.icon !== ""; - const showBuildLogs = Boolean(buildLogs); const editorValue = getFileContent(activePath ?? "", fileTree) as string; + // Auto scroll + const buildLogsRef = useRef(null); useEffect(() => { - window.dispatchEvent(new Event("resize")); - }, [showBuildLogs]); + if (buildLogsRef.current) { + buildLogsRef.current.scrollTop = buildLogsRef.current.scrollHeight; + } + }, [buildLogs]); 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 +427,14 @@ export const TemplateVersionEditor: FC = ({
-
+
{activePath ? ( = ({ )}
-
-
- - - {!disableUpdate && ( + + +
+ + {selectedTab && ( + { + setSelectedTab(undefined); + }} + css={{ + marginLeft: "auto", + width: 36, + height: 36, + borderRadius: 0, + }} + > + + )}
{templateVersion.job.error && (
@@ -412,9 +552,39 @@ export const TemplateVersionEditor: FC = ({
)} + {buildLogs && buildLogs.length === 0 && ( + + )} + {buildLogs && buildLogs.length > 0 && ( @@ -422,13 +592,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 ( +