From 08894288bc2f4c5405685ad890d7c305db963ace Mon Sep 17 00:00:00 2001 From: DCCONSTRUCTIONS Date: Sun, 28 Jun 2026 16:42:44 +0300 Subject: [PATCH] Fix workspace scope section mapping --- seo_mode/seo_mode/app/src/App.tsx | 423 ++++++++++++------ .../seo_mode/app/src/ProjectScopeFlow.tsx | 39 +- .../seo_mode/server/src/preview/routes.ts | 45 +- 3 files changed, 334 insertions(+), 173 deletions(-) diff --git a/seo_mode/seo_mode/app/src/App.tsx b/seo_mode/seo_mode/app/src/App.tsx index c07bff6..d2e3698 100644 --- a/seo_mode/seo_mode/app/src/App.tsx +++ b/seo_mode/seo_mode/app/src/App.tsx @@ -931,16 +931,112 @@ function getSelectedPagesCount(scan: ProjectScanSummary) { return scan.pages.filter((page) => page.selected).length; } -function getSelectedCoveragePages(scan: ProjectScanSummary) { - return scan.pages - .map((page, index) => ({ index, page })) - .filter(({ page }) => isWorkspaceScopePage(page)) - .sort((left, right) => getWorkspaceScopeOrder(left.page, left.index) - getWorkspaceScopeOrder(right.page, right.index)) - .map(({ page }) => page); +function getPageScopeId(page: ProjectScanSummary["pages"][number]) { + return `page:${page.pageId}`; } -function getWorkspaceKey(projectId: string, pageId: string) { - return `${projectId}:${pageId}`; +function getSectionScopeId(section: ProjectScanSummary["siteSections"][number]) { + return `section:${section.sectionId}`; +} + +type WorkspaceScopeItem = { + scopeId: string; + kind: "page" | "section"; + pageId: string; + sectionId?: string; + title: string; + pathLabel: string; + sourcePath: string; + previewSourcePath: string | null; + previewTargetSelector: string | null; + previewTargetIndex: number | null; + previewKind: ProjectScanSummary["pages"][number]["previewKind"]; + order: number; +}; + +function getPageWorkspaceScopeItem(page: ProjectScanSummary["pages"][number], index: number): WorkspaceScopeItem { + return { + kind: "page", + order: getWorkspaceScopeOrder(page, index), + pageId: page.pageId, + pathLabel: getPagePathLabel(page), + previewKind: page.previewKind, + previewSourcePath: page.previewSourcePath ?? page.sourcePath, + previewTargetIndex: null, + previewTargetSelector: page.previewTargetSelector, + scopeId: getPageScopeId(page), + sourcePath: page.sourcePath, + title: getPageTitle(page) + }; +} + +function getSectionWorkspaceScopeItem( + section: ProjectScanSummary["siteSections"][number], + fallbackOrder: number +): WorkspaceScopeItem | null { + if (!section.pageId || !section.enabled || !section.selected) { + return null; + } + + if (section.previewKind === "none" && !section.previewSourcePath && !section.previewTargetSelector) { + return null; + } + + const indexSuffix = section.previewTargetIndex != null ? ` #${section.previewTargetIndex + 1}` : ""; + + return { + kind: "section", + order: section.order ?? fallbackOrder, + pageId: section.pageId, + pathLabel: `${section.sourcePath} · секция главной${indexSuffix}`, + previewKind: section.previewKind, + previewSourcePath: section.previewSourcePath, + previewTargetIndex: section.previewTargetIndex, + previewTargetSelector: section.previewTargetSelector, + scopeId: getSectionScopeId(section), + sectionId: section.sectionId, + sourcePath: section.sourcePath, + title: section.title + }; +} + +function getSelectedWorkspaceScopeItems(scan: ProjectScanSummary): WorkspaceScopeItem[] { + const sectionPageIds = new Set( + scan.siteSections + .filter((section) => section.selected && section.enabled && section.pageId) + .map((section) => section.pageId!) + ); + const pageItems = scan.pages + .map((page, index) => ({ index, page })) + .filter(({ page }) => isWorkspaceScopePage(page)) + .filter(({ page }) => page.scope !== "template_block" || !sectionPageIds.has(page.pageId)) + .map(({ page, index }) => getPageWorkspaceScopeItem(page, index)); + const sectionItems = scan.siteSections + .map((section, index) => getSectionWorkspaceScopeItem(section, index)) + .filter((item): item is WorkspaceScopeItem => Boolean(item)); + + return [...pageItems, ...sectionItems].sort((left, right) => { + if (left.kind !== right.kind) { + if (left.kind === "page" && left.order === -1) return -1; + if (right.kind === "page" && right.order === -1) return 1; + } + + return left.order - right.order || left.title.localeCompare(right.title, "ru"); + }); +} + +function getSelectedWorkspaceIssueCount(scan: ProjectScanSummary | null | undefined, scopeItems: WorkspaceScopeItem[]) { + if (!scan?.audit) { + return 0; + } + + const pageIds = new Set(scopeItems.map((item) => item.pageId)); + + return scan.audit.issues.filter((issue) => issue.pageId && pageIds.has(issue.pageId)).length; +} + +function getWorkspaceKey(projectId: string, scopeId: string) { + return `${projectId}:${scopeId}`; } type WorkspaceSection = PageWorkspace["sections"][number]; @@ -1399,13 +1495,13 @@ export function App() { const [auditIssueLimits, setAuditIssueLimits] = useState>({}); const [auditSeverityFilters, setAuditSeverityFilters] = useState>({}); const [openPageAuditDetails, setOpenPageAuditDetails] = useState>({}); - const [activeWorkspacePageIds, setActiveWorkspacePageIds] = useState>({}); + const [activeWorkspaceScopeIds, setActiveWorkspaceScopeIds] = useState>({}); const [activeWorkspaceSectionIds, setActiveWorkspaceSectionIds] = useState>({}); const [activeWorkspaceTargetIds, setActiveWorkspaceTargetIds] = useState>({}); const [workspacePreviewMarkers, setWorkspacePreviewMarkers] = useState>({}); const [workspaceDrawers, setWorkspaceDrawers] = useState>({}); const [workspacePageMenus, setWorkspacePageMenus] = useState>({}); - const [workspacePreviewPageIds, setWorkspacePreviewPageIds] = useState>({}); + const [workspacePreviewScopeIds, setWorkspacePreviewScopeIds] = useState>({}); const [pageWorkspaces, setPageWorkspaces] = useState>({}); const [workspaceDraftTexts, setWorkspaceDraftTexts] = useState>({}); const [workspaceSectionTexts, setWorkspaceSectionTexts] = useState>({}); @@ -1619,26 +1715,27 @@ export function App() { return () => undefined; } - const scopePages = getSelectedCoveragePages(scanSummary); - const activePageId = activeWorkspacePageIds[projectId] ?? scopePages[0]?.pageId ?? ""; - const workspaceKey = activePageId ? getWorkspaceKey(projectId, activePageId) : ""; + const scopeItems = getSelectedWorkspaceScopeItems(scanSummary); + const activeScopeId = activeWorkspaceScopeIds[projectId] ?? scopeItems[0]?.scopeId ?? ""; + const activeScopeItem = scopeItems.find((item) => item.scopeId === activeScopeId) ?? scopeItems[0] ?? null; + const workspaceKey = activeScopeItem ? getWorkspaceKey(projectId, activeScopeItem.scopeId) : ""; const workspace = workspaceKey ? pageWorkspaces[workspaceKey] : null; if (!workspaceKey || !workspace) { return () => undefined; } - const issues = getPageIssues(scanSummary, activePageId); + const issues = getPageIssues(scanSummary, activeScopeItem?.pageId ?? null); const targets = buildWorkspaceTargets(workspace, issues); - timers.push(window.setTimeout(() => updateWorkspacePreviewMarkers(workspaceKey, targets, scopePages), 80)); - timers.push(window.setTimeout(() => updateWorkspacePreviewMarkers(workspaceKey, targets, scopePages), 360)); - timers.push(window.setTimeout(() => updateWorkspacePreviewMarkers(workspaceKey, targets, scopePages), 1000)); + timers.push(window.setTimeout(() => updateWorkspacePreviewMarkers(workspaceKey, targets, scopeItems, activeScopeItem), 80)); + timers.push(window.setTimeout(() => updateWorkspacePreviewMarkers(workspaceKey, targets, scopeItems, activeScopeItem), 360)); + timers.push(window.setTimeout(() => updateWorkspacePreviewMarkers(workspaceKey, targets, scopeItems, activeScopeItem), 1000)); return () => { timers.forEach((timer) => window.clearTimeout(timer)); }; - }, [activeProjectId, activeStageIndex, activeWorkspacePageIds, pageWorkspaces, scanSummaries, workspacePreviewModes]); + }, [activeProjectId, activeStageIndex, activeWorkspaceScopeIds, pageWorkspaces, scanSummaries, workspacePreviewModes]); function scrollWorkspaceFieldIntoView(workspaceKey: string, targetId: string, attempt = 0) { window.requestAnimationFrame(() => { @@ -1663,21 +1760,27 @@ export function App() { if (data?.type === "seo-mode:active-scope-page") { const sourceWorkspaceKey = typeof data.workspaceKey === "string" ? data.workspaceKey : ""; const pageId = typeof data.pageId === "string" ? data.pageId : ""; + const scopeId = + typeof data.scopeId === "string" && data.scopeId.length > 0 + ? data.scopeId + : pageId + ? `page:${pageId}` + : ""; const [projectId = ""] = sourceWorkspaceKey.split(":"); - if (!projectId || !pageId) { + if (!projectId || !scopeId) { return; } - const nextWorkspaceKey = getWorkspaceKey(projectId, pageId); + const nextWorkspaceKey = getWorkspaceKey(projectId, scopeId); if (!pageWorkspaces[nextWorkspaceKey]) { const project = projects.find((item) => item.id === projectId); const scanSummary = scanSummaries[projectId]; - const page = scanSummary ? getSelectedCoveragePages(scanSummary).find((item) => item.pageId === pageId) : null; + const scopeItem = scanSummary ? getSelectedWorkspaceScopeItems(scanSummary).find((item) => item.scopeId === scopeId) : null; - if (project && page && busyWorkspaceKey !== nextWorkspaceKey) { - void handleOpenPageWorkspace(project, page, { + if (project && scopeItem && busyWorkspaceKey !== nextWorkspaceKey) { + void handleOpenPageWorkspace(project, scopeItem, { deferActiveUntilLoaded: true, navigatePreview: false, silent: true @@ -1687,14 +1790,14 @@ export function App() { return; } - setActiveWorkspacePageIds((currentPageIds) => { - if (currentPageIds[projectId] === pageId) { - return currentPageIds; + setActiveWorkspaceScopeIds((currentScopeIds) => { + if (currentScopeIds[projectId] === scopeId) { + return currentScopeIds; } return { - ...currentPageIds, - [projectId]: pageId + ...currentScopeIds, + [projectId]: scopeId }; }); return; @@ -2267,10 +2370,14 @@ export function App() { async function handleOpenPageWorkspace( project: ProjectSummary, - page: ProjectScanSummary["pages"][number], + target: ProjectScanSummary["pages"][number] | WorkspaceScopeItem, options: { deferActiveUntilLoaded?: boolean; navigatePreview?: boolean; silent?: boolean } = {} ) { - const workspaceKey = getWorkspaceKey(project.id, page.pageId); + const isScopeItem = "scopeId" in target; + const pageId = target.pageId; + const scopeId = isScopeItem ? target.scopeId : getPageScopeId(target); + const workspaceTitle = isScopeItem ? target.title : getPageTitle(target); + const workspaceKey = getWorkspaceKey(project.id, scopeId); setBusyWorkspaceKey(workspaceKey); @@ -2281,21 +2388,21 @@ export function App() { } if (options.navigatePreview !== false) { - setWorkspacePreviewPageIds((currentPageIds) => ({ - ...currentPageIds, - [project.id]: page.pageId + setWorkspacePreviewScopeIds((currentScopeIds) => ({ + ...currentScopeIds, + [project.id]: scopeId })); } if (!options.deferActiveUntilLoaded) { - setActiveWorkspacePageIds((currentPageIds) => ({ - ...currentPageIds, - [project.id]: page.pageId + setActiveWorkspaceScopeIds((currentScopeIds) => ({ + ...currentScopeIds, + [project.id]: scopeId })); } try { - const result = await openPageWorkspace(project.id, page.pageId); + const result = await openPageWorkspace(project.id, pageId); setPageWorkspaces((currentWorkspaces) => ({ ...currentWorkspaces, @@ -2318,14 +2425,14 @@ export function App() { [workspaceKey]: currentTargetIds[workspaceKey] ?? result.workspace.sections[0]?.id ?? "" })); if (options.deferActiveUntilLoaded) { - setActiveWorkspacePageIds((currentPageIds) => ({ - ...currentPageIds, - [project.id]: page.pageId + setActiveWorkspaceScopeIds((currentScopeIds) => ({ + ...currentScopeIds, + [project.id]: scopeId })); } if (!options.silent) { - setProjectActionMessage(`Рабочая зона открыта: ${getPageTitle(page)}.`); + setProjectActionMessage(`Рабочая зона открыта: ${workspaceTitle}.`); } } catch (error: unknown) { setProjectActionError(getErrorMessage(error, "Не удалось открыть рабочую зону страницы.")); @@ -2345,8 +2452,7 @@ export function App() { }); } - async function handleSavePageWorkspace(projectId: string, pageId: string) { - const workspaceKey = getWorkspaceKey(projectId, pageId); + async function handleSavePageWorkspace(projectId: string, pageId: string, workspaceKey = getWorkspaceKey(projectId, `page:${pageId}`)) { const workspace = pageWorkspaces[workspaceKey]; const workingText = workspace ? buildWorkingTextFromSections(workspace, workspaceSectionTexts[workspaceKey]) @@ -2383,15 +2489,13 @@ export function App() { } } - async function handleResetPageWorkspace(projectId: string, pageId: string) { + async function handleResetPageWorkspace(projectId: string, pageId: string, workspaceKey = getWorkspaceKey(projectId, `page:${pageId}`)) { const confirmed = window.confirm("Сбросить working draft к original snapshot? Исходный проект не изменится."); if (!confirmed) { return; } - const workspaceKey = getWorkspaceKey(projectId, pageId); - setBusyWorkspaceKey(workspaceKey); setProjectActionError(null); setProjectActionMessage(null); @@ -2496,7 +2600,8 @@ export function App() { function updateWorkspacePreviewMarkers( workspaceKey: string, targets: WorkspaceTarget[], - scopePages: ProjectScanSummary["pages"] = [] + scopeItems: WorkspaceScopeItem[] = [], + activeScopeItem: WorkspaceScopeItem | null = null ) { const frameWindow = previewFrameRefs.current[workspaceKey]?.contentWindow; @@ -2513,27 +2618,41 @@ export function App() { { type: "seo-mode:measure-targets", workspaceKey, - activePageId: workspaceKey.split(":")[1] ?? "", - scopePages: scopePages - .filter((page) => page.previewTargetSelector) - .map((page) => ({ - pageId: page.pageId, - label: getPageTitle(page), - selector: page.previewTargetSelector + activePageId: activeScopeItem?.pageId ?? "", + activeScopeId: activeScopeItem?.scopeId ?? "", + scopePages: scopeItems + .filter((item) => item.previewTargetSelector) + .map((item) => ({ + label: item.title, + pageId: item.pageId, + scopeId: item.scopeId, + selector: item.previewTargetSelector, + selectorIndex: item.previewTargetIndex })), targets: targets .filter((target) => (target.markerSelectors.length > 0 || target.markerFallbackSelector) && target.issues.length > 0) - .map((target) => ({ - id: target.id, - label: target.title, - number: target.number, - selector: target.selector, - selectors: target.markerSelectors, - fallbackSelector: target.markerFallbackSelector, - pageStartFallback: target.kind !== "media", - preferPageStart: target.sectionId === "seo-title" || target.sectionId === "seo-description" || target.sectionId === "seo-h1", - sectionId: target.sectionId - })) + .map((target) => { + const shouldAnchorToRenderedSection = activeScopeItem?.kind === "section" && Boolean(activeScopeItem.previewTargetSelector); + const sectionFallbackSelector = shouldAnchorToRenderedSection + ? activeScopeItem?.previewTargetSelector ?? null + : target.markerFallbackSelector; + + return { + fallbackSelector: sectionFallbackSelector, + fallbackSelectorIndex: shouldAnchorToRenderedSection ? activeScopeItem?.previewTargetIndex ?? 0 : null, + id: target.id, + label: target.title, + number: target.number, + pageStartFallback: !shouldAnchorToRenderedSection && target.kind !== "media", + preferPageStart: + !shouldAnchorToRenderedSection && + (target.sectionId === "seo-title" || target.sectionId === "seo-description" || target.sectionId === "seo-h1"), + sectionId: target.sectionId, + selector: target.selector, + selectorIndex: null, + selectors: shouldAnchorToRenderedSection ? [] : target.markerSelectors + }; + }) }, "*" ); @@ -2543,25 +2662,27 @@ export function App() { workspaceKey: string, mode: WorkspacePreviewMode, targets: WorkspaceTarget[], - scopePages: ProjectScanSummary["pages"] = [] + scopeItems: WorkspaceScopeItem[] = [], + activeScopeItem: WorkspaceScopeItem | null = null ) { setWorkspacePreviewModes((currentModes) => ({ ...currentModes, [workspaceKey]: mode })); - window.setTimeout(() => updateWorkspacePreviewMarkers(workspaceKey, targets, scopePages), 120); - window.setTimeout(() => updateWorkspacePreviewMarkers(workspaceKey, targets, scopePages), 450); + window.setTimeout(() => updateWorkspacePreviewMarkers(workspaceKey, targets, scopeItems, activeScopeItem), 120); + window.setTimeout(() => updateWorkspacePreviewMarkers(workspaceKey, targets, scopeItems, activeScopeItem), 450); } function handleWorkspacePreviewLoad( workspaceKey: string, targets: WorkspaceTarget[], - scopePages: ProjectScanSummary["pages"] = [] + scopeItems: WorkspaceScopeItem[] = [], + activeScopeItem: WorkspaceScopeItem | null = null ) { - updateWorkspacePreviewMarkers(workspaceKey, targets, scopePages); - window.setTimeout(() => updateWorkspacePreviewMarkers(workspaceKey, targets, scopePages), 350); - window.setTimeout(() => updateWorkspacePreviewMarkers(workspaceKey, targets, scopePages), 1000); + updateWorkspacePreviewMarkers(workspaceKey, targets, scopeItems, activeScopeItem); + window.setTimeout(() => updateWorkspacePreviewMarkers(workspaceKey, targets, scopeItems, activeScopeItem), 350); + window.setTimeout(() => updateWorkspacePreviewMarkers(workspaceKey, targets, scopeItems, activeScopeItem), 1000); } function setAuditIssuesVisibleCount(scanId: string, visibleCount: number) { @@ -2614,15 +2735,15 @@ export function App() { delete nextIds[project.id]; return nextIds; }); - setActiveWorkspacePageIds((currentPageIds) => { - const nextPageIds = { ...currentPageIds }; - delete nextPageIds[project.id]; - return nextPageIds; + setActiveWorkspaceScopeIds((currentScopeIds) => { + const nextScopeIds = { ...currentScopeIds }; + delete nextScopeIds[project.id]; + return nextScopeIds; }); - setWorkspacePreviewPageIds((currentPageIds) => { - const nextPageIds = { ...currentPageIds }; - delete nextPageIds[project.id]; - return nextPageIds; + setWorkspacePreviewScopeIds((currentScopeIds) => { + const nextScopeIds = { ...currentScopeIds }; + delete nextScopeIds[project.id]; + return nextScopeIds; }); setPageWorkspaces((currentWorkspaces) => Object.fromEntries( @@ -2719,30 +2840,30 @@ export function App() { continue; } - const selectedWorkspacePages = getSelectedCoveragePages(scanSummary); - const selectedPage = selectedWorkspacePages[0]; + const selectedWorkspaceScopeItems = getSelectedWorkspaceScopeItems(scanSummary); + const selectedScopeItem = selectedWorkspaceScopeItems[0]; - if (!selectedPage) { + if (!selectedScopeItem) { continue; } - const activePageId = activeWorkspacePageIds[project.id] ?? selectedPage.pageId; - const pageToOpen = - selectedWorkspacePages.find((page) => page.pageId === activePageId) ?? - selectedPage; - const workspaceKey = getWorkspaceKey(project.id, pageToOpen.pageId); + const activeScopeId = activeWorkspaceScopeIds[project.id] ?? selectedScopeItem.scopeId; + const scopeItemToOpen = + selectedWorkspaceScopeItems.find((item) => item.scopeId === activeScopeId) ?? + selectedScopeItem; + const workspaceKey = getWorkspaceKey(project.id, scopeItemToOpen.scopeId); if (pageWorkspaces[workspaceKey] || busyWorkspaceKey === workspaceKey) { continue; } - void handleOpenPageWorkspace(project, pageToOpen, { - navigatePreview: !workspacePreviewPageIds[project.id], + void handleOpenPageWorkspace(project, scopeItemToOpen, { + navigatePreview: !workspacePreviewScopeIds[project.id], silent: true }); break; } - }, [activeWorkspacePageIds, busyWorkspaceKey, pageWorkspaces, projects, scanSummaries, workspacePreviewPageIds]); + }, [activeWorkspaceScopeIds, busyWorkspaceKey, pageWorkspaces, projects, scanSummaries, workspacePreviewScopeIds]); const createNameConflict = pickedFolder ? hasProjectNameConflict(pickedFolder.rootName) : false; const canCreateProject = Boolean(pickedFolder) && !isSubmitting && !createNameConflict; @@ -3325,23 +3446,26 @@ export function App() { const isSelectingPages = selectingPagesProjectId === project.id; const selectedPagesCount = scanSummary ? getSelectedPagesCount(scanSummary) : 0; const selectedPages = scanSummary ? scanSummary.pages.filter((page) => page.selected) : []; - const selectedCoveragePages = scanSummary ? getSelectedCoveragePages(scanSummary) : []; + const selectedWorkspaceScopeItems = scanSummary ? getSelectedWorkspaceScopeItems(scanSummary) : []; const scanScopeCounts = scanSummary ? getScanScopeCounts(scanSummary) : null; const projectSiteTitle = getProjectSiteTitle(scanSummary); const pageTreeGroups = scanSummary ? getPageTreeGroups(scanSummary.pages) : []; - const activeWorkspacePageId = activeWorkspacePageIds[project.id] ?? selectedCoveragePages[0]?.pageId ?? null; - const activeWorkspaceKey = activeWorkspacePageId ? getWorkspaceKey(project.id, activeWorkspacePageId) : null; - const activeWorkspace = activeWorkspaceKey ? pageWorkspaces[activeWorkspaceKey] : null; - const activeWorkspacePage = - selectedCoveragePages.find((page) => page.pageId === activeWorkspacePageId) ?? - scanSummary?.pages.find((page) => page.pageId === activeWorkspacePageId) ?? + const activeWorkspaceScopeId = + activeWorkspaceScopeIds[project.id] ?? selectedWorkspaceScopeItems[0]?.scopeId ?? null; + const activeWorkspaceScopeItem = + selectedWorkspaceScopeItems.find((item) => item.scopeId === activeWorkspaceScopeId) ?? + selectedWorkspaceScopeItems[0] ?? null; + const activeWorkspaceKey = activeWorkspaceScopeItem + ? getWorkspaceKey(project.id, activeWorkspaceScopeItem.scopeId) + : null; + const activeWorkspace = activeWorkspaceKey ? pageWorkspaces[activeWorkspaceKey] : null; const activeWorkspaceText = activeWorkspaceKey && activeWorkspace ? (workspaceDraftTexts[activeWorkspaceKey] ?? activeWorkspace.item.workingText) : ""; const isWorkspaceBusy = activeWorkspaceKey ? busyWorkspaceKey === activeWorkspaceKey : false; - const activeWorkspaceIssues = getPageIssues(scanSummary, activeWorkspacePageId); + const activeWorkspaceIssues = getPageIssues(scanSummary, activeWorkspaceScopeItem?.pageId ?? null); const activeWorkspaceTargets = activeWorkspace ? buildWorkspaceTargets(activeWorkspace, activeWorkspaceIssues) : []; const activeWorkspaceTargetId = activeWorkspaceKey && activeWorkspaceTargets.length > 0 @@ -3355,24 +3479,22 @@ export function App() { const activeWorkspacePreviewMode = activeWorkspaceKey ? (workspacePreviewModes[activeWorkspaceKey] ?? "desktop") : "desktop"; const activeWorkspaceDrawers = activeWorkspaceKey ? (workspaceDrawers[activeWorkspaceKey] ?? []) : []; const isWorkspacePageMenuOpen = activeWorkspaceKey ? Boolean(workspacePageMenus[activeWorkspaceKey]) : false; - const previewWorkspacePageId = workspacePreviewPageIds[project.id] ?? activeWorkspacePageId; - const previewWorkspacePage = - selectedCoveragePages.find((page) => page.pageId === previewWorkspacePageId) ?? - activeWorkspacePage; + const previewWorkspaceScopeId = workspacePreviewScopeIds[project.id] ?? activeWorkspaceScopeItem?.scopeId ?? null; + const previewWorkspaceScopeItem = + selectedWorkspaceScopeItems.find((item) => item.scopeId === previewWorkspaceScopeId) ?? + activeWorkspaceScopeItem; const activeWorkspacePageIndex = Math.max( 0, - selectedCoveragePages.findIndex((page) => page.pageId === activeWorkspacePageId) + selectedWorkspaceScopeItems.findIndex((item) => item.scopeId === activeWorkspaceScopeItem?.scopeId) ); const activeWorkspacePreviewSourcePath = - activeWorkspace && previewWorkspacePage - ? (previewWorkspacePage.previewSourcePath ?? previewWorkspacePage.sourcePath) + activeWorkspace && previewWorkspaceScopeItem + ? (previewWorkspaceScopeItem.previewSourcePath ?? previewWorkspaceScopeItem.sourcePath) : ""; - const activeWorkspacePreviewFocusSelector = previewWorkspacePage?.previewTargetSelector ?? null; + const activeWorkspacePreviewFocusSelector = previewWorkspaceScopeItem?.previewTargetSelector ?? null; + const activeWorkspacePreviewFocusIndex = previewWorkspaceScopeItem?.previewTargetIndex ?? null; const activeWorkspaceIssueCounts = getAuditIssueCounts(activeWorkspaceIssues); - const selectedCoverageIssueCount = selectedCoveragePages.reduce( - (total, page) => total + getPageIssues(scanSummary, page.pageId).length, - 0 - ); + const selectedCoverageIssueCount = getSelectedWorkspaceIssueCount(scanSummary, selectedWorkspaceScopeItems); const selectedAuditIssuesAll = scanSummary?.audit ? getSelectedAuditIssues(scanSummary, scanSummary.audit.issues) : []; @@ -3534,7 +3656,7 @@ export function App() { Инженерный список аудита
{scanSummary.pages.map((page) => { - const workspaceKey = getWorkspaceKey(project.id, page.pageId); + const workspaceKey = getWorkspaceKey(project.id, getPageScopeId(page)); const isOpeningPage = busyWorkspaceKey === workspaceKey; const auditDetailsKey = `${scanSummary.id}:${page.pageId}`; const isAuditDetailsOpen = Boolean(openPageAuditDetails[auditDetailsKey]); @@ -4584,34 +4706,34 @@ export function App() { Apply пока не трогаем
- {selectedCoveragePages.map((page) => { - const workspaceKey = getWorkspaceKey(project.id, page.pageId); - const isActivePage = activeWorkspacePageId === page.pageId; + {selectedWorkspaceScopeItems.map((scopeItem) => { + const workspaceKey = getWorkspaceKey(project.id, scopeItem.scopeId); + const isActivePage = activeWorkspaceScopeItem?.scopeId === scopeItem.scopeId; const isOpening = busyWorkspaceKey === workspaceKey; return ( ); })}
- {activeWorkspace && activeWorkspacePage && activeWorkspaceKey ? ( + {activeWorkspace && activeWorkspaceScopeItem && activeWorkspaceKey ? (
- Активная страница {activeWorkspacePageIndex + 1}/{selectedCoveragePages.length} · выбранный scope + Активная зона {activeWorkspacePageIndex + 1}/{selectedWorkspaceScopeItems.length} · выбранный scope - {getPageTitle(activeWorkspacePage)} - {getPagePathLabel(activeWorkspacePage)} + {activeWorkspaceScopeItem.title} + {activeWorkspaceScopeItem.pathLabel}
0 ? "drawer-open" : ""}`}> @@ -4633,7 +4755,7 @@ export function App() { Страница - {activeWorkspacePageIndex + 1}/{selectedCoveragePages.length} + {activeWorkspacePageIndex + 1}/{selectedWorkspaceScopeItems.length} @@ -4643,32 +4765,32 @@ export function App() { Страницы и секции scope Переключает preview, ошибки и поля рабочей зоны.
- {selectedCoveragePages.length > 0 ? ( + {selectedWorkspaceScopeItems.length > 0 ? (
- {selectedCoveragePages.map((page, pageIndex) => { - const pageWorkspaceKey = getWorkspaceKey(project.id, page.pageId); - const isActivePage = activeWorkspacePageId === page.pageId; + {selectedWorkspaceScopeItems.map((scopeItem, pageIndex) => { + const pageWorkspaceKey = getWorkspaceKey(project.id, scopeItem.scopeId); + const isActivePage = activeWorkspaceScopeItem?.scopeId === scopeItem.scopeId; const isOpening = busyWorkspaceKey === pageWorkspaceKey; - const pageIssues = getPageIssues(scanSummary, page.pageId); + const pageIssues = getPageIssues(scanSummary, scopeItem.pageId); return ( ); @@ -4689,7 +4811,7 @@ export function App() { > Scope - {selectedCoveragePages.length} + {selectedWorkspaceScopeItems.length} ); @@ -4902,7 +5027,11 @@ export function App() { className="tiny-action primary" disabled={isWorkspaceBusy} onClick={() => - void handleSavePageWorkspace(project.id, activeWorkspace.pageId) + void handleSavePageWorkspace( + project.id, + activeWorkspace.pageId, + activeWorkspaceKey + ) } type="button" > diff --git a/seo_mode/seo_mode/app/src/ProjectScopeFlow.tsx b/seo_mode/seo_mode/app/src/ProjectScopeFlow.tsx index 0971c5c..2ec1a98 100644 --- a/seo_mode/seo_mode/app/src/ProjectScopeFlow.tsx +++ b/seo_mode/seo_mode/app/src/ProjectScopeFlow.tsx @@ -1039,16 +1039,38 @@ function buildScopeFlow( } function getScopeStats(scan: ProjectScanSummary) { - return scan.pages.reduce( + const pageStats = scan.pages.reduce( (stats, page) => { stats.total += 1; if (page.scope === "indexable_page") stats.indexable += 1; + if (page.scope === "admin_page") stats.admin += 1; + if (page.scope === "technical_page") stats.technical += 1; if (page.selected && page.scope === "indexable_page") stats.selectedIndexable += 1; + if (page.selected && page.scope === "admin_page") stats.selectedAdmin += 1; + if (page.selected && page.scope === "technical_page") stats.selectedTechnical += 1; if (page.selected && page.scope !== "indexable_page") stats.selectedExcluded += 1; return stats; }, - { indexable: 0, selectedExcluded: 0, selectedIndexable: 0, total: 0 } + { + admin: 0, + indexable: 0, + selectedAdmin: 0, + selectedExcluded: 0, + selectedIndexable: 0, + selectedTechnical: 0, + technical: 0, + total: 0 + } ); + const selectedSections = scan.siteSections.filter((section) => section.selected && section.enabled && section.pageId).length; + const enabledSections = scan.siteSections.filter((section) => section.enabled && section.pageId).length; + + return { + ...pageStats, + enabledSections, + selectedSections, + selectedWorkspaceItems: pageStats.selectedIndexable + selectedSections + pageStats.selectedAdmin + pageStats.selectedTechnical + }; } function shouldAutosaveNodeChanges(changes: NodeChange[]) { @@ -1268,6 +1290,10 @@ export default function ProjectScopeFlow({ const groups = scopePriority .map((scope) => ({ count: scope === "template_block" ? scan.siteSections.length : scan.pages.filter((page) => page.scope === scope).length, + selected: + scope === "template_block" + ? scan.siteSections.filter((section) => section.selected && section.enabled && section.pageId).length + : scan.pages.filter((page) => page.scope === scope && page.selected).length, scope })) .filter((group) => group.count > 0); @@ -1278,15 +1304,16 @@ export default function ProjectScopeFlow({
Конфигурация проекта - Выбрано посадочных {stats.selectedIndexable}/{stats.indexable}; технических выбранных{" "} - {stats.selectedExcluded}. + Выбрано рабочих зон {stats.selectedWorkspaceItems}: посадочных {stats.selectedIndexable}/{stats.indexable}, + секций {stats.selectedSections}/{stats.enabledSections}, админка {stats.selectedAdmin}/{stats.admin}, + техслой {stats.selectedTechnical}/{stats.technical}.
-
@@ -1295,7 +1322,7 @@ export default function ProjectScopeFlow({ {groups.map((group) => ( - {scopeGroupNames[group.scope]} · {group.count} + {scopeGroupNames[group.scope]} · {group.selected}/{group.count} ))}
diff --git a/seo_mode/seo_mode/server/src/preview/routes.ts b/seo_mode/seo_mode/server/src/preview/routes.ts index 364c156..b87dde7 100644 --- a/seo_mode/seo_mode/server/src/preview/routes.ts +++ b/seo_mode/seo_mode/server/src/preview/routes.ts @@ -337,7 +337,7 @@ function buildPreviewBridgeScript() { return `