diff --git a/site/src/AppRouter.tsx b/site/src/AppRouter.tsx index 045f25aa3a9c3..20761336885e9 100644 --- a/site/src/AppRouter.tsx +++ b/site/src/AppRouter.tsx @@ -1,6 +1,7 @@ import { useSelector } from "@xstate/react" import { FeatureNames } from "api/types" import { RequirePermission } from "components/RequirePermission/RequirePermission" +import { OIDCSettingsPage } from "pages/DeploySettingsPage/OIDCSettingsPage" import { SetupPage } from "pages/SetupPage/SetupPage" import { TemplateSettingsPage } from "pages/TemplateSettingsPage/TemplateSettingsPage" import { FC, lazy, Suspense, useContext } from "react" @@ -152,6 +153,17 @@ export const AppRouter: FC = () => { /> + + + + + + } + /> + }> } /> } /> diff --git a/site/src/components/DeploySettingsLayout/DeploySettingsLayout.tsx b/site/src/components/DeploySettingsLayout/DeploySettingsLayout.tsx new file mode 100644 index 0000000000000..b17d3f3caabe4 --- /dev/null +++ b/site/src/components/DeploySettingsLayout/DeploySettingsLayout.tsx @@ -0,0 +1,246 @@ +import Button from "@material-ui/core/Button" +import { makeStyles } from "@material-ui/core/styles" +import LaunchOutlined from "@material-ui/icons/LaunchOutlined" +import VpnKeyOutlined from "@material-ui/icons/VpnKeyOutlined" +import { Margins } from "components/Margins/Margins" +import { Stack } from "components/Stack/Stack" +import React, { ElementType, PropsWithChildren, ReactNode } from "react" +import { NavLink } from "react-router-dom" +import { combineClasses } from "util/combineClasses" + +const Sidebar: React.FC = ({ children }) => { + const styles = useStyles() + return +} + +const SidebarNavItem: React.FC> = ({ + children, + href, + icon, +}) => { + const styles = useStyles() + return ( + + combineClasses([styles.sidebarNavItem, isActive ? styles.sidebarNavItemActive : undefined]) + } + > + + {icon} + {children} + + + ) +} + +const SidebarNavItemIcon: React.FC<{ icon: ElementType }> = ({ icon: Icon }) => { + const styles = useStyles() + return +} + +const SidebarCaption: React.FC = ({ children }) => { + const styles = useStyles() + return {children} +} + +export const SettingsHeader: React.FC<{ + title: string + description: string | JSX.Element + docsHref: string +}> = ({ title, description, docsHref }) => { + const styles = useStyles() + + return ( + +
+

{title}

+ {description} +
+ + +
+ ) +} + +export const SettingsBadges: React.FC<{ isEnterprise?: boolean; isEnabled?: boolean }> = ({ + isEnterprise, + isEnabled, +}) => { + const styles = useStyles() + + return ( + + {isEnabled ? ( + Enabled + ) : ( + Enabled + )} + {isEnterprise ? Enterprise : null} + + ) +} + +export const DeploySettingsLayout: React.FC = ({ children }) => { + const styles = useStyles() + + return ( + + + + } + > + Deployment + + Authentication + } + > + OAuth + + } + > + OpenID Connect + + + +
{children}
+
+
+ ) +} + +const useStyles = makeStyles((theme) => ({ + wrapper: { + padding: theme.spacing(6, 0), + }, + + sidebar: { + width: 245, + }, + + sidebarNavItem: { + color: "inherit", + display: "block", + fontSize: 16, + textDecoration: "none", + padding: theme.spacing(1.5, 1.5, 1.5, 3), + borderRadius: theme.shape.borderRadius / 2, + transition: "background-color 0.15s ease-in-out", + marginBottom: 1, + position: "relative", + + "&:hover": { + backgroundColor: theme.palette.action.hover, + }, + }, + + sidebarNavItemActive: { + backgroundColor: theme.palette.action.hover, + + "&:before": { + content: '""', + display: "block", + width: 3, + height: "100%", + position: "absolute", + left: 0, + top: 0, + backgroundColor: theme.palette.secondary.dark, + borderRadius: theme.shape.borderRadius, + }, + }, + + sidebarNavItemIcon: { + width: theme.spacing(2), + height: theme.spacing(2), + }, + + sidebarCaption: { + fontSize: 14, + color: theme.palette.text.secondary, + fontWeight: 600, + margin: theme.spacing(2, 0, 1.5, 3), + display: "block", + }, + + content: { + maxWidth: 800, + width: "100%", + }, + + headingGroup: { + maxWidth: 420, + }, + + title: { + fontSize: 36, + fontWeight: 700, + display: "flex", + alignItems: "center", + lineHeight: "initial", + margin: 0, + marginBottom: theme.spacing(1), + }, + + description: { + fontSize: 14, + color: theme.palette.text.secondary, + lineHeight: "160%", + }, + + badges: { + marginTop: theme.spacing(3), + marginBottom: theme.spacing(3), + }, + + enterpriseBadge: { + fontSize: 10, + fontWeight: 600, + textTransform: "uppercase", + letterSpacing: "0.085em", + backgroundColor: theme.palette.info.dark, + padding: theme.spacing(0.5, 2), + borderRadius: 9999, + border: `1px solid ${theme.palette.info.light}`, + lineHeight: "160%", + }, + + enabledBadge: { + fontSize: 10, + fontWeight: 600, + textTransform: "uppercase", + letterSpacing: "0.085em", + backgroundColor: theme.palette.success.dark, + padding: theme.spacing(0.5, 2), + borderRadius: 9999, + border: `1px solid ${theme.palette.success.light}`, + lineHeight: "160%", + }, + + disabledBadge: { + fontSize: 10, + fontWeight: 600, + textTransform: "uppercase", + letterSpacing: "0.085em", + backgroundColor: theme.palette.background.paper, + padding: theme.spacing(0.5, 2), + borderRadius: 9999, + border: `1px solid ${theme.palette.divider}`, + lineHeight: "160%", + }, +})) diff --git a/site/src/components/NavbarView/NavbarView.tsx b/site/src/components/NavbarView/NavbarView.tsx index 6eeeda814e1eb..2f41d15b3c327 100644 --- a/site/src/components/NavbarView/NavbarView.tsx +++ b/site/src/components/NavbarView/NavbarView.tsx @@ -181,7 +181,7 @@ const useStyles = makeStyles((theme) => ({ fontSize: 16, padding: `${theme.spacing(1.5)}px ${theme.spacing(2)}px`, textDecoration: "none", - transition: "background-color 0.3s ease", + transition: "background-color 0.15s ease-in-out", "&:hover": { backgroundColor: theme.palette.action.hover, diff --git a/site/src/pages/DeploySettingsPage/OIDCSettingsPage.tsx b/site/src/pages/DeploySettingsPage/OIDCSettingsPage.tsx new file mode 100644 index 0000000000000..68afa281c5e45 --- /dev/null +++ b/site/src/pages/DeploySettingsPage/OIDCSettingsPage.tsx @@ -0,0 +1,88 @@ +import { makeStyles } from "@material-ui/core/styles" +import Table from "@material-ui/core/Table" +import TableBody from "@material-ui/core/TableBody" +import TableCell from "@material-ui/core/TableCell" +import TableContainer from "@material-ui/core/TableContainer" +import TableHead from "@material-ui/core/TableHead" +import TableRow from "@material-ui/core/TableRow" +import { useActor } from "@xstate/react" +import { + DeploySettingsLayout, + SettingsBadges, + SettingsHeader, +} from "components/DeploySettingsLayout/DeploySettingsLayout" +import React, { useContext, useEffect } from "react" +import { MONOSPACE_FONT_FAMILY } from "theme/constants" +import { XServiceContext } from "../../xServices/StateContext" + +export const OIDCSettingsPage: React.FC = () => { + const styles = useStyles() + const xServices = useContext(XServiceContext) + const [authState, authSend] = useActor(xServices.authXService) + useEffect(() => { + authSend({ type: "GET_AUTH_METHODS" }) + }, [authSend]) + + return ( + + + + + + + + Option + Value + + + + + + Address + + The address to serve the API and dashboard. + + + + + 127.0.0.1:3000 + + + + + Access URL + + Specifies the external URL to access Coder. + + + + + https://www.dev.coder.com + + + +
+
+
+ ) +} + +const useStyles = makeStyles((theme) => ({ + optionName: { + display: "block", + }, + optionDescription: { + display: "block", + color: theme.palette.text.secondary, + fontSize: 14, + marginTop: theme.spacing(0.5), + }, + optionValue: { + fontSize: 14, + fontFamily: MONOSPACE_FONT_FAMILY, + }, +})) diff --git a/site/src/xServices/auth/authXService.ts b/site/src/xServices/auth/authXService.ts index 1fa3040e9fb96..84d050f3e3498 100644 --- a/site/src/xServices/auth/authXService.ts +++ b/site/src/xServices/auth/authXService.ts @@ -86,6 +86,7 @@ export type AuthEvent = | { type: "REGENERATE_SSH_KEY" } | { type: "CONFIRM_REGENERATE_SSH_KEY" } | { type: "CANCEL_REGENERATE_SSH_KEY" } + | { type: "GET_AUTH_METHODS" } export const authMachine = /** @xstate-layout N4IgpgJg5mDOIC5QEMCuAXAFgZXc9YAdLAJZQB2kA8hgMTYCSA4gHID6DLioADgPal0JPuW4gAHogBsATimEAzDIAcAFgUB2VTJ26ANCACeiAIwaADKsLKFtu-dsBfRwbRZc+IqQolyUBuS0ECJEvgBufADWXmTkAWL8gsKiSBKICubKhKpSyhoArAbGCGaqGoRSlVXVlfnOrhg4eATEsb7+gWAATl18XYQ8ADb4AGZ9ALatFPGpiSRCImKSCLLySmqa2ro6RaYFAEzWDscK9SBuTZ6EMOhCfgCqsN1BIYThUUQ3ALJgCQLzySW6Uy2VyBV2JXyUhMFRqcLqLnOjQ8LRudygj2e3V6-SGowm1zA6B+fySi1Sy32MnyhHyyksYK2ug0EJM1IURxO9jOFxRnyJ6IACt1xiRYKQRLAXpQ3uQItFCABjTBgRWRYVdUXi5LwWb-BYpUDLZRScygvKFIyIGQaQ4FHnI5r827tDVaiXkKXYvoDYboMaapUqtVusUe3W8fWAimIE1mnIW1l0rImOE1BENdxOwkuvw-LB8CBS4Iy94K75EzCFiMgOYGoEIZRUwgyVT5BQmfaW4omGxZGxcuwOrNXNHtfNVou0b24v0ByYVgtF0kA8lG2PN1vtzvd0zszmD06I3nZ7yUCABAa9EYkQahCB32j3QUAEQAggAVACibEFACUqAAMQYAAZL8V3rGMSjbGQKihLsISkOlaWHS4WjPSBLx4a9byIVAeAgfBXRwx8S1COUPkIE8rgwi9yCvPgbzvQh8MIoUSLABB3kVIiRAAbXMABdCDo3XaD8lgpCpAQq0EA0eTUL5KZzywjiWIIoi-EFDjpx6H08X9AlqPQ2JMPo7DGNw9S2OIyy7y4iieINAThL1MlDTScTJPg3dGxkfZFNPUy6OIWBMDeB8wFoJgvw-NhsGwAAJNgAGkvwATREtdPM7VQrE0XyTF7coB0PQKaOCy9xXCsc-ASxKUrAQxpXI+UiGMmIKDM0KaoFdp6sawwHIiJzkhcrKPOWIqkOscFZNTfYOXtY9HQqrqQuqnN0QGprdJxX18UDDrlO6zbaqgHahu43jyHGtzV0m0x9jyxQ5p7ExzBhUrB3Kkz1qqsLCEGPhkAgSAIsfP8vxilgvz-T8f3q1KMomht9g+8ocnMGQCtZDRZAPLkMyREc-pU+jNuB0HwcVEQb01S6-zAGBKC6TxaAAYTfFgOa-EC2ChmG4YR+KkuRzL7sgsT0bMQhzCUcxpMK20vsPBRieO2iAfCqmwYgJU6ZIBmksGpmWe6dmOaoFhgL-L4Behr9Yfh79ReStKJcjdy0Yx0Fsdx+b23MX7OvJnqgZBvXCC6ZmwFZzSLpN3ayNlNqqNWsnTsB3XwZj822e2pOrscm67q9h6GzMBQrDy7cZJ7DR1ZQlbSdDrOdcj3PY-jwuGt2mcDsMo6M7bjbs87-W87ji3e8G4a+FG-ihNRqCq5rtsO3r0wpG0ZvMzQ0eqtVVAunmQwIai5931d7Avw5+4-wYD9PdrKNsqmsoOQyBM3sQZ6pBDidDax9T7oHPqxBO2AQFnxaqnSimtKoU2gWA6ykDkHFxGqXZektRI5U-ooBkiZZIKFyHvEmB8gFH0VCfM+qDtroL2vpOcRkR6UKQdQ0B4CNL0I4Wfeei9brYPLlLPBjcCE-18m2KwGtWFa0CIwVgbAqD3A-CvaWWgYSEN-iUDIZpUxpiqBoQBZ52g0HQLAssoczFqM8k2WCW5N6+X2OYeShMTgyNbspUxdAB4GXnMpaxOD35-w0XLCRrJ0ZWH0QYqQRiW4UOVKqSI7RAJG1gOgTEXQLEUQVMdRJaoUlpIyU8Lo-CsGuWEbg5YmhCD7B3nU-YA5lA6HVhCZxYjoRshyDvJQ+NVCAIAO7IABH4QCfQPwqlSV0dJmT6DMHYJwGx1Sm7Yx0I3SwziTBOLqWaLQdIpC2HyKoLZ5gESInIIWOAYgEHrUCZU4JJRsY0iIT2akZoPEUJMX4GY9zHoIDbIcdGLzt4qFhDEpCgDzqZKWYgVQ+xWSyCyOC2okK+paRFGGHUML-mmnNNorZbIwUxI+Upc6E5qzYq2LUuQwKSitnymrI8+8lJyIYkxe8zELlfj0l0bFVcaRlCklvRspzjGILZVZEgkVCAzj5Y3AV+MfIQjyEy8hLLxUWXZRfOVRVsiKqVhCRuhxtgaBVY3A5MgxX-XMmpCB7E7K-CCX8oq+RnnaIKJa+J6rrUSrvHykwHZZq+X2bScwYbzUSTUBCr1QUfWbSlX6p1lctlusKp2Q4JLY1hzOmixOfdii-MrlIiotKPpyCtdm8e1N9YJsdYWqCmyshyH9vigoVhkWxIre3CO1aDbkHpuMRm3cZ51tft7BtqhzAZoVga+aGhexuOOJmtalaO69qnj3fqRc+UKHpIQIq87S25GXZnMea69Y7qhPuswxVCptnKNsOQ7Z2xUhPYfCmYV-WBtLVOjkJqzUkP6TGldp10EX0IFynlcroRywsI4iEu6ArAdPVQmhKDa0yqg0m1e+NNFwZ3BCNswdkPvuIGB2tcqakuPlgR4h2MWzMgAxartwDeEoLtf1dB-rXVBoQyQljqHOFfq+q2v9jHG7mqUKqm55N-UuN4-NT6DGdD5C0Gp-Ypq31eL8HcsdFcG0yA+rB5QtHijOKOYoNWgD8nJNGUU6F2GxK9LlvSTIcLGn5C2ZUNpLj5CxIUFSD6XmuyDOGeiMZXQJlgCmTMkp2LGm1OUFCHQORGkyEVqoZQbTNly2-poaERy6kh0pYl5LrZpLNIy1l2Sm5dAkPRs4wz+NlDOGcEAA */ @@ -330,6 +331,36 @@ export const authMachine = }, }, }, + methods: { + initial: "idle", + states: { + idle: { + on: { + GET_AUTH_METHODS: { + target: "gettingMethods" + }, + }, + }, + gettingMethods: { + entry: "clearGetMethodsError", + invoke: { + src: "getMethods", + onDone: [ + { + actions: ["assignMethods", "clearGetMethodsError"], + target: "idle", + }, + ], + onError: [ + { + actions: "assignGetMethodsError", + target: "idle", + }, + ], + }, + }, + }, + }, security: { initial: "idle", states: {