From e26c57ff2ca9c89ee0166c4fafaba343d285006d Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Mon, 20 Mar 2023 17:16:15 +0000 Subject: [PATCH 1/9] refactor template fetching --- .../TemplateLayout/TemplateLayout.tsx | 101 +++++++++++------- .../TemplatePermissionsPage.tsx | 5 +- .../TemplateSummaryPage.tsx | 23 ++-- 3 files changed, 69 insertions(+), 60 deletions(-) diff --git a/site/src/components/TemplateLayout/TemplateLayout.tsx b/site/src/components/TemplateLayout/TemplateLayout.tsx index 4f05cf3d077f4..6b31afb05ac73 100644 --- a/site/src/components/TemplateLayout/TemplateLayout.tsx +++ b/site/src/components/TemplateLayout/TemplateLayout.tsx @@ -1,36 +1,65 @@ import { makeStyles } from "@material-ui/core/styles" -import { useMachine } from "@xstate/react" import { useOrganizationId } from "hooks/useOrganizationId" import { createContext, FC, Suspense, useContext } from "react" import { NavLink, Outlet, useNavigate, useParams } from "react-router-dom" import { combineClasses } from "util/combineClasses" -import { - TemplateContext, - templateMachine, -} from "xServices/template/templateXService" import { Margins } from "components/Margins/Margins" import { Stack } from "components/Stack/Stack" -import { Permissions } from "xServices/auth/authXService" import { Loader } from "components/Loader/Loader" -import { usePermissions } from "hooks/usePermissions" import { TemplatePageHeader } from "./TemplatePageHeader" import { AlertBanner } from "components/AlertBanner/AlertBanner" +import { + checkAuthorization, + getTemplateByName, + getTemplateDAUs, + getTemplateVersion, + getTemplateVersionResources, + getTemplateVersions, +} from "api/api" +import { useQuery } from "@tanstack/react-query" + +const templatePermissions = (templateId: string) => ({ + canUpdateTemplate: { + object: { + resource_type: "template", + resource_id: templateId, + }, + action: "update", + }, +}) -const useTemplateName = () => { - const { template } = useParams() +const fetchTemplate = async (orgId: string, templateName: string) => { + const template = await getTemplateByName(orgId, templateName) + const [activeVersion, resources, versions, daus, permissions] = + await Promise.all([ + getTemplateVersion(template.active_version_id), + getTemplateVersionResources(template.active_version_id), + getTemplateVersions(template.id), + getTemplateDAUs(template.id), + checkAuthorization({ + checks: templatePermissions(template.id), + }), + ]) - if (!template) { - throw new Error("No template found in the URL") + return { + template, + activeVersion, + resources, + versions, + daus, + permissions, } - - return template } -type TemplateLayoutContextValue = { - context: TemplateContext - permissions?: Permissions +const useTemplateData = (orgId: string, templateName: string) => { + return useQuery({ + queryKey: ["template", templateName], + queryFn: () => fetchTemplate(orgId, templateName), + }) } +type TemplateLayoutContextValue = Awaited> + const TemplateLayoutContext = createContext< TemplateLayoutContextValue | undefined >(undefined) @@ -50,38 +79,32 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({ }) => { const navigate = useNavigate() const styles = useStyles() - const organizationId = useOrganizationId() - const templateName = useTemplateName() - const [templateState, _] = useMachine(templateMachine, { - context: { - templateName, - organizationId, - }, - }) - const { - template, - permissions: templatePermissions, - getTemplateError, - } = templateState.context - const permissions = usePermissions() + const orgId = useOrganizationId() + const { template } = useParams() as { template: string } + const templateData = useTemplateData(orgId, template) - if (getTemplateError) { + if (templateData.error) { return (
- +
) } - if (!template || !templatePermissions) { + if (templateData.isLoading) { return } + // Make typescript happy + if (!templateData.data) { + return <> + } + return ( <> { navigate("/templates") }} @@ -92,7 +115,7 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({ combineClasses([ styles.tabItem, @@ -103,7 +126,7 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({ Summary combineClasses([ styles.tabItem, @@ -118,9 +141,7 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({ - + }>{children} diff --git a/site/src/pages/TemplatePage/TemplatePermissionsPage/TemplatePermissionsPage.tsx b/site/src/pages/TemplatePage/TemplatePermissionsPage/TemplatePermissionsPage.tsx index a71ab12d024cf..56b4e36793e75 100644 --- a/site/src/pages/TemplatePage/TemplatePermissionsPage/TemplatePermissionsPage.tsx +++ b/site/src/pages/TemplatePage/TemplatePermissionsPage/TemplatePermissionsPage.tsx @@ -18,11 +18,10 @@ export const TemplatePermissionsPage: FC< React.PropsWithChildren > = () => { const organizationId = useOrganizationId() - const { context } = useTemplateLayoutContext() - const { template, permissions } = context + const { template, permissions } = useTemplateLayoutContext() const { template_rbac: isTemplateRBACEnabled } = useFeatureVisibility() const [state, send] = useMachine(templateACLMachine, { - context: { templateId: template?.id }, + context: { templateId: template.id }, }) const { templateACL, userToBeUpdated, groupToBeUpdated } = state.context diff --git a/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPage.tsx b/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPage.tsx index 42cbd7dd63ae8..f3f435b0cc57f 100644 --- a/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPage.tsx +++ b/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPage.tsx @@ -3,21 +3,10 @@ import { FC } from "react" import { Helmet } from "react-helmet-async" import { pageTitle } from "util/page" import { TemplateSummaryPageView } from "./TemplateSummaryPageView" -import { Loader } from "components/Loader/Loader" export const TemplateSummaryPage: FC = () => { - const { context } = useTemplateLayoutContext() - const { - template, - activeTemplateVersion, - templateResources, - templateVersions, - templateDAUs, - } = context - - if (!template || !activeTemplateVersion || !templateResources) { - return - } + const { template, activeVersion, resources, versions, daus } = + useTemplateLayoutContext() return ( <> @@ -34,10 +23,10 @@ export const TemplateSummaryPage: FC = () => { ) From 98420fbf8f88e76306cafc07ba5761f54e36178d Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Mon, 20 Mar 2023 17:17:34 +0000 Subject: [PATCH 2/9] Remove unused template service --- .../xServices/template/templateXService.ts | 276 ------------------ 1 file changed, 276 deletions(-) delete mode 100644 site/src/xServices/template/templateXService.ts diff --git a/site/src/xServices/template/templateXService.ts b/site/src/xServices/template/templateXService.ts deleted file mode 100644 index b7b836e4565ef..0000000000000 --- a/site/src/xServices/template/templateXService.ts +++ /dev/null @@ -1,276 +0,0 @@ -import { assign, createMachine } from "xstate" -import { - checkAuthorization, - getTemplateByName, - getTemplateDAUs, - getTemplateVersion, - getTemplateVersionResources, - getTemplateVersions, -} from "api/api" -import { - AuthorizationResponse, - Template, - TemplateDAUsResponse, - TemplateVersion, - WorkspaceResource, -} from "api/typesGenerated" - -export interface TemplateContext { - organizationId: string - templateName: string - template?: Template - activeTemplateVersion?: TemplateVersion - templateResources?: WorkspaceResource[] - templateVersions?: TemplateVersion[] - templateDAUs?: TemplateDAUsResponse - permissions?: AuthorizationResponse - getTemplateError?: Error | unknown -} - -const getPermissionsToCheck = (templateId: string) => ({ - canUpdateTemplate: { - object: { - resource_type: "template", - resource_id: templateId, - }, - action: "update", - }, -}) - -export const templateMachine = - /** @xstate-layout N4IgpgJg5mDOIC5QAoC2BDAxgCwJYDswBKAOhgBdyCoAVMVABwBt1ywBiCAe0JIIDcuAazAk0WPIVIUq+WvWaswCAV0ytcPANoAGALqJQDLrFxUehkAA9EAJgDsOkgGZbO2wE5bANg8BGABZnHR0AgBoQAE9EPx0-Eg9EpIAOe28-D28ggF9siPEcAmI+fDNcdCYASXwAMy4SLCp+MDpGFjYANTAAJ1MeMjBKagBBTCaWhXawLt7NfE4eUVURMQxCqRKyiuq6hrHcZtbFTp6+-AGhuVHxo6mZs5V8QXVzfF0DJBBjU1fLGwQAgF4t4AKzeWzOTIhEEhZIRaIIZI6EEkEIhZyg2xuEF+XL5NaSYoELZVWr1NhtJQAJTgXAArt1MHALrJ5JS2DTYPTGXAFrxlqICoTSMSqNsySQKccwJzuUzYCzqLdqbSGfLHs8NNp9JZvmULJ9-kDkiRks4QR50SDkh5nH5nPCYjpXKi0SDnObbeaAniQEKiiLSmLSbspXdTnMFTIlZMlPdI3ylk9hIKCQHNsGduTYydZjwo4NWcrc2dYBq1Fq3jrPnrfobEAFQqbQt5kiCcf4go6ECD7Ca0XFMskAmksr7-RtReUQ1xEyRYOQlKsJOmp+K6rqTPr8H9ELaTclkg5wQEzRidB5u-Y0q6QgFbAEsuCMuO0xsmFx0BBIOwACIAUQAGX-Gh-03H45ksBFrycGE7zcW1bD8e0In+Pw3G8EggT8M0-Fbc8PGSV8Vw2TAeBqXBulQahfzAJhBg4ABhAB5AA5AAxSoqQAWQAfQA4DQPA7ddwQZCMVRUF7Bw3svXsEFuz8MFMNtC8bRtHQzWHYj1mKMjako6i5Fo+i2HYRjhlYxigP4oCQLAmstzrUA0OcaSSHsZwbSUjwAl83zwiiGJGw8LCwTtU8vGQnI8j9N9im-UzqDnAUSEShjizAYTnOsRB7BHEglLwoqskCWxFJ8ewPJ0bx8tsC1IQhZwdOFNK6MGZKem6LhuhIY46iotrTImdkssciCDRcvcbVRJrwr8fLXHsRTYRIOCau8WqYRxIjfXwLhv3gT4J2KaM5Ey7LIPrAFyqChBLVvEJPGk2w21PFrVyDacsz2G4c2mCN+jOqBrgOEbpXjSavicq6prEhar1tR7IXSaSfVik7AxJH7GjBzLIfOWA6UweUjqMGGof+TzbEKsEMiRdwYUhbtkhw5HnHvXx0g8D7Jy+9d6lxw5-oJy7Kb3B07vsJDkekjwcSyWxeaJfmZ0lf7ZTVZlgcyzWeTJ6GJp3a7kOWu7YjcR6wQCEFAQfbxlaxzMJTDFUuS1hUiZJuADdrWHcoQVtMJxdtWfvaL0hWm2rfcRXHxBR2M2+l2NdVfWxeNuHbW7Xz+xCWJkm8ZE3LbRO1zV12S0jRVzpFwH8F9inM4D03u2Ux6sWvQj2wTjH4qd5PQzrvMG-nYnSYz0TQRNG3gnUyE+zNNv-EevDrUya9mr7kiVexlPRoJxujdE7O7r8gIO+dXtUkyMvVazSfrt8bt7xpgcFttC1nXsROPy-SBH5w1iO6MKwRghAkbH2BSUtHBrTRPeC8rhxKJ30hRKiNF2psEAS3ZCNMkR4R8MOWqfloEIntCvDmnoRzyUIvLRO6VWTYP+F4TCNt0g22REhV6pCYgEJIPVDENsPBpA9GkehmCAHjREtdVmmFPCKy2u6RwcJzaQicMOb0wQ3ALVxNvXSRAmExBUWQvOA5baqXlrbXIuQgA */ - createMachine( - { - id: "templateMachine", - predictableActionArguments: true, - tsTypes: {} as import("./templateXService.typegen").Typegen0, - schema: { - context: {} as TemplateContext, - services: {} as { - getTemplate: { - data: Template - } - getActiveTemplateVersion: { - data: TemplateVersion - } - getTemplateResources: { - data: WorkspaceResource[] - } - getTemplateVersions: { - data: TemplateVersion[] - } - getTemplateDAUs: { - data: TemplateDAUsResponse - } - getTemplatePermissions: { - data: AuthorizationResponse - } - }, - }, - initial: "gettingTemplate", - states: { - gettingTemplate: { - invoke: { - src: "getTemplate", - onDone: [ - { - actions: "assignTemplate", - target: "initialInfo", - }, - ], - onError: [ - { - actions: "assignGetTemplateError", - target: "error", - }, - ], - }, - }, - initialInfo: { - type: "parallel", - states: { - activeTemplateVersion: { - initial: "gettingActiveTemplateVersion", - states: { - gettingActiveTemplateVersion: { - invoke: { - src: "getActiveTemplateVersion", - onDone: [ - { - actions: "assignActiveTemplateVersion", - target: "success", - }, - ], - }, - }, - success: { - type: "final", - }, - }, - }, - templateResources: { - initial: "gettingTemplateResources", - states: { - gettingTemplateResources: { - invoke: { - src: "getTemplateResources", - onDone: [ - { - actions: "assignTemplateResources", - target: "success", - }, - ], - }, - }, - success: { - type: "final", - }, - }, - }, - templateVersions: { - initial: "gettingTemplateVersions", - states: { - gettingTemplateVersions: { - invoke: { - src: "getTemplateVersions", - onDone: [ - { - actions: "assignTemplateVersions", - target: "success", - }, - ], - }, - }, - success: { - type: "final", - }, - }, - }, - templateDAUs: { - initial: "gettingTemplateDAUs", - states: { - gettingTemplateDAUs: { - invoke: { - src: "getTemplateDAUs", - onDone: [ - { - actions: "assignTemplateDAUs", - target: "success", - }, - ], - }, - }, - success: { - type: "final", - }, - }, - }, - templatePermissions: { - initial: "gettingTemplatePermissions", - states: { - gettingTemplatePermissions: { - invoke: { - src: "getTemplatePermissions", - onDone: { - target: "success", - actions: "assignPermissions", - }, - }, - }, - success: { - type: "final", - }, - }, - }, - }, - onDone: { - target: "loaded", - }, - }, - loaded: { - initial: "waiting", - states: { - refreshingTemplate: { - invoke: { - id: "refreshTemplate", - src: "getTemplate", - onDone: { target: "waiting", actions: "assignTemplate" }, - }, - }, - waiting: { - after: { - 5000: "refreshingTemplate", - }, - }, - }, - }, - error: { - type: "final", - }, - }, - }, - { - services: { - getTemplate: (ctx) => - getTemplateByName(ctx.organizationId, ctx.templateName), - getActiveTemplateVersion: (ctx) => { - if (!ctx.template) { - throw new Error("Template not loaded") - } - - return getTemplateVersion(ctx.template.active_version_id) - }, - getTemplateResources: (ctx) => { - if (!ctx.template) { - throw new Error("Template not loaded") - } - - return getTemplateVersionResources(ctx.template.active_version_id) - }, - getTemplateVersions: (ctx) => { - if (!ctx.template) { - throw new Error("Template not loaded") - } - - return getTemplateVersions(ctx.template.id) - }, - getTemplateDAUs: (ctx) => { - if (!ctx.template) { - throw new Error("Template not loaded") - } - return getTemplateDAUs(ctx.template.id) - }, - getTemplatePermissions: (ctx) => { - if (!ctx.template) { - throw new Error("Template not loaded") - } - return checkAuthorization({ - checks: getPermissionsToCheck(ctx.template.id), - }) - }, - }, - actions: { - assignTemplate: assign({ - template: (_, event) => event.data, - }), - assignActiveTemplateVersion: assign({ - activeTemplateVersion: (_, event) => event.data, - }), - assignGetTemplateError: assign({ - getTemplateError: (_, event) => event.data, - }), - assignTemplateResources: assign({ - templateResources: (_, event) => event.data, - }), - assignTemplateVersions: assign({ - templateVersions: (_, event) => event.data, - }), - assignTemplateDAUs: assign({ - templateDAUs: (_, event) => event.data, - }), - assignPermissions: assign({ - permissions: (_, event) => event.data, - }), - }, - }, - ) From cb51c1c561f756778d8072e22984287edcec2fd2 Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Mon, 20 Mar 2023 17:43:08 +0000 Subject: [PATCH 3/9] Group template actions --- .../TemplateLayout/TemplatePageHeader.tsx | 98 ++++++++++++------- site/src/theme/overrides.ts | 8 ++ 2 files changed, 68 insertions(+), 38 deletions(-) diff --git a/site/src/components/TemplateLayout/TemplatePageHeader.tsx b/site/src/components/TemplateLayout/TemplatePageHeader.tsx index fdb43aa4875f3..fcf33b574c319 100644 --- a/site/src/components/TemplateLayout/TemplatePageHeader.tsx +++ b/site/src/components/TemplateLayout/TemplatePageHeader.tsx @@ -1,8 +1,5 @@ import Button from "@material-ui/core/Button" -import DeleteOutlined from "@material-ui/icons/DeleteOutlined" import AddCircleOutline from "@material-ui/icons/AddCircleOutline" -import SettingsOutlined from "@material-ui/icons/SettingsOutlined" -import CodeOutlined from "@material-ui/icons/CodeOutlined" import { AuthorizationResponse, Template } from "api/typesGenerated" import { Avatar } from "components/Avatar/Avatar" import { Maybe } from "components/Conditionals/Maybe" @@ -13,10 +10,13 @@ import { PageHeaderSubtitle, } from "components/PageHeader/PageHeader" import { Stack } from "components/Stack/Stack" -import { FC } from "react" +import { FC, useState } from "react" import { Link as RouterLink } from "react-router-dom" import { useDeleteTemplate } from "./deleteTemplate" import { Margins } from "components/Margins/Margins" +import MoreVertOutlined from "@material-ui/icons/MoreVertOutlined" +import Menu from "@material-ui/core/Menu" +import MenuItem from "@material-ui/core/MenuItem" const Language = { editButton: "Edit", @@ -26,31 +26,60 @@ const Language = { deleteButton: "Delete", } -const TemplateSettingsButton: FC<{ templateName: string }> = ({ +const TemplateMenu: FC<{ templateName: string; onDelete: () => void }> = ({ templateName, -}) => ( - -) + onDelete, +}) => { + const [anchorEl, setAnchorEl] = useState(null) -const TemplateVariablesButton: FC<{ templateName: string }> = ({ - templateName, -}) => ( - -) + const handleClose = () => { + setAnchorEl(null) + } + + return ( +
+ + + + + {Language.settingsButton} + + + {Language.variablesButton} + + { + onDelete() + handleClose() + }} + > + {Language.deleteButton} + + +
+ ) +} const CreateWorkspaceButton: FC<{ templateName: string @@ -65,12 +94,6 @@ const CreateWorkspaceButton: FC<{ ) -const DeleteTemplateButton: FC<{ onClick: () => void }> = ({ onClick }) => ( - -) - export type TemplatePageHeaderProps = { template: Template permissions: AuthorizationResponse @@ -90,14 +113,13 @@ export const TemplatePageHeader: FC = ({ + - - - - } > diff --git a/site/src/theme/overrides.ts b/site/src/theme/overrides.ts index 19b2947940ab2..e52681da016bb 100644 --- a/site/src/theme/overrides.ts +++ b/site/src/theme/overrides.ts @@ -234,5 +234,13 @@ export const getOverrides = ({ backgroundColor: colors.gray[12], }, }, + MuiMenu: { + paper: { + marginTop: 8, + borderRadius: 4, + padding: "4px 0", + minWidth: 120, + }, + }, } } From f248b52feaacf015498551b3a9863dc4ddf6d331 Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Mon, 20 Mar 2023 18:01:30 +0000 Subject: [PATCH 4/9] Refactor template summary reload --- .../TemplateSummaryPage.tsx | 51 +++++++++++++--- .../TemplateSummaryPageView.stories.tsx | 58 +++++++++---------- .../TemplateSummaryPageView.tsx | 44 ++++++-------- .../TemplatePage/TemplateSummaryPage/data.ts | 40 +++++++++++++ 4 files changed, 128 insertions(+), 65 deletions(-) create mode 100644 site/src/pages/TemplatePage/TemplateSummaryPage/data.ts diff --git a/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPage.tsx b/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPage.tsx index f3f435b0cc57f..28658fb0b1358 100644 --- a/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPage.tsx +++ b/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPage.tsx @@ -1,12 +1,51 @@ +import { useQuery } from "@tanstack/react-query" +import { + getTemplateVersion, + getTemplateVersionResources, + getTemplateVersions, + getTemplateDAUs, +} from "api/api" import { useTemplateLayoutContext } from "components/TemplateLayout/TemplateLayout" import { FC } from "react" import { Helmet } from "react-helmet-async" import { pageTitle } from "util/page" import { TemplateSummaryPageView } from "./TemplateSummaryPageView" +const fetchTemplateSummary = async ( + templateId: string, + activeVersionId: string, +) => { + const [activeVersion, resources, versions, daus] = await Promise.all([ + getTemplateVersion(activeVersionId), + getTemplateVersionResources(activeVersionId), + getTemplateVersions(templateId), + getTemplateDAUs(templateId), + ]) + + return { + activeVersion, + resources, + versions, + daus, + } +} + +const useTemplateSummaryData = ( + templateId: string, + activeVersionId: string, +) => { + return useQuery({ + queryKey: ["template", templateId, "summary"], + queryFn: () => fetchTemplateSummary(templateId, activeVersionId), + }) +} + export const TemplateSummaryPage: FC = () => { - const { template, activeVersion, resources, versions, daus } = - useTemplateLayoutContext() + const { template } = useTemplateLayoutContext() + const { data } = useTemplateSummaryData( + template.id, + template.active_version_id, + ) return ( <> @@ -21,13 +60,7 @@ export const TemplateSummaryPage: FC = () => { )} - + ) } diff --git a/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.stories.tsx b/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.stories.tsx index f3f348695ddea..eff23fd81b8b8 100644 --- a/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.stories.tsx +++ b/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.stories.tsx @@ -17,47 +17,47 @@ const Template: Story = (args) => ( export const Example = Template.bind({}) Example.args = { template: Mocks.MockTemplate, - activeTemplateVersion: Mocks.MockTemplateVersion, - templateResources: [ - Mocks.MockWorkspaceResource, - Mocks.MockWorkspaceResource2, - ], - templateVersions: [Mocks.MockTemplateVersion], + data: { + activeVersion: Mocks.MockTemplateVersion, + resources: [Mocks.MockWorkspaceResource, Mocks.MockWorkspaceResource2], + versions: [Mocks.MockTemplateVersion], + daus: Mocks.MockTemplateDAUResponse, + }, } export const NoIcon = Template.bind({}) NoIcon.args = { template: { ...Mocks.MockTemplate, icon: "" }, - activeTemplateVersion: Mocks.MockTemplateVersion, - templateResources: [ - Mocks.MockWorkspaceResource, - Mocks.MockWorkspaceResource2, - ], - templateVersions: [Mocks.MockTemplateVersion], + data: { + activeVersion: Mocks.MockTemplateVersion, + resources: [Mocks.MockWorkspaceResource, Mocks.MockWorkspaceResource2], + versions: [Mocks.MockTemplateVersion], + daus: Mocks.MockTemplateDAUResponse, + }, } export const SmallViewport = Template.bind({}) SmallViewport.args = { template: Mocks.MockTemplate, - activeTemplateVersion: { - ...Mocks.MockTemplateVersion, - readme: `--- -name:Template test ---- -## Instructions -You can add instructions here + data: { + activeVersion: { + ...Mocks.MockTemplateVersion, + readme: `--- + name:Template test + --- + ## Instructions + You can add instructions here -[Some link info](https://coder.com) -\`\`\` -# This is a really long sentence to test that the code block wraps into a new line properly. -\`\`\` -`, + [Some link info](https://coder.com) + \`\`\` + # This is a really long sentence to test that the code block wraps into a new line properly. + \`\`\` + `, + }, + resources: [Mocks.MockWorkspaceResource, Mocks.MockWorkspaceResource2], + versions: [Mocks.MockTemplateVersion], + daus: Mocks.MockTemplateDAUResponse, }, - templateResources: [ - Mocks.MockWorkspaceResource, - Mocks.MockWorkspaceResource2, - ], - templateVersions: [Mocks.MockTemplateVersion], } SmallViewport.parameters = { chromatic: { viewports: [600] }, diff --git a/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.tsx b/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.tsx index e04e9e18eb83c..29f580d540396 100644 --- a/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.tsx +++ b/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.tsx @@ -1,10 +1,6 @@ import { makeStyles } from "@material-ui/core/styles" -import { - Template, - TemplateDAUsResponse, - TemplateVersion, - WorkspaceResource, -} from "api/typesGenerated" +import { Template, WorkspaceResource } from "api/typesGenerated" +import { Loader } from "components/Loader/Loader" import { MemoizedMarkdown } from "components/Markdown/Markdown" import { Stack } from "components/Stack/Stack" import { TemplateResourcesTable } from "components/TemplateResourcesTable/TemplateResourcesTable" @@ -13,26 +9,25 @@ import { VersionsTable } from "components/VersionsTable/VersionsTable" import frontMatter from "front-matter" import { FC } from "react" import { DAUChart } from "../../../components/DAUChart/DAUChart" +import { TemplateSummaryData } from "./data" export interface TemplateSummaryPageViewProps { + data?: TemplateSummaryData template: Template - activeTemplateVersion: TemplateVersion - templateResources: WorkspaceResource[] - templateVersions?: TemplateVersion[] - templateDAUs?: TemplateDAUsResponse } -export const TemplateSummaryPageView: FC< - React.PropsWithChildren -> = ({ +export const TemplateSummaryPageView: FC = ({ + data, template, - activeTemplateVersion, - templateResources, - templateVersions, - templateDAUs, }) => { const styles = useStyles() - const readme = frontMatter(activeTemplateVersion.readme) + + if (!data) { + return + } + + const { activeVersion, daus, resources, versions } = data + const readme = frontMatter(activeVersion.readme) const getStartedResources = (resources: WorkspaceResource[]) => { return resources.filter( @@ -42,14 +37,9 @@ export const TemplateSummaryPageView: FC< return ( - - {templateDAUs && } - + + {daus && } +
README.md
@@ -58,7 +48,7 @@ export const TemplateSummaryPageView: FC<
- +
) } diff --git a/site/src/pages/TemplatePage/TemplateSummaryPage/data.ts b/site/src/pages/TemplatePage/TemplateSummaryPage/data.ts new file mode 100644 index 0000000000000..5a0dd9e02b4f9 --- /dev/null +++ b/site/src/pages/TemplatePage/TemplateSummaryPage/data.ts @@ -0,0 +1,40 @@ +import { useQuery } from "@tanstack/react-query" +import { + getTemplateVersion, + getTemplateVersionResources, + getTemplateVersions, + getTemplateDAUs, +} from "api/api" + +const fetchTemplateSummary = async ( + templateId: string, + activeVersionId: string, +) => { + const [activeVersion, resources, versions, daus] = await Promise.all([ + getTemplateVersion(activeVersionId), + getTemplateVersionResources(activeVersionId), + getTemplateVersions(templateId), + getTemplateDAUs(templateId), + ]) + + return { + activeVersion, + resources, + versions, + daus, + } +} + +export const useTemplateSummaryData = ( + templateId: string, + activeVersionId: string, +) => { + return useQuery({ + queryKey: ["template", templateId, "summary"], + queryFn: () => fetchTemplateSummary(templateId, activeVersionId), + }) +} + +export type TemplateSummaryData = Awaited< + ReturnType +> From fc494389e02e437ebc81770ff8ae2a348e0922dd Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Mon, 20 Mar 2023 18:19:35 +0000 Subject: [PATCH 5/9] Add edit file button --- .../TemplateLayout/TemplateLayout.tsx | 26 ++++++--------- .../TemplatePageHeader.stories.tsx | 5 ++- .../TemplateLayout/TemplatePageHeader.tsx | 33 +++++++++++++++---- 3 files changed, 41 insertions(+), 23 deletions(-) diff --git a/site/src/components/TemplateLayout/TemplateLayout.tsx b/site/src/components/TemplateLayout/TemplateLayout.tsx index 6b31afb05ac73..841ece8b3ecee 100644 --- a/site/src/components/TemplateLayout/TemplateLayout.tsx +++ b/site/src/components/TemplateLayout/TemplateLayout.tsx @@ -11,12 +11,10 @@ import { AlertBanner } from "components/AlertBanner/AlertBanner" import { checkAuthorization, getTemplateByName, - getTemplateDAUs, getTemplateVersion, - getTemplateVersionResources, - getTemplateVersions, } from "api/api" import { useQuery } from "@tanstack/react-query" +import { useDashboard } from "components/Dashboard/DashboardProvider" const templatePermissions = (templateId: string) => ({ canUpdateTemplate: { @@ -30,23 +28,16 @@ const templatePermissions = (templateId: string) => ({ const fetchTemplate = async (orgId: string, templateName: string) => { const template = await getTemplateByName(orgId, templateName) - const [activeVersion, resources, versions, daus, permissions] = - await Promise.all([ - getTemplateVersion(template.active_version_id), - getTemplateVersionResources(template.active_version_id), - getTemplateVersions(template.id), - getTemplateDAUs(template.id), - checkAuthorization({ - checks: templatePermissions(template.id), - }), - ]) + const [activeVersion, permissions] = await Promise.all([ + getTemplateVersion(template.active_version_id), + checkAuthorization({ + checks: templatePermissions(template.id), + }), + ]) return { template, activeVersion, - resources, - versions, - daus, permissions, } } @@ -82,6 +73,7 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({ const orgId = useOrganizationId() const { template } = useParams() as { template: string } const templateData = useTemplateData(orgId, template) + const dashboard = useDashboard() if (templateData.error) { return ( @@ -104,7 +96,9 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({ <> { navigate("/templates") }} diff --git a/site/src/components/TemplateLayout/TemplatePageHeader.stories.tsx b/site/src/components/TemplateLayout/TemplatePageHeader.stories.tsx index 07d5334961ea6..1e01abbf5b830 100644 --- a/site/src/components/TemplateLayout/TemplatePageHeader.stories.tsx +++ b/site/src/components/TemplateLayout/TemplatePageHeader.stories.tsx @@ -1,5 +1,5 @@ import { ComponentMeta, Story } from "@storybook/react" -import { MockTemplate } from "testHelpers/entities" +import { MockTemplate, MockTemplateVersion } from "testHelpers/entities" import { TemplatePageHeader, TemplatePageHeaderProps, @@ -12,6 +12,9 @@ export default { template: { defaultValue: MockTemplate, }, + activeVersion: { + defaultValue: MockTemplateVersion, + }, permissions: { defaultValue: { canUpdateTemplate: true, diff --git a/site/src/components/TemplateLayout/TemplatePageHeader.tsx b/site/src/components/TemplateLayout/TemplatePageHeader.tsx index fcf33b574c319..503c09e944d51 100644 --- a/site/src/components/TemplateLayout/TemplatePageHeader.tsx +++ b/site/src/components/TemplateLayout/TemplatePageHeader.tsx @@ -1,6 +1,10 @@ import Button from "@material-ui/core/Button" import AddCircleOutline from "@material-ui/icons/AddCircleOutline" -import { AuthorizationResponse, Template } from "api/typesGenerated" +import { + AuthorizationResponse, + Template, + TemplateVersion, +} from "api/typesGenerated" import { Avatar } from "components/Avatar/Avatar" import { Maybe } from "components/Conditionals/Maybe" import { DeleteDialog } from "components/Dialogs/DeleteDialog/DeleteDialog" @@ -19,17 +23,19 @@ import Menu from "@material-ui/core/Menu" import MenuItem from "@material-ui/core/MenuItem" const Language = { - editButton: "Edit", variablesButton: "Variables", settingsButton: "Settings", createButton: "Create workspace", deleteButton: "Delete", + editFilesButton: "Edit files", } -const TemplateMenu: FC<{ templateName: string; onDelete: () => void }> = ({ - templateName, - onDelete, -}) => { +const TemplateMenu: FC<{ + templateName: string + templateVersion: string + canEditFiles: boolean + onDelete: () => void +}> = ({ templateName, templateVersion, canEditFiles, onDelete }) => { const [anchorEl, setAnchorEl] = useState(null) const handleClose = () => { @@ -68,6 +74,15 @@ const TemplateMenu: FC<{ templateName: string; onDelete: () => void }> = ({ > {Language.variablesButton} + {canEditFiles && ( + + {Language.editFilesButton} + + )} { onDelete() @@ -96,13 +111,17 @@ const CreateWorkspaceButton: FC<{ export type TemplatePageHeaderProps = { template: Template + activeVersion: TemplateVersion permissions: AuthorizationResponse + canEditFiles: boolean onDeleteTemplate: () => void } export const TemplatePageHeader: FC = ({ template, + activeVersion, permissions, + canEditFiles, onDeleteTemplate, }) => { const hasIcon = template.icon && template.icon !== "" @@ -116,8 +135,10 @@ export const TemplatePageHeader: FC = ({ From 77592c79fc9551ec6a2867bfe71eff70a8fd09df Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Mon, 20 Mar 2023 18:26:04 +0000 Subject: [PATCH 6/9] Refactor summary data --- .../TemplateSummaryPage.tsx | 45 +++---------------- .../TemplateSummaryPageView.stories.tsx | 14 +++--- .../TemplateSummaryPageView.tsx | 10 ++++- .../TemplatePage/TemplateSummaryPage/data.ts | 5 +-- 4 files changed, 23 insertions(+), 51 deletions(-) diff --git a/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPage.tsx b/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPage.tsx index 28658fb0b1358..ef9359e618533 100644 --- a/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPage.tsx +++ b/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPage.tsx @@ -1,47 +1,12 @@ -import { useQuery } from "@tanstack/react-query" -import { - getTemplateVersion, - getTemplateVersionResources, - getTemplateVersions, - getTemplateDAUs, -} from "api/api" import { useTemplateLayoutContext } from "components/TemplateLayout/TemplateLayout" import { FC } from "react" import { Helmet } from "react-helmet-async" import { pageTitle } from "util/page" +import { useTemplateSummaryData } from "./data" import { TemplateSummaryPageView } from "./TemplateSummaryPageView" -const fetchTemplateSummary = async ( - templateId: string, - activeVersionId: string, -) => { - const [activeVersion, resources, versions, daus] = await Promise.all([ - getTemplateVersion(activeVersionId), - getTemplateVersionResources(activeVersionId), - getTemplateVersions(templateId), - getTemplateDAUs(templateId), - ]) - - return { - activeVersion, - resources, - versions, - daus, - } -} - -const useTemplateSummaryData = ( - templateId: string, - activeVersionId: string, -) => { - return useQuery({ - queryKey: ["template", templateId, "summary"], - queryFn: () => fetchTemplateSummary(templateId, activeVersionId), - }) -} - export const TemplateSummaryPage: FC = () => { - const { template } = useTemplateLayoutContext() + const { template, activeVersion } = useTemplateLayoutContext() const { data } = useTemplateSummaryData( template.id, template.active_version_id, @@ -60,7 +25,11 @@ export const TemplateSummaryPage: FC = () => { )} - + ) } diff --git a/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.stories.tsx b/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.stories.tsx index eff23fd81b8b8..c4c82debb3048 100644 --- a/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.stories.tsx +++ b/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.stories.tsx @@ -17,8 +17,8 @@ const Template: Story = (args) => ( export const Example = Template.bind({}) Example.args = { template: Mocks.MockTemplate, + activeVersion: Mocks.MockTemplateVersion, data: { - activeVersion: Mocks.MockTemplateVersion, resources: [Mocks.MockWorkspaceResource, Mocks.MockWorkspaceResource2], versions: [Mocks.MockTemplateVersion], daus: Mocks.MockTemplateDAUResponse, @@ -28,8 +28,8 @@ Example.args = { export const NoIcon = Template.bind({}) NoIcon.args = { template: { ...Mocks.MockTemplate, icon: "" }, + activeVersion: Mocks.MockTemplateVersion, data: { - activeVersion: Mocks.MockTemplateVersion, resources: [Mocks.MockWorkspaceResource, Mocks.MockWorkspaceResource2], versions: [Mocks.MockTemplateVersion], daus: Mocks.MockTemplateDAUResponse, @@ -39,10 +39,9 @@ NoIcon.args = { export const SmallViewport = Template.bind({}) SmallViewport.args = { template: Mocks.MockTemplate, - data: { - activeVersion: { - ...Mocks.MockTemplateVersion, - readme: `--- + activeVersion: { + ...Mocks.MockTemplateVersion, + readme: `--- name:Template test --- ## Instructions @@ -53,7 +52,8 @@ SmallViewport.args = { # This is a really long sentence to test that the code block wraps into a new line properly. \`\`\` `, - }, + }, + data: { resources: [Mocks.MockWorkspaceResource, Mocks.MockWorkspaceResource2], versions: [Mocks.MockTemplateVersion], daus: Mocks.MockTemplateDAUResponse, diff --git a/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.tsx b/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.tsx index 29f580d540396..63178a08ad74c 100644 --- a/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.tsx +++ b/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.tsx @@ -1,5 +1,9 @@ import { makeStyles } from "@material-ui/core/styles" -import { Template, WorkspaceResource } from "api/typesGenerated" +import { + Template, + TemplateVersion, + WorkspaceResource, +} from "api/typesGenerated" import { Loader } from "components/Loader/Loader" import { MemoizedMarkdown } from "components/Markdown/Markdown" import { Stack } from "components/Stack/Stack" @@ -14,11 +18,13 @@ import { TemplateSummaryData } from "./data" export interface TemplateSummaryPageViewProps { data?: TemplateSummaryData template: Template + activeVersion: TemplateVersion } export const TemplateSummaryPageView: FC = ({ data, template, + activeVersion, }) => { const styles = useStyles() @@ -26,7 +32,7 @@ export const TemplateSummaryPageView: FC = ({ return } - const { activeVersion, daus, resources, versions } = data + const { daus, resources, versions } = data const readme = frontMatter(activeVersion.readme) const getStartedResources = (resources: WorkspaceResource[]) => { diff --git a/site/src/pages/TemplatePage/TemplateSummaryPage/data.ts b/site/src/pages/TemplatePage/TemplateSummaryPage/data.ts index 5a0dd9e02b4f9..6470f402693b9 100644 --- a/site/src/pages/TemplatePage/TemplateSummaryPage/data.ts +++ b/site/src/pages/TemplatePage/TemplateSummaryPage/data.ts @@ -1,6 +1,5 @@ import { useQuery } from "@tanstack/react-query" import { - getTemplateVersion, getTemplateVersionResources, getTemplateVersions, getTemplateDAUs, @@ -10,15 +9,13 @@ const fetchTemplateSummary = async ( templateId: string, activeVersionId: string, ) => { - const [activeVersion, resources, versions, daus] = await Promise.all([ - getTemplateVersion(activeVersionId), + const [resources, versions, daus] = await Promise.all([ getTemplateVersionResources(activeVersionId), getTemplateVersions(templateId), getTemplateDAUs(templateId), ]) return { - activeVersion, resources, versions, daus, From 09280114d14a669e75b7eec4010607c960b085df Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Mon, 20 Mar 2023 18:42:48 +0000 Subject: [PATCH 7/9] Show source code in the template --- site/src/AppRouter.tsx | 5 +- .../TemplateFiles/TemplateFiles.tsx | 150 ++++++++++++++++++ .../TemplateLayout/TemplateLayout.tsx | 11 ++ .../TemplateFilesPage/TemplateFilesPage.tsx | 76 +++++++++ 4 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 site/src/components/TemplateFiles/TemplateFiles.tsx create mode 100644 site/src/pages/TemplateFilesPage/TemplateFilesPage.tsx diff --git a/site/src/AppRouter.tsx b/site/src/AppRouter.tsx index 69a14a113b0fb..6bce538c20373 100644 --- a/site/src/AppRouter.tsx +++ b/site/src/AppRouter.tsx @@ -125,10 +125,12 @@ const TemplateVariablesPage = lazy( const WorkspaceSettingsPage = lazy( () => import("./pages/WorkspaceSettingsPage/WorkspaceSettingsPage"), ) - const CreateTokenPage = lazy( () => import("./pages/CreateTokenPage/CreateTokenPage"), ) +const TemplateFilesPage = lazy( + () => import("./pages/TemplateFilesPage/TemplateFilesPage"), +) export const AppRouter: FC = () => { return ( @@ -162,6 +164,7 @@ export const AppRouter: FC = () => { path="permissions" element={} /> + } /> } /> diff --git a/site/src/components/TemplateFiles/TemplateFiles.tsx b/site/src/components/TemplateFiles/TemplateFiles.tsx new file mode 100644 index 0000000000000..d235a44eeb1b0 --- /dev/null +++ b/site/src/components/TemplateFiles/TemplateFiles.tsx @@ -0,0 +1,150 @@ +import { makeStyles } from "@material-ui/core/styles" +import { DockerIcon } from "components/Icons/DockerIcon" +import { MarkdownIcon } from "components/Icons/MarkdownIcon" +import { TerraformIcon } from "components/Icons/TerraformIcon" +import { SyntaxHighlighter } from "components/SyntaxHighlighter/SyntaxHighlighter" +import { UseTabResult } from "hooks/useTab" +import { FC } from "react" +import { combineClasses } from "util/combineClasses" +import { TemplateVersionFiles } from "util/templateVersion" + +const iconByExtension: Record = { + tf: , + md: , + mkd: , + Dockerfile: , +} + +const getExtension = (filename: string) => { + if (filename.includes(".")) { + const [_, extension] = filename.split(".") + return extension + } + + return filename +} + +const languageByExtension: Record = { + tf: "hcl", + md: "markdown", + mkd: "markdown", + Dockerfile: "dockerfile", +} + +export const TemplateFiles: FC<{ + currentFiles: TemplateVersionFiles + previousFiles?: TemplateVersionFiles + tab: UseTabResult +}> = ({ currentFiles, previousFiles, tab }) => { + const styles = useStyles() + const filenames = Object.keys(currentFiles) + const selectedFilename = filenames[Number(tab.value)] + const currentFile = currentFiles[selectedFilename] + const previousFile = previousFiles && previousFiles[selectedFilename] + + return ( +
+
+ {filenames.map((filename, index) => { + const tabValue = index.toString() + const extension = getExtension(filename) + const icon = iconByExtension[extension] + const hasDiff = + previousFiles && + previousFiles[filename] && + currentFiles[filename] !== previousFiles[filename] + + return ( + + ) + })} +
+ + +
+ ) +} +const useStyles = makeStyles((theme) => ({ + tabs: { + display: "flex", + alignItems: "baseline", + borderBottom: `1px solid ${theme.palette.divider}`, + gap: 1, + }, + + tab: { + background: "transparent", + border: 0, + padding: theme.spacing(0, 3), + display: "flex", + alignItems: "center", + height: theme.spacing(6), + opacity: 0.85, + cursor: "pointer", + gap: theme.spacing(0.5), + position: "relative", + + "& svg": { + width: 22, + maxHeight: 16, + }, + + "&:hover": { + backgroundColor: theme.palette.action.hover, + }, + }, + + tabActive: { + opacity: 1, + background: theme.palette.action.hover, + + "&:after": { + content: '""', + display: "block", + height: 1, + width: "100%", + bottom: 0, + left: 0, + backgroundColor: theme.palette.secondary.dark, + position: "absolute", + }, + }, + + tabDiff: { + height: 6, + width: 6, + backgroundColor: theme.palette.warning.light, + borderRadius: "100%", + marginLeft: theme.spacing(0.5), + }, + + codeWrapper: { + background: theme.palette.background.paperLight, + }, + + files: { + borderRadius: theme.shape.borderRadius, + border: `1px solid ${theme.palette.divider}`, + }, + + prism: { + borderRadius: 0, + }, +})) diff --git a/site/src/components/TemplateLayout/TemplateLayout.tsx b/site/src/components/TemplateLayout/TemplateLayout.tsx index 841ece8b3ecee..92c58af7a91ad 100644 --- a/site/src/components/TemplateLayout/TemplateLayout.tsx +++ b/site/src/components/TemplateLayout/TemplateLayout.tsx @@ -130,6 +130,17 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({ > Permissions
+ + combineClasses([ + styles.tabItem, + isActive ? styles.tabItemActive : undefined, + ]) + } + > + Source Code +
diff --git a/site/src/pages/TemplateFilesPage/TemplateFilesPage.tsx b/site/src/pages/TemplateFilesPage/TemplateFilesPage.tsx new file mode 100644 index 0000000000000..f5af6182a5734 --- /dev/null +++ b/site/src/pages/TemplateFilesPage/TemplateFilesPage.tsx @@ -0,0 +1,76 @@ +import { useQuery } from "@tanstack/react-query" +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 { useOrganizationId } from "hooks/useOrganizationId" +import { useTab } from "hooks/useTab" +import { FC } from "react" +import { Helmet } from "react-helmet-async" +import { pageTitle } from "util/page" +import { getTemplateVersionFiles } from "util/templateVersion" + +const fetchTemplateFiles = async ( + organizationId: string, + templateName: string, + activeVersion: TemplateVersion, +) => { + const previousVersion = await getPreviousTemplateVersionByName( + organizationId, + templateName, + activeVersion.name, + ) + const loadFilesPromises: ReturnType[] = [] + loadFilesPromises.push(getTemplateVersionFiles(activeVersion)) + if (previousVersion) { + loadFilesPromises.push(getTemplateVersionFiles(previousVersion)) + } + const [currentFiles, previousFiles] = await Promise.all(loadFilesPromises) + return { + currentFiles, + previousFiles, + } +} + +const useTemplateFiles = ( + organizationId: string, + templateName: string, + activeVersion: TemplateVersion, +) => + useQuery({ + queryKey: ["templateFiles", templateName], + queryFn: () => + fetchTemplateFiles(organizationId, templateName, activeVersion), + }) + +const TemplateFilesPage: FC = () => { + const { template, activeVersion } = useTemplateLayoutContext() + const orgId = useOrganizationId() + const tab = useTab("file", "0") + const { data: templateFiles } = useTemplateFiles( + orgId, + template.name, + activeVersion, + ) + + return ( + <> + + {pageTitle(`${template?.name} ยท Source Code`)} + + + {templateFiles ? ( + + ) : ( + + )} + + ) +} + +export default TemplateFilesPage From b85de80184e4c1a71dc6e776a310862047ce5a27 Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Mon, 20 Mar 2023 18:43:56 +0000 Subject: [PATCH 8/9] Show source code --- .../TemplateVersionPageView.tsx | 155 +----------------- 1 file changed, 2 insertions(+), 153 deletions(-) diff --git a/site/src/pages/TemplateVersionPage/TemplateVersionPageView.tsx b/site/src/pages/TemplateVersionPage/TemplateVersionPageView.tsx index dd0291fb4b954..abdf45edf415a 100644 --- a/site/src/pages/TemplateVersionPage/TemplateVersionPageView.tsx +++ b/site/src/pages/TemplateVersionPage/TemplateVersionPageView.tsx @@ -1,11 +1,7 @@ import Button from "@material-ui/core/Button" import Link from "@material-ui/core/Link" -import { makeStyles } from "@material-ui/core/styles" import EditIcon from "@material-ui/icons/Edit" import { AlertBanner } from "components/AlertBanner/AlertBanner" -import { DockerIcon } from "components/Icons/DockerIcon" -import { MarkdownIcon } from "components/Icons/MarkdownIcon" -import { TerraformIcon } from "components/Icons/TerraformIcon" import { Loader } from "components/Loader/Loader" import { Margins } from "components/Margins/Margins" import { @@ -15,89 +11,14 @@ import { } from "components/PageHeader/PageHeader" import { Stack } from "components/Stack/Stack" import { Stats, StatsItem } from "components/Stats/Stats" -import { SyntaxHighlighter } from "components/SyntaxHighlighter/SyntaxHighlighter" +import { TemplateFiles } from "components/TemplateFiles/TemplateFiles" import { UseTabResult } from "hooks/useTab" import { FC } from "react" import { useTranslation } from "react-i18next" import { Link as RouterLink } from "react-router-dom" -import { combineClasses } from "util/combineClasses" import { createDayString } from "util/createDayString" -import { TemplateVersionFiles } from "util/templateVersion" import { TemplateVersionMachineContext } from "xServices/templateVersion/templateVersionXService" -const iconByExtension: Record = { - tf: , - md: , - mkd: , - Dockerfile: , -} - -const getExtension = (filename: string) => { - if (filename.includes(".")) { - const [_, extension] = filename.split(".") - return extension - } - - return filename -} - -const languageByExtension: Record = { - tf: "hcl", - md: "markdown", - mkd: "markdown", - Dockerfile: "dockerfile", -} - -const Files: FC<{ - currentFiles: TemplateVersionFiles - previousFiles?: TemplateVersionFiles - tab: UseTabResult -}> = ({ currentFiles, previousFiles, tab }) => { - const styles = useStyles() - const filenames = Object.keys(currentFiles) - const selectedFilename = filenames[Number(tab.value)] - const currentFile = currentFiles[selectedFilename] - const previousFile = previousFiles && previousFiles[selectedFilename] - - return ( -
-
- {filenames.map((filename, index) => { - const tabValue = index.toString() - const extension = getExtension(filename) - const icon = iconByExtension[extension] - const hasDiff = - previousFiles && - previousFiles[filename] && - currentFiles[filename] !== previousFiles[filename] - - return ( - - ) - })} -
- - -
- ) -} export interface TemplateVersionPageViewProps { /** * Used to display the version name before loading the version in the API @@ -165,7 +86,7 @@ export const TemplateVersionPageView: FC = ({ /> - = ({ ) } -const useStyles = makeStyles((theme) => ({ - tabsWrapper: { - borderBottom: `1px solid ${theme.palette.divider}`, - }, - - tabs: { - display: "flex", - alignItems: "baseline", - borderBottom: `1px solid ${theme.palette.divider}`, - gap: 1, - }, - - tab: { - background: "transparent", - border: 0, - padding: theme.spacing(0, 3), - display: "flex", - alignItems: "center", - height: theme.spacing(6), - opacity: 0.85, - cursor: "pointer", - gap: theme.spacing(0.5), - position: "relative", - - "& svg": { - width: 22, - maxHeight: 16, - }, - - "&:hover": { - backgroundColor: theme.palette.action.hover, - }, - }, - - tabActive: { - opacity: 1, - background: theme.palette.action.hover, - - "&:after": { - content: '""', - display: "block", - height: 1, - width: "100%", - bottom: 0, - left: 0, - backgroundColor: theme.palette.secondary.dark, - position: "absolute", - }, - }, - - tabDiff: { - height: 6, - width: 6, - backgroundColor: theme.palette.warning.light, - borderRadius: "100%", - marginLeft: theme.spacing(0.5), - }, - - codeWrapper: { - background: theme.palette.background.paperLight, - }, - - files: { - borderRadius: theme.shape.borderRadius, - border: `1px solid ${theme.palette.divider}`, - }, - - prism: { - borderRadius: 0, - }, -})) - export default TemplateVersionPageView From c449252602d85529bdde7039bfc35c1a159b38a0 Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Tue, 21 Mar 2023 16:42:11 +0000 Subject: [PATCH 9/9] Fix template layout --- site/src/components/TemplateLayout/TemplateLayout.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/site/src/components/TemplateLayout/TemplateLayout.tsx b/site/src/components/TemplateLayout/TemplateLayout.tsx index 92c58af7a91ad..547c927b9395a 100644 --- a/site/src/components/TemplateLayout/TemplateLayout.tsx +++ b/site/src/components/TemplateLayout/TemplateLayout.tsx @@ -83,15 +83,10 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({ ) } - if (templateData.isLoading) { + if (templateData.isLoading || !templateData.data) { return } - // Make typescript happy - if (!templateData.data) { - return <> - } - return ( <>