Skip to content

Commit

Permalink
Enteprise onboarding settings (#20508)
Browse files Browse the repository at this point in the history
* UI reorg

* generated stuffs

* Make it woooooooork

* Classier placeholder

* Remove unneeded fragment

* Introduce `enterprise_onboarding_enabled` flag

* move things properly

* add ipv6 localhost
  • Loading branch information
filiptronicek authored Jan 8, 2025
1 parent 663fcb9 commit d54bd04
Show file tree
Hide file tree
Showing 261 changed files with 2,866 additions and 1,371 deletions.
2 changes: 2 additions & 0 deletions components/dashboard/src/app/AppRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ const TeamUsageBasedBilling = React.lazy(() => import(/* webpackPrefetch: true *
const SSO = React.lazy(() => import(/* webpackPrefetch: true */ "../teams/SSO"));
const TeamGitIntegrations = React.lazy(() => import(/* webpackPrefetch: true */ "../teams/GitIntegrationsPage"));
const TeamPolicies = React.lazy(() => import(/* webpackPrefetch: true */ "../teams/TeamPolicies"));
const TeamOnboarding = React.lazy(() => import(/* webpackPrefetch: true */ "../teams/TeamOnboarding"));
const TeamNetworking = React.lazy(() => import(/* webpackPrefetch: true */ "../teams/TeamNetworking"));
const TeamAuthentication = React.lazy(() => import(/* webpackPrefetch: true */ "../teams/TeamAuthentication"));
const InstallGitHubApp = React.lazy(() => import(/* webpackPrefetch: true */ "../projects/InstallGitHubApp"));
Expand Down Expand Up @@ -198,6 +199,7 @@ export const AppRoutes = () => {
<Route exact path="/settings" component={TeamSettings} />
<Route exact path="/settings/git" component={TeamGitIntegrations} />
<Route exact path="/settings/policy" component={TeamPolicies} />
<Route exact path="/settings/onboarding" component={TeamOnboarding} />
<Route exact path="/settings/networking" component={TeamNetworking} />
<Route exact path="/settings/auth" component={TeamAuthentication} />
{/* TODO: migrate other org settings pages underneath /settings prefix so we can utilize nested routes */}
Expand Down
1 change: 1 addition & 0 deletions components/dashboard/src/data/featureflag-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const featureFlags = {
showBrowserExtensionPromotion: false,
enable_experimental_jbtb: false,
enabled_configuration_prebuild_full_clone: false,
enterprise_onboarding_enabled: false,
};

type FeatureFlags = typeof featureFlags;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type UpdateOrganizationSettingsArgs = Partial<
| "timeoutSettings"
| "roleRestrictions"
| "maxParallelRunningWorkspaces"
| "onboardingSettings"
>
>;

Expand All @@ -45,6 +46,7 @@ export const useUpdateOrgSettingsMutation = () => {
timeoutSettings,
roleRestrictions,
maxParallelRunningWorkspaces,
onboardingSettings,
}) => {
const settings = await organizationClient.updateOrganizationSettings({
organizationId: teamId,
Expand All @@ -60,6 +62,7 @@ export const useUpdateOrgSettingsMutation = () => {
roleRestrictions,
updateRoleRestrictions: !!roleRestrictions,
maxParallelRunningWorkspaces,
onboardingSettings,
});
return settings.settings!;
},
Expand Down
29 changes: 25 additions & 4 deletions components/dashboard/src/teams/OrgSettingsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { useCurrentOrg } from "../data/organizations/orgs-query";
import { useFeatureFlag } from "../data/featureflag-query";
import { Organization } from "@gitpod/public-api/lib/gitpod/v1/organization_pb";
import { useIsOwner } from "../data/organizations/members-query";
import { isGitpodIo } from "../utils";
import { useInstallationConfiguration } from "../data/installation/default-workspace-image-query";

export interface OrgSettingsPageProps {
children: React.ReactNode;
Expand All @@ -27,6 +27,9 @@ export function OrgSettingsPage({ children }: OrgSettingsPageProps) {
const orgBillingMode = useOrgBillingMode();
const oidcServiceEnabled = useFeatureFlag("oidcServiceEnabled");
const orgGitAuthProviders = useFeatureFlag("orgGitAuthProviders");
const isOnboardingEnabled = useFeatureFlag("enterprise_onboarding_enabled");
const { data: installationConfig } = useInstallationConfiguration();
const isDedicatedInstallation = !!installationConfig?.isDedicatedInstallation;

const menu = useMemo(
() =>
Expand All @@ -36,8 +39,18 @@ export function OrgSettingsPage({ children }: OrgSettingsPageProps) {
ssoEnabled: oidcServiceEnabled,
orgGitAuthProviders,
isOwner,
isDedicatedInstallation,
showOnboarding: isOnboardingEnabled && isDedicatedInstallation,
}),
[org.data, orgBillingMode.data, oidcServiceEnabled, orgGitAuthProviders, isOwner],
[
org.data,
orgBillingMode.data,
oidcServiceEnabled,
orgGitAuthProviders,
isOwner,
isDedicatedInstallation,
isOnboardingEnabled,
],
);

const title = "Organization Settings";
Expand Down Expand Up @@ -76,8 +89,10 @@ function getOrgSettingsMenu(params: {
ssoEnabled?: boolean;
orgGitAuthProviders: boolean;
isOwner?: boolean;
showOnboarding?: boolean;
isDedicatedInstallation?: boolean;
}) {
const { billingMode, ssoEnabled, orgGitAuthProviders, isOwner } = params;
const { billingMode, ssoEnabled, orgGitAuthProviders, isOwner, showOnboarding, isDedicatedInstallation } = params;
const result = [
{
title: "General",
Expand All @@ -88,7 +103,7 @@ function getOrgSettingsMenu(params: {
link: [`/settings/policy`],
},
];
if (isGitpodIo()) {
if (!isDedicatedInstallation) {
result.push(
{
title: "Networking",
Expand All @@ -100,6 +115,12 @@ function getOrgSettingsMenu(params: {
},
);
}
if (showOnboarding) {
result.push({
title: "Onboarding",
link: [`/settings/onboarding`],
});
}
if (isOwner && ssoEnabled) {
result.push({
title: "SSO",
Expand Down
1 change: 0 additions & 1 deletion components/dashboard/src/teams/TeamNetworking.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
*/

import { isGitpodIo } from "../utils";
import React from "react";
import { Heading2, Heading3, Subheading } from "../components/typography/headings";
import { OrgSettingsPage } from "./OrgSettingsPage";
import { ConfigurationSettingsField } from "../repositories/detail/ConfigurationSettingsField";
Expand Down
105 changes: 105 additions & 0 deletions components/dashboard/src/teams/TeamOnboarding.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/**
* Copyright (c) 2025 Gitpod GmbH. All rights reserved.
* Licensed under the GNU Affero General Public License (AGPL).
* See License.AGPL.txt in the project root for license information.
*/

import { OrganizationSettings } from "@gitpod/public-api/lib/gitpod/v1/organization_pb";
import { FormEvent, useCallback, useEffect, useState } from "react";
import { Heading2, Heading3, Subheading } from "../components/typography/headings";
import { useIsOwner } from "../data/organizations/members-query";
import { useOrgSettingsQuery } from "../data/organizations/org-settings-query";
import { useCurrentOrg } from "../data/organizations/orgs-query";
import { useUpdateOrgSettingsMutation } from "../data/organizations/update-org-settings-mutation";
import { OrgSettingsPage } from "./OrgSettingsPage";
import { ConfigurationSettingsField } from "../repositories/detail/ConfigurationSettingsField";
import { useDocumentTitle } from "../hooks/use-document-title";
import { useToast } from "../components/toasts/Toasts";
import type { PlainMessage } from "@bufbuild/protobuf";
import { InputField } from "../components/forms/InputField";
import { TextInput } from "../components/forms/TextInputField";
import { LoadingButton } from "@podkit/buttons/LoadingButton";

export default function TeamOnboardingPage() {
useDocumentTitle("Organization Settings - Onboarding");
const { toast } = useToast();
const org = useCurrentOrg().data;
const isOwner = useIsOwner();

const { data: settings } = useOrgSettingsQuery();
const updateTeamSettings = useUpdateOrgSettingsMutation();

const [internalLink, setInternalLink] = useState<string | undefined>(undefined);

const handleUpdateTeamSettings = useCallback(
async (newSettings: Partial<PlainMessage<OrganizationSettings>>, options?: { throwMutateError?: boolean }) => {
if (!org?.id) {
throw new Error("no organization selected");
}
if (!isOwner) {
throw new Error("no organization settings change permission");
}
try {
await updateTeamSettings.mutateAsync({
...settings,
...newSettings,
});
toast("Organization settings updated");
} catch (error) {
if (options?.throwMutateError) {
throw error;
}
toast(`Failed to update organization settings: ${error.message}`);
console.error(error);
}
},
[updateTeamSettings, org?.id, isOwner, settings, toast],
);

const handleUpdateInternalLink = useCallback(
async (e: FormEvent) => {
e.preventDefault();

await handleUpdateTeamSettings({ onboardingSettings: { internalLink } });
},
[handleUpdateTeamSettings, internalLink],
);

useEffect(() => {
if (settings) {
setInternalLink(settings.onboardingSettings?.internalLink);
}
}, [settings]);

return (
<OrgSettingsPage>
<div className="space-y-8">
<div>
<Heading2>Policies</Heading2>
<Subheading>Restrict workspace classes, editors and sharing across your organization.</Subheading>
</div>
<ConfigurationSettingsField>
<Heading3>Internal dashboard</Heading3>
<Subheading>
The link to your internal dashboard. This link will be shown to your organization members during
the onboarding process. You can disable showing a link by leaving this field empty.
</Subheading>
<form onSubmit={handleUpdateInternalLink}>
<InputField label="Internal dashboard link" error={undefined} className="mb-4">
<TextInput
value={internalLink}
type="url"
placeholder="https://en.wikipedia.org/wiki/Heisenbug"
onChange={setInternalLink}
disabled={updateTeamSettings.isLoading || !isOwner}
/>
</InputField>
<LoadingButton type="submit" loading={updateTeamSettings.isLoading} disabled={!isOwner}>
Save
</LoadingButton>
</form>
</ConfigurationSettingsField>
</div>
</OrgSettingsPage>
);
}
Loading

0 comments on commit d54bd04

Please sign in to comment.