feat: update ipal-kit, integrate Media/SiteSettings and configure PL/DE languages

This commit is contained in:
2026-08-26 14:14:19 +02:00
parent cbb0ac4bfa
commit 59823dc04b
17 changed files with 363 additions and 77 deletions
+2
View File
@@ -40,3 +40,5 @@ dev.db*
# media uploads in local dev if any
/public/media
/media
+1 -1
View File
@@ -28,7 +28,7 @@
"license": "ISC",
"packageManager": "[email protected]",
"dependencies": {
"@intecion/ipal-kit": "^1.0.17",
"@intecion/ipal-kit": "^1.0.18",
"@payloadcms/db-postgres": "^3.88.0",
"@payloadcms/db-sqlite": "^3.88.0",
"@payloadcms/next": "^3.88.0",
+5 -6
View File
@@ -19,8 +19,8 @@ importers:
.:
dependencies:
'@intecion/ipal-kit':
specifier: ^1.0.17
version: 1.0.17(e4f897fbda4e3a120e33294a7b805575)
specifier: ^1.0.18
version: 1.0.18(e4f897fbda4e3a120e33294a7b805575)
'@payloadcms/db-postgres':
specifier: 3.88.0
version: 3.88.0(@libsql/[email protected])([email protected]([email protected])([email protected]))
@@ -946,9 +946,8 @@ packages:
cpu: [x64]
os: [win32]
'@intecion/[email protected]7':
resolution: {integrity: sha512-huzjbq4ntCFiTk4eYnGHmMADdKN5rzwBfYqmjuHE+tcxGqqrT8ffvzR3f2O0GiqMvJyMHd6Jw0qbu9a7J2tyzQ==, tarball: https://git.intecion.net/api/packages/IntecionSoftware/npm/%40intecion%2Fipal-kit/-/1.0.17/ipal-kit-1.0.17.tgz}
engines: {node: ^18.20.2 || >=20.9.0, pnpm: ^9 || ^10 || ^11}
'@intecion/[email protected]8':
resolution: {integrity: sha512-NTQEoWFuGcDBRPlnk1iEeVa75BvNkLmtXChLQE9B3epZ0vcrbTrFQgbXjkhgyc5LFFahZTQ/fEYLPOB/9ZDiuA==, tarball: https://git.intecion.net/api/packages/IntecionSoftware/npm/%40intecion%2Fipal-kit/-/1.0.18/ipal-kit-1.0.18.tgz}
peerDependencies:
'@payloadcms/next': 3.88.0
'@payloadcms/plugin-form-builder': 3.88.0
@@ -3635,7 +3634,7 @@ snapshots:
'@img/[email protected]':
optional: true
'@intecion/[email protected]7(e4f897fbda4e3a120e33294a7b805575)':
'@intecion/[email protected]8(e4f897fbda4e3a120e33294a7b805575)':
dependencies:
'@payloadcms/next': 3.88.0(@types/[email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected]([email protected]))([email protected])([email protected]))([email protected]([email protected])([email protected]))([email protected]([email protected]))([email protected])([email protected])
'@payloadcms/plugin-form-builder': 3.88.0(@types/[email protected])([email protected])([email protected](@types/[email protected])([email protected]([email protected]))([email protected])([email protected]))([email protected]([email protected])([email protected]))([email protected]([email protected]))([email protected])([email protected])
+27
View File
@@ -0,0 +1,27 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 115" fill="none">
<!-- 3 Stars / Sparkles (Cyan) -->
<!-- Top Right Star -->
<path d="M85 6 C85 11 89 15 94 15 C89 15 85 19 85 24 C85 19 81 15 76 15 C81 15 85 11 85 6 Z" fill="#01aeee"/>
<!-- Middle Star -->
<path d="M72 17 C72 22 76 26 81 26 C76 26 72 30 72 35 C72 30 68 26 63 26 C68 26 72 22 72 17 Z" fill="#01aeee"/>
<!-- Far Right Star -->
<path d="M91 27 C91 30.5 94 33.5 97.5 33.5 C94 33.5 91 36.5 91 40 C91 36.5 88 33.5 84.5 33.5 C88 33.5 91 30.5 91 27 Z" fill="#01aeee"/>
<!-- Red Roof -->
<path d="M46 15 C47.5 13 48.5 13 50 15 L78 49 C79.5 50.8 78 54 75 54 L69 54 L69 44 C69 41.5 67 39.5 64.5 39.5 L59 39.5 L59 54 L21 54 C18 54 16.5 50.8 18 49 Z" fill="#ee1d23"/>
<!-- White Chimney Outline / Shape -->
<path d="M60 41 L67 41 L67 53 L60 53 Z" fill="#ffffff"/>
<!-- White Roof Trim / Gutter Separation -->
<path d="M12 55 L48 29 C49.2 28.1 50.8 28.1 52 29 L84 55 L78 61 L50 38 L20 61 Z" fill="#ffffff"/>
<!-- Blue House Base -->
<path d="M22 62 L50 40 L74 62 C79 66.5 80 73 78 79 C75 92 63 103 48 103 C33 103 21 92 18 79 C16 73 17 66.5 22 62 Z" fill="#01aeee"/>
<!-- White Arched Window -->
<path d="M40 73 C40 67.5 43.5 64 48 64 C52.5 64 56 67.5 56 73 L56 87 L40 87 Z" fill="#ffffff"/>
<!-- Window Cross Divider (Cyan) -->
<rect x="47" y="64" width="2" height="23" fill="#01aeee"/>
<rect x="40" y="74" width="16" height="2" fill="#01aeee"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

+33
View File
@@ -0,0 +1,33 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 120" fill="none">
<!-- House Icon (Left) -->
<g transform="translate(10, 5) scale(0.95)">
<!-- 3 Stars / Sparkles (Cyan) -->
<path d="M85 6 C85 11 89 15 94 15 C89 15 85 19 85 24 C85 19 81 15 76 15 C81 15 85 11 85 6 Z" fill="#01aeee"/>
<path d="M72 17 C72 22 76 26 81 26 C76 26 72 30 72 35 C72 30 68 26 63 26 C68 26 72 22 72 17 Z" fill="#01aeee"/>
<path d="M91 27 C91 30.5 94 33.5 97.5 33.5 C94 33.5 91 36.5 91 40 C91 36.5 88 33.5 84.5 33.5 C88 33.5 91 30.5 91 27 Z" fill="#01aeee"/>
<!-- Red Roof -->
<path d="M46 15 C47.5 13 48.5 13 50 15 L78 49 C79.5 50.8 78 54 75 54 L69 54 L69 44 C69 41.5 67 39.5 64.5 39.5 L59 39.5 L59 54 L21 54 C18 54 16.5 50.8 18 49 Z" fill="#ee1d23"/>
<!-- White Chimney Outline / Shape -->
<path d="M60 41 L67 41 L67 53 L60 53 Z" fill="#ffffff"/>
<!-- White Roof Trim / Gutter Separation -->
<path d="M12 55 L48 29 C49.2 28.1 50.8 28.1 52 29 L84 55 L78 61 L50 38 L20 61 Z" fill="#ffffff"/>
<!-- Blue House Base -->
<path d="M22 62 L50 40 L74 62 C79 66.5 80 73 78 79 C75 92 63 103 48 103 C33 103 21 92 18 79 C16 73 17 66.5 22 62 Z" fill="#01aeee"/>
<!-- White Arched Window -->
<path d="M40 73 C40 67.5 43.5 64 48 64 C52.5 64 56 67.5 56 73 L56 87 L40 87 Z" fill="#ffffff"/>
<!-- Window Cross Divider (Cyan) -->
<rect x="47" y="64" width="2" height="23" fill="#01aeee"/>
<rect x="40" y="74" width="16" height="2" fill="#01aeee"/>
</g>
<!-- POI Text -->
<text x="122" y="75" font-family="system-ui, -apple-system, sans-serif" font-weight="900" font-style="italic" font-size="62" letter-spacing="1" fill="#231f20">POI</text>
<!-- CLEAN HOUSE Text -->
<text x="122" y="98" font-family="system-ui, -apple-system, sans-serif" font-weight="800" font-style="italic" font-size="16.5" letter-spacing="4" fill="#231f20">CLEAN HOUSE</text>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

+33
View File
@@ -0,0 +1,33 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 120" fill="none">
<!-- House Icon (Left) -->
<g transform="translate(10, 5) scale(0.95)">
<!-- 3 Stars / Sparkles (Cyan) -->
<path d="M85 6 C85 11 89 15 94 15 C89 15 85 19 85 24 C85 19 81 15 76 15 C81 15 85 11 85 6 Z" fill="#01aeee"/>
<path d="M72 17 C72 22 76 26 81 26 C76 26 72 30 72 35 C72 30 68 26 63 26 C68 26 72 22 72 17 Z" fill="#01aeee"/>
<path d="M91 27 C91 30.5 94 33.5 97.5 33.5 C94 33.5 91 36.5 91 40 C91 36.5 88 33.5 84.5 33.5 C88 33.5 91 30.5 91 27 Z" fill="#01aeee"/>
<!-- Red Roof -->
<path d="M46 15 C47.5 13 48.5 13 50 15 L78 49 C79.5 50.8 78 54 75 54 L69 54 L69 44 C69 41.5 67 39.5 64.5 39.5 L59 39.5 L59 54 L21 54 C18 54 16.5 50.8 18 49 Z" fill="#ee1d23"/>
<!-- White Chimney Outline / Shape -->
<path d="M60 41 L67 41 L67 53 L60 53 Z" fill="#ffffff"/>
<!-- White Roof Trim / Gutter Separation -->
<path d="M12 55 L48 29 C49.2 28.1 50.8 28.1 52 29 L84 55 L78 61 L50 38 L20 61 Z" fill="#ffffff"/>
<!-- Blue House Base -->
<path d="M22 62 L50 40 L74 62 C79 66.5 80 73 78 79 C75 92 63 103 48 103 C33 103 21 92 18 79 C16 73 17 66.5 22 62 Z" fill="#01aeee"/>
<!-- White Arched Window -->
<path d="M40 73 C40 67.5 43.5 64 48 64 C52.5 64 56 67.5 56 73 L56 87 L40 87 Z" fill="#ffffff"/>
<!-- Window Cross Divider (Cyan) -->
<rect x="47" y="64" width="2" height="23" fill="#01aeee"/>
<rect x="40" y="74" width="16" height="2" fill="#01aeee"/>
</g>
<!-- POI Text (White) -->
<text x="122" y="75" font-family="system-ui, -apple-system, sans-serif" font-weight="900" font-style="italic" font-size="62" letter-spacing="1" fill="#ffffff">POI</text>
<!-- CLEAN HOUSE Text (White) -->
<text x="122" y="98" font-family="system-ui, -apple-system, sans-serif" font-weight="800" font-style="italic" font-size="16.5" letter-spacing="4" fill="#ffffff">CLEAN HOUSE</text>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

@@ -5,6 +5,8 @@ import { createPageMetadata } from '@intecion/ipal-kit'
import { i18nConfig } from '@/i18n.config'
import { blockRegistry } from '@/blocks/registry'
import { getCachedPayload, resolveRoute } from '@/lib/content'
import { getSettings } from '@/lib/payload'
import type { Media } from '@/payload-types'
interface PageProps {
params: Promise<{
@@ -23,7 +25,31 @@ const pageMetadata = createPageMetadata({
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
const { locale, slug } = await params
return pageMetadata({ payload: await getCachedPayload(), locale, slug: slug ?? [] })
const payload = await getCachedPayload()
const pageMeta = await pageMetadata({ payload, locale, slug: slug ?? [] })
if (!pageMeta.openGraph?.images || pageMeta.openGraph.images.length === 0) {
const settings = await getSettings(locale)
const shareImage =
(typeof settings?.defaultShareImage === 'object' && settings?.defaultShareImage && 'url' in settings.defaultShareImage
? (settings.defaultShareImage as Media)
: null) ||
(typeof settings?.logo === 'object' && settings?.logo && 'url' in settings.logo
? (settings.logo as Media)
: null)
if (shareImage?.url) {
return {
...pageMeta,
openGraph: {
...pageMeta.openGraph,
images: [{ url: shareImage.url }],
},
}
}
}
return pageMeta
}
export default async function Page({ params, searchParams }: PageProps) {
+47 -1
View File
@@ -1,5 +1,6 @@
import { notFound } from 'next/navigation'
import type { ReactNode } from 'react'
import type { Metadata } from 'next'
import {
getConsentTexts,
getAnalyticsConfig,
@@ -18,13 +19,51 @@ import { Footer, type FooterNavItem } from '@/components/Footer'
import { CustomCookieBanner } from '@/components/CustomCookieBanner'
import '../styles.css'
import type { Company, Navigation, Page } from '@/payload-types'
import type { Company, Media, Navigation, Page } from '@/payload-types'
interface LayoutProps {
children: ReactNode
params: Promise<{ locale: string }>
}
export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }): Promise<Metadata> {
const { locale } = await params
const settings = await getSettings(locale)
const favicon =
typeof settings?.favicon === 'object' && settings?.favicon && 'url' in settings.favicon
? (settings.favicon as Media)
: null
const faviconUrl = favicon?.url || undefined
const shareImage =
(typeof settings?.defaultShareImage === 'object' && settings?.defaultShareImage && 'url' in settings.defaultShareImage
? (settings.defaultShareImage as Media)
: null) ||
(typeof settings?.logo === 'object' && settings?.logo && 'url' in settings.logo
? (settings.logo as Media)
: null)
const shareImageUrl = shareImage?.url || undefined
return {
...(faviconUrl
? {
icons: {
icon: faviconUrl,
shortcut: faviconUrl,
apple: faviconUrl,
},
}
: {}),
...(shareImageUrl
? {
openGraph: {
images: [{ url: shareImageUrl }],
},
}
: {}),
}
}
function resolveNavItemHref(item: { page?: Page | number | string | null; url?: string | null }): string {
if (item.url) return item.url
if (item.page && typeof item.page === 'object' && 'slug' in item.page && item.page.slug) {
@@ -40,6 +79,10 @@ export default async function LocaleLayout({ children, params }: LayoutProps) {
const payload = await getCachedPayload()
const settings = await getSettings(locale)
const logo =
typeof settings?.logo === 'object' && settings?.logo && 'url' in settings.logo
? (settings.logo as Media)
: null
const privacyPage = (settings as { privacyPolicy?: unknown })?.privacyPolicy
const cookiePage = (settings as { cookiePolicy?: unknown })?.cookiePolicy
const homePage = (settings as { homepage?: unknown })?.homepage
@@ -154,6 +197,8 @@ export default async function LocaleLayout({ children, params }: LayoutProps) {
currentLocale={locale}
contactHref={contactHref}
contactLabel={contactLabel}
logo={logo}
locales={i18nConfig.locales}
/>
<main className="flex-1">{children}</main>
<Footer
@@ -179,6 +224,7 @@ export default async function LocaleLayout({ children, params }: LayoutProps) {
legalTitle={(navigation as { legalTitle?: string })?.legalTitle}
creditLabel="Powered by NetScale.pl"
creditHref="https://netscale.pl"
logo={logo}
/>
<CustomCookieBanner />
<CookieButton className="fixed bottom-3 left-3 z-40 bg-dark hover:bg-primary text-primary hover:text-dark border-2 border-primary p-2 transition-all shadow-lg cursor-pointer" />
+4 -2
View File
@@ -35,12 +35,13 @@ export function HeroBlockComponent(props: HeroBlockProps) {
const heroImgSrc =
(typeof backgroundImage === 'object' && backgroundImage?.url) ||
(typeof backgroundImage === 'string' && backgroundImage) ||
'/images/paving-hero.webp'
null
return (
<div className="w-full">
{/* Hero Banner Section with Next.js Image */}
<section className="relative w-full min-h-[calc(100vh-8rem)] max-h-[780px] flex items-center overflow-hidden">
<section className="relative w-full min-h-[calc(100vh-8rem)] max-h-[780px] flex items-center overflow-hidden bg-dark">
{heroImgSrc && (
<Image
src={heroImgSrc}
alt={heading}
@@ -49,6 +50,7 @@ export function HeroBlockComponent(props: HeroBlockProps) {
sizes="100vw"
className="object-cover object-center z-0"
/>
)}
{/* Dark gradient overlay for text readability */}
<div className="absolute inset-0 bg-gradient-to-r from-dark/95 via-dark/70 to-black/30 z-[1]" />
+3 -3
View File
@@ -20,12 +20,12 @@ export function ProcessStepsBlockComponent(props: ProcessStepsBlockProps) {
const bgUrl =
(typeof backgroundImage === 'object' && backgroundImage?.url) ||
(typeof backgroundImage === 'string' && backgroundImage) ||
'/images/process-parallax.webp'
null
return (
<section
className="relative py-28 border-b-2 border-dark bg-fixed bg-center bg-cover overflow-hidden"
style={{ backgroundImage: `url('${bgUrl}')` }}
className="relative py-28 border-b-2 border-dark bg-dark bg-fixed bg-center bg-cover overflow-hidden"
style={bgUrl ? { backgroundImage: `url('${bgUrl}')` } : undefined}
>
{/* Dark overlay for contrast */}
<div className="absolute inset-0 bg-dark/85 backdrop-blur-[1px]" />
+3 -3
View File
@@ -20,12 +20,12 @@ export function TechEquipmentBlockComponent(props: TechEquipmentBlockProps) {
const bgUrl =
(typeof backgroundImage === 'object' && backgroundImage?.url) ||
(typeof backgroundImage === 'string' && backgroundImage) ||
'/images/paving-parallax.webp'
null
return (
<section
className="relative py-28 border-b-2 border-dark bg-fixed bg-center bg-cover overflow-hidden"
style={{ backgroundImage: `url('${bgUrl}')` }}
className="relative py-28 border-b-2 border-dark bg-dark bg-fixed bg-center bg-cover overflow-hidden"
style={bgUrl ? { backgroundImage: `url('${bgUrl}')` } : undefined}
>
{/* Dark overlay for contrast */}
<div className="absolute inset-0 bg-dark/85 backdrop-blur-[1px]" />
+9 -1
View File
@@ -2,6 +2,8 @@ import React from 'react'
import { Phone, Mail, ShieldCheck, Cookie, FileText } from 'lucide-react'
import { Logo } from '@/components/Logo'
import type { Media } from '@/payload-types'
export interface FooterNavItem {
id?: string
label: string
@@ -31,6 +33,7 @@ interface FooterProps {
legalTitle?: string | null
creditLabel?: string | null
creditHref?: string | null
logo?: Media | { url?: string | null; alt?: string | null } | null
}
export function Footer({
@@ -56,6 +59,7 @@ export function Footer({
legalTitle,
creditLabel,
creditHref,
logo,
}: FooterProps) {
return (
<footer className="bg-dark text-[#d0d0d0] pt-14 pb-8 border-t-4 border-primary">
@@ -64,7 +68,11 @@ export function Footer({
{/* 1. Company Info */}
<div className="space-y-4">
<div className="flex items-center gap-3">
<Logo variant="dark" className="h-12 w-auto" alt={companyName || undefined} />
{logo ? (
<Logo className="h-12 w-auto" alt={companyName || undefined} media={logo} />
) : companyName ? (
<span className="font-black text-xl tracking-tight text-white uppercase">{companyName}</span>
) : null}
</div>
{footerTagline && (
<p className="text-xs sm:text-sm text-[#a0a0a0] leading-relaxed">
+15 -5
View File
@@ -5,6 +5,8 @@ import { ArrowRight, Globe, ChevronDown } from 'lucide-react'
import { usePathname } from 'next/navigation'
import { Logo } from '@/components/Logo'
import type { Media } from '@/payload-types'
export interface NavItem {
id?: string
label: string
@@ -21,6 +23,8 @@ interface HeaderProps {
contactHref?: string | null
contactLabel?: string | null
phoneLabel?: string | null
logo?: Media | { url?: string | null; alt?: string | null } | null
locales?: readonly { code: string; label: string }[] | { code: string; label: string }[]
}
export function Header({
@@ -33,6 +37,8 @@ export function Header({
contactHref,
contactLabel,
phoneLabel,
logo,
locales,
}: HeaderProps) {
const pathname = usePathname() || ''
const [langDropdownOpen, setLangDropdownOpen] = useState(false)
@@ -63,10 +69,10 @@ export function Header({
(item) => !contactLabel || item.label.trim().toLowerCase() !== contactLabel.trim().toLowerCase()
)
const languages = ['pl', 'en'].map((code) => ({
code,
label: code.toUpperCase(),
}))
const languages =
locales && locales.length > 0
? locales
: [{ code: currentLocale, label: currentLocale.toUpperCase() }]
const activeLang = languages.find((l) => l.code === currentLocale) || languages[0]
@@ -106,7 +112,11 @@ export function Header({
{/* Main Navigation Bar */}
<div className="container mx-auto flex h-20 items-center justify-between px-4 sm:px-6 lg:px-12 max-w-7xl">
<a href={homeHref} className="flex items-center gap-3">
<Logo className="h-12 w-auto" alt={companyName || undefined} />
{logo ? (
<Logo className="h-12 w-auto" alt={companyName || undefined} media={logo} />
) : companyName ? (
<span className="font-black text-xl tracking-tight text-dark uppercase">{companyName}</span>
) : null}
</a>
{/* Navigation Items */}
+29 -6
View File
@@ -1,5 +1,6 @@
import React from 'react'
import Image from 'next/image'
import type { Media } from '@/payload-types'
interface LogoProps {
className?: string
@@ -8,13 +9,29 @@ interface LogoProps {
width?: number
priority?: boolean
alt?: string
media?: Media | { url?: string | null; alt?: string | null } | null
src?: string | null
}
export function LogoIcon({ className = 'h-11 w-auto', alt = '', ...props }: { className?: string; alt?: string }) {
export function LogoIcon({
className = 'h-11 w-auto',
alt = '',
media,
...props
}: {
className?: string
alt?: string
media?: Media | { url?: string | null; alt?: string | null } | null
}) {
const src = (typeof media === 'object' && media?.url) || null
const mediaAlt = (typeof media === 'object' && media?.alt) || alt || ''
if (!src) return null
return (
<Image
src="/images/logo-icon.webp"
alt={alt}
src={src}
alt={mediaAlt}
width={140}
height={140}
priority
@@ -26,17 +43,21 @@ export function LogoIcon({ className = 'h-11 w-auto', alt = '', ...props }: { cl
export function Logo({
className = 'h-12 w-auto',
variant = 'light',
priority = true,
alt = '',
media,
src: customSrc,
...props
}: LogoProps) {
const src = variant === 'dark' ? '/images/logo-white.webp' : '/images/logo.webp'
const src = customSrc || (typeof media === 'object' && media?.url) || null
const mediaAlt = (typeof media === 'object' && media?.alt) || alt || ''
if (!src) return null
return (
<Image
src={src}
alt={alt}
alt={mediaAlt}
width={240}
height={84}
priority={priority}
@@ -45,3 +66,5 @@ export function Logo({
/>
)
}
+2 -1
View File
@@ -4,6 +4,7 @@ export const i18nConfig = {
defaultLocale: 'pl',
locales: [
{ code: 'pl', label: 'PL' },
{ code: 'en', label: 'UK' },
{ code: 'de', label: 'DE' },
],
} as const satisfies I18nConfig
+3 -1
View File
@@ -1,7 +1,9 @@
import { cache } from 'react'
import { getSiteSettings } from '@intecion/ipal-kit'
import type { SiteSetting } from '@/payload-types'
import { getCachedPayload } from './content'
export const getSettings = cache(async (locale: string) =>
getSiteSettings(await getCachedPayload(), { locale: locale as never, depth: 2 }),
getSiteSettings<SiteSetting>(await getCachedPayload(), { locale: locale as never, depth: 2 }),
)
+111 -37
View File
@@ -1,3 +1,5 @@
import path from 'path'
import fs from 'fs'
import type { Payload } from 'payload'
export async function seed(payload: Payload) {
@@ -130,17 +132,94 @@ export async function seed(payload: Payload) {
contactForm = { id: Number(existingForms.docs[0].id) }
}
// 4. Sprawdź czy strony już istnieją
const existingPages = await payload.find({
collection: 'pages',
// 4. Seedowanie mediów (logo oraz favicon)
let logoMediaId: number | null = null
let faviconMediaId: number | null = null
const existingLogo = await payload.find({
collection: 'media',
locale: 'pl',
where: {
or: [
{ filename: { contains: 'logo-poi' } },
{ alt: { equals: 'Logo POI Clean House' } },
],
},
limit: 1,
})
if (existingPages.totalDocs > 0) {
payload.logger.info('Strony już istnieją w bazie. Pomijam seedowanie stron.')
return
if (existingLogo.totalDocs === 0) {
payload.logger.info('Tworzę logo w kolekcji Media...')
const filePath = path.resolve(process.cwd(), 'public/images/logo-poi.svg')
if (fs.existsSync(filePath)) {
const createdLogo = await payload.create({
collection: 'media',
locale: 'pl',
filePath,
data: {
alt: 'Logo POI Clean House',
},
})
logoMediaId = Number(createdLogo.id)
}
} else {
logoMediaId = Number(existingLogo.docs[0].id)
}
const existingFavicon = await payload.find({
collection: 'media',
locale: 'pl',
where: {
or: [
{ filename: { contains: 'favicon-poi' } },
{ alt: { equals: 'Favicon icon POI Clean House' } },
],
},
limit: 1,
})
if (existingFavicon.totalDocs === 0) {
payload.logger.info('Tworzę favicon w kolekcji Media...')
const filePath = path.resolve(process.cwd(), 'public/images/favicon-poi.svg')
if (fs.existsSync(filePath)) {
const createdFavicon = await payload.create({
collection: 'media',
locale: 'pl',
filePath,
data: {
alt: 'Favicon icon POI Clean House',
},
})
faviconMediaId = Number(createdFavicon.id)
}
} else {
faviconMediaId = Number(existingFavicon.docs[0].id)
}
// 5. Sprawdź czy strony już istnieją
const existingPages = await payload.find({
collection: 'pages',
limit: 20,
})
let homePageId: number | null = null
let privacyPageId: number | null = null
let cookiePageId: number | null = null
let realizacjePageId: number | null = null
let blogPageId: number | null = null
let kontaktPageId: number | null = null
if (existingPages.totalDocs > 0) {
payload.logger.info('Strony już istnieją w bazie. Odczytuję ID stron...')
for (const page of existingPages.docs) {
if (page.slug === 'home') homePageId = Number(page.id)
if (page.slug === 'polityka-prywatnosci') privacyPageId = Number(page.id)
if (page.slug === 'polityka-cookies') cookiePageId = Number(page.id)
if (page.slug === 'realizacje') realizacjePageId = Number(page.id)
if (page.slug === 'blog') blogPageId = Number(page.id)
if (page.slug === 'kontakt') kontaktPageId = Number(page.id)
}
} else {
payload.logger.info('Tworzę strony serwisu...')
// A. Strona Główna
@@ -578,24 +657,36 @@ W każdej chwili możesz zmienić swoje preferencje dotyczące plików cookies k
},
})
// 4. Przypisanie stron systemowych do globala site-settings
payload.logger.info('Konfiguruję strony systemowe w SiteSettings...')
homePageId = Number(homePage.id)
privacyPageId = Number(privacyPage.id)
cookiePageId = Number(cookiePage.id)
realizacjePageId = Number(realizacjePage.id)
blogPageId = Number(blogPage.id)
kontaktPageId = Number(kontaktPage.id)
}
// 6. Przypisanie stron systemowych oraz mediów do globala site-settings
payload.logger.info('Konfiguruję SiteSettings (logo, favicon, defaultShareImage, strony systemowe)...')
try {
await payload.updateGlobal({
slug: 'site-settings',
locale: 'pl',
data: {
siteName: 'POI Clean House',
homepage: homePage.id,
privacyPolicy: privacyPage.id,
cookiePolicy: cookiePage.id,
titleOrder: 'page-first',
titleSeparator: '|',
...(logoMediaId ? { logo: logoMediaId, defaultShareImage: logoMediaId } : {}),
...(faviconMediaId ? { favicon: faviconMediaId } : {}),
...(homePageId ? { homepage: homePageId } : {}),
...(privacyPageId ? { privacyPolicy: privacyPageId } : {}),
...(cookiePageId ? { cookiePolicy: cookiePageId } : {}),
} as never,
})
} catch (err) {
payload.logger.warn(`Nie udało się automatycznie zaktualizować siteSettings: ${err}`)
}
// 5. Przypisanie menu do globala navigation
// 7. Przypisanie menu do globala navigation
payload.logger.info('Konfiguruję menu w Navigation...')
try {
await payload.updateGlobal({
@@ -611,42 +702,24 @@ W każdej chwili możesz zmienić swoje preferencje dotyczące plików cookies k
label: 'Cennik',
url: '#cennik',
},
{
label: 'Realizacje',
page: realizacjePage.id,
},
{
label: 'Blog',
page: blogPage.id,
},
...(realizacjePageId ? [{ label: 'Realizacje', page: realizacjePageId }] : []),
...(blogPageId ? [{ label: 'Blog', page: blogPageId }] : []),
],
ctaButton: {
label: 'Kontakt',
page: kontaktPage.id,
...(kontaktPageId ? { page: kontaktPageId } : { url: '/kontakt' }),
},
footerTagline:
'Wyspecjalizowane usługi mycia gorącowodnego, usuwania mchu, odplamiania oleju, spoinowania i impregnacji hydrofobowej nawierzchni brukowych.',
footerItems: [
{
label: 'Strona główna',
page: homePage.id,
},
...(homePageId ? [{ label: 'Strona główna', page: homePageId }] : []),
{
label: 'Cennik usług',
url: '#cennik',
},
{
label: 'Galeria realizacji',
page: realizacjePage.id,
},
{
label: 'Poradnik i Blog',
page: blogPage.id,
},
{
label: 'Kontakt i wycena',
page: kontaktPage.id,
},
...(realizacjePageId ? [{ label: 'Galeria realizacji', page: realizacjePageId }] : []),
...(blogPageId ? [{ label: 'Poradnik i Blog', page: blogPageId }] : []),
...(kontaktPageId ? [{ label: 'Kontakt i wycena', page: kontaktPageId }] : []),
],
} as never,
})
@@ -656,3 +729,4 @@ W każdej chwili możesz zmienić swoje preferencje dotyczące plików cookies k
payload.logger.info('Seedowanie zakończone pomyślnie!')
}