fix(web): error/404 экраны, сплит бандла и bulk-мутации
- RouteErrorComponent/RouteNotFoundComponent на root-маршруте (RU-копирайт, retry + переход на главную; redirect в портал не мигает ошибкой) - settings: форма инициализируется один раз — фоновый refetch больше не затирает ввод пользователя - code splitting: autoCodeSplitting роутов + manualChunks (react/router/query/ charts/dnd); вход ~507KB вместо единого чанка 1.57MB, recharts (330KB) грузится лениво; версия recharts в packages/ui выровнена с apps/web (3.8.0) - bulk-эндпоинты: POST /agents/approve-bulk и PUT /policy-sets/:id/agents — назначение набора агентам одним запросом вместо N×(GET+PUT) - оптимистичные обновления с rollback: approve, approve-bulk, удаление агента, переключение набора - тесты bulk-операций (45 passed)
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
import { Link } from '@tanstack/react-router'
|
||||
import { CircleAlertIcon, SearchXIcon } from 'lucide-react'
|
||||
import { Button } from '@evofw/ui/components/button'
|
||||
|
||||
/**
|
||||
* Route-level error/not-found fallbacks (ReUI Frame look, RU copy).
|
||||
* Preview: https://reui.io/preview/base/feature-4 (centered error panel)
|
||||
*/
|
||||
|
||||
/** Error thrown by the root guard while the browser is already navigating to the portal. */
|
||||
const PORTAL_REDIRECT_MESSAGE = 'redirecting to portal'
|
||||
|
||||
export function RouteErrorComponent({
|
||||
error,
|
||||
reset,
|
||||
}: {
|
||||
error: Error
|
||||
reset: () => void
|
||||
}) {
|
||||
if (error.message === PORTAL_REDIRECT_MESSAGE) return null
|
||||
|
||||
return (
|
||||
<div
|
||||
role="alert"
|
||||
className="flex min-h-[60vh] flex-col items-center justify-center gap-4 p-8 text-center"
|
||||
>
|
||||
<CircleAlertIcon className="text-destructive size-10" aria-hidden />
|
||||
<div className="space-y-1">
|
||||
<h1 className="text-lg font-semibold">Что-то пошло не так</h1>
|
||||
<p className="text-muted-foreground max-w-md text-sm">
|
||||
Раздел не загрузился. Проверьте соединение с API и попробуйте снова.
|
||||
</p>
|
||||
{import.meta.env.DEV && error.message ? (
|
||||
<p className="text-muted-foreground/80 font-mono text-xs">
|
||||
{error.message}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button size="sm" onClick={reset}>
|
||||
Повторить
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" render={<Link to="/" />}>
|
||||
На главную
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function RouteNotFoundComponent() {
|
||||
return (
|
||||
<div className="flex min-h-[60vh] flex-col items-center justify-center gap-4 p-8 text-center">
|
||||
<SearchXIcon className="text-muted-foreground size-10" aria-hidden />
|
||||
<div className="space-y-1">
|
||||
<h1 className="text-lg font-semibold">Страница не найдена</h1>
|
||||
<p className="text-muted-foreground max-w-md text-sm">
|
||||
Адрес не существует или был перемещён.
|
||||
</p>
|
||||
</div>
|
||||
<Button size="sm" render={<Link to="/" />}>
|
||||
На главную
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user