Surface Slovenian A1 content in prompt page; purge English defaults

Three bugs hid the split A1 data behind English junk:

1. /api/auth/me company was only {id, name} — me.company.language never
   existed, so the prompt/formula pages ALWAYS fell back to English as
   the primary content language and never showed the Slovenian ("sl")
   prompt sections by default. handleMe now enriches the active company
   with language + content_languages from the companies table.

2. The unified prompt page injected English default texts: meta title /
   meta description fields were pre-filled via getDefaultMetaTitle/
   Description (and silently SAVED on Save), new description sections
   started with English instructions, and type changes overwrote user
   text with English defaults. All removed — empty stays empty
   (placeholders only), stored tenant content is shown exactly as-is,
   meta-only templates now save (sections no longer required).

3. Sync A1 / repair left English formula artifacts on categories with no
   seed content ("Audio video" etc.): earlier repairs backfilled
   DefaultRetailTitleFormula and cats.json-style English description
   sections onto every category. Repair now (a) never invents a default
   title formula — formulas derive ONLY from legacy naming rules — and
   (b) with the seed as source of truth clears title_template and
   description_template on non-seed categories (new
   title/description_template_cleared counters).

Locally verified end-to-end: corrupted Platform Demo Monitorji (legacy
combined blob) + Audio video (English junk), called the real admin
POST /companies/{id}/sync-a1 as the platform admin — Monitorji restored
to the exact split Slovenian Title/Description/Meta + formulas +
Slovenian meta title/description, Audio video fully cleared; repair
idempotent (0 on re-run); me.company.language returns sl for A1;
zero English-default templates remain in A1 + Platform Demo.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 01:49:44 +02:00
co-authored by Claude Fable 5
parent c9554f2f7c
commit 0ff24b1534
3 changed files with 94 additions and 51 deletions
@@ -10,9 +10,6 @@
buildTemplateToSave,
elementId,
generatePreviewElements,
getDefaultInstructions,
getDefaultMetaDescription,
getDefaultMetaTitle,
parseDescriptionTemplate,
parseTemplateToFormula
} from "$lib/categories/formula";
@@ -137,10 +134,13 @@
let confirmOpen = $state(false);
let itemToDelete = $state<{ type: string; id: string } | null>(null);
// Description formula (categories.description_template) — same editor as /description-formula.
// Description formula (categories.description_template) — same editor as
// /description-formula, but NEVER pre-filled with English default texts:
// empty stays empty (placeholders only) so stored Slovenian/tenant content
// is shown exactly as-is and defaults are never silently saved.
let descSections = $state<DescriptionSection[]>([]);
let metaTitle = $state(getDefaultMetaTitle());
let metaDescription = $state(getDefaultMetaDescription());
let metaTitle = $state("");
let metaDescription = $state("");
let dragIndex = $state<number | null>(null);
const langLabel = $derived(
@@ -224,8 +224,14 @@
titleFormula = parseTemplateToFormula(cat.title_template, customVariables);
const parsedDesc = parseDescriptionTemplate(cat.description_template);
descSections = parsedDesc.sections;
metaTitle = parsedDesc.metaTitle;
metaDescription = parsedDesc.metaDescription;
// Raw stored values only — parseDescriptionTemplate injects English
// defaults for missing meta fields, which must never be displayed or saved.
const rawTpl = (cat.description_template ?? {}) as {
metaTitle?: unknown;
metaDescription?: unknown;
};
metaTitle = typeof rawTpl.metaTitle === "string" ? rawTpl.metaTitle : "";
metaDescription = typeof rawTpl.metaDescription === "string" ? rawTpl.metaDescription : "";
}
function onLangChange(code: string) {
@@ -374,10 +380,9 @@
// ----- Description formula editing -----
function addDescSection() {
descSections = [
...descSections,
{ id: crypto.randomUUID(), type: "p", instructions: getDefaultInstructions("p") }
];
// New sections start EMPTY — placeholder text guides the user; no
// English default instructions are ever inserted into stored content.
descSections = [...descSections, { id: crypto.randomUUID(), type: "p", instructions: "" }];
}
function removeDescSection(id: string) {
@@ -385,14 +390,9 @@
}
function updateDescSection(id: string, updates: Partial<DescriptionSection>) {
descSections = descSections.map((section) => {
if (section.id !== id) return section;
const next = { ...section, ...updates };
if (updates.type && updates.type !== section.type) {
next.instructions = getDefaultInstructions(updates.type);
}
return next;
});
descSections = descSections.map((section) =>
section.id === id ? { ...section, ...updates } : section
);
}
function reorderDescSections(from: number, to: number) {
@@ -403,11 +403,14 @@
}
function buildDescriptionTemplate() {
if (descSections.length === 0) return null;
const keep = descSections.filter((s) => s.instructions.trim() || s.exportId?.trim());
const mt = metaTitle.trim();
const md = metaDescription.trim();
if (keep.length === 0 && !mt && !md) return null;
return {
sections: descSections,
metaTitle: metaTitle.trim() || undefined,
metaDescription: metaDescription.trim() || undefined
sections: keep,
metaTitle: mt || undefined,
metaDescription: md || undefined
};
}
@@ -820,19 +823,9 @@
rows="2"
placeholder={i18n.t("categories.seoTitlePlaceholder")}
></textarea>
<div class="flex items-center justify-between">
<p class="text-xs text-muted-foreground">
{i18n.t("categories.metaTitleHint")}
</p>
<Button
variant="ghost"
size="sm"
class="text-xs"
onclick={() => (metaTitle = getDefaultMetaTitle())}
>
{i18n.t("categories.resetToDefault")}
</Button>
</div>
<p class="text-xs text-muted-foreground">
{i18n.t("categories.metaTitleHint")}
</p>
</div>
<div class="space-y-2">
<Label for="metaDescription">{i18n.t("categories.metaDescriptionFormula")}</Label>
@@ -843,19 +836,9 @@
rows="3"
placeholder={i18n.t("categories.seoDescriptionPlaceholder")}
></textarea>
<div class="flex items-center justify-between">
<p class="text-xs text-muted-foreground">
{i18n.t("categories.metaDescriptionHint")}
</p>
<Button
variant="ghost"
size="sm"
class="text-xs"
onclick={() => (metaDescription = getDefaultMetaDescription())}
>
{i18n.t("categories.resetToDefault")}
</Button>
</div>
<p class="text-xs text-muted-foreground">
{i18n.t("categories.metaDescriptionHint")}
</p>
</div>
</CardContent>
</Card>