#!/bin/bash # ============================================================================ # add-anchor-links.sh (macOS + Linux compatible) # Run from your project root: bash add-anchor-links.sh # ============================================================================ set -e echo "πŸ”— Adding anchor link support to all FD blocks..." echo "" # ── Step 1: Create shared anchorField ────────────────────────────────────── mkdir -p src/fields cat > src/fields/anchorField.ts << 'FIELDEOF' import type { Field } from 'payload' /** * Shared anchorId field for all FD blocks. * Allows editors to set an anchor link ID on any section, * enabling direct linking like /page#section-name * * Usage in block config: * import { anchorField } from '@/fields/anchorField' * fields: [ ...contentFields, anchorField ] * * Usage in block component: *
*/ export const anchorField: Field = { name: 'anchorId', type: 'text', label: 'AnkarlΓ€nk-ID', admin: { description: 'Valfritt. AnvΓ€nds fΓΆr att lΓ€nka direkt till denna sektion, t.ex. "priser" ger /sida#priser. AnvΓ€nd bara smΓ₯ bokstΓ€ver, siffror och bindestreck.', position: 'sidebar', }, validate: (value: unknown) => { if (!value) return true if (typeof value === 'string' && /^[a-z0-9][a-z0-9-]*$/.test(value)) return true return 'AnvΓ€nd bara smΓ₯ bokstΓ€ver (a-z), siffror (0-9) och bindestreck (-). Inga mellanslag.' }, } FIELDEOF echo "βœ… Created src/fields/anchorField.ts" # ── Step 2: Add anchorField to all block configs ─────────────────────────── BLOCKS=( FDHeroBlock FDAlternateHeroBlock FDTextBlock FDPricingCardBlock FDFaqBlock FDContactBlock FDContactFormBlock FDDataTableBlock FDCardGridBlock FDCtaSideImageBlock FDCtaBannerBlock FDCodeEmbedBlock FDFeatureAnnouncementBlock FDHeaderTextImageBlock FDServicesGridBlock FDServiceChooserBlock FDUspChecklistBlock FDUspTableBlock FDTechPropertiesBlock FDStatisticsBlock FDTestimonialBlock FDTeamBlock FDVideoBlock FDVpsCalculatorBlock FDWideCardBlock FDServiceCalculatorBlock ) for BLOCK in "${BLOCKS[@]}"; do CONFIG="src/blocks/$BLOCK/config.ts" if [ ! -f "$CONFIG" ]; then echo "⚠️ Skipped $BLOCK β€” config not found" continue fi # Skip if already has anchorField if grep -q "anchorField" "$CONFIG"; then echo "⏭️ Skipped $BLOCK β€” anchorField already present" continue fi # Add import after the first line perl -i -pe 'if ($. == 1) { $_ .= "import { anchorField } from '\''\@/fields/anchorField'\''\n" }' "$CONFIG" # Add anchorField before the last "]," in the file perl -i -0pe 's/( \],\n\})\s*$/ anchorField,\n$1/s' "$CONFIG" echo "βœ… Updated $CONFIG" done echo "" # ── Step 3: Add anchorId to all block components ────────────────────────── echo "πŸ”§ Updating block components..." for BLOCK in "${BLOCKS[@]}"; do COMP="src/blocks/$BLOCK/Component.tsx" if [ ! -f "$COMP" ]; then echo "⚠️ Skipped $BLOCK component β€” not found" continue fi # Skip if already has anchorId if grep -q "anchorId" "$COMP"; then echo "⏭️ Skipped $BLOCK component β€” anchorId already present" continue fi # Add anchorId to destructured props: insert before "}) => {" perl -i -pe 's/\}\) => \{/ anchorId,\n}) => {/' "$COMP" # Add id={anchorId || undefined} to the first
/dev/null; then echo "⏭️ Smooth scroll already in globals.css" else TMPFILE=$(mktemp) cat > "$TMPFILE" << 'CSSEOF' /* Smooth scroll + header offset for anchor links */ html { scroll-behavior: smooth; scroll-padding-top: 100px; } CSSEOF cat "$GLOBALS_CSS" >> "$TMPFILE" mv "$TMPFILE" "$GLOBALS_CSS" echo "βœ… Added smooth scroll to globals.css" fi echo "" # ── Step 5: Create BackToTop component ──────────────────────────────────── mkdir -p src/components/BackToTop cat > src/components/BackToTop/index.tsx << 'BTEOF' 'use client' import React, { useState, useEffect, useCallback } from 'react' import { ChevronUpIcon } from 'lucide-react' /** * Floating "back to top" button. * Appears after scrolling down 400px, smooth-scrolls to top on click. * Styled to match FD design system. */ export const BackToTop: React.FC = () => { const [visible, setVisible] = useState(false) useEffect(() => { const onScroll = () => { setVisible(window.scrollY > 400) } window.addEventListener('scroll', onScroll, { passive: true }) return () => window.removeEventListener('scroll', onScroll) }, []) const scrollToTop = useCallback(() => { window.scrollTo({ top: 0, behavior: 'smooth' }) }, []) return ( ) } BTEOF echo "βœ… Created src/components/BackToTop/index.tsx" # ── Step 6: Add BackToTop to layout ─────────────────────────────────────── LAYOUT="src/app/(frontend)/layout.tsx" if grep -q "BackToTop" "$LAYOUT" 2>/dev/null; then echo "⏭️ BackToTop already in layout" else # Add import after Footer import perl -i -pe 'if (/import \{ Footer \}/) { $_ .= "import { BackToTop } from '\''\@/components/BackToTop'\''\n" }' "$LAYOUT" # Add component after