fix: hook-based slug with Swedish chars, remove built-in hero from Pages, and more rich text fix

This commit is contained in:
Jeffrey 2026-02-18 12:15:07 +01:00
parent 839773c271
commit c3b3868288
27 changed files with 527 additions and 904 deletions

View File

@ -1,2 +0,0 @@
# Web Paylod CMS testing

44
Todo - Updates.md Normal file
View File

@ -0,0 +1,44 @@
# Web Paylod CMS testing
## Comments on what to fix
Hero should default to none, or we should try to remove it!
Preview thumbnails for blocks ??
Can we get filler text in blocks as standard ?
Why is slug defaulting to one letter?
Fix corners and embed for video block
Fix posts formatting to be better
FAQ is not rendering answer text
Where does the form submission text come from ???
Skicka förfrågan forms button needs to be updated
A CTA block without Image
Change the cookies banner to full screen width instead of rounded corners floating
---
## Funktionalitet
#### Subscribe to newsletters, connection to API GANL
#### Få en offert (Lime, GANL, epost?)
#### Forms generellt ?
#### Matomo Tracking
#### Cookies consent
#### Lime forms integration
#### Other code embedding

View File

@ -1,243 +0,0 @@
#!/bin/bash
# Run from the root of your fdweb2 project
set -e
cd "$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
echo "=== FD TypeScript Error Fix Script ==="
echo ""
# ─────────────────────────────────────────────────────────────
# GROUP 1: Remove dead template block imports from RenderBlocks
# ─────────────────────────────────────────────────────────────
RENDER_BLOCKS="src/app/(frontend)/(pages)/[slug]/RenderBlocks.tsx"
# Try alternate path if first doesn't exist
if [ ! -f "$RENDER_BLOCKS" ]; then
RENDER_BLOCKS=$(find src -name "RenderBlocks.tsx" | head -1)
fi
echo "→ Fixing RenderBlocks.tsx at: $RENDER_BLOCKS"
# Remove dead template block imports (leave all FD* blocks intact)
sed -i '' \
'/^import { ArchiveBlock } from/d' \
"$RENDER_BLOCKS"
sed -i '' \
'/^import { CallToActionBlock } from/d' \
"$RENDER_BLOCKS"
sed -i '' \
'/^import { ContentBlock } from/d' \
"$RENDER_BLOCKS"
sed -i '' \
'/^import { FormBlock } from/d' \
"$RENDER_BLOCKS"
sed -i '' \
'/^import { MediaBlock } from/d' \
"$RENDER_BLOCKS"
# Remove corresponding entries from blockComponents object
sed -i '' \
'/^ archive: ArchiveBlock,/d' \
"$RENDER_BLOCKS"
sed -i '' \
'/^ cta: CallToActionBlock,/d' \
"$RENDER_BLOCKS"
sed -i '' \
'/^ content: ContentBlock,/d' \
"$RENDER_BLOCKS"
sed -i '' \
'/^ formBlock: FormBlock,/d' \
"$RENDER_BLOCKS"
sed -i '' \
'/^ mediaBlock: MediaBlock,/d' \
"$RENDER_BLOCKS"
echo " ✓ Removed 5 dead template block imports and blockComponent entries"
# ─────────────────────────────────────────────────────────────
# GROUP 2: Fix CallToActionBlock import in RichText
# ─────────────────────────────────────────────────────────────
RICH_TEXT="src/components/RichText/index.tsx"
if [ -f "$RICH_TEXT" ]; then
# Remove CallToActionBlock from the payload-types import
sed -i '' \
's/import type { CallToActionBlock } from '\''@\/payload-types'\''/\/\/ CallToActionBlock removed - not in schema/' \
"$RICH_TEXT"
# More general: remove just the named import if it's part of a multi-import
sed -i '' \
's/, CallToActionBlock//' \
"$RICH_TEXT"
sed -i '' \
's/CallToActionBlock, //' \
"$RICH_TEXT"
echo " ✓ Removed CallToActionBlock from RichText"
fi
# ─────────────────────────────────────────────────────────────
# GROUP 3: Fix null safety in FD block components
# ─────────────────────────────────────────────────────────────
echo ""
echo "→ Fixing null safety issues in FD blocks..."
# FDLocationsGridBlock - cards is possibly null
LOCATIONS="src/blocks/FDLocationsGridBlock/Component.tsx"
if [ -f "$LOCATIONS" ]; then
# Change: cards.map( → (cards ?? []).map(
sed -i '' \
's/cards\.map(/\(cards ?? []\)\.map(/g' \
"$LOCATIONS"
echo " ✓ FDLocationsGridBlock: cards null check"
fi
# FDNewsletterBlock - bulletPoints is possibly null
NEWSLETTER="src/blocks/FDNewsletterBlock/Component.tsx"
if [ -f "$NEWSLETTER" ]; then
sed -i '' \
's/bulletPoints\.map(/\(bulletPoints ?? []\)\.map(/g' \
"$NEWSLETTER"
sed -i '' \
's/bulletPoints\.length/\(bulletPoints ?? []\)\.length/g' \
"$NEWSLETTER"
# Fix conditional renders: bulletPoints && → bulletPoints?.length &&
sed -i '' \
's/bulletPoints &&/bulletPoints?.length \&\&/g' \
"$NEWSLETTER"
echo " ✓ FDNewsletterBlock: bulletPoints null check"
fi
# FDServiceChooserBlock - categories is possibly null
SERVICE_CHOOSER="src/blocks/FDServiceChooserBlock/Component.tsx"
if [ -f "$SERVICE_CHOOSER" ]; then
sed -i '' \
's/categories\.map(/\(categories ?? []\)\.map(/g' \
"$SERVICE_CHOOSER"
sed -i '' \
's/categories\.filter(/\(categories ?? []\)\.filter(/g' \
"$SERVICE_CHOOSER"
sed -i '' \
's/categories\.find(/\(categories ?? []\)\.find(/g' \
"$SERVICE_CHOOSER"
sed -i '' \
's/categories\.length/\(categories ?? []\)\.length/g' \
"$SERVICE_CHOOSER"
echo " ✓ FDServiceChooserBlock: categories null check"
fi
# FDStatisticsBlock - stats is possibly null
STATISTICS="src/blocks/FDStatisticsBlock/Component.tsx"
if [ -f "$STATISTICS" ]; then
sed -i '' \
's/stats\.map(/\(stats ?? []\)\.map(/g' \
"$STATISTICS"
sed -i '' \
's/stats\.length/\(stats ?? []\)\.length/g' \
"$STATISTICS"
echo " ✓ FDStatisticsBlock: stats null check"
fi
# ─────────────────────────────────────────────────────────────
# GROUP 4: Fix revalidatePath — Next.js now requires 2nd arg
# ─────────────────────────────────────────────────────────────
echo ""
echo "→ Fixing revalidatePath calls (adding 'page' as second argument)..."
REVALIDATE_FILES=(
"src/collections/Pages/hooks/revalidatePage.ts"
"src/collections/Posts/hooks/revalidatePost.ts"
"src/Footer/hooks/revalidateFooter.ts"
"src/Header/hooks/revalidateHeader.ts"
"src/globals/PopupAnnouncement/hooks/revalidatePopup.ts"
"src/hooks/revalidateRedirects.ts"
)
for FILE in "${REVALIDATE_FILES[@]}"; do
if [ -f "$FILE" ]; then
# revalidatePath('/some/path') → revalidatePath('/some/path', 'page')
# Match revalidatePath with a single string arg (no comma inside the parens)
sed -i '' \
"s/revalidatePath('\([^']*\)')/revalidatePath('\1', 'page')/g" \
"$FILE"
sed -i '' \
's/revalidatePath("\([^"]*\)")/revalidatePath("\1", "page")/g' \
"$FILE"
# Handle template literals: revalidatePath(\`...\`) → revalidatePath(\`...\`, 'page')
# This one is trickier with backticks, handle separately
perl -i '' -pe 's/revalidatePath\(`([^`]*)`\)/revalidatePath(`$1`, '\''page'\'')/g' "$FILE" 2>/dev/null || true
echo "$FILE"
fi
done
# ─────────────────────────────────────────────────────────────
# GROUP 5: Delete BACKUP file (it's causing compilation errors)
# ─────────────────────────────────────────────────────────────
echo ""
echo "→ Removing backup files from compilation..."
BACKUP="src/Header/Nav/index BACKUP.tsx"
if [ -f "$BACKUP" ]; then
rm "$BACKUP"
echo " ✓ Deleted 'src/Header/Nav/index BACKUP.tsx'"
else
echo " - BACKUP file not found (already removed?)"
fi
# ─────────────────────────────────────────────────────────────
# GROUP 6: Fix Footer/Header getCachedGlobal type cast
# ─────────────────────────────────────────────────────────────
echo ""
echo "→ Fixing Footer and Header getCachedGlobal type cast..."
FOOTER_COMPONENT="src/Footer/Component.tsx"
if [ -f "$FOOTER_COMPONENT" ]; then
# Add 'as Footer' cast to getCachedGlobal call
sed -i '' \
"s/const \(.*\) = await getCachedGlobal('footer'/const \1 = (await getCachedGlobal('footer'/" \
"$FOOTER_COMPONENT"
echo " ⚠ Footer/Component.tsx needs manual fix — see note below"
fi
HEADER_COMPONENT="src/Header/Component.tsx"
if [ -f "$HEADER_COMPONENT" ]; then
echo " ⚠ Header/Component.tsx needs manual fix — see note below"
fi
# ─────────────────────────────────────────────────────────────
# DONE
# ─────────────────────────────────────────────────────────────
echo ""
echo "=== Script complete ==="
echo ""
echo "MANUAL FIXES STILL NEEDED:"
echo ""
echo "1. FDTagsBlock and FDTextBlock missing from payload-types:"
echo " → Run: npx payload generate:types"
echo " → If still missing, check that FDTagsBlock and FDTextBlock"
echo " are imported and added to the Pages collection layout field"
echo " in src/collections/Pages/index.ts"
echo ""
echo "2. Footer/Component.tsx and Header/Component.tsx type mismatch:"
echo " → Change the getCachedGlobal line to add a type cast, e.g.:"
echo " const footerData = await getCachedGlobal('footer', 1) as Footer"
echo " const headerData = await getCachedGlobal('header', 1) as Header"
echo ""
echo "3. Footer/RowLabel.tsx and Header/RowLabel.tsx — 'link' property:"
echo " → Change: data.link?.label"
echo " To: data.label (the nav item schema changed, link is now flat)"
echo ""
echo "4. Media.caption doesn't exist — removed from your schema:"
echo " → In MediaBlock/Component.tsx line 33: remove the .caption reference"
echo " → In heros/MediumImpact/index.tsx lines 36/38: remove .caption"
echo " → In src/endpoints/seed/image-*.ts: remove caption field"
echo ""
echo "5. Seed files (contact-page.ts, home.ts, index.ts):"
echo " → These are dev seeds, not used in production."
echo " → Quickest fix: add // @ts-ignore above each flagged line,"
echo " or delete the seed files if you no longer use them."
echo ""
echo "After manual fixes, run: npx tsc --noEmit to verify"

213
fix2.py
View File

@ -1,213 +0,0 @@
#!/usr/bin/env python3
"""Fix remaining TypeScript errors in fdweb2 project."""
import os, re, sys
def read(path):
with open(path, 'r') as f: return f.read()
def write(path, content):
with open(path, 'w') as f: f.write(content)
print(f"{path}")
def prepend_nocheck(path):
if not os.path.exists(path): return
content = read(path)
if '// @ts-nocheck' not in content:
write(path, '// @ts-nocheck\n' + content)
# ── 1. Add @ts-nocheck to old template blocks we don't use ───────────────
print("\n→ Suppressing old template block errors...")
for p in [
'src/blocks/ArchiveBlock/Component.tsx',
'src/blocks/CallToAction/Component.tsx',
'src/blocks/Content/Component.tsx',
'src/blocks/MediaBlock/Component.tsx',
]:
prepend_nocheck(p)
# ── 2. Add @ts-nocheck to seed files ─────────────────────────────────────
print("\n→ Suppressing seed file errors...")
for p in [
'src/endpoints/seed/contact-page.ts',
'src/endpoints/seed/home.ts',
'src/endpoints/seed/image-1.ts',
'src/endpoints/seed/image-2.ts',
'src/endpoints/seed/image-3.ts',
'src/endpoints/seed/index.ts',
]:
prepend_nocheck(p)
# ── 3. Fix revalidatePath — add 'page' second argument ───────────────────
print("\n→ Fixing revalidatePath calls...")
revalidate_files = [
'src/collections/Pages/hooks/revalidatePage.ts',
'src/collections/Posts/hooks/revalidatePost.ts',
'src/Footer/hooks/revalidateFooter.ts',
'src/Header/hooks/revalidateHeader.ts',
'src/globals/PopupAnnouncement/hooks/revalidatePopup.ts',
'src/hooks/revalidateRedirects.ts',
]
# Match revalidatePath(anything) where there's no second arg
# i.e. revalidatePath( <stuff> ) with no comma at top level inside parens
pattern = re.compile(r'revalidatePath\(([^,)]+)\)')
def add_page_arg(m):
inner = m.group(1).strip()
return f"revalidatePath({inner}, 'page')"
for path in revalidate_files:
if not os.path.exists(path): continue
content = read(path)
new_content = pattern.sub(add_page_arg, content)
if new_content != content:
write(path, new_content)
else:
print(f" - No match in {path} (may already be fixed)")
# ── 4. Fix Pages/index.ts — add FDTagsBlock + FDTextBlock to layout ──────
print("\n→ Fixing Pages/index.ts block registrations...")
pages_path = 'src/collections/Pages/index.ts'
if os.path.exists(pages_path):
content = read(pages_path)
# Add FDTextBlock import if missing
if 'FDTextBlock' not in content:
content = content.replace(
"import { FDTagsBlock } from '../../blocks/FDTagsBlock/config'",
"import { FDTagsBlock } from '../../blocks/FDTagsBlock/config'\nimport { FDTextBlock } from '../../blocks/FDTextBlock/config'"
)
# Add FDTagsBlock and FDTextBlock to blocks array if missing
if 'FDTagsBlock' not in content.split('blocks: [')[1].split(']')[0]:
content = content.replace(
'FDVpsCalculatorBlock]',
'FDVpsCalculatorBlock, FDTagsBlock, FDTextBlock]'
)
elif 'FDTextBlock' not in content.split('blocks: [')[1].split(']')[0]:
content = content.replace(
'FDVpsCalculatorBlock]',
'FDVpsCalculatorBlock, FDTextBlock]'
)
write(pages_path, content)
# ── 5. Fix Footer/Component.tsx — add type cast ───────────────────────────
print("\n→ Fixing Footer/Component.tsx type cast...")
footer_path = 'src/Footer/Component.tsx'
if os.path.exists(footer_path):
content = read(footer_path)
# Add Footer import if needed and cast the getCachedGlobal result
# Pattern: const X = await getCachedGlobal('footer', ...)
new = re.sub(
r"(const\s+\w+\s*=\s*await getCachedGlobal\('footer'[^)]*\))",
r"\1 as Footer",
content
)
if new == content:
# Try without await
new = re.sub(
r"(getCachedGlobal\('footer'[^)]*\))",
r"(\1 as Footer)",
content
)
if new != content:
write(footer_path, new)
else:
print(f" ⚠ Footer/Component.tsx — pattern not matched, needs manual fix")
# ── 6. Fix Header/Component.tsx — add type cast ───────────────────────────
print("\n→ Fixing Header/Component.tsx type cast...")
header_path = 'src/Header/Component.tsx'
if os.path.exists(header_path):
content = read(header_path)
new = re.sub(
r"(const\s+\w+\s*=\s*await getCachedGlobal\('header'[^)]*\))",
r"\1 as Header",
content
)
if new == content:
new = re.sub(
r"(getCachedGlobal\('header'[^)]*\))",
r"(\1 as Header)",
content
)
if new != content:
write(header_path, new)
else:
print(f" ⚠ Header/Component.tsx — pattern not matched, needs manual fix")
# ── 7. Fix RowLabel files — .link property doesn't exist ─────────────────
print("\n→ Fixing RowLabel files...")
for path in ['src/Footer/RowLabel.tsx', 'src/Header/RowLabel.tsx']:
if not os.path.exists(path): continue
content = read(path)
# data.link.label → data.label
# data.link.url → data.url
new = content.replace('.link.label', '.label').replace('.link.url', '.url').replace('.link?.label', '.label').replace('.link?.url', '.url')
if new != content:
write(path, new)
else:
print(f"{path} — pattern not matched, needs manual fix")
# ── 8. Fix MediaBlock caption ─────────────────────────────────────────────
print("\n→ Fixing MediaBlock/Component.tsx caption property...")
media_block = 'src/blocks/MediaBlock/Component.tsx'
if os.path.exists(media_block):
content = read(media_block)
# Cast media to any to access caption, or just remove it
new = re.sub(r'\(media as Media\)\.caption', '(media as any).caption', content)
new = re.sub(r'media\.caption', '(media as any).caption', new)
if new != content:
write(media_block, new)
# ── 9. Fix heros/MediumImpact caption ────────────────────────────────────
print("\n→ Fixing heros/MediumImpact/index.tsx caption property...")
medium_impact = 'src/heros/MediumImpact/index.tsx'
if os.path.exists(medium_impact):
content = read(medium_impact)
new = re.sub(r'\(media as Media\)\.caption', '(media as any).caption', content)
new = re.sub(r'(?<!\(media as any\))media\.caption', '(media as any).caption', new)
if new != content:
write(medium_impact, new)
# ── 10. Fix generateMeta.ts — .og doesn't exist ──────────────────────────
print("\n→ Fixing generateMeta.ts og property...")
meta_path = 'src/utilities/generateMeta.ts'
if os.path.exists(meta_path):
content = read(meta_path)
# Cast the image to any to access og
new = re.sub(r'(\w+)\.og\b', r'(\1 as any).og', content)
if new != content:
write(meta_path, new)
# ── 11. Fix FDServiceChooserBlock categories null ────────────────────────
print("\n→ Fixing FDServiceChooserBlock categories null...")
svc_path = 'src/blocks/FDServiceChooserBlock/Component.tsx'
if os.path.exists(svc_path):
content = read(svc_path)
# Any remaining direct categories. access
new = re.sub(r'(?<!\?\? \[\]\))categories\.map\(', '(categories ?? []).map(', content)
new = re.sub(r'(?<!\?\? \[\]\))categories\.filter\(', '(categories ?? []).filter(', new)
new = re.sub(r'(?<!\?\? \[\]\))categories\.find\(', '(categories ?? []).find(', new)
new = re.sub(r'(?<!\?\? \[\]\))categories\.length', '(categories ?? []).length', new)
# Handle line 31 which may be something like: if (categories && categories[
new = re.sub(r'\bcategories\[', '(categories ?? [])[', new)
if new != content:
write(svc_path, new)
# ── 12. Fix RichText CallToActionBlock import ─────────────────────────────
print("\n→ Fixing RichText CallToActionBlock import...")
richtext = 'src/components/RichText/index.tsx'
if os.path.exists(richtext):
content = read(richtext)
# Remove CallToActionBlock from any import line
new = re.sub(r',\s*CallToActionBlock', '', content)
new = re.sub(r'CallToActionBlock,\s*', '', new)
# If it was the only import, remove the whole import line
new = re.sub(r"import type \{ \} from '@/payload-types'\n?", '', new)
if new != content:
write(richtext, new)
print("\n=== Done! ===")
print("\nNow run:")
print(" npx payload generate:types")
print(" npx tsc --noEmit 2>&1 | grep 'error TS'")

109
fix3.py
View File

@ -1,109 +0,0 @@
#!/usr/bin/env python3
"""Fix all remaining TypeScript errors."""
import os, re
def read(path):
with open(path, 'r') as f: return f.read()
def write(path, content):
with open(path, 'w') as f: f.write(content)
print(f"{path}")
# ── 1. revalidateTag needs 2 args in Next.js 16 — suppress with cast ──────
print("\n→ Fixing revalidateTag calls...")
revalidate_tag_files = [
'src/collections/Pages/hooks/revalidatePage.ts',
'src/collections/Posts/hooks/revalidatePost.ts',
'src/Footer/hooks/revalidateFooter.ts',
'src/Header/hooks/revalidateHeader.ts',
'src/globals/PopupAnnouncement/hooks/revalidatePopup.ts',
'src/hooks/revalidateRedirects.ts',
]
for path in revalidate_tag_files:
if not os.path.exists(path): continue
content = read(path)
# Replace revalidateTag('anything') with (revalidateTag as any)('anything')
new = re.sub(r'\brevalidateTag\(', '(revalidateTag as any)(', content)
if new != content:
write(path, new)
# ── 2. Fix Footer/Component.tsx — python script broke the cast ────────────
print("\n→ Fixing Footer/Component.tsx getCachedGlobal cast...")
footer_path = 'src/Footer/Component.tsx'
if os.path.exists(footer_path):
content = read(footer_path)
# Fix the broken: await (getCachedGlobal('footer', 1) as Footer)()
new = re.sub(
r'await \(getCachedGlobal\(([^)]+)\) as Footer\)\(\)',
r'await getCachedGlobal(\1) as unknown as Footer',
content
)
# Also fix if it got the type annotation wrong
new = re.sub(
r'const footerData: Footer =',
'const footerData =',
new
)
if new != content:
write(footer_path, new)
# ── 3. Fix Header/Component.tsx — same broken cast ────────────────────────
print("\n→ Fixing Header/Component.tsx getCachedGlobal cast...")
header_path = 'src/Header/Component.tsx'
if os.path.exists(header_path):
content = read(header_path)
new = re.sub(
r'await \(getCachedGlobal\(([^)]+)\) as Header\)\(\)',
r'await getCachedGlobal(\1) as unknown as Header',
content
)
new = re.sub(
r'const headerData: Header =',
'const headerData =',
new
)
if new != content:
write(header_path, new)
# ── 4. Fix RichText — CTABlockProps and CallToActionBlock still referenced ─
print("\n→ Fixing RichText/index.tsx...")
richtext_path = 'src/components/RichText/index.tsx'
if os.path.exists(richtext_path):
content = read(richtext_path)
# Remove CTABlockProps from the SerializedBlockNode union type
new = re.sub(r'CTABlockProps \| ', '', content)
new = re.sub(r' \| CTABlockProps', '', new)
new = re.sub(r'CTABlockProps', '', new)
# Replace <CallToActionBlock ... /> usage with a null render
new = re.sub(
r"cta: \(\{ node \}\) => <CallToActionBlock \{\.\.\.node\.fields\} />",
"cta: () => null",
new
)
# Remove the import line if still present
new = re.sub(r"import \{ CallToActionBlock \} from '@/blocks/CallToAction/Component'\n", '', new)
if new != content:
write(richtext_path, new)
# ── 5. Fix MediumImpact hero — caption doesn't exist on Media ─────────────
print("\n→ Fixing heros/MediumImpact/index.tsx caption...")
medium_path = 'src/heros/MediumImpact/index.tsx'
if os.path.exists(medium_path):
content = read(medium_path)
# Cast media to any where .caption is accessed
new = re.sub(r'media(\??)\.(caption)', r'(media as any)\1.\2', content)
if new != content:
write(medium_path, new)
# ── 6. Fix generateMeta.ts — og size doesn't exist ───────────────────────
print("\n→ Fixing utilities/generateMeta.ts og size...")
meta_path = 'src/utilities/generateMeta.ts'
if os.path.exists(meta_path):
content = read(meta_path)
# Cast image.sizes to any
new = re.sub(r'image\.sizes(\??)\.(og)', r'(image as any).sizes\1.\2', content)
if new != content:
write(meta_path, new)
print("\n=== Done! ===")
print("\nRun: npx tsc --noEmit")

38
fix4.py
View File

@ -1,38 +0,0 @@
#!/usr/bin/env python3
import os, re
def read(path):
with open(path, 'r') as f: return f.read()
def write(path, content):
with open(path, 'w') as f: f.write(content)
print(f"{path}")
# Fix 1: revalidatePath cast to any (Next.js 16 types it as possibly undefined)
# Fix 2: logger.info`...`) → logger.info(`...`)
files = [
'src/collections/Pages/hooks/revalidatePage.ts',
'src/collections/Posts/hooks/revalidatePost.ts',
'src/Footer/hooks/revalidateFooter.ts',
'src/Header/hooks/revalidateHeader.ts',
'src/hooks/revalidateRedirects.ts',
'src/globals/PopupAnnouncement/hooks/revalidatePopup.ts',
]
for path in files:
if not os.path.exists(path): continue
content = read(path)
# Fix revalidatePath(x, 'page') → (revalidatePath as any)(x, 'page')
new = re.sub(r'\brevalidatePath\(', '(revalidatePath as any)(', content)
# Fix broken logger.info`...`) → logger.info(`...`)
# Pattern: .info`some text ${var}`) → .info(`some text ${var}`)
new = re.sub(r'\.info`([^`]*)`\)', r'.info(`\1`)', new)
if new != content:
write(path, new)
else:
print(f" - no changes: {path}")
print("\nDone. Run: npx tsc --noEmit")

View File

@ -1,72 +0,0 @@
#!/usr/bin/env python3
import re
# Fix 1: Clean up debug globals in payload.config.ts
path = 'src/payload.config.ts'
with open(path) as f: c = f.read()
old = """ globals: (() => {
const g = [Header, Footer, AnnouncementBar, PopupAnnouncement]
g.forEach((x, i) => { if (!x || !x.slug) console.error('UNDEFINED GLOBAL at index', i, ':', ['Header','Footer','AnnouncementBar','PopupAnnouncement'][i]) })
return g.filter(x => x && x.slug)
})(),"""
new = " globals: [Header, Footer, AnnouncementBar, PopupAnnouncement],"
if old in c:
c = c.replace(old, new)
with open(path, 'w') as f: f.write(c)
print('✓ Cleaned up globals debug code')
else:
print('⚠ Pattern not found in payload.config.ts - may already be clean')
for line in c.split('\n'):
if 'globals' in line:
print(' ', repr(line))
# Fix 2: Restore formBuilderPlugin in plugins/index.ts
path = 'src/plugins/index.ts'
with open(path) as f: c = f.read()
if 'formBuilderPlugin' not in c:
# Add import
c = c.replace(
"import { nestedDocsPlugin }",
"import { formBuilderPlugin } from '@payloadcms/plugin-form-builder'\nimport { FixedToolbarFeature, HeadingFeature, lexicalEditor } from '@payloadcms/richtext-lexical'\nimport { nestedDocsPlugin }"
)
# Add plugin before searchPlugin
c = c.replace(
" searchPlugin({",
""" formBuilderPlugin({
fields: {
payment: false,
},
formOverrides: {
fields: ({ defaultFields }) => {
return defaultFields.map((field) => {
if ('name' in field && field.name === 'confirmationMessage') {
return {
...field,
editor: lexicalEditor({
features: ({ rootFeatures }) => {
return [
...rootFeatures,
FixedToolbarFeature(),
HeadingFeature({ enabledHeadingSizes: ['h1', 'h2', 'h3', 'h4'] }),
]
},
}),
}
}
return field
})
},
},
}),
searchPlugin({"""
)
with open(path, 'w') as f: f.write(c)
print('✓ Restored formBuilderPlugin')
else:
print('✓ formBuilderPlugin already present')
print('\nDone. Restart dev server.')

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

BIN
media/Stockholm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 MiB

5
media/Text-file-data.csv Normal file
View File

@ -0,0 +1,5 @@
Packages;SLA;Telefontime;Månadspris;Response
Fiber Basic 10 000;99,90%;kl 8-17; 1 195,00 kr ;Lots of details
Fiber Basic 10 000 Plus;99,90%;kl 8-17; 1 995,00 kr ;
Fiber Premium;99,90%;kl 8-17; 2 995,00 kr ;Lots of details
Fiber Platina;99,90%;kl 8-17; 4 995,00 kr ;
1 Packages SLA Telefontime Månadspris Response
2 Fiber Basic 10 000 99,90% kl 8-17 1 195,00 kr Lots of details
3 Fiber Basic 10 000 Plus 99,90% kl 8-17 1 995,00 kr
4 Fiber Premium 99,90% kl 8-17 2 995,00 kr Lots of details
5 Fiber Platina 99,90% kl 8-17 4 995,00 kr

BIN
media/test-1200x671.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

BIN
media/test-1920x1073.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

BIN
media/test-400x224.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
media/test-800x447.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

BIN
media/test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 MiB

View File

@ -60,7 +60,7 @@ export const FDFaqBlockComponent: React.FC<FDFaqBlockProps> = ({
>
<div className="overflow-hidden">
<div className={`font-joey text-fd-body pl-7 md:pl-9 fd-prose ${proseColor}`}>
<RichText content={item.answer} />
<RichText data={item.answer} />
</div>
</div>
</div>

View File

@ -0,0 +1,74 @@
import type { Block } from 'payload'
import {
lexicalEditor,
BoldFeature,
ItalicFeature,
UnderlineFeature,
LinkFeature,
UnorderedListFeature,
OrderedListFeature,
} from '@payloadcms/richtext-lexical'
const fdRichTextEditor = lexicalEditor({
features: ({ defaultFeatures }) => [
...defaultFeatures,
BoldFeature(),
ItalicFeature(),
UnderlineFeature(),
LinkFeature({ enabledCollections: ['pages', 'posts'] }),
UnorderedListFeature(),
OrderedListFeature(),
],
})
export const FDFaqBlock: Block = {
slug: 'fdFaq',
interfaceName: 'FDFaqBlock',
labels: {
singular: 'FD FAQ',
plural: 'FD FAQs',
},
fields: [
{
name: 'heading',
type: 'text',
localized: true,
required: true,
label: 'Rubrik',
defaultValue: 'Vanliga frågor',
},
{
name: 'items',
type: 'array',
label: 'Frågor',
minRows: 1,
fields: [
{
name: 'question',
type: 'text',
localized: true,
required: true,
label: 'Fråga',
},
{
name: 'answer',
type: 'richText',
localized: true,
label: 'Svar',
editor: fdRichTextEditor,
},
],
},
{
name: 'theme',
type: 'select',
label: 'Tema',
defaultValue: 'gray',
options: [
{ label: 'Grå bakgrund', value: 'gray' },
{ label: 'Ljust', value: 'light' },
{ label: 'Mörkt', value: 'dark' },
],
},
],
}

View File

@ -58,7 +58,7 @@ export const FDTextBlockComponent: React.FC<FDTextBlockProps> = ({
)}
{body && (
<div className={`font-joey text-fd-body-lg fd-prose ${colors.body}`}>
<RichText content={body} />
<RichText data={body} />
</div>
)}
</div>

View File

@ -0,0 +1,102 @@
import type { Block } from 'payload'
import {
lexicalEditor,
BoldFeature,
ItalicFeature,
UnderlineFeature,
LinkFeature,
UnorderedListFeature,
OrderedListFeature,
HeadingFeature,
BlockquoteFeature,
} from '@payloadcms/richtext-lexical'
const fdRichTextEditor = lexicalEditor({
features: ({ defaultFeatures }) => [
...defaultFeatures,
BoldFeature(),
ItalicFeature(),
UnderlineFeature(),
LinkFeature({ enabledCollections: ['pages', 'posts'] }),
UnorderedListFeature(),
OrderedListFeature(),
HeadingFeature({ enabledHeadingSizes: ['h2', 'h3', 'h4'] }),
BlockquoteFeature(),
],
})
export const FDTextBlock: Block = {
slug: 'fdText',
interfaceName: 'FDTextBlock',
labels: {
singular: 'FD Textblock',
plural: 'FD Textblock',
},
fields: [
{
name: 'heading',
type: 'text',
localized: true,
label: 'Rubrik',
},
{
name: 'subheading',
type: 'text',
localized: true,
label: 'Underrubrik',
},
{
name: 'body',
type: 'richText',
localized: true,
label: 'Brödtext',
editor: fdRichTextEditor,
},
{
name: 'alignment',
type: 'select',
label: 'Textjustering',
defaultValue: 'left',
options: [
{ label: 'Vänster', value: 'left' },
{ label: 'Centrerad', value: 'center' },
{ label: 'Höger', value: 'right' },
],
},
{
name: 'textColor',
type: 'select',
label: 'Textfärg',
defaultValue: 'navy',
options: [
{ label: 'Navy', value: 'navy' },
{ label: 'Vit', value: 'white' },
{ label: 'Gul', value: 'yellow' },
],
},
{
name: 'sectionBackground',
type: 'select',
label: 'Bakgrund',
defaultValue: 'white',
options: [
{ label: 'Vit', value: 'white' },
{ label: 'Navy', value: 'navy' },
{ label: 'Grå', value: 'gray' },
{ label: 'Gul', value: 'yellow' },
],
},
{
name: 'maxWidth',
type: 'select',
label: 'Maxbredd',
defaultValue: 'wide',
options: [
{ label: 'Smal (600px)', value: 'narrow' },
{ label: 'Medium (800px)', value: 'medium' },
{ label: 'Bred (1100px)', value: 'wide' },
{ label: 'Full', value: 'full' },
],
},
],
}

View File

@ -65,7 +65,7 @@ export const FDUspTableBlockComponent: React.FC<FDUspTableBlockProps> = ({
<span className={`font-joey-bold text-fd-h3 ${txt}`}>{row.title}</span>
</div>
<div className={`font-joey text-fd-body fd-prose ${txt} ${proseOpacity} md:pl-0 pl-14`}>
<RichText content={row.description} />
<RichText data={row.description} />
</div>
</div>
))}

View File

@ -0,0 +1,92 @@
import type { Block } from 'payload'
import {
lexicalEditor,
BoldFeature,
ItalicFeature,
UnderlineFeature,
LinkFeature,
UnorderedListFeature,
} from '@payloadcms/richtext-lexical'
const fdRichTextEditor = lexicalEditor({
features: ({ defaultFeatures }) => [
...defaultFeatures,
BoldFeature(),
ItalicFeature(),
UnderlineFeature(),
LinkFeature({ enabledCollections: ['pages', 'posts'] }),
UnorderedListFeature(),
],
})
export const FDUspTableBlock: Block = {
slug: 'fdUspTable',
interfaceName: 'FDUspTableBlock',
labels: {
singular: 'FD USP-tabell',
plural: 'FD USP-tabeller',
},
fields: [
{
name: 'heading',
type: 'text',
localized: true,
label: 'Rubrik (valfri)',
},
{
name: 'rows',
type: 'array',
label: 'Rader',
minRows: 1,
fields: [
{
name: 'title',
type: 'text',
localized: true,
required: true,
label: 'Rubrik',
admin: { description: 'T.ex. "Högsta säkerhet"' },
},
{
name: 'description',
type: 'richText',
localized: true,
label: 'Beskrivning',
editor: fdRichTextEditor,
},
],
},
{
name: 'checkColor',
type: 'select',
label: 'Checkikon-färg',
defaultValue: 'navy',
options: [
{ label: 'Navy (vit bock)', value: 'navy' },
{ label: 'Gul (navy bock)', value: 'yellow' },
{ label: 'Grå (navy bock)', value: 'gray' },
],
},
{
name: 'sectionBackground',
type: 'select',
label: 'Sektionsbakgrund',
defaultValue: 'white',
options: [
{ label: 'Vit', value: 'white' },
{ label: 'Grå', value: 'gray' },
{ label: 'Navy', value: 'navy' },
],
},
{
name: 'textColor',
type: 'select',
label: 'Textfärg',
defaultValue: 'navy',
options: [
{ label: 'Navy', value: 'navy' },
{ label: 'Vit', value: 'white' },
],
},
],
}

View File

@ -2,16 +2,10 @@ import type { CollectionConfig } from 'payload'
import { authenticated } from '../../access/authenticated'
import { authenticatedOrPublished } from '../../access/authenticatedOrPublished'
import { Archive } from '../../blocks/ArchiveBlock/config'
import { CallToAction } from '../../blocks/CallToAction/config'
import { Content } from '../../blocks/Content/config'
import { FormBlock } from '../../blocks/Form/config'
import { MediaBlock } from '../../blocks/MediaBlock/config'
import { hero } from '@/heros/config'
import { slugField } from 'payload'
import { populatePublishedAt } from '../../hooks/populatePublishedAt'
import { generatePreviewPath } from '../../utilities/generatePreviewPath'
import { revalidateDelete, revalidatePage } from './hooks/revalidatePage'
import { FDHeroBlock } from '../../blocks/FDHeroBlock/config'
import { FDCtaSideImageBlock } from '../../blocks/FDCtaSideImageBlock/config'
import { FDFeatureAnnouncementBlock } from '../../blocks/FDFeatureAnnouncementBlock/config'
@ -49,6 +43,16 @@ import {
PreviewField,
} from '@payloadcms/plugin-seo/fields'
// ── Slug generator — handles Swedish characters ────────────────────────────
const generateSlug = (value: string): string =>
value
.toLowerCase()
.trim()
.replace(/[åä]/g, 'a')
.replace(/ö/g, 'o')
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '')
export const Pages: CollectionConfig<'pages'> = {
slug: 'pages',
access: {
@ -57,9 +61,6 @@ export const Pages: CollectionConfig<'pages'> = {
read: authenticatedOrPublished,
update: authenticated,
},
// This config controls what's populated by default when a page is referenced
// https://payloadcms.com/docs/queries/select#defaultpopulate-collection-config-property
// Type safe if the collection slug generic is passed to `CollectionConfig` - `CollectionConfig<'pages'>
defaultPopulate: {
title: true,
slug: true,
@ -91,23 +92,48 @@ export const Pages: CollectionConfig<'pages'> = {
{
type: 'tabs',
tabs: [
{
fields: [hero],
label: 'Hero',
},
{
fields: [
{
name: 'layout',
type: 'blocks',
blocks: [FDHeroBlock, FDCtaSideImageBlock, FDFeatureAnnouncementBlock, FDServicesGridBlock, FDContactBlock, FDFaqBlock, FDCardGridBlock, FDPricingCardBlock, FDSpacerBlock, FDIconBarBlock, FDUspChecklistBlock, FDWideCardBlock, FDTechPropertiesBlock, FDUspTableBlock, FDHeaderTextImageBlock, FDContactFormBlock, FDLocationsGridBlock, FDAlternateHeroBlock, FDStatisticsBlock, FDPartnersLogosBlock, FDNewsletterBlock, FDServiceChooserBlock, FDDataTableBlock, FDVpsCalculatorBlock, FDTagsBlock, FDTextBlock, FDCodeEmbedBlock, FDVideoBlock],
blocks: [
FDHeroBlock,
FDCtaSideImageBlock,
FDFeatureAnnouncementBlock,
FDServicesGridBlock,
FDContactBlock,
FDFaqBlock,
FDCardGridBlock,
FDPricingCardBlock,
FDSpacerBlock,
FDIconBarBlock,
FDUspChecklistBlock,
FDWideCardBlock,
FDTechPropertiesBlock,
FDUspTableBlock,
FDHeaderTextImageBlock,
FDContactFormBlock,
FDLocationsGridBlock,
FDAlternateHeroBlock,
FDStatisticsBlock,
FDPartnersLogosBlock,
FDNewsletterBlock,
FDServiceChooserBlock,
FDDataTableBlock,
FDVpsCalculatorBlock,
FDTagsBlock,
FDTextBlock,
FDCodeEmbedBlock,
FDVideoBlock,
],
required: true,
admin: {
initCollapsed: true,
},
},
],
label: 'Content',
label: 'Innehåll',
},
{
name: 'meta',
@ -124,13 +150,9 @@ export const Pages: CollectionConfig<'pages'> = {
MetaImageField({
relationTo: 'media',
}),
MetaDescriptionField({}),
PreviewField({
// if the `generateUrl` function is configured
hasGenerateFn: true,
// field paths to match the target field for data
titlePath: 'meta.title',
descriptionPath: 'meta.description',
}),
@ -145,7 +167,22 @@ export const Pages: CollectionConfig<'pages'> = {
position: 'sidebar',
},
},
slugField(),
// ── Slug ──────────────────────────────────────────────────────────────
{
name: 'slug',
type: 'text',
required: true,
index: true,
admin: {
position: 'sidebar',
description: 'Genereras automatiskt från titeln.',
},
hooks: {
beforeChange: [
({ data }) => generateSlug(data?.title || ''),
],
},
}, // ← slug object closes here, then straight into ],
],
hooks: {
afterChange: [revalidatePage],
@ -155,7 +192,7 @@ export const Pages: CollectionConfig<'pages'> = {
versions: {
drafts: {
autosave: {
interval: 100, // We set this interval for optimal live preview
interval: 100,
},
schedulePublish: true,
},

View File

@ -25,7 +25,16 @@ import {
OverviewField,
PreviewField,
} from '@payloadcms/plugin-seo/fields'
import { slugField } from 'payload'
// ── Slug generator — handles Swedish characters ────────────────────────────
const generateSlug = (value: string): string =>
value
.toLowerCase()
.trim()
.replace(/[åä]/g, 'a')
.replace(/ö/g, 'o')
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '')
export const Posts: CollectionConfig<'posts'> = {
slug: 'posts',
@ -35,9 +44,6 @@ export const Posts: CollectionConfig<'posts'> = {
read: authenticatedOrPublished,
update: authenticated,
},
// This config controls what's populated by default when a post is referenced
// https://payloadcms.com/docs/queries/select#defaultpopulate-collection-config-property
// Type safe if the collection slug generic is passed to `CollectionConfig` - `CollectionConfig<'posts'>
defaultPopulate: {
title: true,
slug: true,
@ -100,7 +106,7 @@ export const Posts: CollectionConfig<'posts'> = {
required: true,
},
],
label: 'Content',
label: 'Innehåll',
},
{
fields: [
@ -147,13 +153,9 @@ export const Posts: CollectionConfig<'posts'> = {
MetaImageField({
relationTo: 'media',
}),
MetaDescriptionField({}),
PreviewField({
// if the `generateUrl` function is configured
hasGenerateFn: true,
// field paths to match the target field for data
titlePath: 'meta.title',
descriptionPath: 'meta.description',
}),
@ -190,9 +192,6 @@ export const Posts: CollectionConfig<'posts'> = {
hasMany: true,
relationTo: 'users',
},
// This field is only used to populate the user data via the `populateAuthors` hook
// This is because the `user` collection has access control locked to protect user privacy
// GraphQL will also not return mutated user data that differs from the underlying schema
{
name: 'populatedAuthors',
type: 'array',
@ -214,7 +213,23 @@ export const Posts: CollectionConfig<'posts'> = {
},
],
},
slugField(),
// ── Slug ──────────────────────────────────────────────────────────────
{
name: 'slug',
type: 'text',
required: true,
index: true,
admin: {
position: 'sidebar',
description: 'Genereras automatiskt från titeln.',
},
hooks: {
beforeChange: [
({ data }) => generateSlug(data?.title || ''),
],
},
}, // ← slug object closes here, then straight into ],
],
hooks: {
afterChange: [revalidatePost],
@ -224,7 +239,7 @@ export const Posts: CollectionConfig<'posts'> = {
versions: {
drafts: {
autosave: {
interval: 100, // We set this interval for optimal live preview
interval: 100,
},
schedulePublish: true,
},

View File

@ -153,49 +153,6 @@ export interface UserAuthOperations {
export interface Page {
id: number;
title: string;
hero: {
type: 'none' | 'highImpact' | 'mediumImpact' | 'lowImpact';
richText?: {
root: {
type: string;
children: {
type: any;
version: number;
[k: string]: unknown;
}[];
direction: ('ltr' | 'rtl') | null;
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
indent: number;
version: number;
};
[k: string]: unknown;
} | null;
links?:
| {
link: {
type?: ('reference' | 'custom') | null;
newTab?: boolean | null;
reference?:
| ({
relationTo: 'pages';
value: number | Page;
} | null)
| ({
relationTo: 'posts';
value: number | Post;
} | null);
url?: string | null;
label: string;
/**
* Choose how the link should be rendered.
*/
appearance?: ('default' | 'outline') | null;
};
id?: string | null;
}[]
| null;
media?: (number | null) | Media;
};
layout: (
| FDHeroBlock
| FDCtaSideImageBlock
@ -236,9 +193,8 @@ export interface Page {
};
publishedAt?: string | null;
/**
* When enabled, the slug will auto-generate from the title field on save and autosave.
* Genereras automatiskt från titeln.
*/
generateSlug?: boolean | null;
slug: string;
updatedAt: string;
createdAt: string;
@ -246,53 +202,32 @@ export interface Page {
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "posts".
* via the `definition` "FDHeroBlock".
*/
export interface Post {
id: number;
title: string;
heroImage?: (number | null) | Media;
content: {
root: {
type: string;
children: {
type: any;
version: number;
[k: string]: unknown;
}[];
direction: ('ltr' | 'rtl') | null;
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
indent: number;
version: number;
};
[k: string]: unknown;
};
relatedPosts?: (number | Post)[] | null;
categories?: (number | Category)[] | null;
meta?: {
title?: string | null;
/**
* Maximum upload file size: 12MB. Recommended file size for images is <500KB.
*/
image?: (number | null) | Media;
description?: string | null;
};
publishedAt?: string | null;
authors?: (number | User)[] | null;
populatedAuthors?:
| {
id?: string | null;
name?: string | null;
}[]
| null;
export interface FDHeroBlock {
heading: string;
subheading?: string | null;
body?: string | null;
ctaText?: string | null;
ctaLink?: string | null;
secondaryCtaText?: string | null;
secondaryCtaLink?: string | null;
/**
* When enabled, the slug will auto-generate from the title field on save and autosave.
* Fullbreddsbild bakom texten. Lämna tom för enfärgad bakgrund.
*/
generateSlug?: boolean | null;
slug: string;
updatedAt: string;
createdAt: string;
_status?: ('draft' | 'published') | null;
backgroundImage?: (number | null) | Media;
/**
* Hur mörk overlay över bilden (för läsbarhet)
*/
overlayOpacity?: ('30' | '50' | '70') | null;
textColor?: ('auto' | 'white' | 'navy') | null;
/**
* Ignoreras om bakgrundsbild är vald
*/
theme?: ('light' | 'dark') | null;
id?: string | null;
blockName?: string | null;
blockType: 'fdHero';
}
/**
* This interface was referenced by `Config`'s JSON-Schema
@ -347,81 +282,6 @@ export interface Media {
};
};
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "categories".
*/
export interface Category {
id: number;
title: string;
slug: string;
parent?: (number | null) | Category;
breadcrumbs?:
| {
doc?: (number | null) | Category;
url?: string | null;
label?: string | null;
id?: string | null;
}[]
| null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users".
*/
export interface User {
id: number;
name?: string | null;
updatedAt: string;
createdAt: string;
email: string;
resetPasswordToken?: string | null;
resetPasswordExpiration?: string | null;
salt?: string | null;
hash?: string | null;
loginAttempts?: number | null;
lockUntil?: string | null;
sessions?:
| {
id: string;
createdAt?: string | null;
expiresAt: string;
}[]
| null;
password?: string | null;
collection: 'users';
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "FDHeroBlock".
*/
export interface FDHeroBlock {
heading: string;
subheading?: string | null;
body?: string | null;
ctaText?: string | null;
ctaLink?: string | null;
secondaryCtaText?: string | null;
secondaryCtaLink?: string | null;
/**
* Fullbreddsbild bakom texten. Lämna tom för enfärgad bakgrund.
*/
backgroundImage?: (number | null) | Media;
/**
* Hur mörk overlay över bilden (för läsbarhet)
*/
overlayOpacity?: ('30' | '50' | '70') | null;
textColor?: ('auto' | 'white' | 'navy') | null;
/**
* Ignoreras om bakgrundsbild är vald
*/
theme?: ('light' | 'dark') | null;
id?: string | null;
blockName?: string | null;
blockType: 'fdHero';
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "FDCtaSideImageBlock".
@ -1376,6 +1236,101 @@ export interface FDVideoBlock {
blockName?: string | null;
blockType: 'fdVideo';
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "posts".
*/
export interface Post {
id: number;
title: string;
heroImage?: (number | null) | Media;
content: {
root: {
type: string;
children: {
type: any;
version: number;
[k: string]: unknown;
}[];
direction: ('ltr' | 'rtl') | null;
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
indent: number;
version: number;
};
[k: string]: unknown;
};
relatedPosts?: (number | Post)[] | null;
categories?: (number | Category)[] | null;
meta?: {
title?: string | null;
/**
* Maximum upload file size: 12MB. Recommended file size for images is <500KB.
*/
image?: (number | null) | Media;
description?: string | null;
};
publishedAt?: string | null;
authors?: (number | User)[] | null;
populatedAuthors?:
| {
id?: string | null;
name?: string | null;
}[]
| null;
/**
* Genereras automatiskt från titeln.
*/
slug: string;
updatedAt: string;
createdAt: string;
_status?: ('draft' | 'published') | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "categories".
*/
export interface Category {
id: number;
title: string;
slug: string;
parent?: (number | null) | Category;
breadcrumbs?:
| {
doc?: (number | null) | Category;
url?: string | null;
label?: string | null;
id?: string | null;
}[]
| null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users".
*/
export interface User {
id: number;
name?: string | null;
updatedAt: string;
createdAt: string;
email: string;
resetPasswordToken?: string | null;
resetPasswordExpiration?: string | null;
salt?: string | null;
hash?: string | null;
loginAttempts?: number | null;
lockUntil?: string | null;
sessions?:
| {
id: string;
createdAt?: string | null;
expiresAt: string;
}[]
| null;
password?: string | null;
collection: 'users';
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "redirects".
@ -1615,28 +1570,6 @@ export interface PayloadMigration {
*/
export interface PagesSelect<T extends boolean = true> {
title?: T;
hero?:
| T
| {
type?: T;
richText?: T;
links?:
| T
| {
link?:
| T
| {
type?: T;
newTab?: T;
reference?: T;
url?: T;
label?: T;
appearance?: T;
};
id?: T;
};
media?: T;
};
layout?:
| T
| {
@ -1677,7 +1610,6 @@ export interface PagesSelect<T extends boolean = true> {
description?: T;
};
publishedAt?: T;
generateSlug?: T;
slug?: T;
updatedAt?: T;
createdAt?: T;
@ -2273,7 +2205,6 @@ export interface PostsSelect<T extends boolean = true> {
id?: T;
name?: T;
};
generateSlug?: T;
slug?: T;
updatedAt?: T;
createdAt?: T;