Skip to content

Commit 74d8f28

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 74d8f28

File tree

3 files changed

+94
-39
lines changed

3 files changed

+94
-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: 60 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,18 @@ function AddServiceAccountForm(props: {
238239
nickname: "",
239240
});
240241

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

243248
async function submitor(data: typeof formInputs): Promise<SubmitorOutput> {
244249
return await teamAddServiceAccount({
245250
config: props.config,
246251
params: { team_name: props.teamName },
247252
queryParams: {},
248-
data: { nickname: data.nickname },
253+
data: { nickname: data.nickname.trim() },
249254
});
250255
}
251256

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

@@ -286,6 +288,7 @@ function AddServiceAccountForm(props: {
286288
setServiceAccountAdded(false);
287289
setAddedServiceAccountToken("");
288290
setAddedServiceAccountNickname("");
291+
setError(null);
289292
updateFormFieldState({ field: "nickname", value: "" });
290293
}
291294
};
@@ -321,26 +324,44 @@ function AddServiceAccountForm(props: {
321324
</Modal.Body>
322325
) : (
323326
<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>
327+
<form
328+
className="service-accounts__form"
329+
onSubmit={(e) => {
330+
e.preventDefault();
331+
if (isValid) {
332+
strongForm.submit();
333+
}
334+
}}
335+
>
336+
<div>
337+
Enter the nickname of the service account you wish to add to the
338+
team <span>{props.teamName}</span>
339+
</div>
340+
<div className="service-accounts__nickname-input">
341+
<NewTextInput
342+
value={formInputs.nickname}
343+
onChange={(e) => {
344+
updateFormFieldState({
345+
field: "nickname",
346+
value: e.target.value,
347+
});
348+
}}
349+
placeholder={"ExampleName"}
350+
maxLength={32}
351+
/>
352+
<div className="service-accounts__nickname-input-max-length">
353+
Max. 32 characters
354+
</div>
355+
</div>
356+
{error && <NewAlert csVariant="danger">{error}</NewAlert>}
357+
</form>
339358
</Modal.Body>
340359
)}
341360
{serviceAccountAdded ? null : (
342361
<Modal.Footer>
343-
<NewButton onClick={strongForm.submit}>Add Service Account</NewButton>
362+
<NewButton onClick={strongForm.submit} disabled={!isValid}>
363+
Add Service Account
364+
</NewButton>
344365
</Modal.Footer>
345366
)}
346367
</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)