Skip to content

Commit 0a7a030

Browse files
committed
Begin form work
1 parent a83a5e6 commit 0a7a030

File tree

7 files changed

+185
-4
lines changed

7 files changed

+185
-4
lines changed

site/src/AppRouter.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ const GeneralSettingsPage = lazy(
7676
const SecuritySettingsPage = lazy(
7777
() => import("./pages/DeploySettingsPage/SecuritySettingsPage"),
7878
)
79+
const ServiceBannerSettingsPage = lazy(
80+
() => import("./pages/DeploySettingsPage/ServiceBannerSettingsPage"),
81+
)
7982
const UserAuthSettingsPage = lazy(
8083
() => import("./pages/DeploySettingsPage/UserAuthSettingsPage"),
8184
)
@@ -303,6 +306,20 @@ export const AppRouter: FC = () => {
303306
</AuthAndFrame>
304307
}
305308
/>
309+
<Route
310+
path="service-banner"
311+
element={
312+
<AuthAndFrame>
313+
<RequirePermission
314+
isFeatureVisible={Boolean(permissions?.viewDeploymentConfig)}
315+
>
316+
<DeploySettingsLayout>
317+
<ServiceBannerSettingsPage />
318+
</DeploySettingsLayout>
319+
</RequirePermission>
320+
</AuthAndFrame>
321+
}
322+
/>
306323
<Route
307324
path="network"
308325
element={

site/src/api/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ export enum FeatureNames {
2222
BrowserOnly = "browser_only",
2323
SCIM = "scim",
2424
TemplateRBAC = "template_rbac",
25+
HighAvailability = "high_availability",
2526
}

site/src/api/typesGenerated.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,7 @@ export interface ServerSentEvent {
606606

607607
// From codersdk/servicebanner.go
608608
export interface ServiceBanner {
609-
readonly enabled?: boolean
609+
readonly enabled: boolean
610610
readonly message?: string
611611
readonly background_color?: string
612612
}

site/src/components/DeploySettingsLayout/Sidebar.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import LaunchOutlined from "@material-ui/icons/LaunchOutlined"
33
import LockRounded from "@material-ui/icons/LockRounded"
44
import Globe from "@material-ui/icons/Public"
55
import VpnKeyOutlined from "@material-ui/icons/VpnKeyOutlined"
6+
import Info from "@material-ui/icons/Info"
67
import { useSelector } from "@xstate/react"
78
import { GitIcon } from "components/Icons/GitIcon"
89
import { Stack } from "components/Stack/Stack"
@@ -87,6 +88,12 @@ export const Sidebar: React.FC = () => {
8788
>
8889
Security
8990
</SidebarNavItem>
91+
<SidebarNavItem
92+
href="../service-banner"
93+
icon={<SidebarNavItemIcon icon={Info} />}
94+
>
95+
Service Banner
96+
</SidebarNavItem>
9097
</nav>
9198
)
9299
}

site/src/components/ServiceBanner/ServiceBannerView.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export const ServiceBannerView: React.FC<ServiceBannerViewProps> = ({
1414
const styles = useStyles()
1515
const markdownElementsAllowed = [
1616
"text",
17+
"a",
1718
"strong",
1819
"delete",
1920
"emphasis",
@@ -27,6 +28,7 @@ export const ServiceBannerView: React.FC<ServiceBannerViewProps> = ({
2728
<div className={styles.centerContent}>
2829
<ReactMarkdown
2930
allowedElements={markdownElementsAllowed}
31+
linkTarget="_blank"
3032
unwrapDisallowed
3133
>
3234
{message}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import { makeStyles } from "@material-ui/core"
2+
import TextField from "@material-ui/core/TextField"
3+
import { useActor } from "@xstate/react"
4+
import { FeatureNames } from "api/types"
5+
import {
6+
Badges,
7+
DisabledBadge,
8+
EnabledBadge,
9+
EnterpriseBadge,
10+
} from "components/DeploySettingsLayout/Badges"
11+
import { useDeploySettings } from "components/DeploySettingsLayout/DeploySettingsLayout"
12+
import { Header } from "components/DeploySettingsLayout/Header"
13+
import OptionsTable from "components/DeploySettingsLayout/OptionsTable"
14+
import { LoadingButton } from "components/LoadingButton/LoadingButton"
15+
import { Section } from "components/Section/Section"
16+
import { Stack } from "components/Stack/Stack"
17+
import { FormikContextType, useFormik } from "formik"
18+
import React, { useContext, useEffect } from "react"
19+
import { Helmet } from "react-helmet-async"
20+
import { pageTitle } from "util/page"
21+
import * as Yup from "yup"
22+
import { XServiceContext } from "xServices/StateContext"
23+
import { ServiceBanner } from "api/typesGenerated"
24+
import { getFormHelpers } from "util/formUtils"
25+
26+
export const Language = {
27+
messageLabel: "Message",
28+
backgroundColorLabel: "Background Color",
29+
updateBanner: "Update",
30+
}
31+
32+
export interface ServiceBannerFormValues {
33+
message?: string
34+
backgroundColor?: string
35+
enabled?: boolean
36+
}
37+
38+
// TODO:
39+
const validationSchema = Yup.object({})
40+
41+
const ServiceBannerSettingsPage: React.FC = () => {
42+
const xServices = useContext(XServiceContext)
43+
const [serviceBannerState, serviceBannerSend] = useActor(
44+
xServices.serviceBannerXService,
45+
)
46+
47+
const [entitlementsState] = useActor(xServices.entitlementsXService)
48+
49+
const serviceBanner = serviceBannerState.context.serviceBanner
50+
51+
/** Gets license data on app mount because LicenseBanner is mounted in App */
52+
useEffect(() => {
53+
serviceBannerSend("GET_BANNER")
54+
}, [serviceBannerSend])
55+
56+
const styles = useStyles()
57+
58+
const isEntitled =
59+
entitlementsState.context.entitlements.features[
60+
FeatureNames.HighAvailability
61+
].entitlement !== "not_entitled"
62+
63+
const onSubmit = (values: ServiceBannerFormValues) => {
64+
const newBanner = {
65+
...serviceBanner,
66+
}
67+
if (values.message !== undefined) {
68+
newBanner.message = values.message
69+
}
70+
if (values.enabled !== undefined) {
71+
newBanner.enabled = values.enabled
72+
}
73+
if (values.backgroundColor !== undefined) {
74+
newBanner.background_color = values.backgroundColor
75+
}
76+
77+
serviceBannerSend({
78+
type: "SET_PREVIEW",
79+
serviceBanner: newBanner,
80+
})
81+
}
82+
83+
const initialValues: ServiceBannerFormValues = {
84+
message: serviceBanner.message,
85+
enabled: serviceBanner.enabled,
86+
backgroundColor: serviceBanner.background_color,
87+
}
88+
89+
const form: FormikContextType<ServiceBannerFormValues> =
90+
useFormik<ServiceBannerFormValues>({
91+
initialValues,
92+
validationSchema,
93+
onSubmit,
94+
})
95+
const getFieldHelpers = getFormHelpers<ServiceBannerFormValues>(form)
96+
97+
return (
98+
<>
99+
<Helmet>
100+
<title>{pageTitle("Service Banner Settings")}</title>
101+
</Helmet>
102+
103+
<Header
104+
title="Service Banner"
105+
description="Configure the Service Banner"
106+
docsHref="https://coder.com/docs/coder-oss/latest/admin/high-availability#service-banners"
107+
/>
108+
<Badges>
109+
{isEntitled ? <EnabledBadge /> : <DisabledBadge />}
110+
<EnterpriseBadge />
111+
</Badges>
112+
113+
<form className={styles.form} onSubmit={form.handleSubmit}>
114+
<Stack>
115+
<TextField
116+
fullWidth
117+
{...getFieldHelpers("message")}
118+
label={Language.messageLabel}
119+
variant="outlined"
120+
/>
121+
122+
<LoadingButton
123+
loading={false}
124+
// aria-disabled={!editable}
125+
// disabled={!editable}
126+
type="submit"
127+
variant="contained"
128+
>
129+
{Language.updateBanner}
130+
</LoadingButton>
131+
</Stack>
132+
</form>
133+
</>
134+
)
135+
}
136+
const useStyles = makeStyles(() => ({
137+
form: {
138+
maxWidth: "500px",
139+
},
140+
}))
141+
142+
export default ServiceBannerSettingsPage

site/src/xServices/serviceBanner/serviceBannerXService.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ export type ServiceBannerContext = {
1111
getServiceBannerError?: Error | unknown
1212
}
1313

14-
export type ServiceBannerEvent = {
15-
type: "GET_BANNER"
16-
}
14+
export type ServiceBannerEvent =
15+
| {
16+
type: "GET_BANNER"
17+
}
18+
| { type: "SET_PREVIEW"; serviceBanner: ServiceBanner }
1719

1820
const emptyBanner = {
1921
enabled: false,
@@ -41,6 +43,7 @@ export const serviceBannerMachine = createMachine(
4143
idle: {
4244
on: {
4345
GET_BANNER: "gettingBanner",
46+
SET_PREVIEW: "settingPreview",
4447
},
4548
},
4649
gettingBanner: {
@@ -58,10 +61,19 @@ export const serviceBannerMachine = createMachine(
5861
},
5962
},
6063
},
64+
settingPreview: {
65+
entry: ["clearGetBannerError", "assignPreviewBanner"],
66+
always: {
67+
target: "idle",
68+
},
69+
},
6170
},
6271
},
6372
{
6473
actions: {
74+
assignPreviewBanner: assign({
75+
serviceBanner: (_, event) => event.serviceBanner,
76+
}),
6577
assignBanner: assign({
6678
serviceBanner: (_, event) => event.data as ServiceBanner,
6779
}),

0 commit comments

Comments
 (0)