mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-10-15 22:55:42 -04:00
fix(web): show change-password validation messages and failure reason
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
5 files changed
+24
-21
No files matched your search
@@ -47,8 +47,9 @@ import { Spinner } from "@/components/ui/spinner";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { useResetUserState } from "@/hooks/use-reset-user-state";
|
||||
import { authClient, signOut } from "@/lib/auth/client";
|
||||
import { getErrorMessage } from "@/lib/error-messages";
|
||||
import { getAuthErrorMessage, getErrorMessage } from "@/lib/error-messages";
|
||||
import { useAppForm } from "@/lib/form";
|
||||
import { formatFieldErrors } from "@/lib/form/fields";
|
||||
import { orpc } from "@/lib/orpc/client";
|
||||
import type { NormalizedImport } from "@sofa/api/schemas";
|
||||
import { formatDate } from "@sofa/i18n/format";
|
||||
@@ -611,7 +612,7 @@ function ChangePasswordDialog() {
|
||||
revokeOtherSessions: value.revokeOtherSessions,
|
||||
});
|
||||
if (result.error) {
|
||||
setError(t`Failed to change password`);
|
||||
setError(getAuthErrorMessage(result.error, t`Failed to change password`));
|
||||
return;
|
||||
}
|
||||
toast.success(t`Password updated`);
|
||||
@@ -677,9 +678,7 @@ function ChangePasswordDialog() {
|
||||
/>
|
||||
{field.state.meta.errors.length > 0 && (
|
||||
<p className="text-destructive text-xs">
|
||||
{field.state.meta.errors
|
||||
.map((e) => (typeof e === "string" ? e : ""))
|
||||
.join(", ")}
|
||||
{formatFieldErrors(field.state.meta.errors)}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
@@ -703,9 +702,7 @@ function ChangePasswordDialog() {
|
||||
/>
|
||||
{field.state.meta.errors.length > 0 && (
|
||||
<p className="text-destructive text-xs">
|
||||
{field.state.meta.errors
|
||||
.map((e) => (typeof e === "string" ? e : ""))
|
||||
.join(", ")}
|
||||
{formatFieldErrors(field.state.meta.errors)}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
@@ -729,9 +726,7 @@ function ChangePasswordDialog() {
|
||||
/>
|
||||
{field.state.meta.errors.length > 0 && (
|
||||
<p className="text-destructive text-xs">
|
||||
{field.state.meta.errors
|
||||
.map((e) => (typeof e === "string" ? e : ""))
|
||||
.join(", ")}
|
||||
{formatFieldErrors(field.state.meta.errors)}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Trans, useLingui } from "@lingui/react/macro";
|
||||
import { IconDoorEnter } from "@tabler/icons-react";
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import { useOptimistic, useState, useTransition } from "react";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useOptimistic, useTransition } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import { CardContent, CardDescription, CardTitle } from "@/components/ui/card";
|
||||
@@ -12,9 +12,9 @@ import { orpc } from "@/lib/orpc/client";
|
||||
export function RegistrationSection() {
|
||||
const { t } = useLingui();
|
||||
const { data, isPending: isLoading } = useQuery(orpc.admin.settings.get.queryOptions());
|
||||
const [registrationOpen, setRegistrationOpen] = useState<boolean | null>(null);
|
||||
const currentOpen = registrationOpen ?? data?.registration.open ?? false;
|
||||
const currentOpen = data?.registration.open ?? false;
|
||||
const [optimisticOpen, setOptimisticOpen] = useOptimistic(currentOpen);
|
||||
const queryClient = useQueryClient();
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const toggleMutation = useMutation(orpc.admin.settings.update.mutationOptions());
|
||||
|
||||
@@ -31,7 +31,7 @@ export function RegistrationSection() {
|
||||
setOptimisticOpen(checked);
|
||||
try {
|
||||
await toggleMutation.mutateAsync({ registration: { open: checked } });
|
||||
setRegistrationOpen(checked);
|
||||
await queryClient.invalidateQueries({ queryKey: orpc.admin.settings.get.key() });
|
||||
toast.success(checked ? t`Registration opened` : t`Registration closed`);
|
||||
} catch {
|
||||
toast.error(t`Failed to update registration setting`);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Trans, useLingui } from "@lingui/react/macro";
|
||||
import { IconWorldUpload } from "@tabler/icons-react";
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import { useOptimistic, useState, useTransition } from "react";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useOptimistic, useTransition } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import { CardContent, CardDescription, CardTitle } from "@/components/ui/card";
|
||||
@@ -12,9 +12,9 @@ import { orpc } from "@/lib/orpc/client";
|
||||
export function UpdateCheckSection() {
|
||||
const { t } = useLingui();
|
||||
const { data, isPending: isLoading } = useQuery(orpc.admin.settings.get.queryOptions());
|
||||
const [localEnabled, setLocalEnabled] = useState<boolean | null>(null);
|
||||
const currentEnabled = localEnabled ?? data?.updateCheck.enabled ?? true;
|
||||
const currentEnabled = data?.updateCheck.enabled ?? true;
|
||||
const [optimisticEnabled, setOptimisticEnabled] = useOptimistic(currentEnabled);
|
||||
const queryClient = useQueryClient();
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const toggleMutation = useMutation(orpc.admin.settings.update.mutationOptions());
|
||||
|
||||
@@ -31,7 +31,7 @@ export function UpdateCheckSection() {
|
||||
setOptimisticEnabled(checked);
|
||||
try {
|
||||
await toggleMutation.mutateAsync({ updateCheck: { enabled: checked } });
|
||||
setLocalEnabled(checked);
|
||||
await queryClient.invalidateQueries({ queryKey: orpc.admin.settings.get.key() });
|
||||
toast.success(checked ? t`Update checks enabled` : t`Update checks disabled`);
|
||||
} catch {
|
||||
toast.error(t`Failed to update setting`);
|
||||
|
||||
@@ -4,4 +4,5 @@ export const updateToastDismissedVersionAtom = atomWithStorage<string | null>(
|
||||
"sofa:update-toast-dismissed-version",
|
||||
null,
|
||||
createJSONStorage(() => sessionStorage),
|
||||
{ getOnInit: true },
|
||||
);
|
||||
@@ -15,6 +15,13 @@ function normalizeErrors(errors: unknown[]): Array<{ message?: string }> {
|
||||
return errors.map((e) => (typeof e === "string" ? { message: e } : (e as { message?: string })));
|
||||
}
|
||||
|
||||
export function formatFieldErrors(errors: unknown[]): string {
|
||||
return normalizeErrors(errors)
|
||||
.map((e) => e.message ?? "")
|
||||
.filter(Boolean)
|
||||
.join(", ");
|
||||
}
|
||||
|
||||
function TextField({
|
||||
label,
|
||||
description,
|
||||
|
||||
Reference in new issue
Block a user