Merge PR #477: нормализовать detail из ответов API перед рендером (краш на 422)

FastAPI/Pydantic на 422 кладёт в detail массив объектов {type,loc,msg,...};
компоненты рендерили его в JSX как есть → React падал в ErrorBoundary
(Objects are not valid as a React child), пользователь не видел причину.

Все такие обработчики (Login, Profile, ConnectedAccounts, admin AnalyticsTab
и MenuEditorTab) переведены на существующий getApiErrorMessage — строковый
detail возвращается как есть (ветки .includes работают), Pydantic-массив
разворачивается в строку field: msg; ..., любой формат безопасен для рендера.

Валидация на смердженном дереве: tsc, biome lint/format, build — чисто.
This commit is contained in:
Fringg
2026-07-12 23:22:26 +03:00
5 changed files with 32 additions and 32 deletions

View File

@@ -2,6 +2,7 @@ import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { brandingApi } from '../../api/branding';
import { getApiErrorMessage } from '../../utils/api-error';
import { CheckIcon, CloseIcon, PencilIcon } from './icons';
export function AnalyticsTab() {
@@ -31,8 +32,7 @@ export function AnalyticsTab() {
setError(null);
},
onError: (err: unknown) => {
const detail = (err as { response?: { data?: { detail?: string } } })?.response?.data?.detail;
setError(detail || t('common.error'));
setError(getApiErrorMessage(err, t('common.error')));
},
});

View File

@@ -39,6 +39,7 @@ import {
import { Toggle } from './Toggle';
import { useNotify } from '../../platform/hooks/useNotify';
import { useNativeDialog } from '../../platform/hooks/useNativeDialog';
import { getApiErrorMessage } from '../../utils/api-error';
const ChevronIcon = ({ expanded }: { expanded: boolean }) => (
<PiCaretDown className={`h-3.5 w-3.5 transition-transform ${expanded ? 'rotate-180' : ''}`} />
@@ -511,9 +512,7 @@ export function MenuEditorTab() {
queryClient.setQueryData(['menu-layout'], data);
},
onError: (err: unknown) => {
const error = err as { response?: { data?: { detail?: string } } };
const detail = error.response?.data?.detail;
notify.error(detail || t('common.error'));
notify.error(getApiErrorMessage(err, t('common.error')));
},
});