Skip to content

Commit 1b68b0a

Browse files
Fix multiple things related to adding service accunts
* Form validation (for empty value and max. length), which is relflected on the UI * Removed success/error toasts * Display errors on the form itself * Refresh the service account list after one is created * Hide "Add service account" button if the user isn't the owner of the team * Fix mobile device responsivity * Submit form via enter key press
1 parent 41d8862 commit 1b68b0a

File tree

3 files changed

+96
-39
lines changed

3 files changed

+96
-39
lines changed

apps/cyberstorm-remix/app/settings/teams/team/tabs/ServiceAccounts/ServiceAccounts.css

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,24 @@
3737
font-weight: var(--font-weight-bold);
3838
line-height: var(--line-height-bold);
3939
}
40+
41+
.service-accounts__form {
42+
display: flex;
43+
flex-direction: column;
44+
gap: var(--gap-md);
45+
align-items: flex-start;
46+
align-self: stretch;
47+
}
48+
49+
.service-accounts__nickname-input {
50+
display: flex;
51+
flex-direction: row;
52+
gap: var(--gap-md);
53+
align-items: center;
54+
}
55+
56+
.service-accounts__nickname-input-max-length {
57+
color: var(--color-text-tertiary);
58+
font-size: var(--font-size-body-sm);
59+
}
4060
}

apps/cyberstorm-remix/app/settings/teams/team/tabs/ServiceAccounts/ServiceAccounts.tsx

Lines changed: 62 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ export default function ServiceAccounts() {
7676

7777
const toast = useToast();
7878

79+
const currentUserTeam = outletContext.currentUser?.teams_full?.find(
80+
(team) => team.name === teamName
81+
);
82+
const isOwner = currentUserTeam?.role === "owner";
83+
7984
async function serviceAccountRevalidate() {
8085
revalidator.revalidate();
8186
}
@@ -84,11 +89,6 @@ export default function ServiceAccounts() {
8489
const removeServiceAccountAction = ApiAction({
8590
endpoint: teamServiceAccountRemove,
8691
onSubmitSuccess: () => {
87-
toast.addToast({
88-
csVariant: "success",
89-
children: `Service account removed`,
90-
duration: 4000,
91-
});
9292
serviceAccountRevalidate();
9393
},
9494
onSubmitError: (error) => {
@@ -180,11 +180,13 @@ export default function ServiceAccounts() {
180180
<div className="settings-items__meta">
181181
<p className="settings-items__title">Service accounts</p>
182182
<p className="settings-items__description">Your loyal servants</p>
183-
<AddServiceAccountForm
184-
teamName={teamName}
185-
config={outletContext.requestConfig}
186-
serviceAccountRevalidate={serviceAccountRevalidate}
187-
/>
183+
{isOwner && (
184+
<AddServiceAccountForm
185+
teamName={teamName}
186+
config={outletContext.requestConfig}
187+
serviceAccountRevalidate={serviceAccountRevalidate}
188+
/>
189+
)}
188190
</div>
189191
<div className="settings-items__content">
190192
<NewTable
@@ -210,6 +212,7 @@ function AddServiceAccountForm(props: {
210212
const [addedServiceAccountToken, setAddedServiceAccountToken] = useState("");
211213
const [addedServiceAccountNickname, setAddedServiceAccountNickname] =
212214
useState("");
215+
const [error, setError] = useState<string | null>(null);
213216

214217
function onSuccess(
215218
result: Awaited<ReturnType<typeof teamAddServiceAccount>>
@@ -219,8 +222,6 @@ function AddServiceAccountForm(props: {
219222
setAddedServiceAccountNickname(result.nickname);
220223
}
221224

222-
const toast = useToast();
223-
224225
function formFieldUpdateAction(
225226
state: TeamServiceAccountAddRequestData,
226227
action: {
@@ -238,14 +239,17 @@ function AddServiceAccountForm(props: {
238239
nickname: "",
239240
});
240241

242+
const isValid =
243+
formInputs.nickname.trim().length > 0 && formInputs.nickname.length <= 32;
244+
241245
type SubmitorOutput = Awaited<ReturnType<typeof teamAddServiceAccount>>;
242246

243247
async function submitor(data: typeof formInputs): Promise<SubmitorOutput> {
244248
return await teamAddServiceAccount({
245249
config: props.config,
246250
params: { team_name: props.teamName },
247251
queryParams: {},
248-
data: { nickname: data.nickname },
252+
data: { nickname: data.nickname.trim() },
249253
});
250254
}
251255

@@ -265,18 +269,15 @@ function AddServiceAccountForm(props: {
265269
submitor,
266270
onSubmitSuccess: (result) => {
267271
onSuccess(result);
268-
toast.addToast({
269-
csVariant: "success",
270-
children: `Service account added`,
271-
duration: 4000,
272-
});
272+
setError(null);
273+
// Refresh the service accounts list to show the newly created account
274+
// TODO: When API returns identifier in response, we can append the new
275+
// service account to the list instead of refreshing from backend
276+
props.serviceAccountRevalidate?.();
273277
},
274278
onSubmitError: (error) => {
275-
toast.addToast({
276-
csVariant: "danger",
277-
children: `Error occurred: ${error.message || "Unknown error"}`,
278-
duration: 8000,
279-
});
279+
const message = `Error occurred: ${error.message || "Unknown error"}`;
280+
setError(message);
280281
},
281282
});
282283

@@ -286,6 +287,7 @@ function AddServiceAccountForm(props: {
286287
setServiceAccountAdded(false);
287288
setAddedServiceAccountToken("");
288289
setAddedServiceAccountNickname("");
290+
setError(null);
289291
updateFormFieldState({ field: "nickname", value: "" });
290292
}
291293
};
@@ -321,26 +323,47 @@ function AddServiceAccountForm(props: {
321323
</Modal.Body>
322324
) : (
323325
<Modal.Body>
324-
<div>
325-
Enter the nickname of the service account you wish to add to the
326-
team <span>{props.teamName}</span>
327-
</div>
328-
<div>
329-
<NewTextInput
330-
onChange={(e) => {
331-
updateFormFieldState({
332-
field: "nickname",
333-
value: e.target.value,
334-
});
335-
}}
336-
placeholder={"ExampleName"}
337-
/>
338-
</div>
326+
<form
327+
className="service-accounts__form"
328+
onSubmit={(e) => {
329+
e.preventDefault();
330+
if (isValid) {
331+
strongForm.submit();
332+
}
333+
}}
334+
>
335+
<div>
336+
Enter the nickname of the service account you wish to add to the
337+
team <span>{props.teamName}</span>
338+
</div>
339+
<div className="service-accounts__nickname-input">
340+
<NewTextInput
341+
onChange={(e) => {
342+
updateFormFieldState({
343+
field: "nickname",
344+
value: e.target.value,
345+
});
346+
}}
347+
placeholder={"ExampleName"}
348+
maxLength={32}
349+
/>
350+
<div className="service-accounts__nickname-input-max-length">
351+
Max. 32 characters
352+
</div>
353+
</div>
354+
{error && (
355+
<div style={{ width: "100%" }}>
356+
<NewAlert csVariant="danger">{error}</NewAlert>
357+
</div>
358+
)}
359+
</form>
339360
</Modal.Body>
340361
)}
341362
{serviceAccountAdded ? null : (
342363
<Modal.Footer>
343-
<NewButton onClick={strongForm.submit}>Add Service Account</NewButton>
364+
<NewButton onClick={strongForm.submit} disabled={!isValid}>
365+
Add Service Account
366+
</NewButton>
344367
</Modal.Footer>
345368
)}
346369
</Modal>

apps/cyberstorm-remix/app/styles/settingsItem.css

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,18 @@
5858
border-radius: var(--radius-md);
5959
background: var(--color-surface-default);
6060
}
61+
62+
@media (width <= 48rem) {
63+
.settings-items__item {
64+
flex-direction: column;
65+
}
66+
67+
.settings-items__meta {
68+
width: 100%;
69+
}
70+
71+
.settings-items__content {
72+
width: 100%;
73+
}
74+
}
6175
}

0 commit comments

Comments
 (0)