Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/dashboard/src/onboarding/StepUserInfo.tsx
2500 views
1
/**
2
* Copyright (c) 2023 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 { LinkedInProfile } from "@gitpod/gitpod-protocol";
8
import { FC, useCallback, useState } from "react";
9
import { TextInputField } from "../components/forms/TextInputField";
10
import { useUpdateCurrentUserMutation } from "../data/current-user/update-mutation";
11
import { useOnBlurError } from "../hooks/use-onblur-error";
12
import { OnboardingStep } from "./OnboardingStep";
13
import { LinkedInBanner } from "./LinkedInBanner";
14
import { User } from "@gitpod/public-api/lib/gitpod/v1/user_pb";
15
import { getPrimaryEmail } from "@gitpod/public-api-common/lib/user-utils";
16
17
type Props = {
18
user: User;
19
onComplete(user: User): void;
20
};
21
export const StepUserInfo: FC<Props> = ({ user, onComplete }) => {
22
const updateUser = useUpdateCurrentUserMutation();
23
// attempt to split provided name for default input values
24
const { first, last } = getInitialNameParts(user);
25
26
const [firstName, setFirstName] = useState(first);
27
const [lastName, setLastName] = useState(last);
28
// Email purposefully not pre-filled
29
const [emailAddress, setEmailAddress] = useState("");
30
31
const handleSubmit = useCallback(async () => {
32
const updates = {
33
// we only split these out currently for form collection, but combine in the db
34
fullName: `${firstName} ${lastName}`,
35
additionalData: {
36
profile: {
37
// If still no email provided, default to "primary" email
38
emailAddress: emailAddress || getPrimaryEmail(user),
39
lastUpdatedDetailsNudge: new Date().toISOString(),
40
},
41
},
42
};
43
44
try {
45
const updatedUser = await updateUser.mutateAsync(updates);
46
onComplete(updatedUser);
47
} catch (e) {
48
console.error(e);
49
}
50
}, [emailAddress, firstName, lastName, onComplete, updateUser, user]);
51
52
const onLinkedInSuccess = useCallback(
53
async (profile: LinkedInProfile) => {
54
if (!firstName && profile.firstName) {
55
setFirstName(profile.firstName);
56
}
57
if (!lastName && profile.lastName) {
58
setLastName(profile.lastName);
59
}
60
if (!emailAddress && profile.emailAddress) {
61
setEmailAddress(profile.emailAddress);
62
}
63
handleSubmit();
64
},
65
[emailAddress, firstName, handleSubmit, lastName],
66
);
67
68
const firstNameError = useOnBlurError("Please enter a value", !!firstName);
69
const lastNameError = useOnBlurError("Please enter a value", !!lastName);
70
71
const isValid = [firstNameError, lastNameError].every((e) => e.isValid);
72
73
return (
74
<OnboardingStep
75
title="Welcome to Gitpod"
76
subtitle="You are one step away from shipping software faster."
77
error={updateUser.isError ? "There was a problem updating your profile" : undefined}
78
isValid={isValid}
79
isSaving={updateUser.isLoading}
80
onSubmit={handleSubmit}
81
submitButtonText={"Continue with 10 hours per month"}
82
submitButtonType={"secondary"}
83
>
84
{user.avatarUrl && (
85
<div className="my-4 flex justify-center">
86
<img className="rounded-full w-24 h-24" src={user.avatarUrl} alt={user.name} />
87
</div>
88
)}
89
90
<div className="flex justify-between space-x-2 w-full">
91
<TextInputField
92
containerClassName="w-1/2"
93
value={firstName}
94
label="First name"
95
error={firstNameError.message}
96
onBlur={firstNameError.onBlur}
97
onChange={setFirstName}
98
required
99
/>
100
101
<TextInputField
102
containerClassName="w-1/2"
103
value={lastName}
104
label="Last name"
105
error={lastNameError.message}
106
onBlur={lastNameError.onBlur}
107
onChange={setLastName}
108
required
109
/>
110
</div>
111
112
<LinkedInBanner onSuccess={onLinkedInSuccess} />
113
</OnboardingStep>
114
);
115
};
116
117
// Intentionally not using User.getName() here to avoid relying on identity.authName (likely not user's real name)
118
const getInitialNameParts = (user: User) => {
119
const name = user.name || "";
120
let first = name;
121
let last = "";
122
123
const parts = name.split(" ");
124
if (parts.length > 1) {
125
first = parts.shift() || "";
126
last = parts.join(" ");
127
}
128
129
return { first, last };
130
};
131
132