updated todo with instructions to implement english EN
This commit is contained in:
parent
f48b60bb5d
commit
68d6e68cba
5
.gitignore
vendored
5
.gitignore
vendored
@ -17,6 +17,5 @@ src/**/*.bak.*
|
||||
next-env.d.ts
|
||||
tsconfig.tsbuildinfo
|
||||
media/
|
||||
src/migrations/*.ts
|
||||
src/migrations/*.json
|
||||
!src/migrations/index.ts
|
||||
!src/migrations/*.ts
|
||||
!src/migrations/*.json
|
||||
|
||||
@ -1,17 +1,48 @@
|
||||
# Web Paylod CMS testing
|
||||
|
||||
## Integrate english in the future
|
||||
|
||||
Two things remaining:
|
||||
|
||||
1. Connect DeepL
|
||||
Get an API key at deepl.com (free tier = 500,000 chars/month, plenty for a B2B site).
|
||||
Add to .env.local locally and .env on the server:
|
||||
DEEPL_API_KEY=your_key_here
|
||||
To translate a page, find its ID in the Payload admin URL (e.g. admin/collections/pages/abc123) then call:
|
||||
bashcurl -X POST https://fiberdirekt.se/api/translate \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Cookie: payload-token=your_admin_token" \
|
||||
-d '{"pageId": "abc123", "collection": "pages"}'
|
||||
It will translate all Swedish text fields and save them as English in one shot. RichText fields (FAQ answers, body text) are skipped — do those manually in the Payload admin by switching the locale tab to EN.
|
||||
|
||||
2. Wire the LocaleSwitcher into the nav
|
||||
When English content is ready, open src/Header/Nav/index.tsx and add the import and component. Find where the nav actions/CTA button sits and place it nearby:
|
||||
tsximport { LocaleSwitcher } from '@/components/LocaleSwitcher'
|
||||
|
||||
// Then inside the JSX, next to the CTA button:
|
||||
<LocaleSwitcher />
|
||||
That's it — the component already handles the path swapping logic. Commit, push, deploy.
|
||||
|
||||
Recommended order:
|
||||
|
||||
Build out Swedish content fully and launch
|
||||
Use DeepL endpoint to batch-translate pages
|
||||
Review and fix translations manually in Payload admin
|
||||
Wire in the switcher
|
||||
Add /en/page.tsx root redirect (already done)
|
||||
|
||||
|
||||
## Comments on what to fix
|
||||
|
||||
Can we get filler text in blocks as standard ?
|
||||
|
||||
~~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 ?
|
||||
~~Preview thumbnails for blocks ??~~
|
||||
|
||||
~~Why is slug defaulting to one letter? ~~
|
||||
|
||||
Fix corners and embed for video block
|
||||
~~Fix corners and embed for video block ~~
|
||||
|
||||
~~Fix posts formatting to be better~~
|
||||
|
||||
@ -27,18 +58,18 @@ Fix corners and embed for video block
|
||||
|
||||
---
|
||||
|
||||
## Funktionalitet att bekräfta
|
||||
## Funktionalitet att bekräfta, test med Erik
|
||||
|
||||
#### Subscribe to newsletters, connection to API GANL
|
||||
* Subscribe to newsletters, connection to API GANL
|
||||
|
||||
#### Få en offert (Lime, GANL, epost?)
|
||||
* Få en offert (Lime, GANL, epost?)
|
||||
|
||||
~~#### Forms generellt ?~~
|
||||
~~* Forms generellt?~~
|
||||
|
||||
~~#### Matomo Tracking~~
|
||||
~~* Matomo Tracking~~
|
||||
|
||||
~~#### Cookies consent~~
|
||||
~~* Cookies consent~~
|
||||
|
||||
~~#### Lime forms integration ~~
|
||||
~~* Lime forms integration ~~
|
||||
|
||||
~~#### Other code embedding~~
|
||||
~~* Other code embedding~~
|
||||
@ -1,260 +0,0 @@
|
||||
#!/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 ""
|
||||
5
src/app/(frontend)/en/page.tsx
Normal file
5
src/app/(frontend)/en/page.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
import { redirect } from 'next/navigation'
|
||||
|
||||
export default function EnHomePage() {
|
||||
redirect('/en/home')
|
||||
}
|
||||
@ -1,74 +0,0 @@
|
||||
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' },
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
@ -1,92 +0,0 @@
|
||||
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' },
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user