Path: blob/main/components/dashboard/src/teams/TeamOnboarding.tsx
2501 views
/**1* Copyright (c) 2025 Gitpod GmbH. All rights reserved.2* Licensed under the GNU Affero General Public License (AGPL).3* See License.AGPL.txt in the project root for license information.4*/56import { FormEvent, useCallback, useEffect, useState } from "react";7import { Heading2, Heading3, Subheading } from "../components/typography/headings";8import { useIsOwner } from "../data/organizations/members-query";9import { useOrgSettingsQuery } from "../data/organizations/org-settings-query";10import { useCurrentOrg } from "../data/organizations/orgs-query";11import {12UpdateOrganizationSettingsArgs,13useUpdateOrgSettingsMutation,14} from "../data/organizations/update-org-settings-mutation";15import { OrgSettingsPage } from "./OrgSettingsPage";16import { ConfigurationSettingsField } from "../repositories/detail/ConfigurationSettingsField";17import { useDocumentTitle } from "../hooks/use-document-title";18import { useToast } from "../components/toasts/Toasts";19import { InputField } from "../components/forms/InputField";20import { TextInput } from "../components/forms/TextInputField";21import { LoadingButton } from "@podkit/buttons/LoadingButton";22import { Link } from "react-router-dom";23import { useOrgSuggestedRepos } from "../data/organizations/suggested-repositories-query";24import { RepositoryListItem } from "../repositories/list/RepoListItem";25import { LoadingState } from "@podkit/loading/LoadingState";26import { Table, TableHeader, TableRow, TableHead, TableBody } from "@podkit/tables/Table";27import { WelcomeMessageConfigurationField } from "./onboarding/WelcomeMessageConfigurationField";2829export type UpdateTeamSettingsOptions = {30throwMutateError?: boolean;31};3233export default function TeamOnboardingPage() {34useDocumentTitle("Organization Settings - Onboarding");35const { toast } = useToast();36const { data: org } = useCurrentOrg();37const isOwner = useIsOwner();3839const { data: settings } = useOrgSettingsQuery();40const updateTeamSettings = useUpdateOrgSettingsMutation();4142const { data: suggestedRepos, isLoading: isLoadingSuggestedRepos } = useOrgSuggestedRepos();4344const [internalLink, setInternalLink] = useState<string | undefined>(undefined);4546const handleUpdateTeamSettings = useCallback(47async (newSettings: UpdateOrganizationSettingsArgs, options?: UpdateTeamSettingsOptions) => {48if (!org?.id) {49throw new Error("no organization selected");50}51if (!isOwner) {52throw new Error("no organization settings change permission");53}54try {55await updateTeamSettings.mutateAsync(newSettings);56toast("Organization settings updated");57} catch (error) {58if (options?.throwMutateError) {59throw error;60}61toast(`Failed to update organization settings: ${error.message}`);62console.error(error);63}64},65[updateTeamSettings, org?.id, isOwner, toast],66);6768const handleUpdateInternalLink = useCallback(69async (e: FormEvent) => {70e.preventDefault();7172await handleUpdateTeamSettings({73onboardingSettings: {74internalLink,75},76});77},78[handleUpdateTeamSettings, internalLink],79);8081useEffect(() => {82if (settings) {83setInternalLink(settings.onboardingSettings?.internalLink);84}85}, [settings]);8687return (88<OrgSettingsPage>89<div className="space-y-8">90<div>91<Heading2>Onboarding</Heading2>92<Subheading>Customize the onboarding experience for your organization members.</Subheading>93</div>94<ConfigurationSettingsField>95<Heading3>Internal landing page</Heading3>96<Subheading>97The link to your internal landing page. This link will be shown to your organization members98during the onboarding process. You can disable showing a link by leaving this field empty.99</Subheading>100<form onSubmit={handleUpdateInternalLink}>101<InputField label="Internal landing page link" error={undefined} className="mb-4">102<TextInput103value={internalLink}104type="url"105placeholder="https://en.wikipedia.org/wiki/Heisenbug"106onChange={setInternalLink}107disabled={updateTeamSettings.isLoading || !isOwner}108/>109</InputField>110<LoadingButton type="submit" loading={updateTeamSettings.isLoading} disabled={!isOwner}>111Save112</LoadingButton>113</form>114</ConfigurationSettingsField>115116<ConfigurationSettingsField>117<Heading3>Suggested repositories</Heading3>118<Subheading>119A list of repositories suggested to new organization members. You can toggle a repository's120visibility in the onboarding process by visiting the{" "}121<Link to="/repositories" className="gp-link">122Repository settings123</Link>{" "}124page and toggling the "Mark this repository as Suggested" setting under the details of the125repository.126</Subheading>127{(suggestedRepos ?? []).length > 0 && (128<Table className="mt-4">129<TableHeader>130<TableRow>131<TableHead className="w-52">Name</TableHead>132<TableHead hideOnSmallScreen>Repository</TableHead>133<TableHead className="w-32" hideOnSmallScreen>134Created135</TableHead>136<TableHead className="w-24" hideOnSmallScreen>137Prebuilds138</TableHead>139{/* Action column, loading status in header */}140<TableHead className="w-24 text-right">141{isLoadingSuggestedRepos && (142<div className="flex flex-right justify-end items-center">143<LoadingState delay={false} size={16} />144</div>145)}146</TableHead>147</TableRow>148</TableHeader>149<TableBody>150{suggestedRepos?.map((repo) => (151<RepositoryListItem152key={repo.configurationId}153configuration={repo.configuration}154isSuggested={true}155/>156))}157</TableBody>158</Table>159)}160</ConfigurationSettingsField>161162<WelcomeMessageConfigurationField handleUpdateTeamSettings={handleUpdateTeamSettings} />163</div>164</OrgSettingsPage>165);166}167168169