fix(ui): replace raw AxiosError dumps with QueryErrorState across routes
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react'
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Alert, Badge, Button, LoadingBlock, Modal, TextInput } from '@/ui'
|
import { Alert, Badge, Button, LoadingBlock, Modal, QueryErrorState, TextInput } from '@/ui'
|
||||||
import { WarningIcon, XCircleIcon } from '@phosphor-icons/react'
|
import { WarningIcon, XCircleIcon } from '@phosphor-icons/react'
|
||||||
import { useCascadePreview } from '@/api/queries'
|
import { useCascadePreview } from '@/api/queries'
|
||||||
import { useCascadeCloseRecord } from '@/api/mutations'
|
import { useCascadeCloseRecord } from '@/api/mutations'
|
||||||
@@ -116,9 +116,7 @@ export const CascadeConfirmDialog = ({
|
|||||||
{preview.isLoading && <LoadingBlock size="md" label={t('loading')} />}
|
{preview.isLoading && <LoadingBlock size="md" label={t('loading')} />}
|
||||||
|
|
||||||
{preview.error && (
|
{preview.error && (
|
||||||
<Alert variant="error" title={t('error.failed')}>
|
<QueryErrorState error={preview.error} onRetry={() => preview.refetch()} />
|
||||||
{String(preview.error)}
|
|
||||||
</Alert>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{plan && (
|
{plan && (
|
||||||
@@ -261,9 +259,9 @@ export const CascadeConfirmDialog = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{cascadeMut.error && (
|
{cascadeMut.error && (
|
||||||
<Alert variant="error" title={t('error.failed')}>
|
/* Mutation error — no retry button (user triggers via Confirm
|
||||||
{String(cascadeMut.error)}
|
again). QueryErrorState classifies 4xx/5xx code/message. */
|
||||||
</Alert>
|
<QueryErrorState error={cascadeMut.error} />
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -967,9 +967,10 @@ function DictionaryDetail() {
|
|||||||
{recordsResult.isLoading && <LoadingBlock size="md" label={t('loading')} />}
|
{recordsResult.isLoading && <LoadingBlock size="md" label={t('loading')} />}
|
||||||
|
|
||||||
{recordsResult.error && (
|
{recordsResult.error && (
|
||||||
<Alert variant="error" title={t('error.failed')}>
|
<QueryErrorState
|
||||||
{String(recordsResult.error)}
|
error={recordsResult.error}
|
||||||
</Alert>
|
onRetry={() => recordsResult.refetch()}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{recordsResult.data && recordsResult.data.length === 0 && (
|
{recordsResult.data && recordsResult.data.length === 0 && (
|
||||||
@@ -1279,9 +1280,10 @@ function DictionaryDetail() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{detailQuery.data && edit.kind === 'edit' && rawRecordQuery.error && (
|
{detailQuery.data && edit.kind === 'edit' && rawRecordQuery.error && (
|
||||||
<Alert variant="error" title={t('error.failed')}>
|
<QueryErrorState
|
||||||
{String(rawRecordQuery.error)}
|
error={rawRecordQuery.error}
|
||||||
</Alert>
|
onRetry={() => rawRecordQuery.refetch()}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{detailQuery.data && edit.kind === 'edit' && rawRecordQuery.data && (
|
{detailQuery.data && edit.kind === 'edit' && rawRecordQuery.data && (
|
||||||
@@ -2086,7 +2088,8 @@ function highlightJson(json: string): string {
|
|||||||
*/
|
*/
|
||||||
function EventsTabContent({ dictName }: { dictName: string }) {
|
function EventsTabContent({ dictName }: { dictName: string }) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { data, isLoading, error } = useAudit({ dictionaryName: dictName, size: 50 })
|
const auditQ = useAudit({ dictionaryName: dictName, size: 50 })
|
||||||
|
const { data, isLoading, error } = auditQ
|
||||||
// Filter chips per prototype: все / edit / publish / review / webhook / export.
|
// Filter chips per prototype: все / edit / publish / review / webhook / export.
|
||||||
// Backend audit actions: CREATE / UPDATE / CLOSE / EXPORT etc. — map to display
|
// Backend audit actions: CREATE / UPDATE / CLOSE / EXPORT etc. — map to display
|
||||||
// groups for chip filter.
|
// groups for chip filter.
|
||||||
@@ -2094,11 +2097,7 @@ function EventsTabContent({ dictName }: { dictName: string }) {
|
|||||||
|
|
||||||
if (isLoading) return <LoadingBlock size="md" label={t('loading')} />
|
if (isLoading) return <LoadingBlock size="md" label={t('loading')} />
|
||||||
if (error)
|
if (error)
|
||||||
return (
|
return <QueryErrorState error={error} onRetry={() => auditQ.refetch()} />
|
||||||
<Alert variant="error" title={t('error.failed')}>
|
|
||||||
{String(error)}
|
|
||||||
</Alert>
|
|
||||||
)
|
|
||||||
const rows = data?.content ?? []
|
const rows = data?.content ?? []
|
||||||
if (rows.length === 0)
|
if (rows.length === 0)
|
||||||
return (
|
return (
|
||||||
@@ -2298,7 +2297,8 @@ function EventsTabContent({ dictName }: { dictName: string }) {
|
|||||||
function HistoryTabContent({ detail }: { detail?: import('@/api/client').DictionaryDetail }) {
|
function HistoryTabContent({ detail }: { detail?: import('@/api/client').DictionaryDetail }) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [diffEntryId, setDiffEntryId] = useState<number | undefined>(undefined)
|
const [diffEntryId, setDiffEntryId] = useState<number | undefined>(undefined)
|
||||||
const { data, isLoading, error } = useChangelog(detail?.name)
|
const changelogQ = useChangelog(detail?.name)
|
||||||
|
const { data, isLoading, error } = changelogQ
|
||||||
|
|
||||||
if (!detail) return null
|
if (!detail) return null
|
||||||
|
|
||||||
@@ -2306,9 +2306,7 @@ function HistoryTabContent({ detail }: { detail?: import('@/api/client').Diction
|
|||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{isLoading && <LoadingBlock size="md" label={t('loading')} />}
|
{isLoading && <LoadingBlock size="md" label={t('loading')} />}
|
||||||
{error && (
|
{error && (
|
||||||
<Alert variant="error" title={t('error.failed')}>
|
<QueryErrorState error={error} onRetry={() => changelogQ.refetch()} />
|
||||||
{String(error)}
|
|
||||||
</Alert>
|
|
||||||
)}
|
)}
|
||||||
{data && data.entries.length === 0 && (
|
{data && data.entries.length === 0 && (
|
||||||
<EmptyState
|
<EmptyState
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import { useState } from 'react'
|
|||||||
import { Link, createFileRoute } from '@tanstack/react-router'
|
import { Link, createFileRoute } from '@tanstack/react-router'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import {
|
import {
|
||||||
Alert,
|
|
||||||
Badge,
|
Badge,
|
||||||
Button,
|
Button,
|
||||||
EmptyState,
|
EmptyState,
|
||||||
LoadingBlock,
|
LoadingBlock,
|
||||||
PageHeader,
|
PageHeader,
|
||||||
Panel,
|
Panel,
|
||||||
|
QueryErrorState,
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
TableCell,
|
TableCell,
|
||||||
@@ -92,15 +92,18 @@ function MyDraftsPage() {
|
|||||||
<LoadingBlock size="md" label={t('loading')} />
|
<LoadingBlock size="md" label={t('loading')} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* QueryErrorState classifies status code (502 → "Сервис временно
|
||||||
|
недоступен", timeout → "Запрос превысил время ожидания", etc.) и
|
||||||
|
показывает retry button. Раньше тут был <Alert>{String(error)}</Alert>
|
||||||
|
который дампил raw AxiosError юзеру — анти-паттерн UX. */}
|
||||||
{drafts.error && (
|
{drafts.error && (
|
||||||
<Alert variant="error" title={t('error.failed')}>
|
<QueryErrorState error={drafts.error} onRetry={() => drafts.refetch()} />
|
||||||
{String(drafts.error)}
|
|
||||||
</Alert>
|
|
||||||
)}
|
)}
|
||||||
{schemaDrafts.error && (
|
{schemaDrafts.error && (
|
||||||
<Alert variant="error" title={t('error.failed')}>
|
<QueryErrorState
|
||||||
{String(schemaDrafts.error)}
|
error={schemaDrafts.error}
|
||||||
</Alert>
|
onRetry={() => schemaDrafts.refetch()}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{bothEmpty && <EmptyState title={t('myDrafts.empty')} />}
|
{bothEmpty && <EmptyState title={t('myDrafts.empty')} />}
|
||||||
|
|||||||
@@ -715,9 +715,7 @@ function ReviewDrawer({ draftId, onClose }: DrawerProps) {
|
|||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{draftQ.isLoading && <LoadingBlock size="md" label={t('loading')} />}
|
{draftQ.isLoading && <LoadingBlock size="md" label={t('loading')} />}
|
||||||
{draftQ.error && (
|
{draftQ.error && (
|
||||||
<Alert variant="error" title={t('error.failed')}>
|
<QueryErrorState error={draftQ.error} onRetry={() => draftQ.refetch()} />
|
||||||
{String(draftQ.error)}
|
|
||||||
</Alert>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{draft && (
|
{draft && (
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
MultiSelect,
|
MultiSelect,
|
||||||
PageHeader,
|
PageHeader,
|
||||||
Panel,
|
Panel,
|
||||||
|
QueryErrorState,
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
TableCell,
|
TableCell,
|
||||||
@@ -79,7 +80,8 @@ const EVENT_TYPE_OPTIONS: SelectOption[] = [
|
|||||||
|
|
||||||
function WebhooksPage() {
|
function WebhooksPage() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { data, isLoading, error } = useWebhookSubscriptions()
|
const subsQ = useWebhookSubscriptions()
|
||||||
|
const { data, isLoading, error } = subsQ
|
||||||
const [createOpen, setCreateOpen] = useState(false)
|
const [createOpen, setCreateOpen] = useState(false)
|
||||||
const [revealedSecret, setRevealedSecret] = useState<{
|
const [revealedSecret, setRevealedSecret] = useState<{
|
||||||
name: string
|
name: string
|
||||||
@@ -94,11 +96,7 @@ function WebhooksPage() {
|
|||||||
|
|
||||||
if (isLoading) return <LoadingBlock size="md" label={t('loading')} />
|
if (isLoading) return <LoadingBlock size="md" label={t('loading')} />
|
||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return <QueryErrorState error={error} onRetry={() => subsQ.refetch()} />
|
||||||
<Alert variant="error" title={t('error.failed')}>
|
|
||||||
{String(error)}
|
|
||||||
</Alert>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user