fix: dark mode for video/newsletter/datatable/codeembed, FAQ answer alignment, video rounded corners on thumbnail

This commit is contained in:
Jeffrey 2026-02-20 18:04:18 +01:00
parent dbc336996f
commit 606a4c27b4
7 changed files with 225 additions and 53 deletions

54
add-block-thumbnails.sh Normal file
View File

@ -0,0 +1,54 @@
#!/bin/bash
# Run from your project root (wwwfiberdirekt)
B="src/blocks"
add_thumbnail() {
local file="$1"
local image="$2"
local alt="$3"
if [ ! -f "$file" ]; then
echo "⚠ MISSING: $file"
return
fi
# Skip if already added
if grep -q "imageURL" "$file"; then
echo "⏭ SKIPPED (already has imageURL): $file"
return
fi
sed -i '' "s|slug: '\([^']*\)',|slug: '\1',\n imageURL: '/block-thumbnails/${image}',\n imageAltText: '${alt}',|" "$file"
echo "$file"
}
# Blocks with thumbnails
add_thumbnail "$B/FDHeroBlock/config.ts" "fd-hero.png" "FD Hero"
add_thumbnail "$B/FDAlternateHeroBlock/config.ts" "fd-header-text-image-alt.png" "FD Alternativ Hero"
add_thumbnail "$B/FDHeaderTextImageBlock/config.ts" "fd-header-text-image.png" "FD Rubrik med bild"
add_thumbnail "$B/FDCardGridBlock/config.ts" "fd-card-grid.png" "FD Kortrutnät"
add_thumbnail "$B/FDWideCardBlock/config.ts" "fd-wide-card.png" "FD Bredt kort"
add_thumbnail "$B/FDPricingCardBlock/config.ts" "fd-pricing-card.png" "FD Priskort"
add_thumbnail "$B/FDUspTableBlock/config.ts" "fd-usp-table.png" "FD USP-tabell"
add_thumbnail "$B/FDUspChecklistBlock/config.ts" "fd-usp-checklist.png" "FD USP-checklista"
add_thumbnail "$B/FDServicesGridBlock/config.ts" "fd-services-grid.png" "FD Tjänsterrutnät"
add_thumbnail "$B/FDIconBarBlock/config.ts" "fd-icon-bar.png" "FD Ikonrad"
add_thumbnail "$B/FDFeatureAnnouncementBlock/config.ts" "fd-feature-announcement.png" "FD Funktionsnyhet"
add_thumbnail "$B/FDTechPropertiesBlock/config.ts" "fd-tech-properties.png" "FD Tekniska Egenskaper"
add_thumbnail "$B/FDCtaSideImageBlock/config.ts" "fd-cta-side-image.png" "FD CTA med bild"
add_thumbnail "$B/FDFaqBlock/config.ts" "fd-faq.png" "FD FAQ"
add_thumbnail "$B/FDContactBlock/config.ts" "fd-contact.png" "FD Kontaktformulär"
add_thumbnail "$B/FDContactFormBlock/config.ts" "fd-contact.png" "FD Kontaktformulär"
add_thumbnail "$B/FDSpacerBlock/config.ts" "fd-spacer.png" "FD Mellanrum"
add_thumbnail "$B/FDLocationsGridBlock/config.ts" "fd-places-grid.png" "FD Platser Grid"
echo ""
echo "Blocks without thumbnails yet (add images later):"
echo " FDCtaBannerBlock, FDCodeEmbedBlock, FDDataTableBlock"
echo " FDNewsletterBlock, FDPartnersLogosBlock, FDServiceChooserBlock"
echo " FDStatisticsBlock, FDTagsBlock, FDTeamBlock, FDTestimonialBlock"
echo " FDTextBlock, FDVideoBlock, FDVpsCalculatorBlock"
echo ""
echo "Done. Verify: grep -r 'imageURL' src/blocks/ | wc -l"

91
add_block_thumbnails.py Normal file
View File

@ -0,0 +1,91 @@
#!/usr/bin/env python3
# Run from your project root: python3 add_block_thumbnails.py
import os
import re
BLOCKS_DIR = "src/blocks"
THUMBNAILS_DIR = "public/block-thumbnails"
# Fix double extension if present
double = os.path.join(THUMBNAILS_DIR, "fd-team.png.png")
correct = os.path.join(THUMBNAILS_DIR, "fd-team.png")
if os.path.exists(double):
os.rename(double, correct)
print("✓ Fixed: fd-team.png.png → fd-team.png")
# Block config → thumbnail mapping
BLOCKS = [
("FDHeroBlock", "fd-hero.png", "FD Hero"),
("FDAlternateHeroBlock", "fd-header-text-image-alt.png", "FD Alternativ Hero"),
("FDHeaderTextImageBlock", "fd-header-text-image.png", "FD Rubrik med bild"),
("FDCardGridBlock", "fd-card-grid.png", "FD Kortrutnät"),
("FDWideCardBlock", "fd-wide-card.png", "FD Bredt kort"),
("FDPricingCardBlock", "fd-pricing-card.png", "FD Priskort"),
("FDUspTableBlock", "fd-usp-table.png", "FD USP-tabell"),
("FDUspChecklistBlock", "fd-usp-checklist.png", "FD USP-checklista"),
("FDServicesGridBlock", "fd-services-grid.png", "FD Tjänsterrutnät"),
("FDIconBarBlock", "fd-icon-bar.png", "FD Ikonrad"),
("FDFeatureAnnouncementBlock", "fd-feature-announcement.png", "FD Funktionsnyhet"),
("FDTechPropertiesBlock", "fd-tech-properties.png", "FD Tekniska Egenskaper"),
("FDCtaSideImageBlock", "fd-cta-side-image.png", "FD CTA med bild"),
("FDFaqBlock", "fd-faq.png", "FD FAQ"),
("FDContactBlock", "fd-contact.png", "FD Kontaktformulär"),
("FDContactFormBlock", "fd-contact.png", "FD Kontaktformulär"),
("FDSpacerBlock", "fd-spacer.png", "FD Mellanrum"),
("FDLocationsGridBlock", "fd-places-grid.png", "FD Platser Grid"),
("FDStatisticsBlock", "fd-statistics.png", "FD Statistik"),
("FDPartnersLogosBlock", "fd-partners-logos.png", "FD Partnerlogotyper"),
("FDNewsletterBlock", "fd-newsletter.png", "FD Nyhetsbrev"),
("FDServiceChooserBlock", "fd-service-chooser.png", "FD Tjänsteväljare"),
("FDDataTableBlock", "fd-data-table.png", "FD Datatabell"),
("FDVpsCalculatorBlock", "fd-vps-calculator.png", "FD VPS-kalkylator"),
("FDTagsBlock", "fd-tags.png", "FD Taggar"),
("FDTextBlock", "fd-text-block.png", "FD Textblock"),
("FDCodeEmbedBlock", "fd-code-embed.png", "FD Kodinbäddning"),
("FDVideoBlock", "fd-video.png", "FD Video"),
("FDCtaBannerBlock", "fd-cta-banner.png", "FD CTA-banner"),
("FDTestimonialBlock", "fd-testimonial.png", "FD Omdöme"),
("FDTeamBlock", "fd-team.png", "FD Team"),
]
print()
skipped, updated, missing = 0, 0, 0
for block_name, image, alt in BLOCKS:
config_path = os.path.join(BLOCKS_DIR, block_name, "config.ts")
if not os.path.exists(config_path):
print(f"⚠ NOT FOUND: {config_path}")
missing += 1
continue
content = open(config_path, encoding="utf-8").read()
if "imageURL" in content:
print(f"⏭ SKIPPED: {block_name} (already has imageURL)")
skipped += 1
continue
# Insert after the slug line
new_content = re.sub(
r"(slug:\s*'[^']+',)",
f"\\1\n imageURL: '/block-thumbnails/{image}',\n imageAltText: '{alt}',",
content,
count=1,
)
if new_content == content:
print(f"⚠ NO MATCH: {block_name} — slug line not found, check manually")
missing += 1
continue
open(config_path, "w", encoding="utf-8").write(new_content)
print(f"✓ UPDATED: {block_name}")
updated += 1
print(f"\nDone — {updated} updated, {skipped} skipped, {missing} not found")
print("\nNext steps:")
print(" git add public/block-thumbnails/ src/blocks/")
print(' git commit -m "feat: block thumbnails in admin UI"')
print(" git push")

View File

@ -5,16 +5,17 @@ import type { FDCodeEmbedBlock as FDCodeEmbedBlockProps } from '@/payload-types'
const maxWidthClasses: Record<string, string> = {
default: 'max-w-[1200px]',
narrow: 'max-w-[800px]',
wide: 'max-w-[1400px]',
full: 'max-w-full',
narrow: 'max-w-[800px]',
wide: 'max-w-[1400px]',
full: 'max-w-full',
}
// Navy stays dark. White/gray adapt to OS dark mode.
const bgClasses: Record<string, string> = {
white: 'bg-white',
navy: 'bg-fd-navy',
gray: 'bg-fd-surface-alt',
yellow: 'bg-fd-yellow',
white: 'bg-white dark:bg-fd-navy',
navy: 'bg-fd-navy',
gray: 'bg-fd-surface-alt dark:bg-fd-navy',
yellow: 'bg-fd-yellow',
transparent: 'bg-transparent',
}
@ -36,30 +37,27 @@ export const FDCodeEmbedBlockComponent: React.FC<FDCodeEmbedBlockProps> = ({
const customCodeRef = useRef<HTMLDivElement>(null)
const isDark = sectionBackground === 'navy'
const headingColor =
textColor === 'white'
? 'text-white'
: textColor === 'navy'
? 'text-fd-navy'
: isDark
? 'text-fd-yellow'
: 'text-fd-navy'
textColor === 'white' ? 'text-white'
: textColor === 'navy' ? 'text-fd-navy'
: isDark
? 'text-fd-yellow'
: 'text-fd-navy dark:text-fd-yellow'
const bodyColor =
textColor === 'white'
textColor === 'white' ? 'text-white'
: textColor === 'navy' ? 'text-fd-navy'
: isDark
? 'text-white'
: textColor === 'navy'
? 'text-fd-navy'
: isDark
? 'text-white'
: 'text-fd-navy'
: 'text-fd-navy dark:text-white'
const bgClass = bgClasses[sectionBackground ?? 'white'] || 'bg-white'
const bgClass = bgClasses[sectionBackground ?? 'white'] || 'bg-white dark:bg-fd-navy'
const containerClass = maxWidthClasses[maxWidth ?? 'default'] || 'max-w-[1200px]'
const embedWrapperClass =
embedBackground === 'card'
? 'bg-white rounded-[70px] shadow-lg p-6 md:p-10 overflow-hidden'
? 'bg-white dark:bg-white/10 rounded-[70px] shadow-lg p-6 md:p-10 overflow-hidden'
: embedBackground === 'navy-card'
? 'bg-fd-navy rounded-[70px] shadow-lg p-6 md:p-10 overflow-hidden'
: ''

View File

@ -46,7 +46,7 @@ async function parseFile(url: string, filename: string): Promise<TableData> {
const data: string[][] = []
sheet.eachRow((row) => {
const cells = (row.values as unknown[])
.slice(1) // exceljs row.values is 1-indexed with undefined at [0]
.slice(1)
.map((c) => {
if (c === null || c === undefined) return ''
if (typeof c === 'object' && 'text' in c) return String((c as any).text ?? '')
@ -58,10 +58,7 @@ async function parseFile(url: string, filename: string): Promise<TableData> {
const nonEmpty = data.filter((r) => r.some((c) => c.trim()))
const [headerRow, ...bodyRows] = nonEmpty
return {
headers: headerRow || [],
rows: bodyRows,
}
return { headers: headerRow || [], rows: bodyRows }
} else {
const text = await response.text()
const lines = text.split(/\r?\n/).map((l) => l.trim()).filter(Boolean)
@ -122,32 +119,50 @@ export const FDDataTableBlockComponent: React.FC<Props> = ({
}
}, [dataSource, file, manualHeaders, manualRows])
useEffect(() => {
resolveData()
}, [resolveData])
useEffect(() => { resolveData() }, [resolveData])
const isDark = sectionBackground === 'navy'
// Navy stays navy. White/gray adapt to OS dark mode.
const bgClass =
sectionBackground === 'navy' ? 'bg-fd-navy' :
sectionBackground === 'gray' ? 'bg-fd-surface-alt' : 'bg-white'
const titleClass = isDark ? 'text-fd-yellow' : 'text-fd-navy'
const bodyClass = isDark ? 'text-white' : 'text-fd-navy'
sectionBackground === 'gray' ? 'bg-fd-surface-alt dark:bg-fd-navy' :
'bg-white dark:bg-fd-navy'
const titleClass = isDark
? 'text-fd-yellow'
: 'text-fd-navy dark:text-fd-yellow'
const bodyClass = isDark
? 'text-white'
: 'text-fd-navy dark:text-white'
// Header row style — navy header stays navy on dark bg; other styles get dark: adjustments
const headerBgClass =
headerStyle === 'yellow' ? 'bg-fd-yellow text-fd-navy' :
headerStyle === 'mint' ? 'bg-fd-mint text-fd-navy' :
headerStyle === 'gray' ? 'bg-gray-200 text-fd-navy' :
'bg-fd-navy text-white'
headerStyle === 'gray' ? 'bg-gray-200 text-fd-navy dark:bg-white/10 dark:text-white' :
isDark ? 'bg-white/10 text-white' :
'bg-fd-navy text-white dark:bg-white/10'
const getRowBg = (i: number) => {
if (!stripeRows) return isDark ? 'bg-white/5' : 'bg-white'
if (!stripeRows) return isDark ? 'bg-white/5' : 'bg-white dark:bg-white/5'
if (isDark) return i % 2 === 0 ? 'bg-white/5' : 'bg-white/10'
return i % 2 === 0 ? 'bg-white' : 'bg-fd-surface-alt'
return i % 2 === 0
? 'bg-white dark:bg-white/5'
: 'bg-fd-surface-alt dark:bg-white/10'
}
const borderClass = bordered
? isDark ? 'border border-white/10' : 'border border-fd-navy/10'
? isDark
? 'border border-white/10'
: 'border border-fd-navy/10 dark:border-white/10'
: ''
const cellBorderClass = bordered
? isDark ? 'border-r border-b border-white/10 last:border-r-0'
: 'border-r border-b border-fd-navy/10 last:border-r-0'
? isDark
? 'border-r border-b border-white/10 last:border-r-0'
: 'border-r border-b border-fd-navy/10 dark:border-white/10 last:border-r-0'
: ''
return (

View File

@ -72,7 +72,8 @@ 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}`}>
{/* No pl- here — answer aligns flush with the left edge of the question row */}
<div className={`font-joey text-fd-body fd-prose ${proseColor}`}>
<RichText data={(item.answer) as any} />
</div>
</div>

View File

@ -3,10 +3,11 @@
import React, { useState, useCallback } from 'react'
import type { FDNewsletterBlock as FDNewsletterBlockProps } from '@/payload-types'
// Navy is always dark. White/gray adapt to OS dark mode.
const bgClasses: Record<string, string> = {
white: 'bg-white',
navy: 'bg-fd-navy',
gray: 'bg-fd-surface-alt',
white: 'bg-white dark:bg-fd-navy',
navy: 'bg-fd-navy',
gray: 'bg-fd-surface-alt dark:bg-fd-navy',
yellow: 'bg-fd-yellow',
}
@ -32,15 +33,20 @@ export const FDNewsletterBlockComponent: React.FC<FDNewsletterBlockProps> = ({
const [errorMsg, setErrorMsg] = useState('')
const isDark = sectionBackground === 'navy'
const headingColor =
textColor === 'white' ? 'text-white'
: textColor === 'navy' ? 'text-fd-navy'
: isDark ? 'text-fd-yellow' : 'text-fd-navy'
: isDark
? 'text-fd-yellow'
: 'text-fd-navy dark:text-fd-yellow'
const bodyColor =
textColor === 'white' ? 'text-white'
: textColor === 'navy' ? 'text-fd-navy'
: isDark ? 'text-white' : 'text-fd-navy'
: isDark
? 'text-white'
: 'text-fd-navy dark:text-white'
const bgClass = bgClasses[sectionBackground ?? 'navy'] || 'bg-fd-navy'
@ -225,7 +231,7 @@ const ConsentCheckbox: React.FC<ConsentCheckboxProps> = ({
href={privacyPolicyLink}
target="_blank"
rel="noopener noreferrer"
className={`underline ${isDark ? 'text-fd-yellow hover:text-fd-yellow/80' : 'text-fd-navy hover:text-fd-navy/70'}`}
className={`underline ${isDark ? 'text-fd-yellow hover:text-fd-yellow/80' : 'text-fd-navy hover:text-fd-navy/70 dark:text-fd-yellow dark:hover:text-fd-yellow/80'}`}
>
Läs mer
</a>

View File

@ -10,10 +10,11 @@ const maxWidthClasses: Record<string, string> = {
wide: 'max-w-[1400px]',
}
// Navy is always dark. White/gray adapt to OS dark mode.
const bgClasses: Record<string, string> = {
white: 'bg-white',
white: 'bg-white dark:bg-fd-navy',
navy: 'bg-fd-navy',
gray: 'bg-fd-surface-alt',
gray: 'bg-fd-surface-alt dark:bg-fd-navy',
yellow: 'bg-fd-yellow',
transparent: 'bg-transparent',
}
@ -58,17 +59,23 @@ export const FDVideoBlockComponent: React.FC<FDVideoBlockProps> = ({
const [isPlaying, setIsPlaying] = useState(false)
const isDark = sectionBackground === 'navy'
// Manual override takes priority; otherwise auto-adapt with dark: fallback
const headingColor =
textColor === 'white' ? 'text-white'
: textColor === 'navy' ? 'text-fd-navy'
: isDark ? 'text-fd-yellow' : 'text-fd-navy'
: isDark
? 'text-fd-yellow'
: 'text-fd-navy dark:text-fd-yellow'
const bodyColor =
textColor === 'white' ? 'text-white'
: textColor === 'navy' ? 'text-fd-navy'
: isDark ? 'text-white' : 'text-fd-navy'
: isDark
? 'text-white'
: 'text-fd-navy dark:text-white'
const bgClass = bgClasses[sectionBackground ?? 'white'] || 'bg-white'
const bgClass = bgClasses[sectionBackground ?? 'white'] || 'bg-white dark:bg-fd-navy'
const containerClass = maxWidthClasses[maxWidth ?? 'default'] || 'max-w-[1200px]'
const videoMedia = videoFile as Media | undefined
@ -118,7 +125,7 @@ export const FDVideoBlockComponent: React.FC<FDVideoBlockProps> = ({
</div>
)}
<div className="relative w-full rounded-[70px] overflow-hidden bg-black">
<div className={`relative w-full overflow-hidden bg-black transition-all duration-300 ${isPlaying ? 'rounded-none' : 'rounded-[70px]'}`}>
<div className="relative w-full" style={{ paddingBottom: aspectRatio === '16/10' ? '62.5%' : '56.25%' }}>
{videoSource === 'upload' && videoMedia?.url && (