diff --git a/apps/console/src/pages/iam/auth/ForgotPasswordPage.tsx b/apps/console/src/pages/iam/auth/ForgotPasswordPage.tsx index 0b27edddb..7603d6394 100644 --- a/apps/console/src/pages/iam/auth/ForgotPasswordPage.tsx +++ b/apps/console/src/pages/iam/auth/ForgotPasswordPage.tsx @@ -8,6 +8,7 @@ import z from "zod"; import { graphql } from "relay-runtime"; import { useMutation } from "react-relay"; import type { ForgotPasswordPageMutation } from "/__generated__/iam/ForgotPasswordPageMutation.graphql"; +import { formatError } from "@probo/helpers"; const sendInstructionsMutation = graphql` mutation ForgotPasswordPageMutation($input: ForgotPasswordInput!) { @@ -46,11 +47,23 @@ export default function ForgotPasswordPage() { onError: (e: Error) => { toast({ title: __("Request failed"), - description: e.message || __("Failed to send reset instructions"), + description: e.message, variant: "error", }); }, - onCompleted: () => { + onCompleted: (_, e) => { + if (e) { + toast({ + title: __("Request failed"), + description: formatError( + __("Failed to send reset instructions"), + e, + ), + variant: "error", + }); + return; + } + toast({ title: __("Success"), description: __("Password reset instructions sent to your email"), diff --git a/apps/console/src/pages/iam/auth/ResetPasswordPage.tsx b/apps/console/src/pages/iam/auth/ResetPasswordPage.tsx index f51c2ab44..84f9ffb0c 100644 --- a/apps/console/src/pages/iam/auth/ResetPasswordPage.tsx +++ b/apps/console/src/pages/iam/auth/ResetPasswordPage.tsx @@ -7,6 +7,7 @@ import { useFormWithSchema } from "/hooks/useFormWithSchema"; import { graphql } from "relay-runtime"; import { useMutation } from "react-relay"; import type { ResetPasswordPageMutation } from "/__generated__/iam/ResetPasswordPageMutation.graphql"; +import { formatError, type GraphQLError } from "@probo/helpers"; const resetPasswordMutation = graphql` mutation ResetPasswordPageMutation($input: ResetPasswordInput!) { @@ -66,11 +67,22 @@ export default function ResetPasswordPage() { onError: (e: Error) => { toast({ title: __("Reset failed"), - description: e.message || __("Password reset failed"), + description: e.message, variant: "error", }); }, - onCompleted: () => { + onCompleted: (_, e) => { + if (e) { + toast({ + title: __("Reset failed"), + description: formatError( + __("Password reset failed"), + e as GraphQLError, + ), + variant: "error", + }); + return; + } toast({ title: __("Success"), description: __("Password reset successfully"), diff --git a/apps/console/src/pages/iam/auth/sign-in/SSOSignInPage.tsx b/apps/console/src/pages/iam/auth/sign-in/SSOSignInPage.tsx index d631f87ef..67d81fb50 100644 --- a/apps/console/src/pages/iam/auth/sign-in/SSOSignInPage.tsx +++ b/apps/console/src/pages/iam/auth/sign-in/SSOSignInPage.tsx @@ -103,7 +103,6 @@ function NavigateToSSOLoginURL(props: { useEffect(() => { if (!ssoLoginURL.ok) { - console.log(ssoLoginURL); toast({ title: __("Error"), description: diff --git a/apps/console/src/pages/iam/auth/sign-up/SignUpFromInvitationPage.tsx b/apps/console/src/pages/iam/auth/sign-up/SignUpFromInvitationPage.tsx index 9342dd825..a94c371b7 100644 --- a/apps/console/src/pages/iam/auth/sign-up/SignUpFromInvitationPage.tsx +++ b/apps/console/src/pages/iam/auth/sign-up/SignUpFromInvitationPage.tsx @@ -6,6 +6,7 @@ import z from "zod"; import { useFormWithSchema } from "/hooks/useFormWithSchema"; import { graphql } from "relay-runtime"; import { useMutation } from "react-relay"; +import { formatError } from "@probo/helpers"; const signUpFromIvitationMutation = graphql` mutation SignUpFromInvitationPageMutation( @@ -63,7 +64,16 @@ export default function SignUpFromInvitationPage() { fullName: data.fullName, }, }, - onCompleted: () => { + onCompleted: (_, e) => { + if (e) { + toast({ + title: __("Signup failed"), + description: formatError(__("Signup failed"), e), + variant: "error", + }); + return; + } + toast({ title: __("Success"), description: __( @@ -73,10 +83,10 @@ export default function SignUpFromInvitationPage() { }); navigate("/", { replace: true }); }, - onError: (errorData) => { + onError: (e) => { toast({ title: __("Signup failed"), - description: errorData.message || __("Signup failed"), + description: e.message, variant: "error", }); }, diff --git a/apps/console/src/pages/iam/auth/sign-up/SignUpPage.tsx b/apps/console/src/pages/iam/auth/sign-up/SignUpPage.tsx index bc65791af..0651591e3 100644 --- a/apps/console/src/pages/iam/auth/sign-up/SignUpPage.tsx +++ b/apps/console/src/pages/iam/auth/sign-up/SignUpPage.tsx @@ -6,6 +6,7 @@ import z from "zod"; import { useFormWithSchema } from "/hooks/useFormWithSchema"; import { graphql } from "relay-runtime"; import { useMutation } from "react-relay"; +import { formatError } from "@probo/helpers"; const signUpMutation = graphql` mutation SignUpPageMutation($input: SignUpInput!) { @@ -49,7 +50,16 @@ export default function SignUpPage() { password: data.password, fullName: data.fullName, }, - onCompleted: () => { + onCompleted: (_, e) => { + if (e) { + toast({ + title: __("Registration failed"), + description: formatError(__("Registration failed"), e), + variant: "error", + }); + return; + } + toast({ title: __("Success"), description: __("Account created successfully"), @@ -57,10 +67,10 @@ export default function SignUpPage() { }); navigate("/", { replace: true }); }, - onError: (errorData) => { + onError: (e) => { toast({ title: __("Registration failed"), - description: errorData.message || __("Registration failed"), + description: e.message, variant: "error", }); }, diff --git a/apps/console/src/pages/iam/memberships/_components/InvitationCard.tsx b/apps/console/src/pages/iam/memberships/_components/InvitationCard.tsx index 28c18517d..0c5dd5fee 100644 --- a/apps/console/src/pages/iam/memberships/_components/InvitationCard.tsx +++ b/apps/console/src/pages/iam/memberships/_components/InvitationCard.tsx @@ -1,6 +1,6 @@ -import { formatDate } from "@probo/helpers"; +import { formatDate, formatError } from "@probo/helpers"; import { useTranslate } from "@probo/i18n"; -import { Button, Card } from "@probo/ui"; +import { Button, Card, useToast } from "@probo/ui"; import { graphql } from "relay-runtime"; import { useFragment, useMutation } from "react-relay"; import type { InvitationCardFragment$key } from "/__generated__/iam/InvitationCardFragment.graphql"; @@ -39,6 +39,7 @@ export function InvitationCard(props: InvitationCardProps) { const navigate = useNavigate(); const { __ } = useTranslate(); + const { toast } = useToast(); const invitation = useFragment(fragment, fKey); @@ -51,12 +52,23 @@ export function InvitationCard(props: InvitationCardProps) { invitationId: invitation.id, }, }, - onCompleted: () => { + onCompleted: (_, e) => { + if (e) { + toast({ + title: __("Request failed"), + description: formatError(__("Cannot accept invitation"), e), + variant: "error", + }); + return; + } navigate(`/organizations/${invitation.organization.id}`); }, - onError: (err) => { - console.error("Failed to accept invitation:", err); - alert(__("Failed to accept invitation")); + onError: (e) => { + toast({ + title: __("Request failed"), + description: e.message, + variant: "error", + }); }, }); }; diff --git a/apps/console/src/pages/iam/memberships/_components/SessionDropdown.tsx b/apps/console/src/pages/iam/memberships/_components/SessionDropdown.tsx index a64d15ec9..8c15dfffb 100644 --- a/apps/console/src/pages/iam/memberships/_components/SessionDropdown.tsx +++ b/apps/console/src/pages/iam/memberships/_components/SessionDropdown.tsx @@ -13,6 +13,7 @@ import { useOrganizationId } from "/hooks/useOrganizationId"; import { useFragment, useMutation } from "react-relay"; import { useTranslate } from "@probo/i18n"; import type { SessionDropdownFragment$key } from "/__generated__/iam/SessionDropdownFragment.graphql"; +import { formatError } from "@probo/helpers"; export const fragment = graphql` fragment SessionDropdownFragment on Organization { @@ -57,13 +58,21 @@ export function SessionDropdown(props: { fKey: SessionDropdownFragment$key }) { signOut({ variables: {}, - onCompleted: () => { + onCompleted: (_, e) => { + if (e) { + toast({ + title: __("Request failed"), + description: formatError(__("Cannot sign out"), e), + variant: "error", + }); + return; + } window.location.reload(); }, onError: (e) => { toast({ title: __("Error"), - description: e.message as string, + description: e.message, variant: "error", }); }, diff --git a/apps/console/src/pages/iam/organizations/NewOrganizationPage.tsx b/apps/console/src/pages/iam/organizations/NewOrganizationPage.tsx index eee7fba7e..44a5c6178 100644 --- a/apps/console/src/pages/iam/organizations/NewOrganizationPage.tsx +++ b/apps/console/src/pages/iam/organizations/NewOrganizationPage.tsx @@ -3,7 +3,7 @@ import { Button, Card, Field, PageHeader, useToast } from "@probo/ui"; import { graphql, useMutation } from "react-relay"; import type { FormEventHandler } from "react"; import { useNavigate } from "react-router"; -import { formatError, type GraphQLError } from "@probo/helpers"; +import { formatError } from "@probo/helpers"; import type { NewOrganizationPageMutation } from "/__generated__/iam/NewOrganizationPageMutation.graphql"; import { IAMRelayProvider } from "/providers/IAMRelayProvider"; @@ -45,7 +45,16 @@ function NewOrganizationPage() { name, }, }, - onCompleted: (r) => { + onCompleted: (r, e) => { + if (e) { + toast({ + title: __("Error"), + description: formatError(__("Failed to create organization"), e), + variant: "error", + }); + return; + } + const org = r.createOrganization!.organization; navigate(`/organizations/${org!.id}`); toast({ @@ -54,10 +63,10 @@ function NewOrganizationPage() { variant: "success", }); }, - onError: (e: GraphQLError) => { + onError: (e) => { toast({ title: __("Error"), - description: formatError(__("Failed to create organization"), e), + description: e.message, variant: "error", }); }, diff --git a/apps/console/src/pages/iam/organizations/settings/SCIMSettingsPage.tsx b/apps/console/src/pages/iam/organizations/settings/SCIMSettingsPage.tsx index c125ba3d1..b82992527 100644 --- a/apps/console/src/pages/iam/organizations/settings/SCIMSettingsPage.tsx +++ b/apps/console/src/pages/iam/organizations/settings/SCIMSettingsPage.tsx @@ -42,7 +42,6 @@ export function SCIMSettingsPage(props: {

{__("SCIM Provisioning")}

( @@ -94,10 +99,24 @@ export function EditSAMLConfigurationForm(props: { attributeMappings: data.attributeMappings, }, }, - onCompleted: onUpdate, + onCompleted: (_, e) => { + if (e) { + toast({ + variant: "error", + title: __("Error"), + description: formatError( + __("Failed to update SAML configuration"), + e, + ), + }); + return; + } + + onUpdate(); + }, }); }, - [onUpdate, organizationId, samlConfiguration.id, update], + [onUpdate, organizationId, samlConfiguration.id, update, __, toast], ); return ( diff --git a/apps/console/src/pages/iam/organizations/settings/_components/NewSAMLConfigurationForm.tsx b/apps/console/src/pages/iam/organizations/settings/_components/NewSAMLConfigurationForm.tsx index 3f8710dec..2f1383d82 100644 --- a/apps/console/src/pages/iam/organizations/settings/_components/NewSAMLConfigurationForm.tsx +++ b/apps/console/src/pages/iam/organizations/settings/_components/NewSAMLConfigurationForm.tsx @@ -7,6 +7,9 @@ import { import { useOrganizationId } from "/hooks/useOrganizationId"; import { useCallback } from "react"; import { useMutationWithToasts } from "/hooks/useMutationWithToasts"; +import { useToast } from "@probo/ui"; +import { formatError } from "@probo/helpers"; +import { useTranslate } from "@probo/i18n"; const createSAMLConfigurationMutation = graphql` mutation NewSAMLConfigurationForm_createMutation( @@ -32,6 +35,9 @@ export function NewSAMLConfigurationForm(props: { onCreate: () => void }) { const { onCreate } = props; const organizationId = useOrganizationId(); + const { __ } = useTranslate(); + const { toast } = useToast(); + const [create, isCreating] = useMutationWithToasts( createSAMLConfigurationMutation, @@ -61,10 +67,24 @@ export function NewSAMLConfigurationForm(props: { onCreate: () => void }) { }, connections: [connectionID], }, - onCompleted: onCreate, + onCompleted: (response, e) => { + if (e) { + toast({ + variant: "error", + title: __("Error"), + description: formatError( + __("Failed to create SAML configuration"), + e, + ), + }); + return; + } + + onCreate(); + }, }); }, - [organizationId, create, onCreate], + [organizationId, create, onCreate, __, toast], ); return ( diff --git a/apps/console/src/pages/iam/organizations/settings/_components/SCIMConfiguration.tsx b/apps/console/src/pages/iam/organizations/settings/_components/SCIMConfiguration.tsx index 8ee16ed71..8e48199fb 100644 --- a/apps/console/src/pages/iam/organizations/settings/_components/SCIMConfiguration.tsx +++ b/apps/console/src/pages/iam/organizations/settings/_components/SCIMConfiguration.tsx @@ -15,6 +15,8 @@ import type { SCIMConfigurationCreateMutation } from "/__generated__/iam/SCIMCon import type { SCIMConfigurationDeleteMutation } from "/__generated__/iam/SCIMConfigurationDeleteMutation.graphql"; import type { SCIMConfigurationRegenerateTokenMutation } from "/__generated__/iam/SCIMConfigurationRegenerateTokenMutation.graphql"; import type { SCIMConfigurationFragment$key } from "/__generated__/iam/SCIMConfigurationFragment.graphql"; +import { useOrganizationId } from "/hooks/useOrganizationId"; +import { formatError } from "@probo/helpers"; const SCIMConfigurationFragment = graphql` fragment SCIMConfigurationFragment on SCIMConfiguration { @@ -72,12 +74,14 @@ const regenerateSCIMTokenMutation = graphql` `; export function SCIMConfiguration(props: { - organizationId: string; fKey: SCIMConfigurationFragment$key | null; canCreate: boolean; canDelete: boolean; }) { - const { organizationId, canCreate, canDelete, fKey } = props; + const { canCreate, canDelete, fKey } = props; + + const organizationId = useOrganizationId(); + const scimConfiguration = useFragment(SCIMConfigurationFragment, fKey); const { __ } = useTranslate(); const { toast } = useToast(); @@ -88,15 +92,15 @@ export function SCIMConfiguration(props: { const [createSCIMConfiguration, isCreatingSAMLConfiguration] = useMutation( - createSCIMConfigurationMutation + createSCIMConfigurationMutation, ); const [deleteSCIMConfiguration, isDeletingSCIMConfiguration] = useMutation( - deleteSCIMConfigurationMutation + deleteSCIMConfigurationMutation, ); const [regenerateSCIMToken, isRegeneratingSCIMToken] = useMutation( - regenerateSCIMTokenMutation + regenerateSCIMTokenMutation, ); const handleCreate = () => { @@ -106,14 +110,26 @@ export function SCIMConfiguration(props: { organizationId, }, }, - onCompleted: (response) => { + onCompleted: (response, e) => { + if (e) { + toast({ + variant: "error", + title: __("Error"), + description: formatError( + __("SCIM configuration creation failed"), + e, + ), + }); + return; + } + if (response.createSCIMConfiguration) { setToken(response.createSCIMConfiguration.token); } toast({ title: __("SCIM Configuration Created"), description: __( - "Copy the bearer token now. It will not be shown again." + "Copy the bearer token now. It will not be shown again.", ), variant: "success", }); @@ -144,7 +160,7 @@ export function SCIMConfiguration(props: { toast({ title: __("SCIM Configuration Deleted"), description: __( - "All SCIM-provisioned memberships have been changed to manual source." + "All SCIM-provisioned memberships have been changed to manual source.", ), variant: "success", }); @@ -176,7 +192,7 @@ export function SCIMConfiguration(props: { toast({ title: __("Bearer Token Regenerated"), description: __( - "Copy the new bearer token now. It will not be shown again." + "Copy the new bearer token now. It will not be shown again.", ), variant: "success", }); @@ -208,7 +224,7 @@ export function SCIMConfiguration(props: {

{__("SCIM is not configured")}

{__( - "Enable SCIM to automatically provision users from your identity provider." + "Enable SCIM to automatically provision users from your identity provider.", )}

@@ -236,7 +252,7 @@ export function SCIMConfiguration(props: {

{__("SCIM Provisioning Active")}

{__( - "Automatic user provisioning is enabled for this organization." + "Automatic user provisioning is enabled for this organization.", )}

@@ -256,7 +272,7 @@ export function SCIMConfiguration(props: { onClick={() => copyToClipboard( scimConfiguration.endpointUrl, - __("SCIM Endpoint URL") + __("SCIM Endpoint URL"), ) } icon={IconSquareBehindSquare2} @@ -318,7 +334,7 @@ export function SCIMConfiguration(props: {

{__( - "Are you sure you want to delete the SCIM configuration? This will:" + "Are you sure you want to delete the SCIM configuration? This will:", )}

{__( - "Existing users will not be removed, only their membership source will change." + "Existing users will not be removed, only their membership source will change.", )}