Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/dashboard/src/onboarding/StepOrgInfo.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 { FC, useCallback, useMemo, useState } from "react";
8
import { CheckboxInputField, CheckboxListField } from "../components/forms/CheckboxInputField";
9
import { SelectInputField } from "../components/forms/SelectInputField";
10
import { TextInputField } from "../components/forms/TextInputField";
11
import { useUpdateCurrentUserMutation } from "../data/current-user/update-mutation";
12
import { useOnBlurError } from "../hooks/use-onblur-error";
13
import { EXPLORE_REASON_WORK, getExplorationReasons } from "./exploration-reasons";
14
import { getJobRoleOptions, JOB_ROLE_OTHER } from "./job-roles";
15
import { OnboardingStep } from "./OnboardingStep";
16
import { getSignupGoalsOptions, SIGNUP_GOALS_OTHER } from "./signup-goals";
17
import { getCompanySizeOptions } from "./company-size";
18
import { useCurrentOrg } from "../data/organizations/orgs-query";
19
import { useCreateOrgMutation } from "../data/organizations/create-org-mutation";
20
import { User } from "@gitpod/public-api/lib/gitpod/v1/user_pb";
21
22
type Props = {
23
user: User;
24
onComplete(user: User): void;
25
};
26
export const StepOrgInfo: FC<Props> = ({ user, onComplete }) => {
27
const updateUser = useUpdateCurrentUserMutation();
28
const jobRoleOptions = useMemo(getJobRoleOptions, []);
29
const explorationReasonsOptions = useMemo(getExplorationReasons, []);
30
const signupGoalsOptions = useMemo(getSignupGoalsOptions, []);
31
const companySizeOptions = useMemo(getCompanySizeOptions, []);
32
const currentOrg = useCurrentOrg();
33
const createOrg = useCreateOrgMutation();
34
35
const [jobRole, setJobRole] = useState(user.profile?.jobRole ?? "");
36
const [jobRoleOther, setJobRoleOther] = useState(user.profile?.jobRoleOther ?? "");
37
const [explorationReasons, setExplorationReasons] = useState<string[]>(user.profile?.explorationReasons ?? []);
38
const [signupGoals, setSignupGoals] = useState<string[]>(user.profile?.signupGoals ?? []);
39
const [signupGoalsOther, setSignupGoalsOther] = useState(user.profile?.signupGoalsOther ?? "");
40
const [companySize, setCompanySize] = useState(user.profile?.companySize ?? "");
41
42
const addSignupGoal = useCallback(
43
(goal: string) => {
44
if (!signupGoals.includes(goal)) {
45
setSignupGoals([...signupGoals, goal]);
46
}
47
},
48
[signupGoals],
49
);
50
51
const removeSignupGoal = useCallback(
52
(goal: string) => {
53
if (signupGoals.includes(goal)) {
54
const idx = signupGoals.indexOf(goal);
55
const newGoals = [...signupGoals];
56
newGoals.splice(idx, 1);
57
setSignupGoals(newGoals);
58
}
59
// clear out freeform other if removing option
60
if (goal === SIGNUP_GOALS_OTHER) {
61
setSignupGoalsOther("");
62
}
63
},
64
[signupGoals],
65
);
66
67
const addExplorationReason = useCallback(
68
(reason: string) => {
69
if (!explorationReasons.includes(reason)) {
70
setExplorationReasons([...explorationReasons, reason]);
71
}
72
},
73
[explorationReasons],
74
);
75
76
const removeExplorationReason = useCallback(
77
(reason: string) => {
78
if (explorationReasons.includes(reason)) {
79
const idx = explorationReasons.indexOf(reason);
80
const newReasons = [...explorationReasons];
81
newReasons.splice(idx, 1);
82
setExplorationReasons(newReasons);
83
}
84
if (reason === EXPLORE_REASON_WORK) {
85
setCompanySize("");
86
}
87
},
88
[explorationReasons],
89
);
90
91
const handleSubmit = useCallback(async () => {
92
// create an org if the user is not a member of one already
93
if (!currentOrg.data) {
94
let orgName = "My Org";
95
function orgify(name: string) {
96
let result = name.split(" ")[0];
97
if (result.endsWith("s")) {
98
return result + `' Org`;
99
}
100
return result + `'s Org`;
101
}
102
if (user.name) {
103
orgName = orgify(user.name);
104
}
105
await createOrg.mutateAsync({
106
name: orgName,
107
});
108
}
109
110
// Filter out any values not present in options
111
const filteredReasons = explorationReasons.filter((val) =>
112
explorationReasonsOptions.find((o) => o.value === val),
113
);
114
const filteredGoals = signupGoals.filter((val) => signupGoalsOptions.find((o) => o.value === val));
115
116
const updates = {
117
additionalData: {
118
profile: {
119
jobRole,
120
jobRoleOther,
121
explorationReasons: filteredReasons,
122
signupGoals: filteredGoals,
123
signupGoalsOther,
124
companySize,
125
},
126
},
127
};
128
129
try {
130
const updatedUser = await updateUser.mutateAsync(updates);
131
onComplete(updatedUser);
132
} catch (e) {
133
console.error(e);
134
}
135
}, [
136
companySize,
137
createOrg,
138
currentOrg.data,
139
explorationReasons,
140
explorationReasonsOptions,
141
jobRole,
142
jobRoleOther,
143
onComplete,
144
signupGoals,
145
signupGoalsOptions,
146
signupGoalsOther,
147
updateUser,
148
user.name,
149
]);
150
151
const jobRoleError = useOnBlurError("Please select one", !!jobRole);
152
const companySizeError = useOnBlurError(
153
"Please select one",
154
!explorationReasons.includes(EXPLORE_REASON_WORK) || !!companySize,
155
);
156
const isValid =
157
jobRoleError.isValid && companySizeError.isValid && signupGoals.length > 0 && explorationReasons.length > 0;
158
159
return (
160
<OnboardingStep
161
title="Tell us more about you"
162
error={updateUser.isError ? "There was a problem saving your answers" : ""}
163
isValid={isValid}
164
isSaving={updateUser.isLoading}
165
onSubmit={handleSubmit}
166
>
167
<SelectInputField
168
value={jobRole}
169
label="What best describes your role"
170
onChange={(val) => {
171
if (val !== "other") {
172
setJobRoleOther("");
173
}
174
setJobRole(val);
175
}}
176
error={jobRoleError.message}
177
onBlur={jobRoleError.onBlur}
178
>
179
{jobRoleOptions.map((o) => (
180
<option key={o.value} value={o.value}>
181
{o.label}
182
</option>
183
))}
184
</SelectInputField>
185
186
{jobRole === JOB_ROLE_OTHER && (
187
<TextInputField value={jobRoleOther} onChange={setJobRoleOther} placeholder="Please share (optional)" />
188
)}
189
190
<CheckboxListField label="You're exploring Gitpod for">
191
{explorationReasonsOptions.map((o) => (
192
<CheckboxInputField
193
key={o.value}
194
value={o.value}
195
label={o.label}
196
checked={explorationReasons.includes(o.value)}
197
topMargin={false}
198
onChange={(checked) => {
199
if (checked) {
200
addExplorationReason(o.value);
201
} else {
202
removeExplorationReason(o.value);
203
}
204
}}
205
/>
206
))}
207
</CheckboxListField>
208
209
{explorationReasons.includes(EXPLORE_REASON_WORK) && (
210
<SelectInputField
211
value={companySize}
212
label="How large is your engineering organization?"
213
onChange={setCompanySize}
214
error={companySizeError.message}
215
onBlur={companySizeError.onBlur}
216
>
217
{companySizeOptions.map((o) => (
218
<option key={o.value} value={o.value}>
219
{o.label}
220
</option>
221
))}
222
</SelectInputField>
223
)}
224
225
<CheckboxListField label="I'm hoping to use Gitpod for" sublabel="Select all that apply">
226
{signupGoalsOptions.map((o) => (
227
<CheckboxInputField
228
key={o.value}
229
value={o.value}
230
label={o.label}
231
checked={signupGoals.includes(o.value)}
232
topMargin={false}
233
onChange={(checked) => {
234
if (checked) {
235
addSignupGoal(o.value);
236
} else {
237
removeSignupGoal(o.value);
238
}
239
}}
240
/>
241
))}
242
</CheckboxListField>
243
244
{signupGoals.includes(SIGNUP_GOALS_OTHER) && (
245
<TextInputField
246
value={signupGoalsOther}
247
placeholder="Please share (optional)"
248
onChange={setSignupGoalsOther}
249
/>
250
)}
251
</OnboardingStep>
252
);
253
};
254
255