fix
This commit is contained in:
@@ -440,7 +440,16 @@ func (s *Server) handleGetProduct(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
}
|
||||
stripCatalogSEOMetaIfOmitted(processing.CompanyOmitsSEOMeta(r.Context(), s.Pool, cid), item)
|
||||
// Deliberately NOT stripped here. The omit rule hides SEO meta from list views
|
||||
// for cohorts that do not sell on it, but this payload feeds the review screen
|
||||
// and meta the pipeline actually generated has to be reviewable.
|
||||
//
|
||||
// The cohort flag still travels, so the panel can say "disabled for this
|
||||
// company" instead of "the AI generated nothing" — without it an empty block is
|
||||
// unexplained, which is exactly what made SEO meta look missing.
|
||||
if item != nil && processing.CompanyOmitsSEOMeta(r.Context(), s.Pool, cid) {
|
||||
item["seo_meta_omitted"] = true
|
||||
}
|
||||
JSON(w, http.StatusOK, item)
|
||||
}
|
||||
|
||||
@@ -467,6 +476,12 @@ func (s *Server) handleUpdateProduct(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// stripCatalogSEOMetaIfOmitted drops meta_title / meta_description from dashboard
|
||||
// product payloads when omit is true (same detection as V1 process / CompanyOmitsSEOMeta).
|
||||
//
|
||||
// Only EMPTY values are dropped. The omit cohort does not generate SEO meta, but
|
||||
// rows enriched before that rule (or by an earlier pipeline) still carry it, and
|
||||
// hiding stored copy from the dashboard meant Review could not show what the AI had
|
||||
// actually produced. Generation still respects the cohort — this only stops the
|
||||
// read path from concealing data that exists.
|
||||
func stripCatalogSEOMetaIfOmitted(omit bool, items ...map[string]any) {
|
||||
if !omit {
|
||||
return
|
||||
@@ -475,14 +490,15 @@ func stripCatalogSEOMetaIfOmitted(omit bool, items ...map[string]any) {
|
||||
if item == nil {
|
||||
continue
|
||||
}
|
||||
delete(item, "meta_title")
|
||||
delete(item, "meta_description")
|
||||
if loc, ok := item["localized_content"].(company.LocalizedContent); ok {
|
||||
for lang, fields := range loc {
|
||||
fields.MetaTitle = ""
|
||||
fields.MetaDescription = ""
|
||||
loc[lang] = fields
|
||||
}
|
||||
// Tell the client the cohort does not generate SEO meta, so an empty block
|
||||
// reads as "disabled here" instead of "the AI produced nothing".
|
||||
item["seo_meta_omitted"] = true
|
||||
if asMapString(item["meta_title"]) == "" {
|
||||
delete(item, "meta_title")
|
||||
}
|
||||
if asMapString(item["meta_description"]) == "" {
|
||||
delete(item, "meta_description")
|
||||
}
|
||||
// Localized meta is left exactly as stored, for the same reason.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,23 +19,53 @@ func catalogSEOItem() map[string]any {
|
||||
}
|
||||
}
|
||||
|
||||
// The omit cohort does not GENERATE SEO meta, but rows enriched before that rule
|
||||
// still carry it. Hiding stored copy meant Review could not show what the AI had
|
||||
// actually produced, so the read path now keeps non-empty values and only flags
|
||||
// that the cohort is opted out. Empty values are still dropped.
|
||||
func assertCatalogSEOOmitted(t *testing.T, item map[string]any) {
|
||||
t.Helper()
|
||||
if _, ok := item["meta_title"]; ok {
|
||||
t.Fatalf("must omit meta_title: %v", item)
|
||||
if item["seo_meta_omitted"] != true {
|
||||
t.Fatalf("omit cohort must be flagged so an empty block explains itself: %v", item)
|
||||
}
|
||||
if _, ok := item["meta_description"]; ok {
|
||||
t.Fatalf("must omit meta_description: %v", item)
|
||||
if item["meta_title"] != "T" || item["meta_description"] != "D" {
|
||||
t.Fatalf("stored meta must stay readable for Review: %v", item)
|
||||
}
|
||||
loc := item["localized_content"].(company.LocalizedContent)
|
||||
if loc["sl"].MetaTitle != "" || loc["sl"].MetaDescription != "" {
|
||||
t.Fatalf("localized meta leaked: %#v", loc["sl"])
|
||||
}
|
||||
if loc["sl"].ProcessedName != "N" {
|
||||
t.Fatalf("stripped too much: %#v", loc["sl"])
|
||||
}
|
||||
}
|
||||
|
||||
// Empty meta on an omit-cohort product is dropped, leaving only the flag — the UI
|
||||
// then says "disabled for this company" instead of showing two blank boxes.
|
||||
func TestStripCatalogSEOMetaIfOmitted_dropsEmptyValues(t *testing.T) {
|
||||
t.Parallel()
|
||||
other := uuid.MustParse("11111111-1111-1111-1111-111111111111")
|
||||
item := map[string]any{"name": "Widget", "meta_title": "", "meta_description": ""}
|
||||
stripCatalogSEOMetaIfOmitted(processing.CompanyOmitsSEOMetaLookup(other, "", "Platform Demo"), item)
|
||||
if _, ok := item["meta_title"]; ok {
|
||||
t.Fatalf("empty meta_title should be dropped: %v", item)
|
||||
}
|
||||
if item["seo_meta_omitted"] != true {
|
||||
t.Fatalf("omit flag missing: %v", item)
|
||||
}
|
||||
}
|
||||
|
||||
// An ordinary tenant is never flagged and never touched.
|
||||
func TestStripCatalogSEOMetaIfOmitted_ordinaryTenantUnflagged(t *testing.T) {
|
||||
t.Parallel()
|
||||
other := uuid.MustParse("11111111-1111-1111-1111-111111111111")
|
||||
item := catalogSEOItem()
|
||||
stripCatalogSEOMetaIfOmitted(processing.CompanyOmitsSEOMetaLookup(other, "", "Acme"), item)
|
||||
if _, ok := item["seo_meta_omitted"]; ok {
|
||||
t.Fatalf("ordinary company must not be flagged: %v", item)
|
||||
}
|
||||
if item["meta_title"] != "T" {
|
||||
t.Fatalf("ordinary company must keep meta: %v", item["meta_title"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestStripCatalogSEOMetaIfOmitted(t *testing.T) {
|
||||
t.Parallel()
|
||||
a1 := uuid.MustParse("604f23a8-b66e-4b21-8b45-0d72b68f4790")
|
||||
|
||||
Reference in New Issue
Block a user