Skip to content

Template delete button/kira pilot #4992

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Nov 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 49 additions & 83 deletions site/src/components/TemplateLayout/TemplateLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ import { makeStyles } from "@material-ui/core/styles"
import AddCircleOutline from "@material-ui/icons/AddCircleOutline"
import SettingsOutlined from "@material-ui/icons/SettingsOutlined"
import { useMachine, useSelector } from "@xstate/react"
import { ChooseOne, Cond } from "components/Conditionals/ChooseOne"
import { DeleteDialog } from "components/Dialogs/DeleteDialog/DeleteDialog"
import { DeleteButton } from "components/DropdownButton/ActionCtas"
import { DropdownButton } from "components/DropdownButton/DropdownButton"
import {
PageHeader,
PageHeaderSubtitle,
Expand All @@ -22,12 +18,7 @@ import {
Suspense,
useContext,
} from "react"
import {
Link as RouterLink,
Navigate,
NavLink,
useParams,
} from "react-router-dom"
import { Link as RouterLink, NavLink, useParams } from "react-router-dom"
import { combineClasses } from "util/combineClasses"
import { firstLetter } from "util/firstLetter"
import { selectPermissions } from "xServices/auth/authSelectors"
Expand All @@ -36,8 +27,8 @@ import {
TemplateContext,
templateMachine,
} from "xServices/template/templateXService"
import { Margins } from "../../components/Margins/Margins"
import { Stack } from "../../components/Stack/Stack"
import { Margins } from "components/Margins/Margins"
import { Stack } from "components/Stack/Stack"
import { Permissions } from "xServices/auth/authXService"
import { Loader } from "components/Loader/Loader"

Expand Down Expand Up @@ -76,11 +67,40 @@ export const useTemplateLayoutContext = (): TemplateLayoutContextValue => {
return context
}

const TemplateSettingsButton: FC<{ templateName: string }> = ({
templateName,
}) => (
<Link
underline="none"
component={RouterLink}
to={`/templates/${templateName}/settings`}
>
<Button variant="outlined" startIcon={<SettingsOutlined />}>
{Language.settingsButton}
</Button>
</Link>
)

const CreateWorkspaceButton: FC<{
templateName: string
className?: string
}> = ({ templateName, className }) => (
<Link
underline="none"
component={RouterLink}
to={`/templates/${templateName}/workspace`}
>
<Button className={className ?? ""} startIcon={<AddCircleOutline />}>
{Language.createButton}
</Button>
</Link>
)

export const TemplateLayout: FC<PropsWithChildren> = ({ children }) => {
const styles = useStyles()
const organizationId = useOrganizationId()
const templateName = useTemplateName()
const [templateState, templateSend] = useMachine(templateMachine, {
const [templateState, _] = useMachine(templateMachine, {
context: {
templateName,
organizationId,
Expand All @@ -103,67 +123,32 @@ export const TemplateLayout: FC<PropsWithChildren> = ({ children }) => {
!templateDAUs ||
!templatePermissions

if (templateState.matches("deleted")) {
return <Navigate to="/templates" />
}

const hasIcon = template && template.icon && template.icon !== ""

const createWorkspaceButton = (className?: string) => (
<Link
underline="none"
component={RouterLink}
to={`/templates/${templateName}/workspace`}
>
<Button
className={className ?? ""}
startIcon={<AddCircleOutline />}
disabled={isLoading}
>
{Language.createButton}
</Button>
</Link>
)
const generatePageHeaderActions = (): JSX.Element[] => {
const pageActions: JSX.Element[] = []

if (!isLoading && templatePermissions.canUpdateTemplate) {
pageActions.push(<TemplateSettingsButton templateName={templateName} />)
}

const handleDeleteTemplate = () => {
templateSend("DELETE")
if (!isLoading) {
pageActions.push(<CreateWorkspaceButton templateName={templateName} />)
}

return pageActions
}

return (
<>
<Margins>
<PageHeader
actions={
isLoading ? undefined : (
<ChooseOne>
<Cond condition={templatePermissions.canUpdateTemplate}>
<Link
underline="none"
component={RouterLink}
to={`/templates/${template.name}/settings`}
>
<Button variant="outlined" startIcon={<SettingsOutlined />}>
{Language.settingsButton}
</Button>
</Link>

<DropdownButton
primaryAction={createWorkspaceButton(styles.actionButton)}
secondaryActions={[
{
action: "delete",
button: (
<DeleteButton handleAction={handleDeleteTemplate} />
),
},
]}
canCancel={false}
/>
</Cond>

<Cond>{createWorkspaceButton()}</Cond>
</ChooseOne>
)
<>
{generatePageHeaderActions().map((action, i) => (
<div key={i}>{action}</div>
))}
</>
}
>
<Stack direction="row" spacing={3} className={styles.pageTitle}>
Expand Down Expand Up @@ -234,31 +219,12 @@ export const TemplateLayout: FC<PropsWithChildren> = ({ children }) => {
<Suspense fallback={<Loader />}>{children}</Suspense>
</TemplateLayoutContext.Provider>
</Margins>

{!isLoading && (
<DeleteDialog
isOpen={templateState.matches("confirmingDelete")}
confirmLoading={templateState.matches("deleting")}
onConfirm={() => {
templateSend("CONFIRM_DELETE")
}}
onCancel={() => {
templateSend("CANCEL_DELETE")
}}
entity="template"
name={template.name}
/>
)}
</>
)
}

export const useStyles = makeStyles((theme) => {
return {
actionButton: {
border: "none",
borderRadius: `${theme.shape.borderRadius}px 0px 0px ${theme.shape.borderRadius}px`,
},
pageTitle: {
alignItems: "center",
},
Expand Down
11 changes: 10 additions & 1 deletion site/src/i18n/en/templatePage.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
{
"deleteSuccess": "Template successfully deleted.",
"createdVersion": "created the version"
"createdVersion": "created the version",
"templateSettings": {
"title": "Template settings",
"dangerZone": {
"dangerZoneHeader": "Danger Zone",
"deleteTemplateHeader": "Delete this template",
"deleteTemplateCaption": "Once you delete a template, there is no going back. Please be certain.",
"deleteCta": "Delete Template"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fireEvent, screen } from "@testing-library/react"
import { screen } from "@testing-library/react"
import { TemplateLayout } from "components/TemplateLayout/TemplateLayout"
import { rest } from "msw"
import { ResizeObserver } from "resize-observer"
Expand Down Expand Up @@ -42,13 +42,6 @@ describe("TemplateSummaryPage", () => {
screen.getByText(MockWorkspaceResource.name)
screen.queryAllByText(`${MockTemplateVersion.name}`).length
})
it("allows an admin to delete a template", async () => {
renderPage()
const dropdownButton = await screen.findByLabelText("open-dropdown")
fireEvent.click(dropdownButton)
const deleteButton = await screen.findByText("Delete")
expect(deleteButton).toBeDefined()
})
it("does not allow a member to delete a template", () => {
// get member-level permissions
server.use(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export const TemplateSummaryPage: FC = () => {
activeTemplateVersion,
templateResources,
templateVersions,
deleteTemplateError,
templateDAUs,
} = context

Expand All @@ -31,7 +30,6 @@ export const TemplateSummaryPage: FC = () => {
templateResources={templateResources}
templateVersions={templateVersions}
templateDAUs={templateDAUs}
deleteTemplateError={deleteTemplateError}
/>
</>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
TemplateVersion,
WorkspaceResource,
} from "api/typesGenerated"
import { AlertBanner } from "components/AlertBanner/AlertBanner"
import { MemoizedMarkdown } from "components/Markdown/Markdown"
import { Stack } from "components/Stack/Stack"
import { TemplateResourcesTable } from "components/TemplateResourcesTable/TemplateResourcesTable"
Expand All @@ -21,7 +20,6 @@ export interface TemplateSummaryPageViewProps {
templateResources: WorkspaceResource[]
templateVersions?: TemplateVersion[]
templateDAUs?: TemplateDAUsResponse
deleteTemplateError: Error | unknown
}

export const TemplateSummaryPageView: FC<
Expand All @@ -32,15 +30,10 @@ export const TemplateSummaryPageView: FC<
templateResources,
templateVersions,
templateDAUs,
deleteTemplateError,
}) => {
const styles = useStyles()
const readme = frontMatter(activeTemplateVersion.readme)

const deleteError = deleteTemplateError ? (
<AlertBanner severity="error" error={deleteTemplateError} dismissible />
) : null

const getStartedResources = (resources: WorkspaceResource[]) => {
return resources.filter(
(resource) => resource.workspace_transition === "start",
Expand All @@ -49,7 +42,6 @@ export const TemplateSummaryPageView: FC<

return (
<Stack spacing={4}>
{deleteError}
<TemplateStats
template={template}
activeVersion={activeTemplateVersion}
Expand Down
5 changes: 1 addition & 4 deletions site/src/pages/TemplateSettingsPage/TemplateSettingsForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import InputAdornment from "@material-ui/core/InputAdornment"
import Popover from "@material-ui/core/Popover"
import { makeStyles } from "@material-ui/core/styles"
import TextField from "@material-ui/core/TextField"
import Typography from "@material-ui/core/Typography"
import { Template, UpdateTemplateMeta } from "api/typesGenerated"
import { OpenDropdown } from "components/DropdownArrows/DropdownArrows"
import { FormFooter } from "components/FormFooter/FormFooter"
Expand Down Expand Up @@ -188,9 +187,7 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
there are no validation errors for that field, display helper text.
We do not use the MUI helper-text prop because it overrides the validation error */}
{form.values.default_ttl_ms && !form.errors.default_ttl_ms && (
<Typography variant="subtitle2">
{Language.ttlHelperText(form.values.default_ttl_ms)}
</Typography>
<span>{Language.ttlHelperText(form.values.default_ttl_ms)}</span>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed this because I thought it was too big. Now it matches the other helper text in the form.

)}
</Stack>

Expand Down
18 changes: 16 additions & 2 deletions site/src/pages/TemplateSettingsPage/TemplateSettingsPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
validationSchema,
} from "./TemplateSettingsForm"
import { TemplateSettingsPage } from "./TemplateSettingsPage"
import { Language as ViewLanguage } from "./TemplateSettingsPageView"
import i18next from "i18next"

const renderTemplateSettingsPage = async () => {
const renderResult = renderWithAuth(<TemplateSettingsPage />, {
Expand Down Expand Up @@ -61,11 +61,25 @@ const fillAndSubmitForm = async ({

describe("TemplateSettingsPage", () => {
it("renders", async () => {
const { t } = i18next
const pageTitle = t("templateSettings.title", {
ns: "templatePage",
})
await renderTemplateSettingsPage()
const element = await screen.findByText(ViewLanguage.title)
const element = await screen.findByText(pageTitle)
expect(element).toBeDefined()
})

it("allows an admin to delete a template", async () => {
const { t } = i18next
await renderTemplateSettingsPage()
const deleteCta = t("templateSettings.dangerZone.deleteCta", {
ns: "templatePage",
})
const deleteButton = await screen.findByText(deleteCta)
expect(deleteButton).toBeDefined()
})

it("succeeds", async () => {
await renderTemplateSettingsPage()

Expand Down
10 changes: 10 additions & 0 deletions site/src/pages/TemplateSettingsPage/TemplateSettingsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const TemplateSettingsPage: FC = () => {
templateSettings: template,
saveTemplateSettingsError,
getTemplateError,
deleteTemplateError,
} = state.context

return (
Expand All @@ -41,13 +42,22 @@ export const TemplateSettingsPage: FC = () => {
errors={{
getTemplateError,
saveTemplateSettingsError,
deleteTemplateError,
}}
onCancel={() => {
navigate(`/templates/${templateName}`)
}}
onSubmit={(templateSettings) => {
send({ type: "SAVE", templateSettings })
}}
onDelete={() => {
send("DELETE")
}}
onConfirmDelete={() => send("CONFIRM_DELETE")}
onCancelDelete={() => send("CANCEL_DELETE")}
isConfirmingDelete={state.matches("confirmingDelete")}
isDeleting={state.matches("deleting")}
isDeleted={state.matches("deleted")}
/>
</>
)
Expand Down
Loading