Skip to content

Commit 489fede

Browse files
Fix access source creation page
Move hooks before early return to fix rules-of-hooks lint error. Clear processedConnectorIdRef on mutation error so OAuth callback can be retried. Replace useMutationWithToasts with explicit error handling. Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
1 parent 5cfd96a commit 489fede

File tree

1 file changed

+105
-30
lines changed

1 file changed

+105
-30
lines changed

apps/console/src/pages/organizations/access-reviews/CreateAccessSourcePage.tsx

Lines changed: 105 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { sprintf } from "@probo/helpers";
1+
import { formatError, type GraphQLError, sprintf } from "@probo/helpers";
22
import { usePageTitle } from "@probo/hooks";
33
import { useTranslate } from "@probo/i18n";
44
import {
@@ -29,7 +29,6 @@ import type { CreateAccessSourcePageCreateAPIKeyConnectorMutation } from "#/__ge
2929
import type { CreateAccessSourcePageQuery } from "#/__generated__/core/CreateAccessSourcePageQuery.graphql";
3030
import type { CreateAccessSourcePageSourcesFragment$key } from "#/__generated__/core/CreateAccessSourcePageSourcesFragment.graphql";
3131
import type { CreateAccessSourcePageSourcesPaginationQuery } from "#/__generated__/core/CreateAccessSourcePageSourcesPaginationQuery.graphql";
32-
import { useMutationWithToasts } from "#/hooks/useMutationWithToasts";
3332
import { useOrganizationId } from "#/hooks/useOrganizationId";
3433

3534
import { deleteAccessSourceMutation } from "./_components/AccessSourceRow";
@@ -185,20 +184,12 @@ export default function CreateAccessSourcePage({
185184
>(createAccessSourcePageSourcesFragment, organization);
186185

187186
const [deleteAccessSource, isDeletingSource]
188-
= useMutationWithToasts<AccessSourceRowDeleteMutation>(
187+
= useMutation<AccessSourceRowDeleteMutation>(
189188
deleteAccessSourceMutation,
190-
{
191-
successMessage: __("Access source disconnected successfully."),
192-
errorMessage: __("Failed to disconnect source"),
193-
},
194189
);
195190
const [createAccessSource, isCreatingSource]
196-
= useMutationWithToasts<CreateAccessSourceDialogMutation>(
191+
= useMutation<CreateAccessSourceDialogMutation>(
197192
createAccessSourceMutation,
198-
{
199-
successMessage: __("Access source created successfully."),
200-
errorMessage: __("Failed to create access source"),
201-
},
202193
);
203194
const [createAPIKeyConnector]
204195
= useMutation<CreateAccessSourcePageCreateAPIKeyConnectorMutation>(
@@ -240,7 +231,7 @@ export default function CreateAccessSourcePage({
240231
}
241232
processedConnectorIdRef.current = callbackConnectorId;
242233

243-
void createAccessSource({
234+
createAccessSource({
244235
variables: {
245236
input: {
246237
organizationId,
@@ -252,12 +243,40 @@ export default function CreateAccessSourcePage({
252243
},
253244
connections: accessSources?.__id ? [accessSources.__id] : [],
254245
},
255-
onCompleted: () => {
246+
onCompleted(_, errors) {
247+
if (errors?.length) {
248+
processedConnectorIdRef.current = null;
249+
toast({
250+
title: __("Error"),
251+
description: formatError(
252+
__("Failed to create access source"),
253+
errors as GraphQLError[],
254+
),
255+
variant: "error",
256+
});
257+
return;
258+
}
259+
toast({
260+
title: __("Success"),
261+
description: __("Access source created successfully."),
262+
variant: "success",
263+
});
256264
setSearchParams((params) => {
257265
params.delete("connector_id");
258266
return params;
259267
}, { replace: true });
260268
},
269+
onError(error) {
270+
processedConnectorIdRef.current = null;
271+
toast({
272+
title: __("Error"),
273+
description: formatError(
274+
__("Failed to create access source"),
275+
error as GraphQLError,
276+
),
277+
variant: "error",
278+
});
279+
},
261280
});
262281
}, [
263282
__,
@@ -270,8 +289,18 @@ export default function CreateAccessSourcePage({
270289
isCreatingSource,
271290
organizationId,
272291
setSearchParams,
292+
toast,
273293
]);
274294

295+
const [apiKeyDialogOpen, setApiKeyDialogOpen] = useState(false);
296+
297+
useEffect(() => {
298+
if (apiKeyDialogOpen) {
299+
apiKeyDialogRef.current?.open();
300+
setApiKeyDialogOpen(false);
301+
}
302+
}, [apiKeyDialogOpen, apiKeyDialogRef]);
303+
275304
if (!organization.canCreateSource) {
276305
return (
277306
<Card padded>
@@ -299,22 +328,13 @@ export default function CreateAccessSourcePage({
299328
window.location.assign(url.toString());
300329
};
301330

302-
const [apiKeyDialogOpen, setApiKeyDialogOpen] = useState(false);
303-
304331
const openAPIKeyDialog = (provider: APIKeyProvider) => {
305332
setApiKeyProvider(provider);
306333
setApiKeyValue("");
307334
setProviderSettingValue("");
308335
setApiKeyDialogOpen(true);
309336
};
310337

311-
useEffect(() => {
312-
if (apiKeyDialogOpen) {
313-
apiKeyDialogRef.current?.open();
314-
setApiKeyDialogOpen(false);
315-
}
316-
}, [apiKeyDialogOpen]);
317-
318338
const providerNeedsExtraSetting = (provider: APIKeyProvider | null): boolean => {
319339
return provider === "TALLY";
320340
};
@@ -346,7 +366,7 @@ export default function CreateAccessSourcePage({
346366
onCompleted: (response) => {
347367
const connectorId = response.createAPIKeyConnector.connector.id;
348368

349-
void createAccessSource({
369+
createAccessSource({
350370
variables: {
351371
input: {
352372
organizationId,
@@ -356,15 +376,39 @@ export default function CreateAccessSourcePage({
356376
},
357377
connections: accessSources?.__id ? [accessSources.__id] : [],
358378
},
359-
onCompleted: () => {
379+
onCompleted(_, errors) {
360380
setIsConnectingAPIKey(false);
381+
if (errors?.length) {
382+
toast({
383+
title: __("Error"),
384+
description: formatError(
385+
__("Failed to create access source"),
386+
errors as GraphQLError[],
387+
),
388+
variant: "error",
389+
});
390+
return;
391+
}
392+
toast({
393+
title: __("Success"),
394+
description: __("Access source created successfully."),
395+
variant: "success",
396+
});
361397
setApiKeyValue("");
362398
setProviderSettingValue("");
363399
setApiKeyProvider(null);
364400
apiKeyDialogRef.current?.close();
365401
},
366-
onError: () => {
402+
onError(error) {
367403
setIsConnectingAPIKey(false);
404+
toast({
405+
title: __("Error"),
406+
description: formatError(
407+
__("Failed to create access source"),
408+
error as GraphQLError,
409+
),
410+
variant: "error",
411+
});
368412
},
369413
});
370414
},
@@ -379,7 +423,7 @@ export default function CreateAccessSourcePage({
379423
});
380424
};
381425

382-
const disconnectProviderSource = async (sourceIds: string[]) => {
426+
const disconnectProviderSource = (sourceIds: string[]) => {
383427
if (sourceIds.length === 0) {
384428
toast({
385429
title: __("Nothing to disconnect"),
@@ -388,18 +432,49 @@ export default function CreateAccessSourcePage({
388432
return;
389433
}
390434

435+
let remaining = sourceIds.length;
391436
for (const sourceId of sourceIds) {
392-
await deleteAccessSource({
437+
deleteAccessSource({
393438
variables: {
394439
input: {
395440
accessSourceId: sourceId,
396441
},
397442
connections: [accessSources.__id],
398443
},
444+
onCompleted(_, errors) {
445+
if (errors?.length) {
446+
toast({
447+
title: __("Error"),
448+
description: formatError(
449+
__("Failed to disconnect source"),
450+
errors as GraphQLError[],
451+
),
452+
variant: "error",
453+
});
454+
return;
455+
}
456+
remaining--;
457+
if (remaining === 0) {
458+
toast({
459+
title: __("Success"),
460+
description: __("Access source disconnected successfully."),
461+
variant: "success",
462+
});
463+
window.location.assign(`/organizations/${organizationId}/access-reviews/sources/new`);
464+
}
465+
},
466+
onError(error) {
467+
toast({
468+
title: __("Error"),
469+
description: formatError(
470+
__("Failed to disconnect source"),
471+
error as GraphQLError,
472+
),
473+
variant: "error",
474+
});
475+
},
399476
});
400477
}
401-
402-
window.location.assign(`/organizations/${organizationId}/access-reviews/sources/new`);
403478
};
404479

405480
const renderProviderCard = (

0 commit comments

Comments
 (0)