Skip to content

Commit ed090b0

Browse files
[dashboard] Restructure Organization Settings (#19788)
* restrcuture Organization settings sub-menu items * [Organization settings] Bisect General settings (#19790) * Divide General settings & Policies pages * Add Enterprise callout * style improvements * Apply style suggestions from @corygrunk * button style changes * feat: Organization Networking settings page (#19794) * feat: Organization Authentication settings page (#19795) * fix typos * Rephrase copy texts * styles improvements * removed unused CSS * more styles fixes * fix border changes * fix submenu in dark mode
1 parent 828daec commit ed090b0

File tree

12 files changed

+595
-221
lines changed

12 files changed

+595
-221
lines changed

components/dashboard/src/app/AppRoutes.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ const TeamSettings = React.lazy(() => import(/* webpackPrefetch: true */ "../tea
6060
const TeamUsageBasedBilling = React.lazy(() => import(/* webpackPrefetch: true */ "../teams/TeamUsageBasedBilling"));
6161
const SSO = React.lazy(() => import(/* webpackPrefetch: true */ "../teams/SSO"));
6262
const TeamGitIntegrations = React.lazy(() => import(/* webpackPrefetch: true */ "../teams/GitIntegrationsPage"));
63+
const TeamPolicies = React.lazy(() => import(/* webpackPrefetch: true */ "../teams/TeamPolicies"));
64+
const TeamNetworking = React.lazy(() => import(/* webpackPrefetch: true */ "../teams/TeamNetworking"));
65+
const TeamAuthentication = React.lazy(() => import(/* webpackPrefetch: true */ "../teams/TeamAuthentication"));
6366
const Projects = React.lazy(() => import(/* webpackPrefetch: true */ "../projects/Projects"));
6467
const Project = React.lazy(() => import(/* webpackPrefetch: true */ "../projects/Project"));
6568
const ProjectSettings = React.lazy(() => import(/* webpackPrefetch: true */ "../projects/ProjectSettings"));
@@ -201,6 +204,9 @@ export const AppRoutes = () => {
201204
<Route exact path="/members" component={Members} />
202205
<Route exact path="/settings" component={TeamSettings} />
203206
<Route exact path="/settings/git" component={TeamGitIntegrations} />
207+
<Route exact path="/settings/policy" component={TeamPolicies} />
208+
<Route exact path="/settings/networking" component={TeamNetworking} />
209+
<Route exact path="/settings/auth" component={TeamAuthentication} />
204210
{/* TODO: migrate other org settings pages underneath /settings prefix so we can utilize nested routes */}
205211
<Route exact path="/billing" component={TeamUsageBasedBilling} />
206212
<Route exact path="/sso" component={SSO} />

components/dashboard/src/components/PageWithSubMenu.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export const SubmenuItem: FC<SubmenuItemProps> = ({ title, link, icon }) => {
7373
if (isCurrent) {
7474
classes += " bg-gray-300 text-gray-800 dark:bg-gray-800 dark:text-gray-50";
7575
} else {
76-
classes += " hover:bg-gray-100 dark:hover:bg-gray-800 dark:text-gray-400";
76+
classes += " hover:bg-gray-100 dark:hover:bg-gray-800 dark:text-pk-content-secondary";
7777
}
7878

7979
return (

components/dashboard/src/components/PillLabel.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export type PillType = "info" | "warn" | "success" | "neutral";
88

99
const PillClsMap: Record<PillType, string> = {
1010
info: "bg-blue-50 text-blue-500 dark:bg-blue-500 dark:text-blue-100",
11-
warn: "bg-orange-100 text-orange-700 dark:bg-orange-600 dark:text-orange-100",
11+
warn: "bg-kumquat-ripe text-gray-900",
1212
success: "bg-green-100 text-green-700 dark:bg-green-600 dark:text-green-100",
1313
neutral: "bg-gray-300 text-gray-800 dark:bg-gray-600 dark:text-gray-100",
1414
};
@@ -27,6 +27,6 @@ const PillClsMap: Record<PillType, string> = {
2727
*/
2828
export default function PillLabel(props: { children?: React.ReactNode; type?: PillType; className?: string }) {
2929
const type = props.type || "info";
30-
const style = `px-2 py-1 text-sm uppercase rounded-xl ${PillClsMap[type]} ${props.className}`;
30+
const style = `px-3 py-0.5 text-xs uppercase rounded-xl font-semibold ${PillClsMap[type]} ${props.className}`;
3131
return <span className={style}>{props.children}</span>;
3232
}

components/dashboard/src/components/podkit/buttons/LinkButton.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import React from "react";
1111
export interface LinkButtonProps extends ButtonProps {
1212
asChild?: false;
1313
href: string;
14+
isExternalUrl?: boolean;
1415
}
1516

1617
/**
@@ -20,7 +21,13 @@ export const LinkButton = React.forwardRef<HTMLButtonElement, LinkButtonProps>(
2021
({ asChild, children, href, ...props }, ref) => {
2122
return (
2223
<Button ref={ref} {...props} asChild>
23-
<Link to={href}>{children}</Link>
24+
{props.isExternalUrl ? (
25+
<a href={href} target="_blank" rel="noreferrer">
26+
{children}
27+
</a>
28+
) : (
29+
<Link to={href}>{children}</Link>
30+
)}
2431
</Button>
2532
);
2633
},

components/dashboard/src/components/podkit/typography/Headings.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export const Heading3: FC<HeadingProps> = ({ id, tracking, className, children,
6464
*/
6565
export const Subheading: FC<HeadingProps> = ({ id, tracking, className, children }) => {
6666
return (
67-
<p id={id} className={cn("text-base text-pk-content-tertiary", getTracking(tracking), className)}>
67+
<p id={id} className={cn("text-base text-pk-content-secondary", getTracking(tracking), className)}>
6868
{children}
6969
</p>
7070
);

components/dashboard/src/components/typography/headings.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,7 @@ export const Heading3: FC<HeadingProps> = ({ id, color, tracking, className, chi
5555
// Intended to be placed beneath a heading to provide more context
5656
export const Subheading: FC<HeadingProps> = ({ id, tracking, className, children }) => {
5757
return (
58-
<p
59-
id={id}
60-
className={classNames("text-base text-gray-500 dark:text-gray-500", getTracking(tracking), className)}
61-
>
58+
<p id={id} className={classNames("text-base text-pk-content-secondary", getTracking(tracking), className)}>
6259
{children}
6360
</p>
6461
);

components/dashboard/src/teams/OrgSettingsPage.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { useCurrentOrg } from "../data/organizations/orgs-query";
1515
import { useFeatureFlag } from "../data/featureflag-query";
1616
import { Organization } from "@gitpod/public-api/lib/gitpod/v1/organization_pb";
1717
import { useIsOwner } from "../data/organizations/members-query";
18+
import { isGitpodIo } from "../utils";
1819

1920
export interface OrgSettingsPageProps {
2021
children: React.ReactNode;
@@ -82,7 +83,23 @@ function getOrgSettingsMenu(params: {
8283
title: "General",
8384
link: [`/settings`],
8485
},
86+
{
87+
title: "Policies",
88+
link: [`/settings/policy`],
89+
},
8590
];
91+
if (isGitpodIo()) {
92+
result.push(
93+
{
94+
title: "Networking",
95+
link: [`/settings/networking`],
96+
},
97+
{
98+
title: "Authentication",
99+
link: [`/settings/auth`],
100+
},
101+
);
102+
}
86103
if (isOwner && ssoEnabled) {
87104
result.push({
88105
title: "SSO",
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/**
2+
* Copyright (c) 2021 Gitpod GmbH. All rights reserved.
3+
* Licensed under the GNU Affero General Public License (AGPL).
4+
* See License.AGPL.txt in the project root for license information.
5+
*/
6+
7+
import { isGitpodIo } from "../utils";
8+
import React from "react";
9+
import { Heading2, Heading3, Subheading } from "../components/typography/headings";
10+
import { OrgSettingsPage } from "./OrgSettingsPage";
11+
import { ConfigurationSettingsField } from "../repositories/detail/ConfigurationSettingsField";
12+
import { useDocumentTitle } from "../hooks/use-document-title";
13+
import { LinkButton } from "@podkit/buttons/LinkButton";
14+
import { CheckCircle2Icon } from "lucide-react";
15+
import { Redirect } from "react-router";
16+
import PillLabel from "../components/PillLabel";
17+
18+
export default function TeamPoliciesPage() {
19+
useDocumentTitle("Organization Settings - Authentication");
20+
21+
if (!isGitpodIo) {
22+
return <Redirect to="/settings" />;
23+
}
24+
25+
return (
26+
<>
27+
<OrgSettingsPage>
28+
<div className="space-y-8">
29+
<div>
30+
<Heading2 className="flex items-center gap-4">
31+
Authentication
32+
<PillLabel type="warn">Enterprise</PillLabel>
33+
</Heading2>
34+
<Subheading className="mt-1">
35+
Manage users through single sign-on and privately authenticate with source control and image
36+
registries.
37+
</Subheading>
38+
</div>
39+
40+
<SSOCard />
41+
<PrivateImageRegistryCard />
42+
<PrivateSourceControlAccess />
43+
</div>
44+
</OrgSettingsPage>
45+
</>
46+
);
47+
}
48+
49+
const SSOCard = () => {
50+
return (
51+
<ConfigurationSettingsField className="bg-pk-surface-secondary">
52+
<Heading3>Single sign-on (SSO)</Heading3>
53+
<Subheading className="mt-1">More control over workspace access for your organization</Subheading>
54+
55+
<div className="mt-8 flex flex-col space-y-2">
56+
<div className="flex flex-row gap-2 items-center text-pk-content-secondary">
57+
<CheckCircle2Icon size={20} className="text-pk-content-primary" />
58+
Includes support for Google, Okta, AWS Cognito and others
59+
</div>
60+
<div className="flex flex-row gap-2 items-center text-pk-content-secondary">
61+
<CheckCircle2Icon size={20} className="text-pk-content-primary" />
62+
Instantly revoke access and off-board users from Gitpod
63+
</div>
64+
</div>
65+
66+
<LinkButton
67+
href="https://www.gitpod.io/contact/enterprise-self-serve"
68+
isExternalUrl={true}
69+
className="mt-8"
70+
>
71+
Request Free Trial
72+
</LinkButton>
73+
</ConfigurationSettingsField>
74+
);
75+
};
76+
77+
const PrivateImageRegistryCard = () => {
78+
return (
79+
<ConfigurationSettingsField className="bg-pk-surface-secondary">
80+
<Heading3>Private container image registry</Heading3>
81+
<Subheading className="mt-1">Provide secure access to private image registries such as ECR</Subheading>
82+
83+
<LinkButton
84+
variant="secondary"
85+
className="mt-8 border border-pk-content-tertiary text-pk-content-primary bg-pk-surface-primary"
86+
href="https://www.gitpod.io/docs/enterprise/setup-gitpod/use-private-ecr-repos-for-workspace-images"
87+
isExternalUrl={true}
88+
>
89+
Documentation
90+
</LinkButton>
91+
</ConfigurationSettingsField>
92+
);
93+
};
94+
95+
const PrivateSourceControlAccess = () => {
96+
return (
97+
<ConfigurationSettingsField className="bg-pk-surface-secondary">
98+
<Heading3>Private source control access</Heading3>
99+
<Subheading className="mt-1">
100+
Connect to your private source control like GitHub, BitBucket and GitLab
101+
</Subheading>
102+
103+
<LinkButton
104+
variant="secondary"
105+
className="mt-8 border border-pk-content-tertiary text-pk-content-primary bg-pk-surface-primary"
106+
href="https://www.gitpod.io/docs/enterprise/setup-gitpod/scm-integration"
107+
isExternalUrl={true}
108+
>
109+
Documentation
110+
</LinkButton>
111+
</ConfigurationSettingsField>
112+
);
113+
};
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/**
2+
* Copyright (c) 2021 Gitpod GmbH. All rights reserved.
3+
* Licensed under the GNU Affero General Public License (AGPL).
4+
* See License.AGPL.txt in the project root for license information.
5+
*/
6+
7+
import { isGitpodIo } from "../utils";
8+
import React from "react";
9+
import { Heading2, Heading3, Subheading } from "../components/typography/headings";
10+
import { OrgSettingsPage } from "./OrgSettingsPage";
11+
import { ConfigurationSettingsField } from "../repositories/detail/ConfigurationSettingsField";
12+
import { useDocumentTitle } from "../hooks/use-document-title";
13+
import { LinkButton } from "@podkit/buttons/LinkButton";
14+
import { CheckCircle2Icon } from "lucide-react";
15+
import { Redirect } from "react-router";
16+
import PillLabel from "../components/PillLabel";
17+
18+
export default function TeamPoliciesPage() {
19+
useDocumentTitle("Organization Settings - Networking");
20+
21+
if (!isGitpodIo) {
22+
return <Redirect to="/settings" />;
23+
}
24+
25+
return (
26+
<>
27+
<OrgSettingsPage>
28+
<div className="space-y-8">
29+
<div>
30+
<Heading2 className="flex items-center gap-4">
31+
Networking
32+
<PillLabel type="warn">Enterprise</PillLabel>
33+
</Heading2>
34+
<Subheading className="mt-1">
35+
Self-host a single-tenant installation in your own cloud account.
36+
</Subheading>
37+
</div>
38+
39+
<SelfHostedCalloutCard />
40+
<DeployedRegionCard />
41+
<VPNCard />
42+
</div>
43+
</OrgSettingsPage>
44+
</>
45+
);
46+
}
47+
48+
const SelfHostedCalloutCard = () => {
49+
return (
50+
<ConfigurationSettingsField className="bg-pk-surface-secondary">
51+
<Heading3>Self-host in your cloud account</Heading3>
52+
<Subheading className="mt-1">
53+
Deploy the Gitpod infrastructure into your own cloud account and connect to your private network
54+
</Subheading>
55+
56+
<div className="mt-8 flex flex-col space-y-2">
57+
<div className="flex flex-row gap-2 items-center text-pk-content-secondary">
58+
<CheckCircle2Icon size={20} className="text-pk-content-primary" />
59+
Managed application feature release and backup process
60+
</div>
61+
<div className="flex flex-row gap-2 items-center text-pk-content-secondary">
62+
<CheckCircle2Icon size={20} className="text-pk-content-primary" />
63+
Managed security updates and patches
64+
</div>
65+
</div>
66+
67+
<LinkButton
68+
href="https://www.gitpod.io/contact/enterprise-self-serve"
69+
isExternalUrl={true}
70+
className="mt-8"
71+
>
72+
Request Free Trial
73+
</LinkButton>
74+
</ConfigurationSettingsField>
75+
);
76+
};
77+
78+
const DeployedRegionCard = () => {
79+
return (
80+
<ConfigurationSettingsField className="bg-pk-surface-secondary">
81+
<Heading3>Choose your deployed region</Heading3>
82+
<Subheading className="mt-1">
83+
Deploy Gitpod to any location, such as: United States, South America, Europe and Asia Pacific
84+
</Subheading>
85+
86+
<div className="mt-8 flex flex-col space-y-2">
87+
<div className="flex flex-row gap-2 items-center text-pk-content-secondary">
88+
<CheckCircle2Icon size={20} className="text-pk-content-primary" />
89+
Meet data residency compliance requirements
90+
</div>
91+
<div className="flex flex-row gap-2 items-center text-pk-content-secondary">
92+
<CheckCircle2Icon size={20} className="text-pk-content-primary" />
93+
Reduce latency and bring your code closer to your data
94+
</div>
95+
</div>
96+
97+
<LinkButton
98+
variant="secondary"
99+
className="mt-8 border border-pk-content-tertiary text-pk-content-primary bg-pk-surface-primary"
100+
href="https://www.gitpod.io/docs/enterprise/overview#aws-support-and-regions"
101+
isExternalUrl={true}
102+
>
103+
Documentation
104+
</LinkButton>
105+
</ConfigurationSettingsField>
106+
);
107+
};
108+
109+
const VPNCard = () => {
110+
return (
111+
<ConfigurationSettingsField className="bg-pk-surface-secondary">
112+
<Heading3>Virtual Private Network (VPN)</Heading3>
113+
<Subheading className="mt-1">
114+
Restrict access to your instance using your own private VPN network
115+
</Subheading>
116+
117+
<LinkButton
118+
variant="secondary"
119+
className="mt-8 border border-pk-content-tertiary text-pk-content-primary bg-pk-surface-primary"
120+
href="https://www.gitpod.io/docs/enterprise/getting-started/networking#private-networking-configuration-highly-restrictive"
121+
isExternalUrl={true}
122+
>
123+
Documentation
124+
</LinkButton>
125+
</ConfigurationSettingsField>
126+
);
127+
};

0 commit comments

Comments
 (0)