refactor(ui): improve error handling and input safety in app flows

Normalize frontend error rendering and tighten input/path handling across auth, builder, dashboard, and shared components for more resilient UX behavior.

Made-with: Cursor
This commit is contained in:
Amruth Pillai
2026-04-25 15:31:13 +02:00
parent a42dbcd452
commit 08e9c80037
56 changed files with 1271 additions and 391 deletions
@@ -18,18 +18,49 @@ import type { AuthProvider } from "@/integrations/auth/types";
import { authClient } from "@/integrations/auth/client";
import { orpc } from "@/integrations/orpc/client";
import { getReadableErrorMessage } from "@/utils/error-message";
/**
* Get the display name for a social provider
*/
export function getProviderName(providerId: AuthProvider): string {
return match(providerId)
.with("credential", () => "Password")
.with("passkey", () => "Passkey")
.with("google", () => "Google")
.with("github", () => "GitHub")
.with("linkedin", () => "LinkedIn")
.with("custom", () => "Custom OAuth")
.with("credential", () =>
t({
comment: "Authentication provider display name in account settings",
message: "Password",
}),
)
.with("passkey", () =>
t({
comment: "Authentication provider display name in account settings",
message: "Passkey",
}),
)
.with("google", () =>
t({
comment: "Authentication provider display name in account settings",
message: "Google",
}),
)
.with("github", () =>
t({
comment: "Authentication provider display name in account settings",
message: "GitHub",
}),
)
.with("linkedin", () =>
t({
comment: "Authentication provider display name in account settings",
message: "LinkedIn",
}),
)
.with("custom", () =>
t({
comment: "Authentication provider display name in account settings",
message: "Custom OAuth",
}),
)
.exhaustive();
}
@@ -85,7 +116,16 @@ export function useAuthProviderActions() {
const { error } = await authClient.linkSocial({ provider, callbackURL: "/dashboard/settings/authentication" });
if (error) {
toast.error(error.message, { id: toastId });
toast.error(
getReadableErrorMessage(
error,
t({
comment: "Fallback toast when linking a social authentication provider fails",
message: "Failed to link provider. Please try again.",
}),
),
{ id: toastId },
);
return;
}
@@ -99,7 +139,16 @@ export function useAuthProviderActions() {
const { error } = await authClient.unlinkAccount({ providerId: provider, accountId });
if (error) {
toast.error(error.message, { id: toastId });
toast.error(
getReadableErrorMessage(
error,
t({
comment: "Fallback toast when unlinking a social authentication provider fails",
message: "Failed to unlink provider. Please try again.",
}),
),
{ id: toastId },
);
return;
}
@@ -9,6 +9,7 @@ import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { usePrompt } from "@/hooks/use-prompt";
import { authClient } from "@/integrations/auth/client";
import { getReadableErrorMessage } from "@/utils/error-message";
export function PasskeysSection() {
const queryClient = useQueryClient();
@@ -26,7 +27,15 @@ export function PasskeysSection() {
},
onSuccess: async ({ data, error }) => {
if (error) {
toast.error(error.message);
toast.error(
getReadableErrorMessage(
error,
t({
comment: "Fallback toast when passkey registration fails",
message: "Failed to register passkey. Please try again.",
}),
),
);
return;
}
@@ -36,7 +45,10 @@ export function PasskeysSection() {
const name = await prompt(t`Enter a name for your passkey.`, {
description: t`This will help you identify it later, if you plan to have multiple passkeys.`,
defaultValue: "",
confirmText: t`Save`,
confirmText: t({
comment: "Passkey rename prompt confirm action in authentication settings",
message: "Save",
}),
});
if (name === null) return;
@@ -46,7 +58,15 @@ export function PasskeysSection() {
const { error: renameError } = await authClient.passkey.updatePasskey({ id: passkeyId, name: passkeyName });
if (renameError) {
toast.error(renameError.message);
toast.error(
getReadableErrorMessage(
renameError,
t({
comment: "Fallback toast when renaming a passkey fails",
message: "Failed to rename passkey. Please try again.",
}),
),
);
return;
}
@@ -63,7 +83,15 @@ export function PasskeysSection() {
},
onSuccess: async ({ error }) => {
if (error) {
toast.error(error.message);
toast.error(
getReadableErrorMessage(
error,
t({
comment: "Fallback toast when deleting a passkey fails",
message: "Failed to delete passkey. Please try again.",
}),
),
);
return;
}
@@ -131,7 +159,7 @@ export function PasskeysSection() {
disabled={deletePasskeyMutation.isPending}
>
<TrashIcon />
<Trans>Delete</Trans>
<Trans comment="Passkey row action to remove the selected passkey">Delete</Trans>
</Button>
</div>
</div>
@@ -61,7 +61,9 @@ export function SocialProviderSection({ provider, name, animationDelay = 0 }: So
>
<Button variant="outline" onClick={handleUnlink}>
<LinkBreakIcon />
<Trans>Disconnect</Trans>
<Trans comment="Authentication settings action to unlink a connected social login provider">
Disconnect
</Trans>
</Button>
</motion.div>
))
@@ -74,7 +76,7 @@ export function SocialProviderSection({ provider, name, animationDelay = 0 }: So
>
<Button variant="outline" onClick={handleLink}>
<LinkIcon />
<Trans>Connect</Trans>
<Trans comment="Authentication settings action to link a social login provider">Connect</Trans>
</Button>
</motion.div>
))