feat(inhalte): text placeholder {{USt-IdNr}} for the VAT ID
Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 2m53s

Admin texts can now contain the placeholder {{USt-IdNr}}. Wherever
content is read (lib/content.ts), it is replaced with the VAT ID
maintained under Admin > Einstellungen > Firmendaten, so every text
follows a change automatically. The placeholder is matched case- and
space-insensitively; an empty VAT ID inserts nothing, and unknown
placeholders stay visible so typos show up. Images, videos, select
fields and the source field itself are never rewritten.

The default imprint text uses the placeholder instead of a hard-coded
number, and the settings page lists the available placeholders.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-10-01 00:59:04 +02:00
1 parent 77bdd9958c
commit f71bcb2b67
8 files changed
+176 -14

No files matched your search

+26
View File
@@ -3,10 +3,12 @@ import { BackLink } from "@/components/back-link";
import { AdminBadge } from "@/components/admin-list";
import type { ContentFieldDef } from "@/lib/content-registry";
import { readIntegrationStatus } from "@/lib/integration-status";
import { TEXT_PLACEHOLDERS } from "@/lib/text-placeholders";
import { SETTINGS_PATH, SETTINGS_TABS, parseSettingsTab, type SettingsTabId } from "@/lib/settings-tabs";
import { getLocale } from "@/i18n/locale";
import { getDictionary } from "@/i18n/get-dictionary";
import type { Dictionary } from "@/i18n/dictionary-type";
import type { Locale } from "@/i18n/config";
import { GroupForm, fieldsForGroup, loadCurrentValues } from "./content-form";
import { SmtpTestPanel } from "./smtp-test-panel";
@@ -76,6 +78,7 @@ export default async function AdminEinstellungenPage(props: PageProps<"/admin/ei
) : (
<>
<p className="mb-8 max-w-2xl text-sm text-ink/65">{s.intro}</p>
<PlaceholderHint t={t} locale={locale} />
{current
? active.groups.map((group) => (
<GroupForm
@@ -95,6 +98,29 @@ export default async function AdminEinstellungenPage(props: PageProps<"/admin/ei
);
}
/**
* Uebersicht der Textbausteine (lib/text-placeholders.ts) ueber den
* Formularen: welche es gibt und was sie einsetzen. Der Baustein ist per
* select-all markierbar, damit er sich mit einem Klick kopieren laesst.
*/
function PlaceholderHint({ t, locale }: { t: Dictionary; locale: Locale }) {
const s = t.adminContentPage;
return (
<aside className="mb-10 max-w-2xl border-l-[3px] border-honey bg-surface px-5 py-4 text-sm">
<h2 className="text-heading">{s.placeholdersHeading}</h2>
<p className="mt-1 text-ink/65">{s.placeholdersIntro}</p>
<ul className="mt-3 space-y-1.5">
{TEXT_PLACEHOLDERS.map((placeholder) => (
<li key={placeholder.token} className="flex flex-wrap items-baseline gap-x-3 gap-y-1">
<code className="select-all rounded bg-background px-1.5 py-0.5 font-mono text-heading">{placeholder.token}</code>
<span className="text-ink/70">{locale === "de" ? placeholder.labelDe : placeholder.labelEn}</span>
</li>
))}
</ul>
</aside>
);
}
function SystemTab({ t }: { t: Dictionary }) {
const s = t.adminContentPage;
const integrations = readIntegrationStatus();