feat: Site audit and Crawls screens with their MCP tools

- golem15.seo.issues (Site audit): current issues errors first, labels,
  summary and fix columns, filters, P7 issue_details, Check this address
  again, Ignore, Stop ignoring, bulk Ignore, ignored rows badged
- golem15.seo.crawls (Crawls): newest first, P6 crawl_summary with one
  status callout, staging and rendered-comparison lines, stats strip and
  per-kind table; Cancel; a running crawl refuses delete
- Start crawl toolbar action on both, refusing already_running and
  no_site_url; navigation Site audit (gauge) and Crawls (globe)
- routes and MCP tools start/cancel/list/get/delete crawls, list_issues
  with filters (open by default), recheck_url, ignore and unignore
- AdminOperations maps both controllers; toolbar actions get their own
  operation kind
- TestCrawlAdmin, TestCrawlTools, TestIssueKindLabels
This commit is contained in:
Jakub Zych
2026-11-04 15:34:22 +01:00
parent edc03cae4b
commit c4f0b4f94c
32 changed files with 2778 additions and 20 deletions

View File

@@ -0,0 +1,48 @@
{{- with .Data -}}
{{- if .Rows -}}
<p><strong>{{ trans "golem15.seo::lang.issues.found_heading" }}</strong></p>
<div class="summer-table-wrap">
<table class="summer-table">
<tbody>
{{- range .Rows -}}
<tr><th scope="row">{{ .Label }}</th><td>{{ if .Mono }}<code>{{ .Value }}</code>{{ else }}{{ .Value }}{{ end }}</td></tr>
{{- end -}}
</tbody>
</table>
</div>
{{- end -}}
{{- if .LinkedFrom -}}
<p>{{ trans "golem15.seo::lang.issues.linked_from" }}</p>
<ul>
{{- range .LinkedFrom -}}<li><code>{{ .URL }}</code></li>{{- end -}}
</ul>
{{- if .More -}}<p class="summer-table__sub">{{ .More }}</p>{{- end -}}
{{- end -}}
{{- if .Orphan -}}
<p>{{ trans "golem15.seo::lang.issues.orphan_suggestions" }}</p>
{{- if .Suggestions -}}
<div class="summer-table-wrap">
<table class="summer-table">
<thead><tr><th>{{ trans "golem15.seo::lang.issues.orphan_suggestions_page" }}</th><th>{{ trans "golem15.seo::lang.issues.orphan_suggestions_phrase" }}</th></tr></thead>
<tbody>
{{- range .Suggestions -}}
<tr><td><code>{{ .URL }}</code></td><td>{{ .Phrase }}</td></tr>
{{- end -}}
</tbody>
</table>
</div>
{{- else -}}
<p class="summer-table__sub">{{ trans "golem15.seo::lang.issues.orphan_none" }}</p>
{{- end -}}
{{- end -}}
<div class="summer-callout" role="status">
<p class="summer-callout__title">{{ trans "golem15.seo::lang.issues.fix_heading" }}</p>
<p class="summer-callout__text">{{ .FixText }}</p>
</div>
{{- if .PageURL -}}
<p>{{ trans "golem15.seo::lang.issues.open_page" }}: <code>{{ .PageURL }}</code></p>
{{- end -}}
{{- if .RichResults -}}
<p>{{ trans "golem15.seo::lang.issues.rich_results" }}: <code>{{ .RichURL }}</code></p>
{{- end -}}
{{- end -}}

View File

@@ -0,0 +1,28 @@
# Site audit filters (UI-SPEC C23): severity, kind, source, fix and status
# (Open or Ignored).
scopes:
severity:
label: golem15.seo::lang.issues.filter_severity
modelClass: Golem15\SEO\Models\Issue
nameFrom: severity
scope: FilterIssueSeverity
kind:
label: golem15.seo::lang.issues.filter_kind
modelClass: Golem15\SEO\Models\Issue
nameFrom: kind
scope: FilterIssueKind
source:
label: golem15.seo::lang.issues.filter_source
modelClass: Golem15\SEO\Models\Issue
nameFrom: source
scope: FilterIssueSource
fix:
label: golem15.seo::lang.issues.filter_fix
modelClass: Golem15\SEO\Models\Issue
nameFrom: fix
scope: FilterIssueFix
status:
label: golem15.seo::lang.issues.filter_status
modelClass: Golem15\SEO\Models\Issue
nameFrom: url
scope: FilterIssueStatus

View File

@@ -0,0 +1,14 @@
# Issue preview (UI-SPEC C23): read-only, P7 above the fields and the
# record actions Check this address again, Ignore and Stop ignoring.
name: golem15.seo::lang.issues.name
form: ~/plugins/golem15/seo/models/issue/fields.yaml
modelClass: Golem15\SEO\Models\Issue
defaultRedirect: golem15/seo/issues
preview:
headerPartial: issue_details
recordActions: [recheck, ignore, unignore]
messages:
preview: golem15.seo::lang.issues.preview

View File

@@ -0,0 +1,25 @@
# Site audit (UI-SPEC C23): the current issues, errors first (the
# controller's ListExtendQuery), then by kind; ignored issues badged.
list: ~/plugins/golem15/seo/models/issue/columns.yaml
modelClass: Golem15\SEO\Models\Issue
title: golem15.seo::lang.issues.list_title
recordUrl: golem15/seo/issues/preview/:id
noRecordsMessage: golem15.seo::lang.issues.list_empty
recordsPerPage: 50
showCheckboxes: true
showSetup: true
filter: config_filter.yaml
bulkActions: [ignore]
defaultSort:
column: kind
direction: asc
toolbar:
buttons: [start_crawl]
search:
prompt: backend::lang.list.search_prompt
messages:
empty: golem15.seo::lang.issues.list_empty
rowStateDisabled: golem15.seo::lang.issues.state_ignored