fix(web): scope Suspense to page body; guard formatConfig against null config
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -24,7 +24,9 @@ test("renders navigation and redirects to domains", async () => {
|
||||
)
|
||||
// Sidebar nav also renders a "Domains" link label, so scope the assertion
|
||||
// to the routed page content to unambiguously confirm the redirect + page.
|
||||
// Suspense is now scoped inside <main>, so <main> mounts with the loading
|
||||
// fallback first — await the lazy chunk resolving to the actual page text.
|
||||
const main = await screen.findByRole("main")
|
||||
expect(within(main).getByText("Domains")).toBeInTheDocument()
|
||||
expect(await within(main).findByText("Domains")).toBeInTheDocument()
|
||||
expect(screen.getByRole("link", { name: /domains/i })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
+7
-3
@@ -17,17 +17,22 @@ const ChannelsPage = lazy(() => import("@/pages/ChannelsPage").then((m) => ({ de
|
||||
|
||||
// Every non-auth route shares the same guard + chrome; wrapping here keeps
|
||||
// each <Route> below a one-liner instead of repeating both on every page.
|
||||
// Suspense is scoped to just the page body (not Layout) so lazy-loading a
|
||||
// route doesn't collapse the sidebar/header chrome to the fallback on nav.
|
||||
function Protected({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<ProtectedRoute>
|
||||
<Layout>{children}</Layout>
|
||||
<Layout>
|
||||
<Suspense fallback={<div className="p-6 text-muted-foreground">Загрузка…</div>}>
|
||||
{children}
|
||||
</Suspense>
|
||||
</Layout>
|
||||
</ProtectedRoute>
|
||||
)
|
||||
}
|
||||
|
||||
export function App() {
|
||||
return (
|
||||
<Suspense fallback={<div className="p-6 text-muted-foreground">Загрузка…</div>}>
|
||||
<Routes>
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
<Route path="/register" element={<RegisterPage />} />
|
||||
@@ -39,6 +44,5 @@ export function App() {
|
||||
<Route path="/schedule" element={<Protected><SchedulePage /></Protected>} />
|
||||
<Route path="/channels" element={<Protected><ChannelsPage /></Protected>} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ const EMPTY_FORM: ChannelForm = { type: "telegram", chatId: "", botToken: "", ur
|
||||
// (Object.entries по всему config печатал бы любое поле, включая случайно
|
||||
// сохранённый секрет).
|
||||
function formatConfig(type: string, config: object): string {
|
||||
const c = config as Record<string, unknown>
|
||||
const c = (config ?? {}) as Record<string, unknown>
|
||||
if (type === "telegram") return c.chat_id ? `chat_id: ${String(c.chat_id)}` : ""
|
||||
if (type === "webhook") return c.url ? `url: ${String(c.url)}` : ""
|
||||
return ""
|
||||
|
||||
Reference in New Issue
Block a user