refactor(web): оболочка берёт padded и keepSearch из пунктов панели модулей (#87)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andrey Epifancev 2026-10-23 23:14:00 +04:00
parent 9ea84c7edd
commit 93007f471b
4 changed files with 16 additions and 7 deletions

View File

@ -26,8 +26,8 @@ export const jiraModule: HubModule = {
],
nav: [
{ to: "/board", label: "Доска связей", icon: <IconBoard /> },
{ to: "/issues", label: "Задачи", icon: <IconList /> },
{ to: "/feed", label: "Лента", icon: <IconHistory /> },
{ to: "/issues", label: "Задачи", icon: <IconList />, padded: true },
{ to: "/feed", label: "Лента", icon: <IconHistory />, padded: true, keepSearch: true },
],
Provider: RefreshProvider,
SidebarStatus,

View File

@ -1,7 +1,11 @@
import type { ComponentType, ReactNode } from "react";
import type { Location, RouteObject } from "react-router";
export type NavItem = { to: string; label: string; icon: ReactNode };
export type NavItem = {
to: string; label: string; icon: ReactNode;
padded?: boolean; // страница с полями (не во всю ширину): оболочка не ставит ей класс page--board; для адресов вне панели класс ставится
keepSearch?: boolean; // при смене проекта строка запроса страницы сохраняется (параметры не проектные); иначе сбрасывается
};
// окно поверх страницы (карточка задачи): pageLocation — адрес страницы под окном, null — окна нет
export type Overlay = { pageLocation: Location | null; element: ReactNode };

View File

@ -10,7 +10,8 @@ import { PAGE_ROUTES } from "./pages.tsx";
import { Sidebar } from "./Sidebar.tsx";
import "./shell.css";
const NAV_PATHS: readonly string[] = MODULES.flatMap((m) => m.nav.map((n) => n.to));
const NAV = MODULES.flatMap((m) => m.nav);
const NAV_PATHS: readonly string[] = NAV.map((n) => n.to);
export function Layout() {
const location = useLocation();
@ -44,7 +45,7 @@ export function Layout() {
const [newOpen, setNewOpen] = useState(false);
// смена проекта: фильтры доски и списка — проектные и сбрасываются; период и виды ленты от проекта не зависят (R-1)
const select = useCallback((label: string) => {
void navigate({ pathname: section ?? NAV_PATHS[0], search: withProject(section === "/feed" ? pageLocation.search : "", label) });
void navigate({ pathname: section ?? NAV_PATHS[0], search: withProject(NAV.find((n) => n.to === section)?.keepSearch ? pageLocation.search : "", label) });
}, [navigate, section, pageLocation.search]);
const ctx = useMemo<ProjectCtx>(() => ({ state, projects: projects.data ?? [], select, openNew: () => setNewOpen(true) }),
[state, projects.data, select]);
@ -54,7 +55,7 @@ export function Layout() {
<ProjectContext value={ctx}>
<div className="shell">
<Sidebar section={section} project={okLabel} />
<main className={"page" + (section !== "/issues" && section !== "/feed" ? " page--board" : "")}>
<main className={"page" + (NAV.find((n) => n.to === section)?.padded ? "" : " page--board")}>
<ErrorBoundary key={at}>{page}</ErrorBoundary>
</main>
</div>

View File

@ -1,11 +1,15 @@
import { Fragment } from "react";
import { Link } from "react-router";
import { MODULES } from "../modules/index.ts";
import { Notice } from "../ui/index.ts";
const NAV = MODULES.flatMap((m) => m.nav);
export function NotFound() {
return (
<div className="crash">
<Notice tone="warning" title={<h2>Страница не найдена</h2>}>
Разделы приложения: <Link to="/board">Доска связей</Link>, <Link to="/issues">Задачи</Link>, <Link to="/feed">Лента</Link>.
Разделы приложения: {NAV.map((n, i) => <Fragment key={n.to}>{i > 0 && ", "}<Link to={n.to}>{n.label}</Link></Fragment>)}.
</Notice>
</div>
);