import type { CollectionConfig } from "payload"; import { isAdmin } from "@/access"; function normalizePath(value: string): string { // Lowercased to match the slugs it points at/from (fields/slug.ts always // lowercases via slugify) - without this, "/Angebote/Doula" and // "/angebote/doula" count as different values to the `unique` constraint // on fromPath, letting near-duplicate redirects coexist. const trimmed = value.trim().toLowerCase(); const withSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`; return withSlash.length > 1 ? withSlash.replace(/\/+$/, "") : withSlash; } /** * Simple slug-change redirect table (see lib/seo/redirects.ts). Deliberately * scoped to the three dynamic [slug] detail routes (Angebote/Termine/ * Aktuelles) - that's the only place slugs actually live and can change, so * looking this up only there avoids adding a database lookup to every * single request the way a catch-all middleware would. */ export const Redirects: CollectionConfig = { slug: "redirects", labels: { singular: "Weiterleitung", plural: "Weiterleitungen", }, admin: { useAsTitle: "fromPath", defaultColumns: ["fromPath", "toPath", "type", "enabled"], group: "SEO", description: "Leitet eine alte URL (z. B. nach einer Slug-Änderung bei Angeboten, Terminen oder Aktuelles) dauerhaft auf eine neue weiter.", }, access: { read: () => true, create: isAdmin, update: isAdmin, delete: isAdmin, }, fields: [ { name: "fromPath", type: "text", label: "Alte URL (Pfad)", required: true, unique: true, index: true, admin: { description: "Nur der Pfad, z. B. /angebote/doula (ohne Domain)." }, hooks: { beforeValidate: [({ value }) => (typeof value === "string" && value ? normalizePath(value) : value)], }, }, { name: "toPath", type: "text", label: "Neue URL (Pfad)", required: true, admin: { description: "Ziel-Pfad, z. B. /angebote/doula-begleitung." }, hooks: { beforeValidate: [({ value }) => (typeof value === "string" && value ? normalizePath(value) : value)], }, }, { name: "type", type: "select", label: "Art der Weiterleitung", required: true, defaultValue: "permanent", options: [ { label: "301 - Dauerhaft", value: "permanent" }, { label: "302 - Vorübergehend", value: "temporary" }, ], }, { name: "enabled", type: "checkbox", label: "Aktiv", defaultValue: true, }, ], };