From 0a1fd832ff514d9a0b6ccb96aeff62e8edb293aa Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Thu, 22 Oct 2026 00:15:25 +0400 Subject: [PATCH] =?UTF-8?q?refactor(web):=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B8=D0=B7=20Jira=20?= =?UTF-8?q?=E2=80=94=20=D1=81=D0=BE=D1=81=D1=82=D0=BE=D1=8F=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=BE=D0=B1=D0=BE=D0=BB=D0=BE=D1=87=D0=BA=D0=B8,=20?= =?UTF-8?q?=D0=B4=D0=BE=D1=81=D0=BA=D0=B0=20=D1=85=D1=80=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D1=82=20=D1=82=D0=BE=D0=BB=D1=8C=D0=BA=D0=BE=20=D0=B8=D1=81?= =?UTF-8?q?=D1=82=D0=BE=D1=87=D0=BD=D0=B8=D0=BA=20(#65)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- web/src/App.tsx | 3 +- web/src/board/BoardPage.tsx | 4 +- web/src/board/components/Toolbar.tsx | 12 +- web/src/board/status.test.ts | 12 +- web/src/board/status.ts | 19 +-- web/src/board/useBoardSource.test.tsx | 195 +++------------------- web/src/board/useBoardSource.ts | 133 ++------------- web/src/shell/RefreshProvider.tsx | 169 ++++++++++++++++++++ web/src/shell/refresh.test.tsx | 222 ++++++++++++++++++++++++++ web/src/shell/refresh.ts | 20 +++ web/src/shell/refreshStatus.test.ts | 20 +++ web/src/shell/refreshStatus.ts | 32 ++++ web/src/test/batchHub.ts | 35 ++++ web/src/test/render.tsx | 3 +- 14 files changed, 543 insertions(+), 336 deletions(-) create mode 100644 web/src/shell/RefreshProvider.tsx create mode 100644 web/src/shell/refresh.test.tsx create mode 100644 web/src/shell/refresh.ts create mode 100644 web/src/shell/refreshStatus.test.ts create mode 100644 web/src/shell/refreshStatus.ts create mode 100644 web/src/test/batchHub.ts diff --git a/web/src/App.tsx b/web/src/App.tsx index c5c022c..34645c9 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -3,6 +3,7 @@ import { useState } from "react"; import { createBrowserRouter, RouterProvider } from "react-router"; import { makeQueryClient } from "./api/queryClient.ts"; import { AuthGate } from "./auth/AuthGate.tsx"; +import { RefreshProvider } from "./shell/RefreshProvider.tsx"; import { routes } from "./routes.tsx"; import "./styles/tokens.css"; import "./styles/base.css"; @@ -15,7 +16,7 @@ export function App({ router }: { router?: Router }) { const [r] = useState(() => router ?? createBrowserRouter(routes, { basename: "/app" })); return ( - + ); } diff --git a/web/src/board/BoardPage.tsx b/web/src/board/BoardPage.tsx index 8ad3c78..a075816 100644 --- a/web/src/board/BoardPage.tsx +++ b/web/src/board/BoardPage.tsx @@ -67,9 +67,9 @@ export function BoardPage({ project }: { project: string }) { return (
- void src.refreshFromJira()} onFile={onFile} + tableOpen={prefs.table} onReload={src.reload} onFile={onFile} onProblems={() => prefs.toggle("problems")} onExport={onExport} onTable={() => prefs.toggle("table")} onSnap={() => prefs.toggle("snap")} onReset={prefs.resetLayout} /> diff --git a/web/src/board/components/Toolbar.tsx b/web/src/board/components/Toolbar.tsx index 5609b66..f367126 100644 --- a/web/src/board/components/Toolbar.tsx +++ b/web/src/board/components/Toolbar.tsx @@ -1,4 +1,6 @@ import { useEffect, useState } from "react"; +import { useJiraRefresh } from "../../shell/refresh.ts"; +import { refreshText } from "../../shell/refreshStatus.ts"; import { applyTheme, readTheme, saveTheme, THEME_NAMES, THEMES, type Theme } from "../../shell/theme.ts"; import { statusText, type BoardStatus } from "../status.ts"; import type { BoardModel, Visible } from "../types.ts"; @@ -6,9 +8,9 @@ import { NOT_IN_HUB } from "../constants.ts"; import { Dropdown } from "../../ui/index.ts"; interface ToolbarProps { - label: string; title: string; status: BoardStatus | null; refreshing: boolean; byDate: boolean; snap: boolean; + label: string; title: string; status: BoardStatus | null; byDate: boolean; snap: boolean; model: BoardModel; vis: Visible; problemCount: number; problemsOpen: boolean; tableOpen: boolean; - onReload(): void; onRefresh(): void; onFile(name: string, text: string): void; onProblems(): void; + onReload(): void; onFile(name: string, text: string): void; onProblems(): void; onExport(): void; onTable(): void; onSnap(): void; onReset(): void; } @@ -16,7 +18,9 @@ export function Toolbar(p: ToolbarProps) { const [theme, setTheme] = useState(readTheme); const [confirm, setConfirm] = useState(false); useEffect(() => { if (!confirm) return; const t = setTimeout(() => setConfirm(false), 8000); return () => clearTimeout(t); }, [confirm]); - const st = p.status ? statusText(p.status) : null; + const refresh = useJiraRefresh(); // кнопка и ход пакета временно здесь; в верхней панели — с Task 5 + const rt = refresh.event ? refreshText(refresh.event) : null; + const st = p.status ? statusText(p.status) : rt ? { text: rt.text, warn: rt.tone === "error" || rt.tone === "warning" } : null; const m = p.model, v = p.vis, r = v.removed; const coreShown = v.nodes.filter((n) => !n.dim).length, ctxShown = v.nodes.length - coreShown; const nextTheme = () => { const t = THEMES[(THEMES.indexOf(theme) + 1) % THEMES.length]; setTheme(t); saveTheme(t); applyTheme(t); }; @@ -26,7 +30,7 @@ export function Toolbar(p: ToolbarProps) { {p.label} -