feat: SEO field types and the complete SEO tab

This commit is contained in:
2026-10-27 11:04:03 +01:00
parent deebd9aa65
commit 93ddbcc9ef
26 changed files with 1827 additions and 50 deletions

View File

@@ -93,6 +93,47 @@ func (c rulesAdminController) ListExtendRecords(ctx context.Context, _ *gorm.DB,
return nil
}
// service returns the SEO service of the booted app, nil before boot.
func (c rulesAdminController) service() *classes.Service {
svc, err := classes.NewService(appOf(c.app))
if err != nil {
return nil
}
return svc
}
// AdminJS loads the SEO element bundle on the rules screens (the result
// preview widget).
func (rulesAdminController) AdminJS() []string { return []string{"assets/dist/seo.js"} }
// AdminCSS loads the SEO element styles on the rules screens.
func (rulesAdminController) AdminCSS() []string { return []string{"assets/dist/seo.css"} }
// AdminActions registers the result preview's widget action (E3).
func (c rulesAdminController) AdminActions() []pact.AdminAction {
return []pact.AdminAction{classes.RuleSnippetAction(c.service, "golem15.seo::lang.snippet.action", PermissionManage)}
}
// PartialData serves P1 seo_status (canonical warnings) and P2
// seo_effective of a rule.
func (c rulesAdminController) PartialData(ctx context.Context, name string, record any) (any, error) {
svc := c.service()
r, ok := record.(*models.Rule)
if svc == nil || !ok || r == nil {
if name == "seo_effective" {
return classes.EffectiveView{}, nil
}
return classes.StatusView{}, nil
}
switch name {
case "seo_status":
return svc.RuleStatus(ctx, *r), nil
case "seo_effective":
return svc.RuleEffective(ctx, *r)
}
return nil, nil
}
// FilterOptions serves the match filter's two choices.
func (rulesAdminController) FilterOptions(scope string) []pact.Option {
if scope != models.FilterMatch {
@@ -113,4 +154,7 @@ var (
_ pact.ListExtendRecords = rulesAdminController{}
_ pact.FilterOptions = rulesAdminController{}
_ pact.FormAfterDelete = rulesAdminController{}
_ pact.HasAdminActions = rulesAdminController{}
_ pact.AdminPartialData = rulesAdminController{}
_ pact.AdminClientAssets = rulesAdminController{}
)

View File

@@ -0,0 +1,24 @@
{{- if .Data.Staging -}}
<div class="summer-callout summer-callout--warning" role="status">
<p class="summer-callout__text">{{ .Data.StagingText }}</p>
</div>
{{- end -}}
{{- range .Data.Locales -}}
<section>
<h4>{{ .Heading }}</h4>
{{- if .NoURL -}}
<p>{{ .NoURLText }}</p>
{{- else -}}
<div class="summer-table-wrap">
<table class="summer-table">
<thead><tr><th>{{ $.Data.PropertyLabel }}</th><th>{{ $.Data.ValueLabel }}</th><th>{{ $.Data.SourceLabel }}</th></tr></thead>
<tbody>
{{- range .Rows -}}
<tr><td>{{ .Property }}</td><td>{{ if .Mono }}<code>{{ .Value }}</code>{{ else }}{{ .Value }}{{ end }}</td><td>{{ .Source }}</td></tr>
{{- end -}}
</tbody>
</table>
</div>
{{- end -}}
</section>
{{- end -}}

View File

@@ -0,0 +1,6 @@
{{- range .Data.Callouts -}}
<div class="summer-callout summer-callout--warning" role="status">
<p class="summer-callout__title">{{ .Title }}</p>
{{- if .Text }}<p class="summer-callout__text">{{ .Text }}</p>{{ end }}
</div>
{{- end -}}

View File

@@ -101,13 +101,21 @@ type stagingView struct {
Staging bool
}
// PartialData serves the staging callout: shown outside production, where
// every page is noindex whatever the settings say (D-22).
func (c settingsAdminController) PartialData(_ context.Context, name string, _ any) (any, error) {
if name != "staging" {
return nil, nil
// PartialData serves the staging callout, shown outside production where
// every page is noindex whatever the settings say (D-22), and the P3
// placeholder table.
func (c settingsAdminController) PartialData(ctx context.Context, name string, _ any) (any, error) {
switch name {
case "staging":
return stagingView{Staging: IsStaging(appOf(c.app))}, nil
case "placeholders":
svc, err := classes.NewService(appOf(c.app))
if err != nil {
return nil, err
}
return svc.Placeholders(ctx), nil
}
return stagingView{Staging: IsStaging(appOf(c.app))}, nil
return nil, nil
}
// IsStaging reports whether the application runs outside production. It

View File

@@ -0,0 +1,11 @@
<div class="summer-table-wrap">
<table class="summer-table">
<caption>{{ .Data.Caption }}</caption>
<thead><tr><th>{{ .Data.CodeLabel }}</th><th>{{ .Data.MeaningLabel }}</th></tr></thead>
<tbody>
{{- range .Data.Rows -}}
<tr><td><code>{{ .Code }}</code></td><td>{{ .Meaning }}</td></tr>
{{- end -}}
</tbody>
</table>
</div>