- Notifications
You must be signed in to change notification settings - Fork1k
refactor: rollback provisioners page to its previous version#16699
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Merged
Uh oh!
There was an error while loading.Please reload this page.
Merged
Changes fromall commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Jump to file
Failed to load files.
Loading
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
48 changes: 48 additions & 0 deletionssite/src/pages/OrganizationSettingsPage/OrganizationProvisionersPage.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { buildInfo } from "api/queries/buildInfo"; | ||
import { provisionerDaemonGroups } from "api/queries/organizations"; | ||
import { EmptyState } from "components/EmptyState/EmptyState"; | ||
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata"; | ||
import { useDashboard } from "modules/dashboard/useDashboard"; | ||
import { useOrganizationSettings } from "modules/management/OrganizationSettingsLayout"; | ||
import type { FC } from "react"; | ||
import { Helmet } from "react-helmet-async"; | ||
import { useQuery } from "react-query"; | ||
import { useParams } from "react-router-dom"; | ||
import { pageTitle } from "utils/page"; | ||
import { OrganizationProvisionersPageView } from "./OrganizationProvisionersPageView"; | ||
const OrganizationProvisionersPage: FC = () => { | ||
const { organization: organizationName } = useParams() as { | ||
organization: string; | ||
}; | ||
const { organization } = useOrganizationSettings(); | ||
const { entitlements } = useDashboard(); | ||
const { metadata } = useEmbeddedMetadata(); | ||
const buildInfoQuery = useQuery(buildInfo(metadata["build-info"])); | ||
const provisionersQuery = useQuery(provisionerDaemonGroups(organizationName)); | ||
if (!organization) { | ||
return <EmptyState message="Organization not found" />; | ||
} | ||
return ( | ||
<> | ||
<Helmet> | ||
<title> | ||
{pageTitle( | ||
"Provisioners", | ||
organization.display_name || organization.name, | ||
)} | ||
</title> | ||
</Helmet> | ||
<OrganizationProvisionersPageView | ||
showPaywall={!entitlements.features.multiple_organizations.enabled} | ||
error={provisionersQuery.error} | ||
buildInfo={buildInfoQuery.data} | ||
provisioners={provisionersQuery.data} | ||
/> | ||
</> | ||
); | ||
}; | ||
export default OrganizationProvisionersPage; |
142 changes: 142 additions & 0 deletionssite/src/pages/OrganizationSettingsPage/OrganizationProvisionersPageView.stories.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
import type { Meta, StoryObj } from "@storybook/react"; | ||
import { screen, userEvent } from "@storybook/test"; | ||
import { | ||
MockBuildInfo, | ||
MockProvisioner, | ||
MockProvisioner2, | ||
MockProvisionerBuiltinKey, | ||
MockProvisionerKey, | ||
MockProvisionerPskKey, | ||
MockProvisionerUserAuthKey, | ||
MockProvisionerWithTags, | ||
MockUserProvisioner, | ||
mockApiError, | ||
} from "testHelpers/entities"; | ||
import { OrganizationProvisionersPageView } from "./OrganizationProvisionersPageView"; | ||
const meta: Meta<typeof OrganizationProvisionersPageView> = { | ||
title: "pages/OrganizationProvisionersPage", | ||
component: OrganizationProvisionersPageView, | ||
args: { | ||
buildInfo: MockBuildInfo, | ||
}, | ||
}; | ||
export default meta; | ||
type Story = StoryObj<typeof OrganizationProvisionersPageView>; | ||
export const Provisioners: Story = { | ||
args: { | ||
provisioners: [ | ||
{ | ||
key: MockProvisionerBuiltinKey, | ||
daemons: [MockProvisioner, MockProvisioner2], | ||
}, | ||
{ | ||
key: MockProvisionerPskKey, | ||
daemons: [ | ||
MockProvisioner, | ||
MockUserProvisioner, | ||
MockProvisionerWithTags, | ||
], | ||
}, | ||
{ | ||
key: MockProvisionerPskKey, | ||
daemons: [MockProvisioner, MockProvisioner2], | ||
}, | ||
{ | ||
key: { ...MockProvisionerKey, id: "ジェイデン", name: "ジェイデン" }, | ||
daemons: [ | ||
MockProvisioner, | ||
{ ...MockProvisioner2, tags: { scope: "organization", owner: "" } }, | ||
], | ||
}, | ||
{ | ||
key: { ...MockProvisionerKey, id: "ベン", name: "ベン" }, | ||
daemons: [ | ||
MockProvisioner, | ||
{ | ||
...MockProvisioner2, | ||
version: "2.0.0", | ||
api_version: "1.0", | ||
}, | ||
], | ||
}, | ||
{ | ||
key: { | ||
...MockProvisionerKey, | ||
id: "ケイラ", | ||
name: "ケイラ", | ||
tags: { | ||
...MockProvisioner.tags, | ||
都市: "ユタ", | ||
きっぷ: "yes", | ||
ちいさい: "no", | ||
}, | ||
}, | ||
daemons: Array.from({ length: 117 }, (_, i) => ({ | ||
...MockProvisioner, | ||
id: `ケイラ-${i}`, | ||
name: `ケイラ-${i}`, | ||
})), | ||
}, | ||
{ | ||
key: MockProvisionerUserAuthKey, | ||
daemons: [ | ||
MockUserProvisioner, | ||
{ | ||
...MockUserProvisioner, | ||
id: "mock-user-provisioner-2", | ||
name: "Test User Provisioner 2", | ||
}, | ||
], | ||
}, | ||
], | ||
}, | ||
play: async ({ step }) => { | ||
await step("open all details", async () => { | ||
const expandButtons = await screen.findAllByRole("button", { | ||
name: "Show provisioner details", | ||
}); | ||
for (const it of expandButtons) { | ||
await userEvent.click(it); | ||
} | ||
}); | ||
await step("close uninteresting/large details", async () => { | ||
const collapseButtons = await screen.findAllByRole("button", { | ||
name: "Hide provisioner details", | ||
}); | ||
await userEvent.click(collapseButtons[2]); | ||
await userEvent.click(collapseButtons[3]); | ||
await userEvent.click(collapseButtons[5]); | ||
}); | ||
await step("show version popover", async () => { | ||
const outOfDate = await screen.findByText("Out of date"); | ||
await userEvent.hover(outOfDate); | ||
}); | ||
}, | ||
}; | ||
export const Empty: Story = { | ||
args: { | ||
provisioners: [], | ||
}, | ||
}; | ||
export const WithError: Story = { | ||
args: { | ||
error: mockApiError({ | ||
message: "Fern is mad", | ||
detail: "Frieren slept in and didn't get groceries", | ||
}), | ||
}, | ||
}; | ||
export const Paywall: Story = { | ||
args: { | ||
showPaywall: true, | ||
}, | ||
}; |
148 changes: 148 additions & 0 deletionssite/src/pages/OrganizationSettingsPage/OrganizationProvisionersPageView.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
import OpenInNewIcon from "@mui/icons-material/OpenInNew"; | ||
import Button from "@mui/material/Button"; | ||
import type { | ||
BuildInfoResponse, | ||
ProvisionerKey, | ||
ProvisionerKeyDaemons, | ||
} from "api/typesGenerated"; | ||
import { ErrorAlert } from "components/Alert/ErrorAlert"; | ||
import { EmptyState } from "components/EmptyState/EmptyState"; | ||
import { Loader } from "components/Loader/Loader"; | ||
import { Paywall } from "components/Paywall/Paywall"; | ||
import { SettingsHeader } from "components/SettingsHeader/SettingsHeader"; | ||
import { Stack } from "components/Stack/Stack"; | ||
import { ProvisionerGroup } from "modules/provisioners/ProvisionerGroup"; | ||
import type { FC } from "react"; | ||
import { docs } from "utils/docs"; | ||
interface OrganizationProvisionersPageViewProps { | ||
/** Determines if the paywall will be shown or not */ | ||
showPaywall?: boolean; | ||
/** An error to display instead of the page content */ | ||
error?: unknown; | ||
/** Info about the version of coderd */ | ||
buildInfo?: BuildInfoResponse; | ||
/** Groups of provisioners, along with their key information */ | ||
provisioners?: readonly ProvisionerKeyDaemons[]; | ||
} | ||
export const OrganizationProvisionersPageView: FC< | ||
OrganizationProvisionersPageViewProps | ||
> = ({ showPaywall, error, buildInfo, provisioners }) => { | ||
return ( | ||
<div> | ||
<Stack | ||
alignItems="baseline" | ||
direction="row" | ||
justifyContent="space-between" | ||
> | ||
<SettingsHeader title="Provisioners" /> | ||
{!showPaywall && ( | ||
<Button | ||
endIcon={<OpenInNewIcon />} | ||
target="_blank" | ||
href={docs("/admin/provisioners")} | ||
> | ||
Create a provisioner | ||
</Button> | ||
)} | ||
</Stack> | ||
{showPaywall ? ( | ||
<Paywall | ||
message="Provisioners" | ||
description="Provisioners run your Terraform to create templates and workspaces. You need a Premium license to use this feature for multiple organizations." | ||
documentationLink={docs("/")} | ||
/> | ||
) : error ? ( | ||
<ErrorAlert error={error} /> | ||
) : !buildInfo || !provisioners ? ( | ||
<Loader /> | ||
) : ( | ||
<ViewContent buildInfo={buildInfo} provisioners={provisioners} /> | ||
)} | ||
</div> | ||
); | ||
}; | ||
type ViewContentProps = Required< | ||
Pick<OrganizationProvisionersPageViewProps, "buildInfo" | "provisioners"> | ||
>; | ||
const ViewContent: FC<ViewContentProps> = ({ buildInfo, provisioners }) => { | ||
const isEmpty = provisioners.every((group) => group.daemons.length === 0); | ||
const provisionerGroupsCount = provisioners.length; | ||
const provisionersCount = provisioners.reduce( | ||
(a, group) => a + group.daemons.length, | ||
0, | ||
); | ||
return ( | ||
<> | ||
{isEmpty ? ( | ||
<EmptyState | ||
message="No provisioners" | ||
description="A provisioner is required before you can create templates and workspaces. You can connect your first provisioner by following our documentation." | ||
cta={ | ||
<Button | ||
endIcon={<OpenInNewIcon />} | ||
target="_blank" | ||
href={docs("/admin/provisioners")} | ||
> | ||
Create a provisioner | ||
</Button> | ||
} | ||
/> | ||
) : ( | ||
<div | ||
css={(theme) => ({ | ||
margin: 0, | ||
fontSize: 12, | ||
paddingBottom: 18, | ||
color: theme.palette.text.secondary, | ||
})} | ||
> | ||
Showing {provisionerGroupsCount} groups and {provisionersCount}{" "} | ||
provisioners | ||
</div> | ||
)} | ||
<Stack spacing={4.5}> | ||
{provisioners.map((group) => ( | ||
<ProvisionerGroup | ||
key={group.key.id} | ||
buildInfo={buildInfo} | ||
keyName={group.key.name} | ||
keyTags={group.key.tags} | ||
type={getGroupType(group.key)} | ||
provisioners={group.daemons} | ||
/> | ||
))} | ||
</Stack> | ||
</> | ||
); | ||
}; | ||
// Ideally these would be generated and appear in typesGenerated.ts, but that is | ||
// not currently the case. In the meantime, these are taken from verbatim from | ||
// the corresponding codersdk declarations. The names remain unchanged to keep | ||
// usage of these special values "grep-able". | ||
// https://github.com/coder/coder/blob/7c77a3cc832fb35d9da4ca27df163c740f786137/codersdk/provisionerdaemons.go#L291-L295 | ||
const ProvisionerKeyIDBuiltIn = "00000000-0000-0000-0000-000000000001"; | ||
const ProvisionerKeyIDUserAuth = "00000000-0000-0000-0000-000000000002"; | ||
const ProvisionerKeyIDPSK = "00000000-0000-0000-0000-000000000003"; | ||
function getGroupType(key: ProvisionerKey) { | ||
switch (key.id) { | ||
case ProvisionerKeyIDBuiltIn: | ||
return "builtin"; | ||
case ProvisionerKeyIDUserAuth: | ||
return "userAuth"; | ||
case ProvisionerKeyIDPSK: | ||
return "psk"; | ||
default: | ||
return "key"; | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
Oops, something went wrong.
Uh oh!
There was an error while loading.Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.