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
|
||||
} from "@xyflow/react";
|
||||
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 {
|
||||
fetchProjectScopeLayout,
|
||||
|
|
@ -1079,8 +1091,6 @@ function shouldAutosaveNodeChanges(changes: NodeChange<ScopeFlowNode>[]) {
|
|||
|
||||
export default function ProjectScopeFlow({
|
||||
isBusy,
|
||||
onApplyRecommendedScope,
|
||||
onClearSelection,
|
||||
onTogglePage,
|
||||
projectId,
|
||||
projectName,
|
||||
|
|
@ -1287,41 +1297,38 @@ export default function ProjectScopeFlow({
|
|||
}, [scheduleLayoutAutosave, selectionSignature]);
|
||||
|
||||
const nodeTypes = useMemo(() => ({ scopeNode: ScopeNode }), []);
|
||||
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);
|
||||
const scopeDashboardItems = [
|
||||
{
|
||||
icon: <FileText size={13} />,
|
||||
key: "indexable",
|
||||
label: "Публичные страницы",
|
||||
value: `${stats.selectedIndexable}/${stats.indexable}`
|
||||
},
|
||||
{
|
||||
icon: <Layers3 size={13} />,
|
||||
key: "sections",
|
||||
label: "Секции главной",
|
||||
value: `${stats.selectedSections}/${scan.siteSections.length}`
|
||||
},
|
||||
{
|
||||
icon: <LockKeyhole size={13} />,
|
||||
key: "admin",
|
||||
label: "Админка",
|
||||
value: `${stats.selectedAdmin}/${stats.admin}`
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="project-scope-flow-panel">
|
||||
<div className="project-scope-flow-head">
|
||||
<strong>Конфигурация проекта</strong>
|
||||
</div>
|
||||
<div className="project-scope-flow-controls">
|
||||
<div className="project-scope-flow-legend">
|
||||
{groups.map((group) => (
|
||||
<span className={`scope-legend-item ${group.scope}`} key={group.scope}>
|
||||
<FileText size={13} />
|
||||
{scopeGroupNames[group.scope]} · {group.selected}/{group.count}
|
||||
<div className="project-scope-flow-canvas">
|
||||
<div className="project-scope-flow-stats nodrag nopan">
|
||||
{scopeDashboardItems.map((item) => (
|
||||
<span key={item.key}>
|
||||
{item.icon}
|
||||
{item.label} · {item.value}
|
||||
</span>
|
||||
))}
|
||||
</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>
|
||||
<ReactFlow
|
||||
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