From 080c9d1393491fa301042e853d1a68a3dd4ed136 Mon Sep 17 00:00:00 2001 From: mvriu5 Date: Sun, 31 May 2026 08:15:16 +0200 Subject: [PATCH 1/3] feat: remove shadcn --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 8267434..7d373a5 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,6 @@ "react": "^19.2.6", "react-dom": "^19.2.6", "sass": "^1.100.0", - "shadcn": "^4.8.0", "sharp": "^0.34.5", "tailwind-merge": "^3.6.0", "tw-animate-css": "^1.4.0", From 2e0c7b6be72643ff7674471470f4571779911bfa Mon Sep 17 00:00:00 2001 From: mvriu5 Date: Sun, 31 May 2026 11:24:40 +0200 Subject: [PATCH 2/3] feat: moved collections to globals --- src/collections/navbarButtons.ts | 59 - src/collections/navbarItems.ts | 74 - src/components/navigation/Navigation.tsx | 5 +- .../navigation/NavigationDesktop.tsx | 5 +- .../navigation/NavigationMobile.tsx | 5 +- src/{collections => globals}/cookieBanner.ts | 6 +- src/{collections => globals}/footer.ts | 6 +- src/globals/navbarButtons.ts | 60 + src/globals/navbarItems.ts | 75 + .../subscriptionConfig.ts | 10 +- src/hooks/useNavigationViewModel.ts | 5 +- src/lib/cms.ts | 53 +- src/lib/navigation.ts | 10 +- .../20260531_084500_navigation_globals.ts | 255 ++++ src/migrations/index.ts | 24 +- src/payload-types.ts | 1360 ++++++++--------- src/payload.config.ts | 20 +- 17 files changed, 1142 insertions(+), 890 deletions(-) delete mode 100644 src/collections/navbarButtons.ts delete mode 100644 src/collections/navbarItems.ts rename src/{collections => globals}/cookieBanner.ts (92%) rename src/{collections => globals}/footer.ts (94%) create mode 100644 src/globals/navbarButtons.ts create mode 100644 src/globals/navbarItems.ts rename src/{collections => globals}/subscriptionConfig.ts (96%) create mode 100644 src/migrations/20260531_084500_navigation_globals.ts diff --git a/src/collections/navbarButtons.ts b/src/collections/navbarButtons.ts deleted file mode 100644 index 45b585c..0000000 --- a/src/collections/navbarButtons.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type { CollectionConfig } from "payload" - -export const NavbarButtons: CollectionConfig = { - slug: "navbarButtons", - admin: { - useAsTitle: "title", - defaultColumns: ["title", "href", "order", "variant", "updatedAt"], - }, - access: { - read: () => true, - create: ({ req }) => Boolean(req.user), - update: ({ req }) => Boolean(req.user), - delete: ({ req }) => Boolean(req.user), - }, - fields: [ - { - name: "title", - type: "text", - required: true, - localized: true, - }, - { - name: "href", - type: "text", - required: true, - }, - { - name: "order", - type: "number", - required: true, - defaultValue: 0, - }, - { - name: "icon", - type: "text", - required: false, - admin: { - description: 'Tabler Icon Name, z.B. "brand-github", oder Simple Icon mit "si", z.B. "siGithub". Leer lassen für kein Icon.', - }, - }, - { - name: "newTab", - type: "checkbox", - defaultValue: false, - }, - { - name: "variant", - type: "select", - required: true, - defaultValue: "normal", - options: [ - { label: "None", value: "none" }, - { label: "Normal", value: "normal" }, - { label: "Outlined", value: "outlined" }, - { label: "Filled", value: "filled" }, - ], - }, - ], -} diff --git a/src/collections/navbarItems.ts b/src/collections/navbarItems.ts deleted file mode 100644 index f7815b4..0000000 --- a/src/collections/navbarItems.ts +++ /dev/null @@ -1,74 +0,0 @@ -import type { CollectionConfig } from "payload" - -export const NavbarItems: CollectionConfig = { - slug: "navbarItems", - admin: { - useAsTitle: "title", - defaultColumns: ["title", "href", "order", "updatedAt"], - }, - access: { - read: () => true, - create: ({ req }) => Boolean(req.user), - update: ({ req }) => Boolean(req.user), - delete: ({ req }) => Boolean(req.user), - }, - fields: [ - { - name: "title", - type: "text", - required: true, - localized: true, - }, - { - name: "href", - type: "text", - admin: { - description: "Leer lassen, wenn ein Submenu angezeigt werden soll.", - }, - }, - { - name: "order", - type: "number", - required: true, - defaultValue: 0, - }, - { - name: "subMenu", - type: "array", - required: false, - fields: [ - { - name: "key", - type: "text", - required: true, - }, - { - name: "title", - type: "text", - required: true, - localized: true, - }, - { - name: "href", - type: "text", - required: true, - }, - { - name: "description", - type: "textarea", - required: true, - localized: true, - }, - { - name: "icon", - type: "text", - required: true, - admin: { - description: - 'Tabler Icon Name. Unbekannte Werte fallen auf "cube" zurück.', - }, - }, - ], - }, - ], -} diff --git a/src/components/navigation/Navigation.tsx b/src/components/navigation/Navigation.tsx index 4724113..73b0d24 100644 --- a/src/components/navigation/Navigation.tsx +++ b/src/components/navigation/Navigation.tsx @@ -1,12 +1,11 @@ -import type { NavbarItem } from "@/payload-types" import { type AppLocale } from "@/lib/i18n" -import type { NavbarButtonData } from "@/lib/navigation" +import type { NavbarButtonData, NavbarItemData } from "@/lib/navigation" import { NavigationDesktop } from "./NavigationDesktop" import { NavigationMobile } from "./NavigationMobile" interface NavigationProps { locale: AppLocale - items: NavbarItem[] + items: NavbarItemData[] buttons: NavbarButtonData[] } diff --git a/src/components/navigation/NavigationDesktop.tsx b/src/components/navigation/NavigationDesktop.tsx index 8a399dd..dab27f4 100644 --- a/src/components/navigation/NavigationDesktop.tsx +++ b/src/components/navigation/NavigationDesktop.tsx @@ -4,8 +4,7 @@ import { cn } from "@/lib/utils" import { useNavigationScrollState } from "@/hooks/useNavigationScrollState" import { useNavigationViewModel } from "@/hooks/useNavigationViewModel" import { type AppLocale } from "@/lib/i18n" -import type { NavbarButtonData } from "@/lib/navigation" -import type { NavbarItem } from "@/payload-types" +import type { NavbarButtonData, NavbarItemData } from "@/lib/navigation" import { NavigationMenu, NavigationMenuContent, @@ -25,7 +24,7 @@ import { NavigationSubMenu } from "./NavigationSubMenu" type NavigationDesktopProps = { locale: AppLocale - items: NavbarItem[] + items: NavbarItemData[] buttons: NavbarButtonData[] } diff --git a/src/components/navigation/NavigationMobile.tsx b/src/components/navigation/NavigationMobile.tsx index 761a962..2e27734 100644 --- a/src/components/navigation/NavigationMobile.tsx +++ b/src/components/navigation/NavigationMobile.tsx @@ -5,8 +5,7 @@ import { useNavigationScrollState } from "@/hooks/useNavigationScrollState" import { useNavigationViewModel } from "@/hooks/useNavigationViewModel" import { useOutsideClick } from "@/hooks/useOutsideClick" import { type AppLocale } from "@/lib/i18n" -import type { NavbarButtonData } from "@/lib/navigation" -import type { NavbarItem } from "@/payload-types" +import type { NavbarButtonData, NavbarItemData } from "@/lib/navigation" import { Container } from "@code0-tech/pictor" import { IconMenu2, IconX } from "@tabler/icons-react" import { AnimatePresence, m as motion } from "motion/react" @@ -19,7 +18,7 @@ import { NavigationMobileItem } from "./NavigationMobileItem" type NavigationMobileProps = { locale: AppLocale - items: NavbarItem[] + items: NavbarItemData[] buttons: NavbarButtonData[] } diff --git a/src/collections/cookieBanner.ts b/src/globals/cookieBanner.ts similarity index 92% rename from src/collections/cookieBanner.ts rename to src/globals/cookieBanner.ts index 81a86af..df6f400 100644 --- a/src/collections/cookieBanner.ts +++ b/src/globals/cookieBanner.ts @@ -1,4 +1,4 @@ -import type { CollectionConfig } from "payload" +import type { GlobalConfig } from "payload" const localizedText = (name: string, label: string) => ({ name, @@ -8,13 +8,11 @@ const localizedText = (name: string, label: string) => ({ localized: true, }) -export const CookieBanner: CollectionConfig = { +export const CookieBanner: GlobalConfig = { slug: "cookie-banner", access: { read: () => true, - create: ({ req }) => Boolean(req.user), update: ({ req }) => Boolean(req.user), - delete: ({ req }) => Boolean(req.user), }, fields: [ { diff --git a/src/collections/footer.ts b/src/globals/footer.ts similarity index 94% rename from src/collections/footer.ts rename to src/globals/footer.ts index fe440d6..bf1b384 100644 --- a/src/collections/footer.ts +++ b/src/globals/footer.ts @@ -1,12 +1,10 @@ -import type { CollectionConfig } from "payload" +import type { GlobalConfig } from "payload" -export const Footer: CollectionConfig = { +export const Footer: GlobalConfig = { slug: "footer", access: { read: () => true, - create: ({ req }) => Boolean(req.user), update: ({ req }) => Boolean(req.user), - delete: ({ req }) => Boolean(req.user), }, fields: [ { diff --git a/src/globals/navbarButtons.ts b/src/globals/navbarButtons.ts new file mode 100644 index 0000000..5c18517 --- /dev/null +++ b/src/globals/navbarButtons.ts @@ -0,0 +1,60 @@ +import type { GlobalConfig } from "payload" + +export const NavbarButtons: GlobalConfig = { + slug: "navbarButtons", + access: { + read: () => true, + update: ({ req }) => Boolean(req.user), + }, + fields: [ + { + name: "buttons", + type: "array", + required: true, + fields: [ + { + name: "title", + type: "text", + required: true, + localized: true, + }, + { + name: "href", + type: "text", + required: true, + }, + { + name: "order", + type: "number", + required: true, + defaultValue: 0, + }, + { + name: "icon", + type: "text", + required: false, + admin: { + description: 'Tabler Icon Name, z.B. "brand-github", oder Simple Icon mit "si", z.B. "siGithub". Leer lassen für kein Icon.', + }, + }, + { + name: "newTab", + type: "checkbox", + defaultValue: false, + }, + { + name: "variant", + type: "select", + required: true, + defaultValue: "normal", + options: [ + { label: "None", value: "none" }, + { label: "Normal", value: "normal" }, + { label: "Outlined", value: "outlined" }, + { label: "Filled", value: "filled" }, + ], + }, + ], + }, + ], +} diff --git a/src/globals/navbarItems.ts b/src/globals/navbarItems.ts new file mode 100644 index 0000000..f04625f --- /dev/null +++ b/src/globals/navbarItems.ts @@ -0,0 +1,75 @@ +import type { GlobalConfig } from "payload" + +export const NavbarItems: GlobalConfig = { + slug: "navbarItems", + access: { + read: () => true, + update: ({ req }) => Boolean(req.user), + }, + fields: [ + { + name: "items", + type: "array", + required: true, + fields: [ + { + name: "title", + type: "text", + required: true, + localized: true, + }, + { + name: "href", + type: "text", + admin: { + description: "Leer lassen, wenn ein Submenu angezeigt werden soll.", + }, + }, + { + name: "order", + type: "number", + required: true, + defaultValue: 0, + }, + { + name: "subMenu", + type: "array", + required: false, + fields: [ + { + name: "key", + type: "text", + required: true, + }, + { + name: "title", + type: "text", + required: true, + localized: true, + }, + { + name: "href", + type: "text", + required: true, + }, + { + name: "description", + type: "textarea", + required: true, + localized: true, + }, + { + name: "icon", + type: "text", + required: true, + admin: { + description: + 'Tabler Icon Name. Unbekannte Werte fallen auf "cube" zurück.', + }, + }, + ], + }, + ], + }, + ], +} diff --git a/src/collections/subscriptionConfig.ts b/src/globals/subscriptionConfig.ts similarity index 96% rename from src/collections/subscriptionConfig.ts rename to src/globals/subscriptionConfig.ts index 040047d..feeb1d4 100644 --- a/src/collections/subscriptionConfig.ts +++ b/src/globals/subscriptionConfig.ts @@ -1,4 +1,4 @@ -import type { CollectionConfig } from "payload" +import type { GlobalConfig } from "payload" const accentColorOptions = [ { label: "Brand", value: "brand" }, @@ -27,17 +27,11 @@ const colorField = { defaultValue: "aqua", } as const -export const SubscriptionCollection: CollectionConfig = { +export const SubscriptionCollection: GlobalConfig = { slug: "subscriptionConfig", - admin: { - useAsTitle: "title", - defaultColumns: ["title", "updatedAt"], - }, access: { read: () => true, - create: ({ req }) => Boolean(req.user), update: ({ req }) => Boolean(req.user), - delete: ({ req }) => Boolean(req.user), }, fields: [ { diff --git a/src/hooks/useNavigationViewModel.ts b/src/hooks/useNavigationViewModel.ts index 20e4f21..3bd4af7 100644 --- a/src/hooks/useNavigationViewModel.ts +++ b/src/hooks/useNavigationViewModel.ts @@ -1,11 +1,10 @@ "use client" -import { mapNavbarButtons, mapNavbarItems, type NavbarButtonData } from "@/lib/navigation" +import { mapNavbarButtons, mapNavbarItems, type NavbarButtonData, type NavbarItemData } from "@/lib/navigation" import type { AppLocale } from "@/lib/i18n" -import type { NavbarItem } from "@/payload-types" import { useMemo } from "react" -export function useNavigationViewModel(locale: AppLocale, items: NavbarItem[], buttons: NavbarButtonData[]) { +export function useNavigationViewModel(locale: AppLocale, items: NavbarItemData[], buttons: NavbarButtonData[]) { return useMemo(() => ({ homeHref: `/${locale}`, navbarItems: mapNavbarItems(items, locale), diff --git a/src/lib/cms.ts b/src/lib/cms.ts index ab81cda..1778a38 100644 --- a/src/lib/cms.ts +++ b/src/lib/cms.ts @@ -2,8 +2,8 @@ import { DEFAULT_LOCALE, type AppLocale } from "@/lib/i18n" import { getPayloadClient } from "@/lib/payloadClient" -import type { Action, Blog, CookieBanner, Feature, Footer, Job, Media, NavbarItem, Page, TeamMember } from "@/payload-types" -import type { NavbarButtonData } from "@/lib/navigation" +import type { Action, Blog, CookieBanner, Feature, Footer, Job, Media, NavbarButton, NavbarItem, Page, TeamMember } from "@/payload-types" +import type { NavbarButtonData, NavbarItemData } from "@/lib/navigation" import { cache } from "react" const isBuildPhase = process.env.NEXT_PHASE === "phase-production-build" || process.env.npm_lifecycle_event === "build" @@ -200,12 +200,25 @@ type CmsFindArgs = { fallbackLocale?: AppLocale } & Record +type CmsGlobalArgs = { + slug: string + locale: AppLocale + fallbackLocale?: AppLocale +} & Record + async function cmsFind(args: CmsFindArgs): Promise { const payload = await getPayloadClient() const result = await payload.find(args as never) return (result.docs as T[]) ?? [] } +async function cmsFindGlobal(operation: string, fallback: T | null, args: CmsGlobalArgs): Promise { + return withCmsFallback(operation, fallback, async () => { + const payload = await getPayloadClient() + return await payload.findGlobal(args as never) as T + }) +} + async function cmsFindOne(operation: string, fallback: T | null, args: CmsFindArgs): Promise { return withCmsFallback(operation, fallback, async () => { const docs = await cmsFind(args) @@ -243,46 +256,42 @@ const getLandingPageCached = cache(async (cachedSlug: string, cachedLocale: AppL }) }) -const getNavbarItemsCached = cache(async (locale: AppLocale): Promise => { - return cmsFindMany(`getNavbarItems(${locale})`, [], { - collection: "navbarItems", +const getNavbarItemsCached = cache(async (locale: AppLocale): Promise => { + const navbar = await cmsFindGlobal(`getNavbarItems(${locale})`, null, { + slug: "navbarItems", locale, fallbackLocale: DEFAULT_LOCALE, - pagination: false, - sort: "order", depth: 0, }) + + return navbar?.items ?? [] }) const getNavbarButtonsCached = cache(async (locale: AppLocale): Promise => { - return cmsFindMany(`getNavbarButtons(${locale})`, [], { - collection: "navbarButtons", + const navbarButtons = await cmsFindGlobal(`getNavbarButtons(${locale})`, null, { + slug: "navbarButtons", locale, fallbackLocale: DEFAULT_LOCALE, - pagination: false, - sort: "order", depth: 0, }) + + return navbarButtons?.buttons ?? [] }) const getFooterCached = cache(async (locale: AppLocale): Promise