Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3cc5274

Browse files
committed
feat: view provisioners from organization settings
1 parent 0ef8514 commit 3cc5274

File tree

11 files changed

+323
-176
lines changed

11 files changed

+323
-176
lines changed

site/src/api/queries/organizations.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,13 @@ export const organizationsPermissions = (
223223
},
224224
action: "create",
225225
},
226+
viewProvisioners: {
227+
object: {
228+
resource_type: "provisioner_daemon",
229+
organization_id: organizationId,
230+
},
231+
action: "read",
232+
},
226233
});
227234

228235
// The endpoint takes a flat array, so to avoid collisions prepend each
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { useTheme } from "@emotion/react";
2+
import Business from "@mui/icons-material/Business";
3+
import Person from "@mui/icons-material/Person";
4+
import SwapHoriz from "@mui/icons-material/SwapHoriz";
5+
import Tooltip from "@mui/material/Tooltip";
6+
import type { HealthMessage, ProvisionerDaemon } from "api/typesGenerated";
7+
import type { FC } from "react";
8+
import { createDayString } from "utils/createDayString";
9+
import { ProvisionerTag } from "./ProvisionerTag";
10+
11+
// TODO: Importing from a page in here sucks, but idk how to refactor this...
12+
// it's kind of a mess of a file...
13+
import { Pill } from "pages/HealthPage/Content";
14+
15+
interface ProvisionerProps {
16+
readonly provisioner: ProvisionerDaemon;
17+
readonly warnings?: readonly HealthMessage[];
18+
}
19+
20+
export const Provisioner: FC<ProvisionerProps> = ({
21+
provisioner,
22+
warnings,
23+
}) => {
24+
const theme = useTheme();
25+
const daemonScope = provisioner.tags.scope || "organization";
26+
const iconScope = daemonScope === "organization" ? <Business /> : <Person />;
27+
28+
const extraTags = Object.entries(provisioner.tags).filter(
29+
([key]) => key !== "scope" && key !== "owner",
30+
);
31+
const isWarning = warnings && warnings.length > 0;
32+
return (
33+
<div
34+
key={provisioner.name}
35+
css={{
36+
borderRadius: 8,
37+
border: `1px solid ${
38+
isWarning ? theme.palette.warning.light : theme.palette.divider
39+
}`,
40+
fontSize: 14,
41+
}}
42+
>
43+
<header
44+
css={{
45+
padding: 24,
46+
display: "flex",
47+
alignItems: "center",
48+
justifyContenxt: "space-between",
49+
gap: 24,
50+
}}
51+
>
52+
<div
53+
css={{
54+
display: "flex",
55+
alignItems: "center",
56+
gap: 24,
57+
objectFit: "fill",
58+
}}
59+
>
60+
<div css={{ lineHeight: "160%" }}>
61+
<h4 css={{ fontWeight: 500, margin: 0 }}>{provisioner.name}</h4>
62+
<span css={{ color: theme.palette.text.secondary }}>
63+
<code>{provisioner.version}</code>
64+
</span>
65+
</div>
66+
</div>
67+
<div
68+
css={{
69+
marginLeft: "auto",
70+
display: "flex",
71+
flexWrap: "wrap",
72+
gap: 12,
73+
}}
74+
>
75+
<Tooltip title="API Version">
76+
<Pill icon={<SwapHoriz />}>
77+
<code>{provisioner.api_version}</code>
78+
</Pill>
79+
</Tooltip>
80+
<Tooltip title="Scope">
81+
<Pill icon={iconScope}>
82+
<span
83+
css={{
84+
":first-letter": { textTransform: "uppercase" },
85+
}}
86+
>
87+
{daemonScope}
88+
</span>
89+
</Pill>
90+
</Tooltip>
91+
{extraTags.map(([key, value]) => (
92+
<ProvisionerTag key={key} tagName={key} tagValue={value} />
93+
))}
94+
</div>
95+
</header>
96+
97+
<div
98+
css={{
99+
borderTop: `1px solid ${theme.palette.divider}`,
100+
display: "flex",
101+
alignItems: "center",
102+
justifyContent: "space-between",
103+
padding: "8px 24px",
104+
fontSize: 12,
105+
color: theme.palette.text.secondary,
106+
}}
107+
>
108+
{warnings && warnings.length > 0 ? (
109+
<div css={{ display: "flex", flexDirection: "column" }}>
110+
{warnings.map((warning) => (
111+
<span key={warning.code}>{warning.message}</span>
112+
))}
113+
</div>
114+
) : (
115+
<span>No warnings</span>
116+
)}
117+
{provisioner.last_seen_at && (
118+
<span css={{ color: theme.roles.info.text }} data-chromatic="ignore">
119+
Last seen {createDayString(provisioner.last_seen_at)}
120+
</span>
121+
)}
122+
</div>
123+
</div>
124+
);
125+
};
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import CloseIcon from "@mui/icons-material/Close";
2+
import Sell from "@mui/icons-material/Sell";
3+
import IconButton from "@mui/material/IconButton";
4+
import type { FC } from "react";
5+
6+
// TODO: Importing from a page in here sucks, but idk how to refactor this...
7+
// it's kind of a mess of a file...
8+
import { BooleanPill, Pill } from "pages/HealthPage/Content";
9+
10+
const parseBool = (s: string): { valid: boolean; value: boolean } => {
11+
switch (s.toLowerCase()) {
12+
case "true":
13+
case "yes":
14+
case "1":
15+
return { valid: true, value: true };
16+
case "false":
17+
case "no":
18+
case "0":
19+
case "":
20+
return { valid: true, value: false };
21+
default:
22+
return { valid: false, value: false };
23+
}
24+
};
25+
26+
interface ProvisionerTagProps {
27+
tagName: string;
28+
tagValue: string;
29+
/** Only used in the TemplateVersionEditor */
30+
onDelete?: (tagName: string) => void;
31+
}
32+
33+
export const ProvisionerTag: FC<ProvisionerTagProps> = ({
34+
tagName,
35+
tagValue,
36+
onDelete,
37+
}) => {
38+
const { valid, value: boolValue } = parseBool(tagValue);
39+
const kv = `${tagName}: ${tagValue}`;
40+
const content = onDelete ? (
41+
<>
42+
{kv}
43+
<IconButton
44+
aria-label={`delete-${tagName}`}
45+
size="small"
46+
color="secondary"
47+
onClick={() => {
48+
onDelete(tagName);
49+
}}
50+
>
51+
<CloseIcon fontSize="inherit" css={{ width: 14, height: 14 }} />
52+
</IconButton>
53+
</>
54+
) : (
55+
kv
56+
);
57+
if (valid) {
58+
return <BooleanPill value={boolValue}>{content}</BooleanPill>;
59+
}
60+
return <Pill icon={<Sell />}>{content}</Pill>;
61+
};

site/src/pages/HealthPage/Content.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ export const BooleanPill: FC<BooleanPillProps> = ({
195195
...divProps
196196
}) => {
197197
const theme = useTheme();
198-
const color = value ? theme.palette.success.light : theme.palette.error.light;
198+
const color = value ? theme.roles.active.outline : theme.roles.danger.outline;
199199

200200
return (
201201
<Pill

0 commit comments

Comments
 (0)