Rework SEO analytics workspace UX
This commit is contained in:
parent
6ed1df6803
commit
54398f86d7
|
|
@ -0,0 +1,79 @@
|
||||||
|
# NDC SEO mod: UX contract стадий 5-8
|
||||||
|
|
||||||
|
Этот документ фиксирует продуктовый смысл середины SEO-pipeline до model review. Цель: оставить Dev Mode доказательным, но не превращать GUI в сырую консоль SEO-инженера.
|
||||||
|
|
||||||
|
## Stage 5: Рыночный спрос
|
||||||
|
|
||||||
|
Вопрос пользователя: есть ли реальный поисковый спрос по темам сайта?
|
||||||
|
|
||||||
|
Основная ценность:
|
||||||
|
- показать, какие semantic seeds подтверждены внешним market evidence;
|
||||||
|
- показать, какие фразы слабые, шумные, related-only или пустые;
|
||||||
|
- отделить deterministic site meaning от реального спроса;
|
||||||
|
- подготовить только evidence-backed inputs для SEO-плана страниц.
|
||||||
|
|
||||||
|
Dev-details могут показывать Wordstat job, provider readiness, raw rows и external-service gaps. Основной экран не должен выглядеть так, будто строки Wordstat уже являются финальными SEO-решениями.
|
||||||
|
|
||||||
|
Выход этапа:
|
||||||
|
- подтвержденные сигналы спроса;
|
||||||
|
- фразы, требующие проверки;
|
||||||
|
- фразы, которые нельзя двигать дальше;
|
||||||
|
- недостающие evidence для уверенного решения.
|
||||||
|
|
||||||
|
## Stage 6: SEO-план страниц
|
||||||
|
|
||||||
|
Вопрос пользователя: что именно SEO-модуль будет улучшать, где и почему?
|
||||||
|
|
||||||
|
Основная ценность:
|
||||||
|
- разложить approved phrases по страницам, секциям и полям;
|
||||||
|
- определить роль: главный интент, дополнительный, поддержка или проверка;
|
||||||
|
- задать лимиты естественного использования;
|
||||||
|
- сохранить причину и evidence-link для каждого решения;
|
||||||
|
- собрать контролируемый contract перед diff/model review.
|
||||||
|
|
||||||
|
Это не ручная таблица ключей. Пользователь подтверждает или корректирует предложенные назначения.
|
||||||
|
|
||||||
|
Выход этапа:
|
||||||
|
- page-level SEO plan;
|
||||||
|
- field-level bindings: title, description, H1, body section;
|
||||||
|
- materialization queue для planned pages, которых нет в scan;
|
||||||
|
- blockers перед rewrite diff.
|
||||||
|
|
||||||
|
## Stage 7: Model review
|
||||||
|
|
||||||
|
Вопрос пользователя: корректны ли предложенные изменения текста, не уехал ли смысл и есть ли польза?
|
||||||
|
|
||||||
|
Основная ценность:
|
||||||
|
- ревьюить model-generated variants только после approved SEO-плана;
|
||||||
|
- показывать before/after diff по полям и секциям;
|
||||||
|
- объяснять, какой intent или keyword закрывает каждое изменение;
|
||||||
|
- держать в контексте forbidden claims, project ontology, tone и style limits;
|
||||||
|
- требовать human acceptance перед validation/apply.
|
||||||
|
|
||||||
|
Выбор модели, provider, plugins и runtime намеренно не решается здесь. Это обсуждаем отдельно только когда diff contract и review interface будут готовы.
|
||||||
|
|
||||||
|
Выход этапа:
|
||||||
|
- accepted/rejected/edited rewrite variants;
|
||||||
|
- unresolved model risks;
|
||||||
|
- final working draft candidates для validation.
|
||||||
|
|
||||||
|
## Stage 8: Quality gate
|
||||||
|
|
||||||
|
Вопрос пользователя: можно ли безопасно применять эти изменения?
|
||||||
|
|
||||||
|
Основная ценность:
|
||||||
|
- блокировать apply на hard SEO, meaning, style или safety errors;
|
||||||
|
- отделять soft warnings от blockers;
|
||||||
|
- проверять, что не появились unsupported claims или legacy context;
|
||||||
|
- проверять anti-spam, ru-text, structure и pending diff state;
|
||||||
|
- давать ясный final apply readiness verdict.
|
||||||
|
|
||||||
|
Выход этапа:
|
||||||
|
- can apply / cannot apply;
|
||||||
|
- hard blockers;
|
||||||
|
- accepted warnings;
|
||||||
|
- changeset candidate для dry-run/apply.
|
||||||
|
|
||||||
|
## Product rule
|
||||||
|
|
||||||
|
Dev Mode может показывать raw data, но каждый видимый stage сначала должен отвечать на пользовательское решение. Raw evidence уходит в drill-down details, а не становится главной ценностью экрана.
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -14,7 +14,19 @@ import {
|
||||||
useReactFlow
|
useReactFlow
|
||||||
} from "@xyflow/react";
|
} from "@xyflow/react";
|
||||||
import "@xyflow/react/dist/style.css";
|
import "@xyflow/react/dist/style.css";
|
||||||
import { FileText, Globe2, Layers3, LockKeyhole, Maximize2, Minus, PanelTop, Plus, RefreshCw, Save, Settings2 } from "lucide-react";
|
import {
|
||||||
|
FileText,
|
||||||
|
Globe2,
|
||||||
|
Layers3,
|
||||||
|
LockKeyhole,
|
||||||
|
Maximize2,
|
||||||
|
Minus,
|
||||||
|
PanelTop,
|
||||||
|
Plus,
|
||||||
|
RefreshCw,
|
||||||
|
Save,
|
||||||
|
Settings2
|
||||||
|
} from "lucide-react";
|
||||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import {
|
import {
|
||||||
fetchProjectScopeLayout,
|
fetchProjectScopeLayout,
|
||||||
|
|
@ -1079,8 +1091,6 @@ function shouldAutosaveNodeChanges(changes: NodeChange<ScopeFlowNode>[]) {
|
||||||
|
|
||||||
export default function ProjectScopeFlow({
|
export default function ProjectScopeFlow({
|
||||||
isBusy,
|
isBusy,
|
||||||
onApplyRecommendedScope,
|
|
||||||
onClearSelection,
|
|
||||||
onTogglePage,
|
onTogglePage,
|
||||||
projectId,
|
projectId,
|
||||||
projectName,
|
projectName,
|
||||||
|
|
@ -1287,41 +1297,38 @@ export default function ProjectScopeFlow({
|
||||||
}, [scheduleLayoutAutosave, selectionSignature]);
|
}, [scheduleLayoutAutosave, selectionSignature]);
|
||||||
|
|
||||||
const nodeTypes = useMemo(() => ({ scopeNode: ScopeNode }), []);
|
const nodeTypes = useMemo(() => ({ scopeNode: ScopeNode }), []);
|
||||||
const groups = scopePriority
|
const scopeDashboardItems = [
|
||||||
.map((scope) => ({
|
{
|
||||||
count: scope === "template_block" ? scan.siteSections.length : scan.pages.filter((page) => page.scope === scope).length,
|
icon: <FileText size={13} />,
|
||||||
selected:
|
key: "indexable",
|
||||||
scope === "template_block"
|
label: "Публичные страницы",
|
||||||
? scan.siteSections.filter((section) => section.selected && section.enabled && section.pageId).length
|
value: `${stats.selectedIndexable}/${stats.indexable}`
|
||||||
: scan.pages.filter((page) => page.scope === scope && page.selected).length,
|
},
|
||||||
scope
|
{
|
||||||
}))
|
icon: <Layers3 size={13} />,
|
||||||
.filter((group) => group.count > 0);
|
key: "sections",
|
||||||
|
label: "Секции главной",
|
||||||
|
value: `${stats.selectedSections}/${scan.siteSections.length}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <LockKeyhole size={13} />,
|
||||||
|
key: "admin",
|
||||||
|
label: "Админка",
|
||||||
|
value: `${stats.selectedAdmin}/${stats.admin}`
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="project-scope-flow-panel">
|
<section className="project-scope-flow-panel">
|
||||||
<div className="project-scope-flow-head">
|
<div className="project-scope-flow-canvas">
|
||||||
<strong>Конфигурация проекта</strong>
|
<div className="project-scope-flow-stats nodrag nopan">
|
||||||
</div>
|
{scopeDashboardItems.map((item) => (
|
||||||
<div className="project-scope-flow-controls">
|
<span key={item.key}>
|
||||||
<div className="project-scope-flow-legend">
|
{item.icon}
|
||||||
{groups.map((group) => (
|
{item.label} · {item.value}
|
||||||
<span className={`scope-legend-item ${group.scope}`} key={group.scope}>
|
|
||||||
<FileText size={13} />
|
|
||||||
{scopeGroupNames[group.scope]} · {group.selected}/{group.count}
|
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="project-scope-flow-actions">
|
|
||||||
<button className="active" disabled={isBusy} onClick={onApplyRecommendedScope} type="button">
|
|
||||||
SEO scope
|
|
||||||
</button>
|
|
||||||
<button disabled={isBusy || stats.selectedWorkspaceItems === 0} onClick={onClearSelection} type="button">
|
|
||||||
Снять все
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="project-scope-flow-canvas">
|
|
||||||
<ReactFlowProvider>
|
<ReactFlowProvider>
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
colorMode="light"
|
colorMode="light"
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue