feat: Wabenhain Honig-Shop Anwendung
Vollstaendige Next.js/Prisma-E-Commerce-Anwendung fuer eine Imkerei: Shop, Warenkorb, Stripe-Checkout, Kundenkonto mit 2FA, Admin-Bereich (Produkte, Ernten, Chargen, Bestellungen, Gutscheine, admin-editierbare Website-Inhalte), i18n (DE/EN), Rate-Limiting/CSP/Audit-Log-Haertung, PostgreSQL- und MySQL-Unterstuetzung ueber austauschbare Prisma- Treiber-Adapter, Unit-/Komponenten-/E2E-Tests.
This commit is contained in:
1 parent
5c76fcc266
commit
c7927f2883
240 files changed
+26096
-137
No files matched your search
@@ -0,0 +1,80 @@
|
||||
import { getDictionary } from "@/i18n/get-dictionary";
|
||||
import { siteConfig } from "@/lib/site-config";
|
||||
import type { Locale } from "@/i18n/config";
|
||||
import type { TitledText, LegalSectionContent } from "@/i18n/dictionary-type";
|
||||
|
||||
export type ContentFieldType =
|
||||
| "text"
|
||||
| "textarea"
|
||||
| "list-simple"
|
||||
| "list-titled"
|
||||
| "list-numbered"
|
||||
| "richtext-sections";
|
||||
|
||||
export interface ContentFieldDef {
|
||||
key: string;
|
||||
group: "company" | "home" | "legal";
|
||||
labelDe: string;
|
||||
labelEn: string;
|
||||
type: ContentFieldType;
|
||||
/** false = ein einziger Wert unabhaengig von der Locale (Firmendaten). */
|
||||
localized: boolean;
|
||||
/** Liefert den aktuellen Fallback-Text im selben Format, das der Parser fuer diesen Typ erwartet. */
|
||||
getDefault: (locale: Locale) => string;
|
||||
}
|
||||
|
||||
function joinSimpleList(items: string[]): string {
|
||||
return items.join("\n");
|
||||
}
|
||||
|
||||
function joinTitledList(items: TitledText[]): string {
|
||||
return items.map((i) => `${i.title} | ${i.text}`).join("\n");
|
||||
}
|
||||
|
||||
function joinNumberedList(items: { number: string; title: string; text: string }[]): string {
|
||||
return items.map((i) => `${i.number} | ${i.title} | ${i.text}`).join("\n");
|
||||
}
|
||||
|
||||
function joinSections(sections: LegalSectionContent[]): string {
|
||||
return sections
|
||||
.map((s) => `## ${s.heading}\n\n${s.paragraphs.join("\n\n")}`)
|
||||
.join("\n\n");
|
||||
}
|
||||
|
||||
export const CONTENT_REGISTRY: ContentFieldDef[] = [
|
||||
// --- Firmendaten (nicht lokalisiert) ---
|
||||
{ key: "company.name", group: "company", labelDe: "Firmenname", labelEn: "Company name", type: "text", localized: false, getDefault: () => siteConfig.name },
|
||||
{ key: "company.claim", group: "company", labelDe: "Slogan", labelEn: "Tagline", type: "text", localized: false, getDefault: () => siteConfig.claim },
|
||||
{ key: "company.claimAlt", group: "company", labelDe: "Alternativer Slogan", labelEn: "Alternate tagline", type: "text", localized: false, getDefault: () => siteConfig.claimAlt },
|
||||
{ key: "company.description", group: "company", labelDe: "Kurzbeschreibung", labelEn: "Short description", type: "textarea", localized: false, getDefault: () => siteConfig.description },
|
||||
{ key: "company.location", group: "company", labelDe: "Standort (Anzeigetext)", labelEn: "Location (display text)", type: "text", localized: false, getDefault: () => siteConfig.location },
|
||||
{ key: "company.email", group: "company", labelDe: "E-Mail", labelEn: "Email", type: "text", localized: false, getDefault: () => siteConfig.email },
|
||||
{ key: "company.phone", group: "company", labelDe: "Telefon", labelEn: "Phone", type: "text", localized: false, getDefault: () => siteConfig.phone },
|
||||
{ key: "company.street", group: "company", labelDe: "Straße", labelEn: "Street", type: "text", localized: false, getDefault: () => siteConfig.address.street },
|
||||
{ key: "company.zip", group: "company", labelDe: "PLZ", labelEn: "ZIP", type: "text", localized: false, getDefault: () => siteConfig.address.zip },
|
||||
{ key: "company.city", group: "company", labelDe: "Ort", labelEn: "City", type: "text", localized: false, getDefault: () => siteConfig.address.city },
|
||||
|
||||
// --- Homepage (lokalisiert) ---
|
||||
{ key: "home.hero.intro", group: "home", labelDe: "Hero: Einleitungstext", labelEn: "Hero: Intro text", type: "textarea", localized: true, getDefault: (l) => getDictionary(l).home.hero.intro },
|
||||
{ key: "home.hero.footnote", group: "home", labelDe: "Hero: Fußnote", labelEn: "Hero: Footnote", type: "text", localized: true, getDefault: (l) => getDictionary(l).home.hero.footnote },
|
||||
{ key: "home.originStory.paragraph1", group: "home", labelDe: "Herkunft: Absatz 1", labelEn: "Origin: Paragraph 1", type: "textarea", localized: true, getDefault: (l) => getDictionary(l).home.originStory.paragraph1 },
|
||||
{ key: "home.originStory.paragraph2", group: "home", labelDe: "Herkunft: Absatz 2", labelEn: "Origin: Paragraph 2", type: "textarea", localized: true, getDefault: (l) => getDictionary(l).home.originStory.paragraph2 },
|
||||
{ key: "home.originStory.facts", group: "home", labelDe: "Herkunft: Fakten (eine Zeile je Fakt)", labelEn: "Origin: Facts (one per line)", type: "list-simple", localized: true, getDefault: (l) => joinSimpleList(getDictionary(l).home.originStory.facts) },
|
||||
{ key: "home.philosophy.items", group: "home", labelDe: "Philosophie: Punkte (Titel | Text, eine Zeile je Punkt)", labelEn: "Philosophy: Items (Title | Text, one per line)", type: "list-titled", localized: true, getDefault: (l) => joinTitledList(getDictionary(l).home.philosophy.items) },
|
||||
{ key: "home.processTimeline.steps", group: "home", labelDe: "Ablauf: Schritte (Nr. | Titel | Text, eine Zeile je Schritt)", labelEn: "Process: Steps (No. | Title | Text, one per line)", type: "list-numbered", localized: true, getDefault: (l) => joinNumberedList(getDictionary(l).home.processTimeline.steps) },
|
||||
{ key: "home.regionalSection.paragraph1", group: "home", labelDe: "Regional: Absatz 1", labelEn: "Regional: Paragraph 1", type: "textarea", localized: true, getDefault: (l) => getDictionary(l).home.regionalSection.paragraph1 },
|
||||
{ key: "home.regionalSection.paragraph2", group: "home", labelDe: "Regional: Absatz 2", labelEn: "Regional: Paragraph 2", type: "textarea", localized: true, getDefault: (l) => getDictionary(l).home.regionalSection.paragraph2 },
|
||||
{ key: "home.sustainability.items", group: "home", labelDe: "Nachhaltigkeit: Punkte (Titel | Text, eine Zeile je Punkt)", labelEn: "Sustainability: Items (Title | Text, one per line)", type: "list-titled", localized: true, getDefault: (l) => joinTitledList(getDictionary(l).home.sustainability.items) },
|
||||
{ key: "home.businessTeaser.paragraph", group: "home", labelDe: "Geschäftskunden-Hinweis: Text", labelEn: "Business teaser: Text", type: "textarea", localized: true, getDefault: (l) => getDictionary(l).home.businessTeaser.paragraph },
|
||||
|
||||
// --- Rechtstexte (lokalisiert) ---
|
||||
{ key: "legal.impressum", group: "legal", labelDe: "Impressum", labelEn: "Legal notice", type: "richtext-sections", localized: true, getDefault: (l) => joinSections(getDictionary(l).impressumPage.sections) },
|
||||
{ key: "legal.datenschutz", group: "legal", labelDe: "Datenschutz", labelEn: "Privacy policy", type: "richtext-sections", localized: true, getDefault: (l) => joinSections(getDictionary(l).datenschutzPage.sections) },
|
||||
{ key: "legal.agb", group: "legal", labelDe: "AGB", labelEn: "Terms and conditions", type: "richtext-sections", localized: true, getDefault: (l) => joinSections(getDictionary(l).agbPage.sections) },
|
||||
{ key: "legal.widerruf", group: "legal", labelDe: "Widerrufsbelehrung", labelEn: "Right of withdrawal", type: "richtext-sections", localized: true, getDefault: (l) => joinSections(getDictionary(l).widerrufPage.sections) },
|
||||
{ key: "legal.cookieRichtlinie", group: "legal", labelDe: "Cookie-Richtlinie", labelEn: "Cookie policy", type: "richtext-sections", localized: true, getDefault: (l) => joinSections(getDictionary(l).cookieRichtliniePage.sections) },
|
||||
];
|
||||
|
||||
export function getContentFieldDef(key: string): ContentFieldDef | undefined {
|
||||
return CONTENT_REGISTRY.find((f) => f.key === key);
|
||||
}
|
||||
Reference in new issue
Block a user