| undefined>(undefined);
useEffect(() => {
if (q === (filters.q ?? "")) return;
- const t = setTimeout(() => {
+ timer.current = setTimeout(() => {
written.current = q;
setParams((p) => { const n = new URLSearchParams(p); if (q) n.set("q", q); else n.delete("q"); return n; }, { replace: true });
}, Q_DELAY);
- return () => clearTimeout(t);
+ return () => clearTimeout(timer.current);
}, [q, filters.q, setParams]);
+ // открытие карточки до срабатывания таймера поиска: q сначала пишется в запись списка (replace), окно открывается поверх неё —
+ // иначе таймер переписал бы адрес карточки и закрыл окно (#42 M-1)
+ const loc = useLocation();
+ const navigate = useNavigate();
+ const open = (key: string) => {
+ clearTimeout(timer.current);
+ let bg = loc;
+ if (q !== (filters.q ?? "")) {
+ const n = new URLSearchParams(loc.search);
+ if (q) n.set("q", q); else n.delete("q");
+ const search = n.size ? "?" + n.toString() : "";
+ written.current = q;
+ void navigate({ pathname: loc.pathname, search }, { replace: true, state: loc.state });
+ bg = { ...loc, search };
+ }
+ void navigate(cardPath(key), { state: openCardState(bg) });
+ };
const board = useBoard(project), settings = useSettings();
const opts = useMemo(() => filterOptions(board.data, settings.data), [board.data, settings.data]);
@@ -47,7 +66,6 @@ export function IssuesPage({ project }: { project: string }) {
[filters, project, showClosed, closed]);
const list = useIssues(query);
const rows = list.data?.pages.flat() ?? [];
- const open = useOpenIssue();
return (
diff --git a/web/src/modules/jira/issues/NoteForm.tsx b/web/src/modules/jira/issues/NoteForm.tsx
index 1a66171..f43a866 100644
--- a/web/src/modules/jira/issues/NoteForm.tsx
+++ b/web/src/modules/jira/issues/NoteForm.tsx
@@ -39,7 +39,11 @@ export function NoteForm({ issue, onDone }: { issue: IssueDetail; onDone: () =>
return () => window.removeEventListener("beforeunload", warn);
}, [dirty]);
// подтверждение ухода может оказаться ниже видимой части окна — прокрутить к нему
- useEffect(() => { if (blocker.state === "blocked") confirmRef.current?.scrollIntoView?.({ block: "nearest" }); }, [blocker.state]);
+ useEffect(() => {
+ if (blocker.state !== "blocked") return;
+ confirmRef.current?.scrollIntoView?.({ block: "nearest" });
+ confirmRef.current?.querySelector("[data-stay]")?.focus();
+ }, [blocker.state]);
const kinds = Object.keys(settings.data?.kind_labels ?? {});
const kindOptions = values.work_kind && !kinds.includes(values.work_kind) ? [values.work_kind, ...kinds] : kinds;
@@ -79,11 +83,11 @@ export function NoteForm({ issue, onDone }: { issue: IssueDetail; onDone: () =>
)}
{blocker.state === "blocked" && (
-
+
Есть несохранённые правки заметки. Уйти без сохранения?
-
+
)}
diff --git a/web/src/modules/jira/issues/overlay.tsx b/web/src/modules/jira/issues/overlay.tsx
index b980c70..4ece74d 100644
--- a/web/src/modules/jira/issues/overlay.tsx
+++ b/web/src/modules/jira/issues/overlay.tsx
@@ -34,13 +34,18 @@ export function useIssueOverlay(): Overlay {
void navigate(ISSUES_PATH + direct.search, { replace: true });
}
}, [card, navigate, direct.search]);
+ // повторное закрытие (двойной щелчок по ✕) на той же записи истории игнорируется (#42 M-2)
+ const closingKey = useRef
(null);
+ useEffect(() => { closingKey.current = null; }, [location.key]);
const close = useCallback(() => {
+ if (closingKey.current === location.key) return;
+ closingKey.current = location.key;
if (!card || card.depth === 0) { void navigate(ISSUES_PATH + direct.search, { replace: true }); return; }
closingDirect.current = card.direct;
void navigate(-card.depth);
- }, [card, navigate, direct.search]);
+ }, [card, navigate, direct.search, location.key]);
- const abortClose = useCallback(() => { closingDirect.current = false; }, []);
+ const abortClose = useCallback(() => { closingDirect.current = false; closingKey.current = null; }, []);
// «Назад» в окне — только если оно открыто переходом из другой карточки (из списка depth = 1; прямой адрес — direct, depth 0)
const onBack = card && (card.depth >= 2 || (card.direct && card.depth >= 1)) ? () => void navigate(-1) : undefined;