From 665c9b317980588a68a32cd572e502cb830ee783 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Thu, 5 Sep 2024 22:27:20 +0000 Subject: [PATCH 1/8] work --- .../modules/provisioners/ProvisionerGroup.tsx | 183 ++++++++++++++++++ .../OrganizationProvisionersPage.tsx | 41 +++- ...ganizationProvisionersPageView.stories.tsx | 43 ++-- .../OrganizationProvisionersPageView.tsx | 23 ++- 4 files changed, 272 insertions(+), 18 deletions(-) create mode 100644 site/src/modules/provisioners/ProvisionerGroup.tsx diff --git a/site/src/modules/provisioners/ProvisionerGroup.tsx b/site/src/modules/provisioners/ProvisionerGroup.tsx new file mode 100644 index 0000000000000..5287917d5d220 --- /dev/null +++ b/site/src/modules/provisioners/ProvisionerGroup.tsx @@ -0,0 +1,183 @@ +import { useTheme } from "@emotion/react"; +import Business from "@mui/icons-material/Business"; +import Person from "@mui/icons-material/Person"; +import Tooltip from "@mui/material/Tooltip"; +import type { HealthMessage, ProvisionerDaemon } from "api/typesGenerated"; +import { Pill } from "components/Pill/Pill"; +import type { FC } from "react"; +import { createDayString } from "utils/createDayString"; +import { ProvisionerTag } from "./ProvisionerTag"; +import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata"; +import { buildInfo } from "api/queries/buildInfo"; +import { useQuery } from "react-query"; +import ArrowDownward from "@mui/icons-material/ArrowDownward"; + +type ProvisionerGroupType = "builtin" | "psk" | "key"; + +interface ProvisionerGroupProps { + readonly keyName?: string; + readonly type: ProvisionerGroupType; + readonly provisioners: ProvisionerDaemon[]; + readonly warnings?: readonly HealthMessage[]; +} + +export const ProvisionerGroup: FC = ({ + keyName, + type, + provisioners, + warnings, +}) => { + const { metadata } = useEmbeddedMetadata(); + const buildInfoQuery = useQuery(buildInfo(metadata["build-info"])); + + const [provisioner] = provisioners; + const theme = useTheme(); + const daemonScope = provisioner.tags.scope || "organization"; + const iconScope = daemonScope === "organization" ? : ; + + const provisionerVersion = provisioner.version; + const allProvisionersAreSameVersion = provisioners.every( + (provisioner) => provisioner.version === provisionerVersion, + ); + const upToDate = + allProvisionersAreSameVersion && buildInfoQuery.data?.version; + const protocolUpToDate = + allProvisionersAreSameVersion && + buildInfoQuery?.data?.provisioner_api_version === provisioner.api_version; + const provisionerCount = + provisioners.length === 1 + ? "1 provisioner" + : `${provisioners.length} provisioners`; + + const extraTags = Object.entries(provisioner.tags).filter( + ([key]) => key !== "scope" && key !== "owner", + ); + const isWarning = warnings && warnings.length > 0; + return ( +
+
+
+ {type === "builtin" && ( +
+

+ Built-in provisioners +

+ + {provisionerCount} — Built-in + +
+ )} + {type === "psk" && ( +
+

PSK provisioners

+ + {provisionerCount} —{" "} + {allProvisionersAreSameVersion ? ( + {provisionerVersion} + ) : ( + Multiple versions + )} + +
+ )} + {type === "key" && ( +
+

+ Key group – {keyName} +

+ + {provisionerCount} —{" "} + {allProvisionersAreSameVersion ? ( + {provisionerVersion} + ) : ( + Multiple versions + )} + +
+ )} +
+
+ + + + {daemonScope} + + + + {type === "key" && + extraTags.map(([key, value]) => ( + + ))} +
+
+ +
+ {warnings && warnings.length > 0 ? ( +
+ {warnings.map((warning) => ( + {warning.message} + ))} +
+ ) : ( + No warnings + )} + + Show provisioner details{" "} + + +
+
+ ); +}; diff --git a/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPage.tsx b/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPage.tsx index c233826ef07fc..edf4c434c2db0 100644 --- a/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPage.tsx +++ b/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPage.tsx @@ -2,7 +2,7 @@ import { organizationsPermissions, provisionerDaemons, } from "api/queries/organizations"; -import type { Organization } from "api/typesGenerated"; +import type { Organization, ProvisionerDaemon } from "api/typesGenerated"; import { ErrorAlert } from "components/Alert/ErrorAlert"; import { EmptyState } from "components/EmptyState/EmptyState"; import { Loader } from "components/Loader/Loader"; @@ -13,6 +13,39 @@ import { useParams } from "react-router-dom"; import { useOrganizationSettings } from "./ManagementSettingsLayout"; import { OrganizationProvisionersPageView } from "./OrganizationProvisionersPageView"; +export interface ProvisionersByGroup { + builtin: ProvisionerDaemon[]; + psk: ProvisionerDaemon[]; + keys: Map; +} + +function groupProvisioners( + provisioners: readonly ProvisionerDaemon[], +): ProvisionersByGroup { + const groups: ProvisionersByGroup = { builtin: [], psk: [], keys: new Map() }; + const type = "builtin"; + const keyName = "TODO"; + + for (const it of provisioners) { + if (type === "builtin") { + groups.builtin.push(it); + continue; + } + if (type === "psk") { + groups.psk.push(it); + continue; + } + + const keyGroup = groups.keys.get(keyName) ?? []; + if (!groups.keys.has(keyName)) { + groups.keys.set(keyName, keyGroup); + } + keyGroup.push(it); + } + + return groups; +} + const OrganizationProvisionersPage: FC = () => { const { organization: organizationName } = useParams() as { organization: string; @@ -54,7 +87,11 @@ const OrganizationProvisionersPage: FC = () => { return ; } - return ; + return ( + + ); }; export default OrganizationProvisionersPage; diff --git a/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPageView.stories.tsx b/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPageView.stories.tsx index 99a0e4494d703..0b73dc05dd312 100644 --- a/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPageView.stories.tsx +++ b/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPageView.stories.tsx @@ -12,18 +12,37 @@ type Story = StoryObj; export const Provisioners: Story = { args: { - provisioners: [ - MockProvisioner, - MockUserProvisioner, - { - ...MockProvisioner, - tags: { - ...MockProvisioner.tags, - 都市: "ユタ", - きっぷ: "yes", - ちいさい: "no", + provisioners: { + builtin: [MockProvisioner, MockProvisioner], + psk: [ + MockProvisioner, + MockUserProvisioner, + { + ...MockProvisioner, + tags: { + ...MockProvisioner.tags, + 都市: "ユタ", + きっぷ: "yes", + ちいさい: "no", + }, }, - }, - ], + ], + keys: new Map([ + [ + "ケイラ", + [ + { + ...MockProvisioner, + tags: { + ...MockProvisioner.tags, + 都市: "ユタ", + きっぷ: "yes", + ちいさい: "no", + }, + }, + ], + ], + ]), + }, }, }; diff --git a/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPageView.tsx b/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPageView.tsx index 4374c02833183..7ae26d77b8f11 100644 --- a/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPageView.tsx +++ b/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPageView.tsx @@ -1,14 +1,15 @@ import OpenInNewIcon from "@mui/icons-material/OpenInNew"; import Button from "@mui/material/Button"; -import type { ProvisionerDaemon } from "api/typesGenerated"; import { PageHeader, PageHeaderTitle } from "components/PageHeader/PageHeader"; import { Stack } from "components/Stack/Stack"; import { Provisioner } from "modules/provisioners/Provisioner"; import type { FC } from "react"; import { docs } from "utils/docs"; +import type { ProvisionersByGroup } from "./OrganizationProvisionersPage"; +import { ProvisionerGroup } from "modules/provisioners/ProvisionerGroup"; interface OrganizationProvisionersPageViewProps { - provisioners: ProvisionerDaemon[]; + provisioners: ProvisionersByGroup; } export const OrganizationProvisionersPageView: FC< @@ -32,8 +33,22 @@ export const OrganizationProvisionersPageView: FC< Provisioners - {provisioners.map((provisioner) => ( - + {provisioners.builtin.length > 0 && ( + + )} + {provisioners.psk.length > 0 && ( + + )} + {[...provisioners.keys].map(([keyId, provisioners]) => ( + ))} From 4558b44a6cb30cf5bca895c6e2c50c61babf1acd Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Fri, 6 Sep 2024 17:48:34 +0000 Subject: [PATCH 2/8] do the stuff --- site/src/modules/provisioners/Provisioner.tsx | 1 + .../modules/provisioners/ProvisionerGroup.tsx | 122 +++++++++++++----- .../OrganizationProvisionersPage.tsx | 17 ++- ...ganizationProvisionersPageView.stories.tsx | 30 ++--- .../OrganizationProvisionersPageView.tsx | 30 ++++- site/src/testHelpers/entities.ts | 19 +++ 6 files changed, 163 insertions(+), 56 deletions(-) diff --git a/site/src/modules/provisioners/Provisioner.tsx b/site/src/modules/provisioners/Provisioner.tsx index 454e85025c164..73afc2272232e 100644 --- a/site/src/modules/provisioners/Provisioner.tsx +++ b/site/src/modules/provisioners/Provisioner.tsx @@ -67,6 +67,7 @@ export const Provisioner: FC = ({ display: "flex", flexWrap: "wrap", gap: 12, + justifyContent: "right", }} > diff --git a/site/src/modules/provisioners/ProvisionerGroup.tsx b/site/src/modules/provisioners/ProvisionerGroup.tsx index 5287917d5d220..0e1e2bfc02f19 100644 --- a/site/src/modules/provisioners/ProvisionerGroup.tsx +++ b/site/src/modules/provisioners/ProvisionerGroup.tsx @@ -1,37 +1,37 @@ import { useTheme } from "@emotion/react"; +import ArrowDownward from "@mui/icons-material/ArrowDownward"; import Business from "@mui/icons-material/Business"; import Person from "@mui/icons-material/Person"; +import Button from "@mui/material/Button"; import Tooltip from "@mui/material/Tooltip"; -import type { HealthMessage, ProvisionerDaemon } from "api/typesGenerated"; +import type { BuildInfoResponse } from "api/typesGenerated"; import { Pill } from "components/Pill/Pill"; -import type { FC } from "react"; -import { createDayString } from "utils/createDayString"; +import type { ProvisionerDaemonWithWarnings } from "pages/ManagementSettingsPage/OrganizationProvisionersPageView"; +import { type FC, useState } from "react"; import { ProvisionerTag } from "./ProvisionerTag"; -import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata"; -import { buildInfo } from "api/queries/buildInfo"; -import { useQuery } from "react-query"; -import ArrowDownward from "@mui/icons-material/ArrowDownward"; +import { createDayString } from "utils/createDayString"; +import { DropdownArrow } from "components/DropdownArrow/DropdownArrow"; type ProvisionerGroupType = "builtin" | "psk" | "key"; interface ProvisionerGroupProps { + readonly buildInfo?: BuildInfoResponse; readonly keyName?: string; readonly type: ProvisionerGroupType; - readonly provisioners: ProvisionerDaemon[]; - readonly warnings?: readonly HealthMessage[]; + readonly provisioners: ProvisionerDaemonWithWarnings[]; } export const ProvisionerGroup: FC = ({ + buildInfo, keyName, type, provisioners, - warnings, }) => { - const { metadata } = useEmbeddedMetadata(); - const buildInfoQuery = useQuery(buildInfo(metadata["build-info"])); - const [provisioner] = provisioners; const theme = useTheme(); + + const [showDetails, setShowDetails] = useState(false); + const daemonScope = provisioner.tags.scope || "organization"; const iconScope = daemonScope === "organization" ? : ; @@ -40,19 +40,30 @@ export const ProvisionerGroup: FC = ({ (provisioner) => provisioner.version === provisionerVersion, ); const upToDate = - allProvisionersAreSameVersion && buildInfoQuery.data?.version; + allProvisionersAreSameVersion && buildInfo?.version === provisioner.version; const protocolUpToDate = allProvisionersAreSameVersion && - buildInfoQuery?.data?.provisioner_api_version === provisioner.api_version; + buildInfo?.provisioner_api_version === provisioner.api_version; const provisionerCount = provisioners.length === 1 ? "1 provisioner" : `${provisioners.length} provisioners`; + // Count how many total warnings there are in this group, and how many + // provisioners they come from. + let warningCount = 0; + let warningProvisionerCount = 0; + for (const provisioner of provisioners) { + const provisionerWarningCount = provisioner.warnings?.length ?? 0; + warningCount += provisionerWarningCount; + warningProvisionerCount += provisionerWarningCount > 0 ? 1 : 0; + } + const extraTags = Object.entries(provisioner.tags).filter( ([key]) => key !== "scope" && key !== "owner", ); - const isWarning = warnings && warnings.length > 0; + const isWarning = warningCount > 0; + return (
= ({ border: `1px solid ${theme.palette.divider}`, fontSize: 14, }, - isWarning && { borderColor: theme.roles.warning.outline }, + isWarning && { borderColor: theme.roles.warning.fill.outline }, ]} >
= ({ display: "flex", flexWrap: "wrap", gap: 12, + justifyContent: "right", }} > @@ -146,37 +158,89 @@ export const ProvisionerGroup: FC = ({
+ {showDetails && ( +
+ {provisioners.map((provisioner) => ( +
0 && { + borderColor: theme.roles.warning.fill.outline, + }, + ]} + > +
+

{provisioner.name}

+ + {type === "builtin" ? ( + Built-in + ) : ( + <> + {upToDate ? "Up to date" : provisioner.version} —{" "} + {provisioner.last_seen_at && ( + + Last seen {createDayString(provisioner.last_seen_at)} + + )} + + )} + +
+
+ ))} +
+ )} +
- {warnings && warnings.length > 0 ? ( -
- {warnings.map((warning) => ( - {warning.message} - ))} -
+ {warningCount > 0 ? ( + + {warningCount === 1 ? "1 warning" : `${warningCount} warnings`} from{" "} + {warningProvisionerCount === 1 + ? "1 provisioner" + : `${warningProvisionerCount} provisioners`} + ) : ( - No warnings + No warnings from {provisionerCount} )} - setShowDetails((it) => !it)} > - Show provisioner details{" "} - - + {showDetails ? "Hide" : "Show"} provisioner details{" "} + +
); diff --git a/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPage.tsx b/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPage.tsx index edf4c434c2db0..a521edbcc9cbc 100644 --- a/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPage.tsx +++ b/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPage.tsx @@ -1,3 +1,4 @@ +import { buildInfo } from "api/queries/buildInfo"; import { organizationsPermissions, provisionerDaemons, @@ -6,18 +7,16 @@ import type { Organization, ProvisionerDaemon } from "api/typesGenerated"; import { ErrorAlert } from "components/Alert/ErrorAlert"; import { EmptyState } from "components/EmptyState/EmptyState"; import { Loader } from "components/Loader/Loader"; +import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata"; import NotFoundPage from "pages/404Page/404Page"; import type { FC } from "react"; import { useQuery } from "react-query"; import { useParams } from "react-router-dom"; import { useOrganizationSettings } from "./ManagementSettingsLayout"; -import { OrganizationProvisionersPageView } from "./OrganizationProvisionersPageView"; - -export interface ProvisionersByGroup { - builtin: ProvisionerDaemon[]; - psk: ProvisionerDaemon[]; - keys: Map; -} +import { + OrganizationProvisionersPageView, + type ProvisionersByGroup, +} from "./OrganizationProvisionersPageView"; function groupProvisioners( provisioners: readonly ProvisionerDaemon[], @@ -52,6 +51,9 @@ const OrganizationProvisionersPage: FC = () => { }; const { organizations } = useOrganizationSettings(); + const { metadata } = useEmbeddedMetadata(); + const buildInfoQuery = useQuery(buildInfo(metadata["build-info"])); + const organization = organizations ? getOrganizationByName(organizations, organizationName) : undefined; @@ -89,6 +91,7 @@ const OrganizationProvisionersPage: FC = () => { return ( ); diff --git a/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPageView.stories.tsx b/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPageView.stories.tsx index 0b73dc05dd312..63d02c3468b37 100644 --- a/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPageView.stories.tsx +++ b/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPageView.stories.tsx @@ -1,10 +1,19 @@ import type { Meta, StoryObj } from "@storybook/react"; -import { MockProvisioner, MockUserProvisioner } from "testHelpers/entities"; +import { + MockBuildInfo, + MockProvisioner, + MockProvisioner2, + MockProvisionerWithTags, + MockUserProvisioner, +} from "testHelpers/entities"; import { OrganizationProvisionersPageView } from "./OrganizationProvisionersPageView"; const meta: Meta = { title: "pages/OrganizationProvisionersPage", component: OrganizationProvisionersPageView, + args: { + buildInfo: MockBuildInfo, + }, }; export default meta; @@ -13,20 +22,8 @@ type Story = StoryObj; export const Provisioners: Story = { args: { provisioners: { - builtin: [MockProvisioner, MockProvisioner], - psk: [ - MockProvisioner, - MockUserProvisioner, - { - ...MockProvisioner, - tags: { - ...MockProvisioner.tags, - 都市: "ユタ", - きっぷ: "yes", - ちいさい: "no", - }, - }, - ], + builtin: [MockProvisioner, MockProvisioner2], + psk: [MockProvisioner, MockUserProvisioner, MockProvisionerWithTags], keys: new Map([ [ "ケイラ", @@ -39,6 +36,9 @@ export const Provisioners: Story = { きっぷ: "yes", ちいさい: "no", }, + warnings: [ + { code: "EUNKNOWN", message: "私は日本語が話せません" }, + ], }, ], ], diff --git a/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPageView.tsx b/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPageView.tsx index 7ae26d77b8f11..22582170a7a26 100644 --- a/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPageView.tsx +++ b/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPageView.tsx @@ -1,20 +1,34 @@ import OpenInNewIcon from "@mui/icons-material/OpenInNew"; import Button from "@mui/material/Button"; +import type { + BuildInfoResponse, + HealthMessage, + ProvisionerDaemon, +} from "api/typesGenerated"; import { PageHeader, PageHeaderTitle } from "components/PageHeader/PageHeader"; import { Stack } from "components/Stack/Stack"; -import { Provisioner } from "modules/provisioners/Provisioner"; +import { ProvisionerGroup } from "modules/provisioners/ProvisionerGroup"; import type { FC } from "react"; import { docs } from "utils/docs"; -import type { ProvisionersByGroup } from "./OrganizationProvisionersPage"; -import { ProvisionerGroup } from "modules/provisioners/ProvisionerGroup"; + +export interface ProvisionerDaemonWithWarnings extends ProvisionerDaemon { + readonly warnings?: readonly HealthMessage[]; +} + +export interface ProvisionersByGroup { + builtin: ProvisionerDaemonWithWarnings[]; + psk: ProvisionerDaemonWithWarnings[]; + keys: Map; +} interface OrganizationProvisionersPageViewProps { + buildInfo?: BuildInfoResponse; provisioners: ProvisionersByGroup; } export const OrganizationProvisionersPageView: FC< OrganizationProvisionersPageViewProps -> = ({ provisioners }) => { +> = ({ buildInfo, provisioners }) => { return (
{provisioners.builtin.length > 0 && ( )} {provisioners.psk.length > 0 && ( - + )} {[...provisioners.keys].map(([keyId, provisioners]) => ( Date: Fri, 6 Sep 2024 17:50:36 +0000 Subject: [PATCH 3/8] =?UTF-8?q?=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- site/src/modules/provisioners/ProvisionerGroup.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/site/src/modules/provisioners/ProvisionerGroup.tsx b/site/src/modules/provisioners/ProvisionerGroup.tsx index 0e1e2bfc02f19..3910769784f81 100644 --- a/site/src/modules/provisioners/ProvisionerGroup.tsx +++ b/site/src/modules/provisioners/ProvisionerGroup.tsx @@ -1,16 +1,15 @@ import { useTheme } from "@emotion/react"; -import ArrowDownward from "@mui/icons-material/ArrowDownward"; import Business from "@mui/icons-material/Business"; import Person from "@mui/icons-material/Person"; import Button from "@mui/material/Button"; import Tooltip from "@mui/material/Tooltip"; import type { BuildInfoResponse } from "api/typesGenerated"; +import { DropdownArrow } from "components/DropdownArrow/DropdownArrow"; import { Pill } from "components/Pill/Pill"; import type { ProvisionerDaemonWithWarnings } from "pages/ManagementSettingsPage/OrganizationProvisionersPageView"; import { type FC, useState } from "react"; -import { ProvisionerTag } from "./ProvisionerTag"; import { createDayString } from "utils/createDayString"; -import { DropdownArrow } from "components/DropdownArrow/DropdownArrow"; +import { ProvisionerTag } from "./ProvisionerTag"; type ProvisionerGroupType = "builtin" | "psk" | "key"; From 82c63626ef170a8f4ad4194f7d05f50ce38fad2c Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Fri, 6 Sep 2024 18:41:32 +0000 Subject: [PATCH 4/8] feat: show more detailed provisioner version info --- .../modules/provisioners/ProvisionerGroup.tsx | 68 +++++++++++++++++-- ...ganizationProvisionersPageView.stories.tsx | 13 ++++ site/src/testHelpers/entities.ts | 2 +- 3 files changed, 78 insertions(+), 5 deletions(-) diff --git a/site/src/modules/provisioners/ProvisionerGroup.tsx b/site/src/modules/provisioners/ProvisionerGroup.tsx index 3910769784f81..8276fc7319eb5 100644 --- a/site/src/modules/provisioners/ProvisionerGroup.tsx +++ b/site/src/modules/provisioners/ProvisionerGroup.tsx @@ -1,4 +1,4 @@ -import { useTheme } from "@emotion/react"; +import { type Interpolation, type Theme, useTheme } from "@emotion/react"; import Business from "@mui/icons-material/Business"; import Person from "@mui/icons-material/Person"; import Button from "@mui/material/Button"; @@ -10,6 +10,13 @@ import type { ProvisionerDaemonWithWarnings } from "pages/ManagementSettingsPage import { type FC, useState } from "react"; import { createDayString } from "utils/createDayString"; import { ProvisionerTag } from "./ProvisionerTag"; +import { + Popover, + PopoverTrigger, + PopoverContent, +} from "components/Popover/Popover"; +import { Link } from "@mui/material"; +import { docs } from "utils/docs"; type ProvisionerGroupType = "builtin" | "psk" | "key"; @@ -183,14 +190,51 @@ export const ProvisionerGroup: FC = ({ }, ]} > -
+

{provisioner.name}

- + {type === "builtin" ? ( Built-in ) : ( <> - {upToDate ? "Up to date" : provisioner.version} —{" "} + + + + {buildInfo + ? provisioner.version === buildInfo.version + ? "Up to date" + : "Out of date" + : provisioner.version} + + + +

Release version

+

{provisioner.version}

+

Protocol version

+

{provisioner.api_version}

+ {provisioner.api_version !== + buildInfo?.provisioner_api_version && ( +

+ This provisioner is out of date. You may + experience issues when using a provisioner version + that doesn’t match your Coder deployment. Please + upgrade to a newer version.{" "} + Learn more… +

+ )} +
+
{" "} + —{" "} {provisioner.last_seen_at && ( Last seen {createDayString(provisioner.last_seen_at)} @@ -244,3 +288,19 @@ export const ProvisionerGroup: FC = ({
); }; + +const styles = { + title: (theme) => ({ + marginTop: 0, + marginBottom: 0, + color: theme.palette.text.primary, + fontSize: 14, + lineHeight: "150%", + fontWeight: 600, + }), + + text: (theme) => ({ + marginTop: 0, + marginBottom: 12, + }), +} satisfies Record>; diff --git a/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPageView.stories.tsx b/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPageView.stories.tsx index 63d02c3468b37..f0f2c2d9e2a47 100644 --- a/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPageView.stories.tsx +++ b/site/src/pages/ManagementSettingsPage/OrganizationProvisionersPageView.stories.tsx @@ -25,6 +25,19 @@ export const Provisioners: Story = { builtin: [MockProvisioner, MockProvisioner2], psk: [MockProvisioner, MockUserProvisioner, MockProvisionerWithTags], keys: new Map([ + [ + "ベン", + [ + MockProvisioner, + { + ...MockProvisioner2, + version: "2.0.0", + api_version: "1.0", + warnings: [{ code: "EUNKNOWN", message: "私は時代遅れです" }], + }, + ], + ], + ["じぇいダレン", [MockProvisioner, MockProvisioner2]], [ "ケイラ", [ diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index a2bb138559769..741646d01dffa 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -217,7 +217,7 @@ export const MockBuildInfo: TypesGen.BuildInfoResponse = { agent_api_version: "1.0", provisioner_api_version: "1.1", external_url: "file:///mock-url", - version: "v99.999.9999+c9cdf14", + version: "v2.99.99", dashboard_url: "https:///mock-url", workspace_proxy: false, upgrade_message: "My custom upgrade message", From 8bb49a73f82c9cf9532e15341b288df5170123ba Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Fri, 6 Sep 2024 18:46:43 +0000 Subject: [PATCH 5/8] =?UTF-8?q?=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- site/src/modules/provisioners/ProvisionerGroup.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/site/src/modules/provisioners/ProvisionerGroup.tsx b/site/src/modules/provisioners/ProvisionerGroup.tsx index 8276fc7319eb5..726525154dc47 100644 --- a/site/src/modules/provisioners/ProvisionerGroup.tsx +++ b/site/src/modules/provisioners/ProvisionerGroup.tsx @@ -2,21 +2,21 @@ import { type Interpolation, type Theme, useTheme } from "@emotion/react"; import Business from "@mui/icons-material/Business"; import Person from "@mui/icons-material/Person"; import Button from "@mui/material/Button"; +import Link from "@mui/material/Link"; import Tooltip from "@mui/material/Tooltip"; import type { BuildInfoResponse } from "api/typesGenerated"; import { DropdownArrow } from "components/DropdownArrow/DropdownArrow"; import { Pill } from "components/Pill/Pill"; -import type { ProvisionerDaemonWithWarnings } from "pages/ManagementSettingsPage/OrganizationProvisionersPageView"; -import { type FC, useState } from "react"; -import { createDayString } from "utils/createDayString"; -import { ProvisionerTag } from "./ProvisionerTag"; import { Popover, - PopoverTrigger, PopoverContent, + PopoverTrigger, } from "components/Popover/Popover"; -import { Link } from "@mui/material"; +import type { ProvisionerDaemonWithWarnings } from "pages/ManagementSettingsPage/OrganizationProvisionersPageView"; +import { type FC, useState } from "react"; +import { createDayString } from "utils/createDayString"; import { docs } from "utils/docs"; +import { ProvisionerTag } from "./ProvisionerTag"; type ProvisionerGroupType = "builtin" | "psk" | "key"; From 716b2573f5e75386016d489fcbfd396c49b9d6a3 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 17 Sep 2024 17:11:30 +0000 Subject: [PATCH 6/8] hook up to api --- .../modules/provisioners/ProvisionerGroup.tsx | 63 +++++-------------- .../OrganizationProvisionersPage.tsx | 20 ++++-- ...ganizationProvisionersPageView.stories.tsx | 1 + .../OrganizationProvisionersPageView.tsx | 17 ++--- site/src/testHelpers/entities.ts | 25 ++++++++ 5 files changed, 62 insertions(+), 64 deletions(-) diff --git a/site/src/modules/provisioners/ProvisionerGroup.tsx b/site/src/modules/provisioners/ProvisionerGroup.tsx index 3910769784f81..eb8bf154bd808 100644 --- a/site/src/modules/provisioners/ProvisionerGroup.tsx +++ b/site/src/modules/provisioners/ProvisionerGroup.tsx @@ -3,10 +3,9 @@ import Business from "@mui/icons-material/Business"; import Person from "@mui/icons-material/Person"; import Button from "@mui/material/Button"; import Tooltip from "@mui/material/Tooltip"; -import type { BuildInfoResponse } from "api/typesGenerated"; +import type { BuildInfoResponse, ProvisionerDaemon } from "api/typesGenerated"; import { DropdownArrow } from "components/DropdownArrow/DropdownArrow"; import { Pill } from "components/Pill/Pill"; -import type { ProvisionerDaemonWithWarnings } from "pages/ManagementSettingsPage/OrganizationProvisionersPageView"; import { type FC, useState } from "react"; import { createDayString } from "utils/createDayString"; import { ProvisionerTag } from "./ProvisionerTag"; @@ -17,7 +16,7 @@ interface ProvisionerGroupProps { readonly buildInfo?: BuildInfoResponse; readonly keyName?: string; readonly type: ProvisionerGroupType; - readonly provisioners: ProvisionerDaemonWithWarnings[]; + readonly provisioners: ProvisionerDaemon[]; } export const ProvisionerGroup: FC = ({ @@ -40,39 +39,22 @@ export const ProvisionerGroup: FC = ({ ); const upToDate = allProvisionersAreSameVersion && buildInfo?.version === provisioner.version; - const protocolUpToDate = - allProvisionersAreSameVersion && - buildInfo?.provisioner_api_version === provisioner.api_version; const provisionerCount = provisioners.length === 1 ? "1 provisioner" : `${provisioners.length} provisioners`; - // Count how many total warnings there are in this group, and how many - // provisioners they come from. - let warningCount = 0; - let warningProvisionerCount = 0; - for (const provisioner of provisioners) { - const provisionerWarningCount = provisioner.warnings?.length ?? 0; - warningCount += provisionerWarningCount; - warningProvisionerCount += provisionerWarningCount > 0 ? 1 : 0; - } - const extraTags = Object.entries(provisioner.tags).filter( ([key]) => key !== "scope" && key !== "owner", ); - const isWarning = warningCount > 0; return (
= ({ {provisioners.map((provisioner) => (
0 && { - borderColor: theme.roles.warning.fill.outline, - }, - ]} + css={{ + borderRadius: 8, + border: `1px solid ${theme.palette.divider}`, + fontSize: 14, + padding: "12px 18px", + width: 310, + }} >

{provisioner.name}

@@ -216,16 +192,7 @@ export const ProvisionerGroup: FC = ({ color: theme.palette.text.secondary, }} > - {warningCount > 0 ? ( - - {warningCount === 1 ? "1 warning" : `${warningCount} warnings`} from{" "} - {warningProvisionerCount === 1 - ? "1 provisioner" - : `${warningProvisionerCount} provisioners`} - - ) : ( - No warnings from {provisionerCount} - )} + No warnings from {provisionerCount}