wwwfiberdirekt/add-anchor-links.sh

261 lines
8.1 KiB
Bash

#!/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:
* <section id={anchorId || undefined} ...>
*/
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 <section
# Handle single-line: <section className=...
perl -i -pe 'if (!$done && s/<section className/<section id={anchorId || undefined} className/) { $done=1 }' "$COMP"
# Handle multi-line: <section alone on a line (like FDHeroBlock)
if ! grep -q 'id={anchorId' "$COMP"; then
perl -i -pe 'if (!$done && /^\s+<section\s*$/) { $done=1; s|<section|<section id={anchorId \|\| undefined}| }' "$COMP"
fi
echo "✅ Updated $COMP"
done
echo ""
# ── Step 4: Add smooth scrolling to globals.css ───────────────────────────
GLOBALS_CSS="src/app/(frontend)/globals.css"
if grep -q "scroll-behavior" "$GLOBALS_CSS" 2>/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 (
<button
onClick={scrollToTop}
aria-label="Tillbaka till toppen"
className={`
fixed bottom-6 right-6 z-40
w-11 h-11 rounded-full
bg-fd-navy/80 backdrop-blur-sm
text-white hover:text-fd-yellow
border border-white/10 hover:border-fd-yellow/30
shadow-lg hover:shadow-xl
flex items-center justify-center
transition-all duration-300 ease-in-out cursor-pointer
${visible
? 'opacity-100 translate-y-0 pointer-events-auto'
: 'opacity-0 translate-y-4 pointer-events-none'
}
`}
>
<ChevronUpIcon className="w-5 h-5" />
</button>
)
}
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 <Footer />
perl -i -pe 's|<Footer />|<Footer />\n <BackToTop />|' "$LAYOUT"
echo "✅ Added BackToTop to layout.tsx"
fi
echo ""
echo "════════════════════════════════════════════════════════════════"
echo "✅ All done! Now follow the deployment steps below."
echo "════════════════════════════════════════════════════════════════"
echo ""
echo "LOCAL:"
echo " 1. Review changes: git diff"
echo " 2. Clean local migrations:"
echo " rm -f src/migrations/2025*.ts src/migrations/2026*.ts"
echo " echo 'export {}' > src/migrations/index.ts"
echo " 3. Commit and push:"
echo " git add -A"
echo " git commit -m 'feat: anchor links, smooth scroll, back-to-top'"
echo " git push origin main"
echo ""
echo "SERVER:"
echo " cd /var/www/fiberdirekt"
echo " git pull origin main"
echo " npm install"
echo " npx payload generate:types"
echo " npx payload migrate:create add-anchor-links"
echo " npx payload migrate"
echo " npm run build && pm2 restart fiberdirekt"
echo ""
echo "If migrate:create says 'no changes detected', skip migrate"
echo "steps and go straight to build. Block fields are stored as"
echo "JSON so new optional fields usually don't need SQL migration."
echo ""