Skip to content

Commit 86d570d

Browse files
Fix multiple things related to adding service accunts (#1598)
* 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 9f5f1e6 commit 86d570d

File tree

3 files changed

+79
-38
lines changed

3 files changed

+79
-38
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@
3838
line-height: var(--line-height-bold);
3939
}
4040

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+
4149
.service-accounts__nickname-input {
4250
display: flex;
4351
flex-direction: row;

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

Lines changed: 57 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {
88
NewTextInput,
99
Heading,
1010
CodeBox,
11-
useToast,
1211
} from "@thunderstore/cyberstorm";
1312
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
1413
import { faPlus } from "@fortawesome/free-solid-svg-icons";
@@ -73,6 +72,11 @@ export default function ServiceAccounts() {
7372

7473
const revalidator = useRevalidator();
7574

75+
const currentUserTeam = outletContext.currentUser?.teams_full?.find(
76+
(team) => team.name === teamName
77+
);
78+
const isOwner = currentUserTeam?.role === "owner";
79+
7680
async function serviceAccountRevalidate() {
7781
revalidator.revalidate();
7882
}
@@ -116,11 +120,13 @@ export default function ServiceAccounts() {
116120
<div className="settings-items__meta">
117121
<p className="settings-items__title">Service accounts</p>
118122
<p className="settings-items__description">Your loyal servants</p>
119-
<AddServiceAccountForm
120-
teamName={teamName}
121-
config={outletContext.requestConfig}
122-
serviceAccountRevalidate={serviceAccountRevalidate}
123-
/>
123+
{isOwner && (
124+
<AddServiceAccountForm
125+
teamName={teamName}
126+
config={outletContext.requestConfig}
127+
serviceAccountRevalidate={serviceAccountRevalidate}
128+
/>
129+
)}
124130
</div>
125131
<div className="settings-items__content">
126132
<NewTable
@@ -146,6 +152,7 @@ function AddServiceAccountForm(props: {
146152
const [addedServiceAccountToken, setAddedServiceAccountToken] = useState("");
147153
const [addedServiceAccountNickname, setAddedServiceAccountNickname] =
148154
useState("");
155+
const [error, setError] = useState<string | null>(null);
149156

150157
function onSuccess(
151158
result: Awaited<ReturnType<typeof teamAddServiceAccount>>
@@ -155,8 +162,6 @@ function AddServiceAccountForm(props: {
155162
setAddedServiceAccountNickname(result.nickname);
156163
}
157164

158-
const toast = useToast();
159-
160165
function formFieldUpdateAction(
161166
state: TeamServiceAccountAddRequestData,
162167
action: {
@@ -174,14 +179,16 @@ function AddServiceAccountForm(props: {
174179
nickname: "",
175180
});
176181

182+
const isValid = formInputs.nickname.trim().length > 0;
183+
177184
type SubmitorOutput = Awaited<ReturnType<typeof teamAddServiceAccount>>;
178185

179186
async function submitor(data: typeof formInputs): Promise<SubmitorOutput> {
180187
return await teamAddServiceAccount({
181188
config: props.config,
182189
params: { team_name: props.teamName },
183190
queryParams: {},
184-
data: { nickname: data.nickname },
191+
data: { nickname: data.nickname.trim() },
185192
});
186193
}
187194

@@ -201,18 +208,15 @@ function AddServiceAccountForm(props: {
201208
submitor,
202209
onSubmitSuccess: (result) => {
203210
onSuccess(result);
204-
toast.addToast({
205-
csVariant: "success",
206-
children: `Service account added`,
207-
duration: 4000,
208-
});
211+
setError(null);
212+
// Refresh the service accounts list to show the newly created account
213+
// TODO: When API returns identifier in response, we can append the new
214+
// service account to the list instead of refreshing from backend
215+
props.serviceAccountRevalidate?.();
209216
},
210217
onSubmitError: (error) => {
211-
toast.addToast({
212-
csVariant: "danger",
213-
children: `Error occurred: ${error.message || "Unknown error"}`,
214-
duration: 8000,
215-
});
218+
const message = `Error occurred: ${error.message || "Unknown error"}`;
219+
setError(message);
216220
},
217221
});
218222

@@ -222,6 +226,7 @@ function AddServiceAccountForm(props: {
222226
setServiceAccountAdded(false);
223227
setAddedServiceAccountToken("");
224228
setAddedServiceAccountNickname("");
229+
setError(null);
225230
updateFormFieldState({ field: "nickname", value: "" });
226231
}
227232
};
@@ -257,30 +262,44 @@ function AddServiceAccountForm(props: {
257262
</Modal.Body>
258263
) : (
259264
<Modal.Body>
260-
<div>
261-
Enter the nickname of the service account you wish to add to the
262-
team <span>{props.teamName}</span>
263-
</div>
264-
<div className="service-accounts__nickname-input">
265-
<NewTextInput
266-
onChange={(e) => {
267-
updateFormFieldState({
268-
field: "nickname",
269-
value: e.target.value,
270-
});
271-
}}
272-
placeholder={"ExampleName"}
273-
maxLength={32}
274-
/>
275-
<div className="service-accounts__nickname-input-max-length">
276-
Max. 32 characters
265+
<form
266+
className="service-accounts__form"
267+
onSubmit={(e) => {
268+
e.preventDefault();
269+
if (isValid) {
270+
strongForm.submit();
271+
}
272+
}}
273+
>
274+
<div>
275+
Enter the nickname of the service account you wish to add to the
276+
team <span>{props.teamName}</span>
277277
</div>
278-
</div>
278+
<div className="service-accounts__nickname-input">
279+
<NewTextInput
280+
value={formInputs.nickname}
281+
onChange={(e) => {
282+
updateFormFieldState({
283+
field: "nickname",
284+
value: e.target.value,
285+
});
286+
}}
287+
placeholder={"ExampleName"}
288+
maxLength={32}
289+
/>
290+
<div className="service-accounts__nickname-input-max-length">
291+
Max. 32 characters
292+
</div>
293+
</div>
294+
{error && <NewAlert csVariant="danger">{error}</NewAlert>}
295+
</form>
279296
</Modal.Body>
280297
)}
281298
{serviceAccountAdded ? null : (
282299
<Modal.Footer>
283-
<NewButton onClick={strongForm.submit}>Add Service Account</NewButton>
300+
<NewButton onClick={strongForm.submit} disabled={!isValid}>
301+
Add Service Account
302+
</NewButton>
284303
</Modal.Footer>
285304
)}
286305
</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)