diff --git a/add-block-thumbnails.sh b/add-block-thumbnails.sh new file mode 100644 index 0000000..be266e7 --- /dev/null +++ b/add-block-thumbnails.sh @@ -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" \ No newline at end of file diff --git a/add_block_thumbnails.py b/add_block_thumbnails.py new file mode 100644 index 0000000..6495d82 --- /dev/null +++ b/add_block_thumbnails.py @@ -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") \ No newline at end of file diff --git a/src/blocks/FDCodeEmbedBlock/Component.tsx b/src/blocks/FDCodeEmbedBlock/Component.tsx index 4367008..22185a2 100644 --- a/src/blocks/FDCodeEmbedBlock/Component.tsx +++ b/src/blocks/FDCodeEmbedBlock/Component.tsx @@ -5,16 +5,17 @@ import type { FDCodeEmbedBlock as FDCodeEmbedBlockProps } from '@/payload-types' const maxWidthClasses: Record = { 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 = { - 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 = ({ const customCodeRef = useRef(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' : '' diff --git a/src/blocks/FDDataTableBlock/Component.tsx b/src/blocks/FDDataTableBlock/Component.tsx index f30883a..e50185e 100644 --- a/src/blocks/FDDataTableBlock/Component.tsx +++ b/src/blocks/FDDataTableBlock/Component.tsx @@ -46,7 +46,7 @@ async function parseFile(url: string, filename: string): Promise { 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 { 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 = ({ } }, [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 ( diff --git a/src/blocks/FDFaqBlock/Component.tsx b/src/blocks/FDFaqBlock/Component.tsx index 2382e77..6ac6874 100644 --- a/src/blocks/FDFaqBlock/Component.tsx +++ b/src/blocks/FDFaqBlock/Component.tsx @@ -72,7 +72,8 @@ export const FDFaqBlockComponent: React.FC = ({ }`} >
-
+ {/* No pl- here — answer aligns flush with the left edge of the question row */} +
diff --git a/src/blocks/FDNewsletterBlock/Component.tsx b/src/blocks/FDNewsletterBlock/Component.tsx index 77d9002..7c23688 100644 --- a/src/blocks/FDNewsletterBlock/Component.tsx +++ b/src/blocks/FDNewsletterBlock/Component.tsx @@ -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 = { - 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 = ({ 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 = ({ 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 diff --git a/src/blocks/FDVideoBlock/Component.tsx b/src/blocks/FDVideoBlock/Component.tsx index 48b1030..61b4737 100644 --- a/src/blocks/FDVideoBlock/Component.tsx +++ b/src/blocks/FDVideoBlock/Component.tsx @@ -10,10 +10,11 @@ const maxWidthClasses: Record = { wide: 'max-w-[1400px]', } +// Navy is always dark. White/gray adapt to OS dark mode. const bgClasses: Record = { - 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 = ({ 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 = ({
)} -
+
{videoSource === 'upload' && videoMedia?.url && (