Clarify keyword snapshot loading state

This commit is contained in:
DCCONSTRUCTIONS 2026-07-05 16:17:03 +03:00
parent 4d82de82c1
commit 3951a9cbc5
2 changed files with 85 additions and 11 deletions

View File

@ -4379,6 +4379,8 @@ function SeoAnchorReviewWorkspace({
actionKey,
activeFilter,
anchorReview,
contextProfileLabel,
contextProfileStatus,
demandCollectionProfile,
draftChangeCount,
draftDecisionCount,
@ -4400,6 +4402,8 @@ function SeoAnchorReviewWorkspace({
actionKey: string | null;
activeFilter: AnchorReviewFilter;
anchorReview: AnchorReviewContract;
contextProfileLabel: string;
contextProfileStatus: "current" | "loading" | "snapshot";
demandCollectionProfile: DemandCollectionProfile;
draftChangeCount: number;
draftDecisionCount: number;
@ -4441,6 +4445,10 @@ function SeoAnchorReviewWorkspace({
<div>
<strong>Этап 1 · Фиксация семантического базиса</strong>
<small>Выбираем смысловые якоря и вручную решаем, что отправлять в очередь Wordstat.</small>
<small className="keyword-stage-profile-line">
Текущий активный профиль: <strong>{contextProfileLabel}</strong>
{contextProfileStatus === "loading" ? " · загружается" : contextProfileStatus === "snapshot" ? " · снимок" : ""}
</small>
<small>
{getAnchorReviewStateLabel(anchorReview.state)} · очередь Wordstat {anchorReview.readiness.wordstatQueueCount} ·
нормализовано: {anchorReview.readiness.normalizedAnchorCount} · предложено:{" "}
@ -14288,11 +14296,17 @@ export function App() {
const activeKeywordContextSnapshotDetail = activeKeywordContextSnapshotKey
? (keywordContextSnapshotDetails[activeKeywordContextSnapshotKey] ?? null)
: null;
const isActiveKeywordContextSnapshotReplay = activeKeywordContextSnapshotId !== "current";
const isActiveKeywordContextSnapshotPending = Boolean(
isActiveKeywordContextSnapshotReplay && !activeKeywordContextSnapshotDetail
);
const isActiveKeywordContextSnapshotLoading = Boolean(
activeKeywordContextSnapshotKey && loadingKeywordContextSnapshotKey === activeKeywordContextSnapshotKey
isActiveKeywordContextSnapshotPending ||
(activeKeywordContextSnapshotKey && loadingKeywordContextSnapshotKey === activeKeywordContextSnapshotKey)
);
const activeSemanticAnalysis =
activeKeywordContextSnapshotDetail?.snapshot.semanticAnalysis ?? (activeProject ? (semanticAnalyses[activeProject.id] ?? null) : null);
activeKeywordContextSnapshotDetail?.snapshot.semanticAnalysis ??
(isActiveKeywordContextSnapshotPending ? null : activeProject ? (semanticAnalyses[activeProject.id] ?? null) : null);
const activeContextReviewRaw = activeProject ? (contextReviews[activeProject.id] ?? null) : null;
const activeContextReview =
activeSemanticAnalysis && activeContextReviewRaw?.semanticRunId === activeSemanticAnalysis.runId
@ -14300,10 +14314,11 @@ export function App() {
: null;
const activeMarketEnrichment =
activeKeywordContextSnapshotDetail?.snapshot.marketEnrichment ??
(activeProject ? (marketEnrichments[activeProject.id] ?? null) : null);
(isActiveKeywordContextSnapshotPending ? null : activeProject ? (marketEnrichments[activeProject.id] ?? null) : null);
const activeKeywordAnalysisWorkflow = activeProject ? (keywordAnalysisWorkflows[activeProject.id] ?? null) : null;
const activeKeywordMap =
activeKeywordContextSnapshotDetail?.snapshot.keywordMap ?? (activeProject ? (keywordMaps[activeProject.id] ?? null) : null);
activeKeywordContextSnapshotDetail?.snapshot.keywordMap ??
(isActiveKeywordContextSnapshotPending ? null : activeProject ? (keywordMaps[activeProject.id] ?? null) : null);
const activeQuotaProjectId = activeProject?.id ?? null;
useEffect(() => {
@ -14884,7 +14899,11 @@ export function App() {
!isPersistingActiveKeywordMap
);
const isSubmittingActiveKeywordStrategy = isPersistingActiveKeywordMap && persistingKeywordMapMode === "strategy";
const isActiveKeywordContextSnapshotReplay = activeKeywordContextSnapshotId !== "current";
const activeKeywordContextSnapshotSummary =
activeKeywordContextSnapshotId !== "current"
? (activeKeywordContextSnapshotList.find((snapshot) => snapshot.id === activeKeywordContextSnapshotId) ?? null)
: null;
const activeKeywordContextProfileLabel = activeKeywordContextSnapshotSummary?.label ?? "Текущий контекст";
const activeKeywordContextSnapshotOptions: NdcGlassSelectOption<string>[] = [
{ label: "Текущий контекст", value: "current" },
...activeKeywordContextSnapshotList.map((snapshot) => ({
@ -14933,7 +14952,7 @@ export function App() {
value={activeKeywordContextSnapshotId}
/>
{isActiveKeywordContextSnapshotReplay ? (
<span className="keyword-context-profile-status">
<span className="keyword-context-profile-status" title={activeKeywordContextProfileLabel}>
{isActiveKeywordContextSnapshotLoading ? "загружаю снимок" : "снимок"}
</span>
) : null}
@ -16467,8 +16486,26 @@ export function App() {
const projectKeywordContextSnapshotDetail = projectKeywordContextSnapshotKey
? (keywordContextSnapshotDetails[projectKeywordContextSnapshotKey] ?? null)
: null;
const projectKeywordContextSnapshotSummary =
projectKeywordContextSnapshotId !== "current"
? ((keywordContextSnapshots[project.id] ?? []).find(
(snapshot) => snapshot.id === projectKeywordContextSnapshotId
) ?? null)
: null;
const projectKeywordContextSnapshotPending = Boolean(
projectKeywordContextSnapshotId !== "current" && !projectKeywordContextSnapshotDetail
);
const projectKeywordContextProfileLabel =
projectKeywordContextSnapshotSummary?.label ?? "Текущий контекст";
const projectKeywordContextProfileStatus: "current" | "loading" | "snapshot" =
projectKeywordContextSnapshotId === "current"
? "current"
: projectKeywordContextSnapshotPending
? "loading"
: "snapshot";
const semanticAnalysis =
projectKeywordContextSnapshotDetail?.snapshot.semanticAnalysis ?? (semanticAnalyses[project.id] ?? null);
projectKeywordContextSnapshotDetail?.snapshot.semanticAnalysis ??
(projectKeywordContextSnapshotPending ? null : semanticAnalyses[project.id] ?? null);
const contextReview = contextReviews[project.id] ?? null;
const isContextReviewAligned = Boolean(
semanticAnalysis && contextReview && contextReview.semanticRunId === semanticAnalysis.runId
@ -16476,10 +16513,14 @@ export function App() {
const activeContextReview = isContextReviewAligned ? contextReview : null;
const projectOntology = projectOntologies[project.id] ?? null;
const marketEnrichment =
projectKeywordContextSnapshotDetail?.snapshot.marketEnrichment ?? (marketEnrichments[project.id] ?? null);
projectKeywordContextSnapshotDetail?.snapshot.marketEnrichment ??
(projectKeywordContextSnapshotPending ? null : marketEnrichments[project.id] ?? null);
const keywordCleaning =
projectKeywordContextSnapshotDetail?.snapshot.keywordCleaning ?? (keywordCleanings[project.id] ?? null);
const keywordMap = projectKeywordContextSnapshotDetail?.snapshot.keywordMap ?? (keywordMaps[project.id] ?? null);
projectKeywordContextSnapshotDetail?.snapshot.keywordCleaning ??
(projectKeywordContextSnapshotPending ? null : keywordCleanings[project.id] ?? null);
const keywordMap =
projectKeywordContextSnapshotDetail?.snapshot.keywordMap ??
(projectKeywordContextSnapshotPending ? null : keywordMaps[project.id] ?? null);
const seoStrategy = seoStrategies[project.id] ?? null;
const yandexEvidence = yandexEvidences[project.id] ?? null;
const serpInterpretation = serpInterpretations[project.id] ?? null;
@ -16507,7 +16548,8 @@ export function App() {
const isKeywordAnalysisRebuilding = isKeywordAnalysisWorkflowActive(keywordAnalysisWorkflow);
const anchorReviewDraft = anchorReviewDraftDecisions[project.id] ?? {};
const rawAnchorReview =
projectKeywordContextSnapshotDetail?.snapshot.anchorReview ?? marketEnrichment?.anchorReview ?? null;
projectKeywordContextSnapshotDetail?.snapshot.anchorReview ??
(projectKeywordContextSnapshotPending ? null : marketEnrichment?.anchorReview ?? null);
const anchorReviewDraftCount = getAnchorReviewDraftDecisionList(rawAnchorReview, anchorReviewDraft).length;
const savedDemandCollectionProfile = rawAnchorReview?.demandCollectionProfile ?? "contextual";
const anchorReviewDemandProfile =
@ -19536,6 +19578,15 @@ export function App() {
Прошло {keywordBasisElapsedLabel}. Обычно до 8 минут; окно можно оставить открытым.
</small>
</div>
) : projectKeywordContextSnapshotPending ? (
<div className="empty-state semantic-empty keyword-context-snapshot-loading">
<Loader2 className="spin" size={20} />
<strong>Загружаю сохранённый профиль</strong>
<span>{projectKeywordContextProfileLabel}</span>
<small>
Поднимаем сохранённые anchors, Wordstat evidence, cleaning и карту ключей из snapshot.
</small>
</div>
) : (
<>
{semanticAnalysis && !activeContextReview ? (
@ -19566,6 +19617,8 @@ export function App() {
actionKey={anchorReviewActionKey}
activeFilter={activeAnchorReviewFilter}
anchorReview={anchorReview}
contextProfileLabel={projectKeywordContextProfileLabel}
contextProfileStatus={projectKeywordContextProfileStatus}
draftDecisionCount={anchorReviewDraftCount}
demandCollectionProfile={anchorReviewDemandProfile}
draftChangeCount={anchorReviewDraftChangeCount}

View File

@ -2985,6 +2985,18 @@ input {
color: var(--ink);
}
.keyword-stage-heading .keyword-stage-profile-line {
color: rgba(10, 12, 14, 0.72);
}
.keyword-stage-heading .keyword-stage-profile-line strong {
display: inline;
color: var(--ink);
font-size: inherit;
font-weight: 900;
line-height: inherit;
}
.keyword-stage-actions {
display: inline-flex;
flex: 0 0 auto;
@ -3117,6 +3129,15 @@ input {
overflow-wrap: anywhere;
}
.keyword-context-snapshot-loading {
min-height: 14rem;
}
.keyword-context-snapshot-loading span {
max-width: min(48rem, 100%);
overflow-wrap: anywhere;
}
.keyword-workflow-step-list {
display: grid;
gap: 7px;