feat: add FDLinkCards, FDSpecCards, FDQuiz blocks
This commit is contained in:
parent
805ec291df
commit
38ecfce2eb
140
src/blocks/FDLinkCardsBlock/Component.tsx
Normal file
140
src/blocks/FDLinkCardsBlock/Component.tsx
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import type { FDLinkCardsBlock as Props, Media } from '@/payload-types'
|
||||||
|
import { FDImage } from '@/components/FDImage'
|
||||||
|
import { sectionBg, isExplicitDark, headingColor, bodyColor, fdCardRadius } from '@/utilities/fdTheme'
|
||||||
|
|
||||||
|
const cardStyleMap: Record<string, { bg: string; title: string; link: string; border: string }> = {
|
||||||
|
outlined: {
|
||||||
|
bg: 'bg-white/5',
|
||||||
|
title: 'text-white dark:text-white',
|
||||||
|
link: 'text-white/60 hover:text-fd-yellow dark:text-white/60 dark:hover:text-fd-yellow',
|
||||||
|
border: 'border border-white/10 dark:border-white/10',
|
||||||
|
},
|
||||||
|
navy: {
|
||||||
|
bg: 'bg-fd-navy dark:bg-white/10',
|
||||||
|
title: 'text-white',
|
||||||
|
link: 'text-fd-yellow hover:text-fd-yellow/80',
|
||||||
|
border: '',
|
||||||
|
},
|
||||||
|
gray: {
|
||||||
|
bg: 'bg-fd-gray-light dark:bg-white/10',
|
||||||
|
title: 'text-fd-navy dark:text-white',
|
||||||
|
link: 'text-fd-navy/60 hover:text-fd-navy dark:text-white/60 dark:hover:text-fd-yellow',
|
||||||
|
border: '',
|
||||||
|
},
|
||||||
|
yellow: {
|
||||||
|
bg: 'bg-fd-yellow',
|
||||||
|
title: 'text-fd-navy',
|
||||||
|
link: 'text-fd-navy/60 hover:text-fd-navy',
|
||||||
|
border: '',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light-section overrides for outlined cards (needs dark text, not white) */
|
||||||
|
const cardStyleMapLight: Record<string, { bg: string; title: string; link: string; border: string }> = {
|
||||||
|
outlined: {
|
||||||
|
bg: 'bg-white dark:bg-white/5',
|
||||||
|
title: 'text-fd-navy dark:text-white',
|
||||||
|
link: 'text-fd-navy/60 hover:text-fd-navy dark:text-white/60 dark:hover:text-fd-yellow',
|
||||||
|
border: 'border border-gray-200 dark:border-white/10',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const colsMap: Record<string, string> = {
|
||||||
|
'2': 'md:grid-cols-2',
|
||||||
|
'3': 'md:grid-cols-3',
|
||||||
|
'4': 'md:grid-cols-2 lg:grid-cols-4',
|
||||||
|
}
|
||||||
|
|
||||||
|
const ArrowIcon: React.FC<{ className?: string }> = ({ className }) => (
|
||||||
|
<svg className={className || 'w-4 h-4'} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2">
|
||||||
|
<path d="M3 8h10M9 4l4 4-4 4" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
|
||||||
|
export const FDLinkCardsBlockComponent: React.FC<Props> = ({
|
||||||
|
icon,
|
||||||
|
heading,
|
||||||
|
description,
|
||||||
|
cards,
|
||||||
|
columns = '3',
|
||||||
|
cardStyle = 'outlined',
|
||||||
|
sectionBackground = 'navy',
|
||||||
|
anchorId,
|
||||||
|
}) => {
|
||||||
|
const dark = isExplicitDark(sectionBackground)
|
||||||
|
const bg = sectionBg(sectionBackground)
|
||||||
|
const hClr = headingColor(dark)
|
||||||
|
const bClr = bodyColor(dark)
|
||||||
|
const gridCols = colsMap[columns || '3'] || colsMap['3']
|
||||||
|
|
||||||
|
/* Pick card style — outlined adapts based on section darkness */
|
||||||
|
const style = (!dark && cardStyle === 'outlined')
|
||||||
|
? cardStyleMapLight.outlined
|
||||||
|
: (cardStyleMap[cardStyle || 'outlined'] || cardStyleMap.outlined)
|
||||||
|
|
||||||
|
const iconMedia = icon as Media | undefined
|
||||||
|
const hasIcon = iconMedia && typeof iconMedia === 'object' && iconMedia.url
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${bg}`}>
|
||||||
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
||||||
|
|
||||||
|
{/* Centered header */}
|
||||||
|
<div className="text-center mb-12 md:mb-16 flex flex-col items-center gap-4">
|
||||||
|
{hasIcon && (
|
||||||
|
<FDImage
|
||||||
|
media={iconMedia}
|
||||||
|
size="thumbnail"
|
||||||
|
className="w-12 h-12 md:w-16 md:h-16 object-contain"
|
||||||
|
sizes="64px"
|
||||||
|
fallbackAlt=""
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<h2 className={`font-joey-heavy text-fd-h1 max-w-[700px] ${hClr}`}>
|
||||||
|
{heading}
|
||||||
|
</h2>
|
||||||
|
{description && (
|
||||||
|
<p className={`font-joey text-fd-body-lg max-w-[600px] ${bClr} opacity-70`}>
|
||||||
|
{description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Card grid */}
|
||||||
|
<div className={`grid grid-cols-1 ${gridCols} gap-4 md:gap-6`}>
|
||||||
|
{cards?.map((card, i) => {
|
||||||
|
const linkIconMedia = card.linkIcon as Media | undefined
|
||||||
|
const hasLinkIcon = linkIconMedia && typeof linkIconMedia === 'object' && linkIconMedia.url
|
||||||
|
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
key={i}
|
||||||
|
href={card.linkUrl || '#'}
|
||||||
|
className={`group ${style.bg} ${style.border} ${fdCardRadius} px-8 py-8 md:px-10 md:py-10 flex flex-col justify-between gap-8 min-h-[220px] md:min-h-[260px] hover:-translate-y-1 transition-all duration-200`}
|
||||||
|
>
|
||||||
|
<h3 className={`font-joey-bold text-fd-h3 leading-snug ${style.title}`}>
|
||||||
|
{card.title}
|
||||||
|
</h3>
|
||||||
|
<div className={`flex items-center gap-2 font-joey text-fd-body transition-colors ${style.link}`}>
|
||||||
|
{hasLinkIcon && (
|
||||||
|
<FDImage
|
||||||
|
media={linkIconMedia}
|
||||||
|
size="thumbnail"
|
||||||
|
className="w-5 h-5 object-contain opacity-60"
|
||||||
|
sizes="20px"
|
||||||
|
fallbackAlt=""
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<span>{card.linkLabel}</span>
|
||||||
|
<ArrowIcon className="w-4 h-4 transition-transform group-hover:translate-x-1" />
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
108
src/blocks/FDLinkCardsBlock/config.ts
Normal file
108
src/blocks/FDLinkCardsBlock/config.ts
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
|
export const FDLinkCardsBlock: Block = {
|
||||||
|
slug: 'fdLinkCards',
|
||||||
|
imageURL: '/block-thumbnails/fd-link-cards.png',
|
||||||
|
imageAltText: 'FD Länkkort',
|
||||||
|
interfaceName: 'FDLinkCardsBlock',
|
||||||
|
labels: {
|
||||||
|
singular: 'FD Länkkort',
|
||||||
|
plural: 'FD Länkkort',
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'icon',
|
||||||
|
type: 'upload',
|
||||||
|
relationTo: 'media',
|
||||||
|
label: 'Ikon ovanför rubrik (valfri)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'heading',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
label: 'Rubrik',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'description',
|
||||||
|
type: 'textarea',
|
||||||
|
localized: true,
|
||||||
|
label: 'Beskrivning (valfri)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cards',
|
||||||
|
type: 'array',
|
||||||
|
label: 'Kort',
|
||||||
|
minRows: 1,
|
||||||
|
maxRows: 6,
|
||||||
|
labels: {
|
||||||
|
singular: 'Kort',
|
||||||
|
plural: 'Kort',
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
type: 'textarea',
|
||||||
|
localized: true,
|
||||||
|
label: 'Korttitel',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'linkLabel',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
label: 'Länktext (visas i botten)',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'linkUrl',
|
||||||
|
type: 'text',
|
||||||
|
label: 'Länk-URL',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'linkIcon',
|
||||||
|
type: 'upload',
|
||||||
|
relationTo: 'media',
|
||||||
|
label: 'Länkikon (valfri, visas bredvid länktexten)',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'columns',
|
||||||
|
type: 'select',
|
||||||
|
label: 'Kolumner (desktop)',
|
||||||
|
defaultValue: '3',
|
||||||
|
options: [
|
||||||
|
{ label: '2 kolumner', value: '2' },
|
||||||
|
{ label: '3 kolumner', value: '3' },
|
||||||
|
{ label: '4 kolumner', value: '4' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cardStyle',
|
||||||
|
type: 'select',
|
||||||
|
label: 'Kortstil',
|
||||||
|
defaultValue: 'outlined',
|
||||||
|
options: [
|
||||||
|
{ label: 'Kantlinje', value: 'outlined' },
|
||||||
|
{ label: 'Navy', value: 'navy' },
|
||||||
|
{ label: 'Grå', value: 'gray' },
|
||||||
|
{ label: 'Gul', value: 'yellow' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'sectionBackground',
|
||||||
|
type: 'select',
|
||||||
|
label: 'Sektionsbakgrund',
|
||||||
|
defaultValue: 'navy',
|
||||||
|
options: [
|
||||||
|
{ label: 'Vit', value: 'white' },
|
||||||
|
{ label: 'Grå', value: 'gray' },
|
||||||
|
{ label: 'Navy', value: 'navy' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
anchorField,
|
||||||
|
],
|
||||||
|
}
|
||||||
370
src/blocks/FDQuizBlock/Component.tsx
Normal file
370
src/blocks/FDQuizBlock/Component.tsx
Normal file
@ -0,0 +1,370 @@
|
|||||||
|
'use client'
|
||||||
|
import React, { useState, useEffect, useCallback, useRef } from 'react'
|
||||||
|
import type { FDQuizBlock as Props } from '@/payload-types'
|
||||||
|
import { FDButton } from '@/components/FDButton'
|
||||||
|
import { sectionBg, isExplicitDark, headingColor, bodyColor, fdCardRadius } from '@/utilities/fdTheme'
|
||||||
|
|
||||||
|
/* ── Types ── */
|
||||||
|
type QuizState = 'idle' | 'active' | 'result'
|
||||||
|
|
||||||
|
/* ── Icons ── */
|
||||||
|
const QuizIcon: React.FC<{ className?: string }> = ({ className }) => (
|
||||||
|
<svg className={className || 'w-5 h-5'} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
|
||||||
|
<circle cx="12" cy="12" r="10" />
|
||||||
|
<path d="M9.09 9a3 3 0 015.83 1c0 2-3 3-3 3" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
|
<circle cx="12" cy="17" r=".5" fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
|
||||||
|
const ArrowRight: React.FC<{ className?: string }> = ({ className }) => (
|
||||||
|
<svg className={className || 'w-4 h-4'} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2">
|
||||||
|
<path d="M3 8h10M9 4l4 4-4 4" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
|
||||||
|
const ArrowLeft: React.FC<{ className?: string }> = ({ className }) => (
|
||||||
|
<svg className={className || 'w-4 h-4'} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2">
|
||||||
|
<path d="M13 8H3M7 4L3 8l4 4" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
|
||||||
|
/* ── Component ── */
|
||||||
|
export const FDQuizBlockComponent: React.FC<Props> = ({
|
||||||
|
heading,
|
||||||
|
description,
|
||||||
|
triggerLabel = 'Starta quiz',
|
||||||
|
outcomes = [],
|
||||||
|
questions = [],
|
||||||
|
nextLabel = 'Nästa',
|
||||||
|
backLabel = 'Tillbaka',
|
||||||
|
resultHeading = 'Rekommenderat:',
|
||||||
|
restartLabel = 'Börja om',
|
||||||
|
sectionBackground = 'navy',
|
||||||
|
anchorId,
|
||||||
|
}) => {
|
||||||
|
const [state, setState] = useState<QuizState>('idle')
|
||||||
|
const [step, setStep] = useState(0)
|
||||||
|
const [answers, setAnswers] = useState<Record<number, number>>({}) // step → option index
|
||||||
|
const [animating, setAnimating] = useState(false)
|
||||||
|
const [prefersReducedMotion, setPrefersReducedMotion] = useState(false)
|
||||||
|
const panelRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const mql = window.matchMedia('(prefers-reduced-motion: reduce)')
|
||||||
|
setPrefersReducedMotion(mql.matches)
|
||||||
|
const handler = (e: MediaQueryListEvent) => setPrefersReducedMotion(e.matches)
|
||||||
|
mql.addEventListener('change', handler)
|
||||||
|
return () => mql.removeEventListener('change', handler)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const dark = isExplicitDark(sectionBackground)
|
||||||
|
const bg = sectionBg(sectionBackground)
|
||||||
|
const hClr = headingColor(dark)
|
||||||
|
const bClr = bodyColor(dark)
|
||||||
|
|
||||||
|
/* Panel colors — always dark-ish card on any section */
|
||||||
|
const panelBg = dark
|
||||||
|
? 'bg-white/5 border border-white/10'
|
||||||
|
: 'bg-white border border-gray-200 dark:bg-white/5 dark:border-white/10'
|
||||||
|
const panelText = dark
|
||||||
|
? 'text-white'
|
||||||
|
: 'text-fd-navy dark:text-white'
|
||||||
|
const panelMuted = dark
|
||||||
|
? 'text-white/50'
|
||||||
|
: 'text-fd-navy/50 dark:text-white/50'
|
||||||
|
const optionBase = dark
|
||||||
|
? 'border border-white/15 hover:border-white/30'
|
||||||
|
: 'border border-gray-200 hover:border-fd-navy/30 dark:border-white/15 dark:hover:border-white/30'
|
||||||
|
const optionSelected = dark
|
||||||
|
? 'border-fd-yellow bg-fd-yellow/10'
|
||||||
|
: 'border-fd-yellow bg-fd-yellow/10 dark:border-fd-yellow dark:bg-fd-yellow/10'
|
||||||
|
const radioActive = 'border-fd-yellow bg-fd-yellow'
|
||||||
|
const radioInactive = dark
|
||||||
|
? 'border-white/30'
|
||||||
|
: 'border-gray-300 dark:border-white/30'
|
||||||
|
|
||||||
|
const totalQuestions = (questions ?? []).length
|
||||||
|
const currentQuestion = (questions ?? [])[step]
|
||||||
|
const hasAnswer = answers[step] !== undefined
|
||||||
|
|
||||||
|
/* ── Scoring logic ── */
|
||||||
|
const calculateResult = useCallback(() => {
|
||||||
|
const scores: Record<string, number> = {}
|
||||||
|
;(outcomes ?? []).forEach((o) => { scores[o.key || ''] = 0 })
|
||||||
|
|
||||||
|
Object.entries(answers).forEach(([stepStr, optionIndex]) => {
|
||||||
|
const q = (questions ?? [])[Number(stepStr)]
|
||||||
|
const option = q?.options?.[optionIndex]
|
||||||
|
if (option?.outcomeKeys) {
|
||||||
|
option.outcomeKeys.split(',').forEach((key) => {
|
||||||
|
const k = key.trim()
|
||||||
|
if (k in scores) scores[k] += 1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
/* Find highest scoring outcome */
|
||||||
|
let maxKey = ''
|
||||||
|
let maxScore = -1
|
||||||
|
Object.entries(scores).forEach(([key, score]) => {
|
||||||
|
if (score > maxScore) { maxScore = score; maxKey = key }
|
||||||
|
})
|
||||||
|
|
||||||
|
return (outcomes ?? []).find((o) => o.key === maxKey) || (outcomes ?? [])[0]
|
||||||
|
}, [answers, outcomes, questions])
|
||||||
|
|
||||||
|
/* ── Navigation ── */
|
||||||
|
const animateTransition = (callback: () => void) => {
|
||||||
|
if (prefersReducedMotion) { callback(); return }
|
||||||
|
setAnimating(true)
|
||||||
|
setTimeout(() => { callback(); setAnimating(false) }, 150)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleStart = () => {
|
||||||
|
setState('active')
|
||||||
|
setStep(0)
|
||||||
|
setAnswers({})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSelect = (optionIndex: number) => {
|
||||||
|
setAnswers((prev) => ({ ...prev, [step]: optionIndex }))
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleNext = () => {
|
||||||
|
if (!hasAnswer) return
|
||||||
|
if (step < totalQuestions - 1) {
|
||||||
|
animateTransition(() => setStep((s) => s + 1))
|
||||||
|
} else {
|
||||||
|
animateTransition(() => setState('result'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleBack = () => {
|
||||||
|
if (step > 0) {
|
||||||
|
animateTransition(() => setStep((s) => s - 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRestart = () => {
|
||||||
|
animateTransition(() => {
|
||||||
|
setState('idle')
|
||||||
|
setStep(0)
|
||||||
|
setAnswers({})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
setState('idle')
|
||||||
|
setStep(0)
|
||||||
|
setAnswers({})
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Render: Idle state ── */
|
||||||
|
if (state === 'idle') {
|
||||||
|
return (
|
||||||
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${bg}`}>
|
||||||
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8 text-center flex flex-col items-center gap-6">
|
||||||
|
<h2 className={`font-joey-heavy text-fd-h1 max-w-[700px] ${hClr}`}>
|
||||||
|
{heading}
|
||||||
|
</h2>
|
||||||
|
{description && (
|
||||||
|
<p className={`font-joey text-fd-body-lg max-w-[600px] ${bClr} opacity-70`}>
|
||||||
|
{description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
<div className="flex items-center gap-3 mt-2">
|
||||||
|
<QuizIcon className={`w-5 h-5 ${dark ? 'text-white/50' : 'text-fd-navy/50 dark:text-white/50'}`} />
|
||||||
|
<button
|
||||||
|
onClick={handleStart}
|
||||||
|
className={`font-joey-medium text-fd-body underline underline-offset-4 transition-colors ${
|
||||||
|
dark
|
||||||
|
? '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'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{triggerLabel}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Transition classes ── */
|
||||||
|
const transitionCls = prefersReducedMotion
|
||||||
|
? ''
|
||||||
|
: `transition-all duration-150 ${animating ? 'opacity-0 translate-y-2' : 'opacity-100 translate-y-0'}`
|
||||||
|
|
||||||
|
/* ── Render: Result state ── */
|
||||||
|
if (state === 'result') {
|
||||||
|
const winner = calculateResult()
|
||||||
|
return (
|
||||||
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${bg}`}>
|
||||||
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8 flex justify-center">
|
||||||
|
<div
|
||||||
|
ref={panelRef}
|
||||||
|
className={`${panelBg} rounded-2xl md:rounded-3xl p-8 md:p-10 w-full max-w-[520px] ${transitionCls}`}
|
||||||
|
>
|
||||||
|
{/* Close */}
|
||||||
|
<div className="flex justify-between items-center mb-6">
|
||||||
|
<span className={`font-joey-medium text-fd-small uppercase tracking-wider ${panelMuted}`}>
|
||||||
|
Resultat
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
onClick={handleClose}
|
||||||
|
className={`w-8 h-8 flex items-center justify-center rounded-full transition-colors ${
|
||||||
|
dark ? 'hover:bg-white/10 text-white/60' : 'hover:bg-fd-navy/5 text-fd-navy/40 dark:hover:bg-white/10 dark:text-white/60'
|
||||||
|
}`}
|
||||||
|
aria-label="Stäng"
|
||||||
|
>
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
|
||||||
|
<path d="M18 6L6 18M6 6l12 12" strokeLinecap="round" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Recommendation */}
|
||||||
|
<div className={`flex flex-col gap-4 ${transitionCls}`}>
|
||||||
|
<p className={`font-joey-medium text-fd-body ${
|
||||||
|
dark ? 'text-fd-yellow' : 'text-fd-navy dark:text-fd-yellow'
|
||||||
|
}`}>
|
||||||
|
{resultHeading}
|
||||||
|
</p>
|
||||||
|
<h3 className={`font-joey-heavy text-fd-h2 leading-tight ${panelText}`}>
|
||||||
|
{winner?.title}
|
||||||
|
</h3>
|
||||||
|
{winner?.description && (
|
||||||
|
<p className={`font-joey text-fd-body leading-relaxed ${
|
||||||
|
dark ? 'text-white/70' : 'text-fd-navy/70 dark:text-white/70'
|
||||||
|
}`}>
|
||||||
|
{winner.description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="flex flex-wrap gap-3 mt-4">
|
||||||
|
<button
|
||||||
|
onClick={handleRestart}
|
||||||
|
className={`px-6 py-3 rounded-full font-joey-medium text-fd-body border transition-colors ${
|
||||||
|
dark
|
||||||
|
? 'border-white/20 text-white hover:bg-white/10'
|
||||||
|
: 'border-gray-300 text-fd-navy hover:bg-fd-navy/5 dark:border-white/20 dark:text-white dark:hover:bg-white/10'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{restartLabel}
|
||||||
|
</button>
|
||||||
|
{winner?.ctaText && (
|
||||||
|
<FDButton href={winner.ctaLink || '/kontakt'} onDark={dark}>
|
||||||
|
{winner.ctaText}
|
||||||
|
</FDButton>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Render: Active (question step) ── */
|
||||||
|
return (
|
||||||
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${bg}`}>
|
||||||
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8 flex justify-center">
|
||||||
|
<div
|
||||||
|
ref={panelRef}
|
||||||
|
className={`${panelBg} rounded-2xl md:rounded-3xl p-8 md:p-10 w-full max-w-[520px]`}
|
||||||
|
>
|
||||||
|
{/* Header: counter + close */}
|
||||||
|
<div className="flex justify-between items-center mb-6">
|
||||||
|
<span className={`font-joey-medium text-fd-small ${panelMuted}`}>
|
||||||
|
Fråga {String(step + 1).padStart(2, '0')} / {String(totalQuestions).padStart(2, '0')}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
onClick={handleClose}
|
||||||
|
className={`w-8 h-8 flex items-center justify-center rounded-full transition-colors ${
|
||||||
|
dark ? 'hover:bg-white/10 text-white/60' : 'hover:bg-fd-navy/5 text-fd-navy/40 dark:hover:bg-white/10 dark:text-white/60'
|
||||||
|
}`}
|
||||||
|
aria-label="Stäng"
|
||||||
|
>
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
|
||||||
|
<path d="M18 6L6 18M6 6l12 12" strokeLinecap="round" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Question */}
|
||||||
|
<div className={transitionCls}>
|
||||||
|
<h3 className={`font-joey-bold text-fd-h3 mb-6 leading-snug ${panelText}`}>
|
||||||
|
{currentQuestion?.question}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
{/* Options */}
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
{currentQuestion?.options?.map((option, i) => {
|
||||||
|
const isSelected = answers[step] === i
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={i}
|
||||||
|
onClick={() => handleSelect(i)}
|
||||||
|
className={`w-full text-left px-5 py-4 rounded-xl flex items-start gap-4 transition-all duration-150 ${
|
||||||
|
isSelected ? optionSelected : optionBase
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{/* Radio circle */}
|
||||||
|
<span className={`mt-0.5 w-5 h-5 rounded-full border-2 flex-shrink-0 flex items-center justify-center transition-colors ${
|
||||||
|
isSelected ? radioActive : radioInactive
|
||||||
|
}`}>
|
||||||
|
{isSelected && (
|
||||||
|
<span className="w-2 h-2 rounded-full bg-fd-navy" />
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
<div className="flex flex-col gap-0.5">
|
||||||
|
<span className={`font-joey-medium text-fd-body ${panelText}`}>
|
||||||
|
{option.label}
|
||||||
|
</span>
|
||||||
|
{option.sublabel && (
|
||||||
|
<span className={`font-joey text-fd-small ${panelMuted}`}>
|
||||||
|
{option.sublabel}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Navigation */}
|
||||||
|
<div className={`flex justify-between items-center mt-8 pt-6 border-t ${
|
||||||
|
dark ? 'border-white/10' : 'border-gray-200 dark:border-white/10'
|
||||||
|
}`}>
|
||||||
|
{step > 0 ? (
|
||||||
|
<button
|
||||||
|
onClick={handleBack}
|
||||||
|
className={`flex items-center gap-2 font-joey-medium text-fd-body transition-colors ${
|
||||||
|
dark ? 'text-white/60 hover:text-white' : 'text-fd-navy/60 hover:text-fd-navy dark:text-white/60 dark:hover:text-white'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<ArrowLeft className="w-4 h-4" />
|
||||||
|
{backLabel}
|
||||||
|
</button>
|
||||||
|
) : <div />}
|
||||||
|
<button
|
||||||
|
onClick={handleNext}
|
||||||
|
disabled={!hasAnswer}
|
||||||
|
className={`flex items-center gap-2 px-6 py-2.5 rounded-full font-joey-medium text-fd-body transition-all ${
|
||||||
|
hasAnswer
|
||||||
|
? 'bg-fd-yellow text-fd-navy hover:-translate-y-0.5'
|
||||||
|
: dark
|
||||||
|
? 'bg-white/10 text-white/30 cursor-not-allowed'
|
||||||
|
: 'bg-gray-100 text-fd-navy/30 cursor-not-allowed dark:bg-white/10 dark:text-white/30'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{step < totalQuestions - 1 ? nextLabel : 'Visa resultat'}
|
||||||
|
<ArrowRight className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
199
src/blocks/FDQuizBlock/config.ts
Normal file
199
src/blocks/FDQuizBlock/config.ts
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
|
export const FDQuizBlock: Block = {
|
||||||
|
slug: 'fdQuiz',
|
||||||
|
imageURL: '/block-thumbnails/fd-quiz.png',
|
||||||
|
imageAltText: 'FD Quiz / Behovsanalys',
|
||||||
|
interfaceName: 'FDQuizBlock',
|
||||||
|
labels: {
|
||||||
|
singular: 'FD Quiz / Behovsanalys',
|
||||||
|
plural: 'FD Quiz / Behovsanalyser',
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'heading',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
label: 'Rubrik',
|
||||||
|
required: true,
|
||||||
|
defaultValue: 'Vilken tjänst passar dig?',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'description',
|
||||||
|
type: 'textarea',
|
||||||
|
localized: true,
|
||||||
|
label: 'Beskrivning (valfri)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'triggerLabel',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
label: 'Startknapp-text',
|
||||||
|
defaultValue: 'Starta quiz',
|
||||||
|
},
|
||||||
|
|
||||||
|
/* ── Outcomes — define these FIRST so editors know the keys ── */
|
||||||
|
{
|
||||||
|
type: 'collapsible',
|
||||||
|
label: 'Resultat / rekommendationer',
|
||||||
|
admin: { initCollapsed: false },
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'outcomes',
|
||||||
|
type: 'array',
|
||||||
|
label: 'Möjliga resultat',
|
||||||
|
minRows: 2,
|
||||||
|
maxRows: 6,
|
||||||
|
labels: { singular: 'Resultat', plural: 'Resultat' },
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'key',
|
||||||
|
type: 'text',
|
||||||
|
label: 'Nyckel (kort, unik, t.ex. "vdc", "colo", "fiber")',
|
||||||
|
required: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Används i frågornas alternativ. Bara små bokstäver, inga mellanslag.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
label: 'Resultatrubrik (t.ex. "Virtuellt Datacenter")',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'description',
|
||||||
|
type: 'textarea',
|
||||||
|
localized: true,
|
||||||
|
label: 'Resultatbeskrivning',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ctaText',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
label: 'CTA-knapp text',
|
||||||
|
defaultValue: 'Läs mer',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ctaLink',
|
||||||
|
type: 'text',
|
||||||
|
label: 'CTA-knapp länk',
|
||||||
|
defaultValue: '/kontakt',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
/* ── Questions ── */
|
||||||
|
{
|
||||||
|
type: 'collapsible',
|
||||||
|
label: 'Frågor',
|
||||||
|
admin: { initCollapsed: false },
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'questions',
|
||||||
|
type: 'array',
|
||||||
|
label: 'Frågor',
|
||||||
|
minRows: 2,
|
||||||
|
maxRows: 10,
|
||||||
|
labels: { singular: 'Fråga', plural: 'Frågor' },
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'question',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
label: 'Frågetext',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'options',
|
||||||
|
type: 'array',
|
||||||
|
label: 'Svarsalternativ',
|
||||||
|
minRows: 2,
|
||||||
|
maxRows: 5,
|
||||||
|
labels: { singular: 'Alternativ', plural: 'Alternativ' },
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'label',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
label: 'Alternativtext',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'sublabel',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
label: 'Undertext (valfri, visas under alternativet)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'outcomeKeys',
|
||||||
|
type: 'text',
|
||||||
|
label: 'Resultatnycklar (kommaseparerade, t.ex. "vdc,fiber")',
|
||||||
|
required: true,
|
||||||
|
admin: {
|
||||||
|
description: 'Varje valt alternativ ger +1 poäng till angivna resultatnycklar.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
/* ── UI labels ── */
|
||||||
|
{
|
||||||
|
type: 'collapsible',
|
||||||
|
label: 'Knappar & etiketter',
|
||||||
|
admin: { initCollapsed: true },
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'nextLabel',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
label: '"Nästa"-knapptext',
|
||||||
|
defaultValue: 'Nästa',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'backLabel',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
label: '"Tillbaka"-knapptext',
|
||||||
|
defaultValue: 'Tillbaka',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'resultHeading',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
label: 'Resultatrubrik',
|
||||||
|
defaultValue: 'Rekommenderat:',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'restartLabel',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
label: '"Börja om"-knapptext',
|
||||||
|
defaultValue: 'Börja om',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
/* ── Styling ── */
|
||||||
|
{
|
||||||
|
name: 'sectionBackground',
|
||||||
|
type: 'select',
|
||||||
|
label: 'Sektionsbakgrund',
|
||||||
|
defaultValue: 'navy',
|
||||||
|
options: [
|
||||||
|
{ label: 'Vit', value: 'white' },
|
||||||
|
{ label: 'Grå', value: 'gray' },
|
||||||
|
{ label: 'Navy', value: 'navy' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
anchorField,
|
||||||
|
],
|
||||||
|
}
|
||||||
191
src/blocks/FDSpecCardsBlock/Component.tsx
Normal file
191
src/blocks/FDSpecCardsBlock/Component.tsx
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import type { FDSpecCardsBlock as Props } from '@/payload-types'
|
||||||
|
import { FDButton } from '@/components/FDButton'
|
||||||
|
import {
|
||||||
|
sectionBg, isExplicitDark, headingColor, bodyColor,
|
||||||
|
bodySubduedColor, fdCardRadius, fdCardRadiusSm,
|
||||||
|
} from '@/utilities/fdTheme'
|
||||||
|
|
||||||
|
const cardStyleMap: Record<string, {
|
||||||
|
bg: string; border: string; title: string; body: string
|
||||||
|
specLabel: string; specValue: string; divider: string; isDark: boolean
|
||||||
|
}> = {
|
||||||
|
outlined: {
|
||||||
|
bg: 'bg-transparent',
|
||||||
|
border: 'border border-white/15 dark:border-white/15',
|
||||||
|
title: 'text-white dark:text-white',
|
||||||
|
body: 'text-white/70 dark:text-white/70',
|
||||||
|
specLabel: 'text-white/50 dark:text-white/50',
|
||||||
|
specValue: 'text-white dark:text-white',
|
||||||
|
divider: 'border-white/10 dark:border-white/10',
|
||||||
|
isDark: true,
|
||||||
|
},
|
||||||
|
navy: {
|
||||||
|
bg: 'bg-fd-navy dark:bg-white/10',
|
||||||
|
border: '',
|
||||||
|
title: 'text-white',
|
||||||
|
body: 'text-white/70',
|
||||||
|
specLabel: 'text-white/50',
|
||||||
|
specValue: 'text-fd-yellow',
|
||||||
|
divider: 'border-white/10',
|
||||||
|
isDark: true,
|
||||||
|
},
|
||||||
|
gray: {
|
||||||
|
bg: 'bg-fd-gray-light dark:bg-white/10',
|
||||||
|
border: '',
|
||||||
|
title: 'text-fd-navy dark:text-white',
|
||||||
|
body: 'text-fd-navy/70 dark:text-white/70',
|
||||||
|
specLabel: 'text-fd-navy/50 dark:text-white/50',
|
||||||
|
specValue: 'text-fd-navy dark:text-white',
|
||||||
|
divider: 'border-fd-navy/10 dark:border-white/10',
|
||||||
|
isDark: false,
|
||||||
|
},
|
||||||
|
white: {
|
||||||
|
bg: 'bg-white dark:bg-white/10 shadow-fd-card dark:shadow-none',
|
||||||
|
border: '',
|
||||||
|
title: 'text-fd-navy dark:text-white',
|
||||||
|
body: 'text-fd-navy/70 dark:text-white/70',
|
||||||
|
specLabel: 'text-fd-navy/50 dark:text-white/50',
|
||||||
|
specValue: 'text-fd-navy dark:text-white',
|
||||||
|
divider: 'border-fd-navy/10 dark:border-white/10',
|
||||||
|
isDark: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light-section overrides for outlined cards */
|
||||||
|
const cardStyleMapLight: Record<string, typeof cardStyleMap.outlined> = {
|
||||||
|
outlined: {
|
||||||
|
bg: 'bg-transparent',
|
||||||
|
border: 'border border-gray-200 dark:border-white/15',
|
||||||
|
title: 'text-fd-navy dark:text-white',
|
||||||
|
body: 'text-fd-navy/70 dark:text-white/70',
|
||||||
|
specLabel: 'text-fd-navy/50 dark:text-white/50',
|
||||||
|
specValue: 'text-fd-navy dark:text-white',
|
||||||
|
divider: 'border-fd-navy/10 dark:border-white/10',
|
||||||
|
isDark: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FDSpecCardsBlockComponent: React.FC<Props> = ({
|
||||||
|
heading,
|
||||||
|
description,
|
||||||
|
ctaText,
|
||||||
|
ctaLink,
|
||||||
|
secondaryCtaText,
|
||||||
|
secondaryCtaLink,
|
||||||
|
cards,
|
||||||
|
layout = 'sideBySide',
|
||||||
|
cardStyle = 'outlined',
|
||||||
|
sectionBackground = 'navy',
|
||||||
|
anchorId,
|
||||||
|
}) => {
|
||||||
|
const dark = isExplicitDark(sectionBackground)
|
||||||
|
const bg = sectionBg(sectionBackground)
|
||||||
|
const hClr = headingColor(dark)
|
||||||
|
const bClr = bodySubduedColor(dark)
|
||||||
|
|
||||||
|
const style = (!dark && cardStyle === 'outlined')
|
||||||
|
? cardStyleMapLight.outlined
|
||||||
|
: (cardStyleMap[cardStyle || 'outlined'] || cardStyleMap.outlined)
|
||||||
|
|
||||||
|
const isSideBySide = layout === 'sideBySide' || layout === 'sideBySideReverse'
|
||||||
|
const isReversed = layout === 'sideBySideReverse'
|
||||||
|
const cardCount = cards?.length || 1
|
||||||
|
|
||||||
|
/* Cards grid: 1 card = 1 col, 2 = 2-col, 3+ = 2-col wrapping */
|
||||||
|
const cardGridCols = cardCount === 1 ? '' : 'sm:grid-cols-2'
|
||||||
|
|
||||||
|
const renderCards = () => (
|
||||||
|
<div className={`grid grid-cols-1 ${cardGridCols} gap-4 md:gap-5`}>
|
||||||
|
{cards?.map((card, i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className={`${style.bg} ${style.border} ${fdCardRadiusSm} px-7 py-7 md:px-9 md:py-9 flex flex-col gap-4 justify-between`}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<h3 className={`font-joey-bold text-fd-h3 leading-snug ${style.title}`}>
|
||||||
|
{card.title}
|
||||||
|
</h3>
|
||||||
|
{card.description && (
|
||||||
|
<p className={`font-joey text-fd-body leading-relaxed ${style.body}`}>
|
||||||
|
{card.description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{(card.specLabel || card.specValue) && (
|
||||||
|
<div className={`pt-4 mt-auto border-t ${style.divider}`}>
|
||||||
|
{card.specLabel && (
|
||||||
|
<span className={`font-joey text-fd-small block mb-1 ${style.specLabel}`}>
|
||||||
|
{card.specLabel}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{card.specValue && (
|
||||||
|
<span className={`font-joey-bold text-fd-h4 ${style.specValue}`}>
|
||||||
|
{card.specValue}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
const renderText = () => (
|
||||||
|
<div className="flex flex-col gap-5 md:gap-6 justify-center">
|
||||||
|
<h2 className={`font-joey-heavy text-fd-h1 leading-tight ${hClr}`}>
|
||||||
|
{heading}
|
||||||
|
</h2>
|
||||||
|
{description && (
|
||||||
|
<p className={`font-joey text-fd-body-lg leading-relaxed ${bClr}`}>
|
||||||
|
{description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{(ctaText || secondaryCtaText) && (
|
||||||
|
<div className="flex flex-wrap gap-4 pt-2">
|
||||||
|
{ctaText && (
|
||||||
|
<FDButton href={ctaLink || '#'} variant="outline" onDark={dark}>
|
||||||
|
{ctaText}
|
||||||
|
</FDButton>
|
||||||
|
)}
|
||||||
|
{secondaryCtaText && (
|
||||||
|
<FDButton href={secondaryCtaLink || '#'} variant="primary" onDark={dark}>
|
||||||
|
{secondaryCtaText}
|
||||||
|
</FDButton>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!isSideBySide) {
|
||||||
|
/* Full-width layout — heading on top, cards below */
|
||||||
|
return (
|
||||||
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${bg}`}>
|
||||||
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
||||||
|
<div className="mb-10 md:mb-14">
|
||||||
|
{renderText()}
|
||||||
|
</div>
|
||||||
|
{renderCards()}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Side-by-side layout */
|
||||||
|
return (
|
||||||
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${bg}`}>
|
||||||
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
||||||
|
<div className={`flex flex-col min-[820px]:flex-row gap-10 min-[820px]:gap-14 items-start ${isReversed ? 'min-[820px]:flex-row-reverse' : ''}`}>
|
||||||
|
<div className="flex-1 min-w-0 min-[820px]:max-w-[380px]">
|
||||||
|
{renderText()}
|
||||||
|
</div>
|
||||||
|
<div className="flex-[1.5] min-w-0">
|
||||||
|
{renderCards()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
125
src/blocks/FDSpecCardsBlock/config.ts
Normal file
125
src/blocks/FDSpecCardsBlock/config.ts
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
import type { Block } from 'payload'
|
||||||
|
import { anchorField } from '@/fields/anchorField'
|
||||||
|
|
||||||
|
export const FDSpecCardsBlock: Block = {
|
||||||
|
slug: 'fdSpecCards',
|
||||||
|
imageURL: '/block-thumbnails/fd-spec-cards.png',
|
||||||
|
imageAltText: 'FD Specifikationskort',
|
||||||
|
interfaceName: 'FDSpecCardsBlock',
|
||||||
|
labels: {
|
||||||
|
singular: 'FD Specifikationskort',
|
||||||
|
plural: 'FD Specifikationskort',
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'heading',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
label: 'Rubrik',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'description',
|
||||||
|
type: 'textarea',
|
||||||
|
localized: true,
|
||||||
|
label: 'Beskrivning (valfri)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ctaText',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
label: 'CTA-knapp text (valfri)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ctaLink',
|
||||||
|
type: 'text',
|
||||||
|
label: 'CTA-knapp länk',
|
||||||
|
defaultValue: '/kontakt',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'secondaryCtaText',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
label: 'Sekundär CTA-text (valfri)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'secondaryCtaLink',
|
||||||
|
type: 'text',
|
||||||
|
label: 'Sekundär CTA-länk',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cards',
|
||||||
|
type: 'array',
|
||||||
|
label: 'Kort',
|
||||||
|
minRows: 1,
|
||||||
|
maxRows: 4,
|
||||||
|
labels: {
|
||||||
|
singular: 'Kort',
|
||||||
|
plural: 'Kort',
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
label: 'Korttitel',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'description',
|
||||||
|
type: 'textarea',
|
||||||
|
localized: true,
|
||||||
|
label: 'Kortbeskrivning',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'specLabel',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
label: 'Spec-etikett (t.ex. "Kostnad", "Hastighet")',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'specValue',
|
||||||
|
type: 'text',
|
||||||
|
localized: true,
|
||||||
|
label: 'Spec-värde (t.ex. "10 Gbit/s", "$12.50 / 1K")',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'layout',
|
||||||
|
type: 'select',
|
||||||
|
label: 'Layout',
|
||||||
|
defaultValue: 'sideBySide',
|
||||||
|
options: [
|
||||||
|
{ label: 'Text vänster, kort höger', value: 'sideBySide' },
|
||||||
|
{ label: 'Text höger, kort vänster', value: 'sideBySideReverse' },
|
||||||
|
{ label: 'Kort i helbredd (utan sidotext)', value: 'fullWidth' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cardStyle',
|
||||||
|
type: 'select',
|
||||||
|
label: 'Kortstil',
|
||||||
|
defaultValue: 'outlined',
|
||||||
|
options: [
|
||||||
|
{ label: 'Kantlinje', value: 'outlined' },
|
||||||
|
{ label: 'Navy', value: 'navy' },
|
||||||
|
{ label: 'Grå', value: 'gray' },
|
||||||
|
{ label: 'Vit (med skugga)', value: 'white' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'sectionBackground',
|
||||||
|
type: 'select',
|
||||||
|
label: 'Sektionsbakgrund',
|
||||||
|
defaultValue: 'navy',
|
||||||
|
options: [
|
||||||
|
{ label: 'Vit', value: 'white' },
|
||||||
|
{ label: 'Grå', value: 'gray' },
|
||||||
|
{ label: 'Navy', value: 'navy' },
|
||||||
|
{ label: 'Navy gradient', value: 'navyGradient' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
anchorField,
|
||||||
|
],
|
||||||
|
}
|
||||||
@ -35,6 +35,9 @@ import { FDCtaBannerBlockComponent } from './FDCtaBannerBlock/Component'
|
|||||||
import { FDTestimonialBlockComponent } from './FDTestimonialBlock/Component'
|
import { FDTestimonialBlockComponent } from './FDTestimonialBlock/Component'
|
||||||
import { FDTeamBlockComponent } from './FDTeamBlock/Component'
|
import { FDTeamBlockComponent } from './FDTeamBlock/Component'
|
||||||
import { FDServiceCalculatorBlockComponent } from '@/blocks/FDServiceCalculatorBlock/Component'
|
import { FDServiceCalculatorBlockComponent } from '@/blocks/FDServiceCalculatorBlock/Component'
|
||||||
|
import { FDLinkCardsBlockComponent } from '@/blocks/FDLinkCardsBlock/Component'
|
||||||
|
import { FDSpecCardsBlockComponent } from '@/blocks/FDSpecCardsBlock/Component'
|
||||||
|
import { FDQuizBlockComponent } from '@/blocks/FDQuizBlock/Component'
|
||||||
|
|
||||||
const blockComponents: Record<string, React.FC<any>> = {
|
const blockComponents: Record<string, React.FC<any>> = {
|
||||||
formBlock: FormBlock,
|
formBlock: FormBlock,
|
||||||
@ -70,6 +73,9 @@ const blockComponents: Record<string, React.FC<any>> = {
|
|||||||
fdTestimonial: FDTestimonialBlockComponent,
|
fdTestimonial: FDTestimonialBlockComponent,
|
||||||
fdTeam: FDTeamBlockComponent,
|
fdTeam: FDTeamBlockComponent,
|
||||||
fdServiceCalc: FDServiceCalculatorBlockComponent,
|
fdServiceCalc: FDServiceCalculatorBlockComponent,
|
||||||
|
fdLinkCards: FDLinkCardsBlockComponent,
|
||||||
|
fdSpecCards: FDSpecCardsBlockComponent,
|
||||||
|
fdQuiz: FDQuizBlockComponent,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -38,6 +38,9 @@ import { FDCtaBannerBlock } from '../../blocks/FDCtaBannerBlock/config'
|
|||||||
import { FDTestimonialBlock } from '../../blocks/FDTestimonialBlock/config'
|
import { FDTestimonialBlock } from '../../blocks/FDTestimonialBlock/config'
|
||||||
import { FDTeamBlock } from '../../blocks/FDTeamBlock/config'
|
import { FDTeamBlock } from '../../blocks/FDTeamBlock/config'
|
||||||
import { FDServiceCalculatorBlock } from '../../blocks/FDServiceCalculatorBlock/config'
|
import { FDServiceCalculatorBlock } from '../../blocks/FDServiceCalculatorBlock/config'
|
||||||
|
import { FDLinkCardsBlock } from '../../blocks/FDLinkCardsBlock/config'
|
||||||
|
import { FDSpecCardsBlock } from '../../blocks/FDSpecCardsBlock/config'
|
||||||
|
import { FDQuizBlock } from '../../blocks/FDQuizBlock/config'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
MetaDescriptionField,
|
MetaDescriptionField,
|
||||||
@ -134,6 +137,9 @@ export const Pages: CollectionConfig<'pages'> = {
|
|||||||
FDCtaBannerBlock,
|
FDCtaBannerBlock,
|
||||||
FDTestimonialBlock,
|
FDTestimonialBlock,
|
||||||
FDTeamBlock,
|
FDTeamBlock,
|
||||||
|
FDLinkCardsBlock,
|
||||||
|
FDSpecCardsBlock,
|
||||||
|
FDQuizBlock,
|
||||||
],
|
],
|
||||||
required: true,
|
required: true,
|
||||||
admin: {
|
admin: {
|
||||||
|
|||||||
37725
src/migrations/20260304_194657.json
Normal file
37725
src/migrations/20260304_194657.json
Normal file
File diff suppressed because it is too large
Load Diff
461
src/migrations/20260304_194657.ts
Normal file
461
src/migrations/20260304_194657.ts
Normal file
@ -0,0 +1,461 @@
|
|||||||
|
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
|
||||||
|
|
||||||
|
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
|
||||||
|
await db.execute(sql`
|
||||||
|
CREATE TYPE "public"."enum_pages_blocks_fd_link_cards_columns" AS ENUM('2', '3', '4');
|
||||||
|
CREATE TYPE "public"."enum_pages_blocks_fd_link_cards_card_style" AS ENUM('outlined', 'navy', 'gray', 'yellow');
|
||||||
|
CREATE TYPE "public"."enum_pages_blocks_fd_link_cards_section_background" AS ENUM('white', 'gray', 'navy');
|
||||||
|
CREATE TYPE "public"."enum_pages_blocks_fd_spec_cards_layout" AS ENUM('sideBySide', 'sideBySideReverse', 'fullWidth');
|
||||||
|
CREATE TYPE "public"."enum_pages_blocks_fd_spec_cards_card_style" AS ENUM('outlined', 'navy', 'gray', 'white');
|
||||||
|
CREATE TYPE "public"."enum_pages_blocks_fd_spec_cards_section_background" AS ENUM('white', 'gray', 'navy', 'navyGradient');
|
||||||
|
CREATE TYPE "public"."enum_pages_blocks_fd_quiz_section_background" AS ENUM('white', 'gray', 'navy');
|
||||||
|
CREATE TYPE "public"."enum__pages_v_blocks_fd_link_cards_columns" AS ENUM('2', '3', '4');
|
||||||
|
CREATE TYPE "public"."enum__pages_v_blocks_fd_link_cards_card_style" AS ENUM('outlined', 'navy', 'gray', 'yellow');
|
||||||
|
CREATE TYPE "public"."enum__pages_v_blocks_fd_link_cards_section_background" AS ENUM('white', 'gray', 'navy');
|
||||||
|
CREATE TYPE "public"."enum__pages_v_blocks_fd_spec_cards_layout" AS ENUM('sideBySide', 'sideBySideReverse', 'fullWidth');
|
||||||
|
CREATE TYPE "public"."enum__pages_v_blocks_fd_spec_cards_card_style" AS ENUM('outlined', 'navy', 'gray', 'white');
|
||||||
|
CREATE TYPE "public"."enum__pages_v_blocks_fd_spec_cards_section_background" AS ENUM('white', 'gray', 'navy', 'navyGradient');
|
||||||
|
CREATE TYPE "public"."enum__pages_v_blocks_fd_quiz_section_background" AS ENUM('white', 'gray', 'navy');
|
||||||
|
CREATE TABLE "pages_blocks_fd_link_cards_cards" (
|
||||||
|
"_order" integer NOT NULL,
|
||||||
|
"_parent_id" varchar NOT NULL,
|
||||||
|
"id" varchar PRIMARY KEY NOT NULL,
|
||||||
|
"link_url" varchar,
|
||||||
|
"link_icon_id" integer
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "pages_blocks_fd_link_cards_cards_locales" (
|
||||||
|
"title" varchar,
|
||||||
|
"link_label" varchar,
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"_locale" "_locales" NOT NULL,
|
||||||
|
"_parent_id" varchar NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "pages_blocks_fd_link_cards" (
|
||||||
|
"_order" integer NOT NULL,
|
||||||
|
"_parent_id" integer NOT NULL,
|
||||||
|
"_path" text NOT NULL,
|
||||||
|
"id" varchar PRIMARY KEY NOT NULL,
|
||||||
|
"icon_id" integer,
|
||||||
|
"columns" "enum_pages_blocks_fd_link_cards_columns" DEFAULT '3',
|
||||||
|
"card_style" "enum_pages_blocks_fd_link_cards_card_style" DEFAULT 'outlined',
|
||||||
|
"section_background" "enum_pages_blocks_fd_link_cards_section_background" DEFAULT 'navy',
|
||||||
|
"anchor_id" varchar,
|
||||||
|
"block_name" varchar
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "pages_blocks_fd_link_cards_locales" (
|
||||||
|
"heading" varchar,
|
||||||
|
"description" varchar,
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"_locale" "_locales" NOT NULL,
|
||||||
|
"_parent_id" varchar NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "pages_blocks_fd_spec_cards_cards" (
|
||||||
|
"_order" integer NOT NULL,
|
||||||
|
"_parent_id" varchar NOT NULL,
|
||||||
|
"id" varchar PRIMARY KEY NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "pages_blocks_fd_spec_cards_cards_locales" (
|
||||||
|
"title" varchar,
|
||||||
|
"description" varchar,
|
||||||
|
"spec_label" varchar,
|
||||||
|
"spec_value" varchar,
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"_locale" "_locales" NOT NULL,
|
||||||
|
"_parent_id" varchar NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "pages_blocks_fd_spec_cards" (
|
||||||
|
"_order" integer NOT NULL,
|
||||||
|
"_parent_id" integer NOT NULL,
|
||||||
|
"_path" text NOT NULL,
|
||||||
|
"id" varchar PRIMARY KEY NOT NULL,
|
||||||
|
"cta_link" varchar DEFAULT '/kontakt',
|
||||||
|
"secondary_cta_link" varchar,
|
||||||
|
"layout" "enum_pages_blocks_fd_spec_cards_layout" DEFAULT 'sideBySide',
|
||||||
|
"card_style" "enum_pages_blocks_fd_spec_cards_card_style" DEFAULT 'outlined',
|
||||||
|
"section_background" "enum_pages_blocks_fd_spec_cards_section_background" DEFAULT 'navy',
|
||||||
|
"anchor_id" varchar,
|
||||||
|
"block_name" varchar
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "pages_blocks_fd_spec_cards_locales" (
|
||||||
|
"heading" varchar,
|
||||||
|
"description" varchar,
|
||||||
|
"cta_text" varchar,
|
||||||
|
"secondary_cta_text" varchar,
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"_locale" "_locales" NOT NULL,
|
||||||
|
"_parent_id" varchar NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "pages_blocks_fd_quiz_outcomes" (
|
||||||
|
"_order" integer NOT NULL,
|
||||||
|
"_parent_id" varchar NOT NULL,
|
||||||
|
"id" varchar PRIMARY KEY NOT NULL,
|
||||||
|
"key" varchar,
|
||||||
|
"cta_link" varchar DEFAULT '/kontakt'
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "pages_blocks_fd_quiz_outcomes_locales" (
|
||||||
|
"title" varchar,
|
||||||
|
"description" varchar,
|
||||||
|
"cta_text" varchar DEFAULT 'Läs mer',
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"_locale" "_locales" NOT NULL,
|
||||||
|
"_parent_id" varchar NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "pages_blocks_fd_quiz_questions_options" (
|
||||||
|
"_order" integer NOT NULL,
|
||||||
|
"_parent_id" varchar NOT NULL,
|
||||||
|
"id" varchar PRIMARY KEY NOT NULL,
|
||||||
|
"outcome_keys" varchar
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "pages_blocks_fd_quiz_questions_options_locales" (
|
||||||
|
"label" varchar,
|
||||||
|
"sublabel" varchar,
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"_locale" "_locales" NOT NULL,
|
||||||
|
"_parent_id" varchar NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "pages_blocks_fd_quiz_questions" (
|
||||||
|
"_order" integer NOT NULL,
|
||||||
|
"_parent_id" varchar NOT NULL,
|
||||||
|
"id" varchar PRIMARY KEY NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "pages_blocks_fd_quiz_questions_locales" (
|
||||||
|
"question" varchar,
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"_locale" "_locales" NOT NULL,
|
||||||
|
"_parent_id" varchar NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "pages_blocks_fd_quiz" (
|
||||||
|
"_order" integer NOT NULL,
|
||||||
|
"_parent_id" integer NOT NULL,
|
||||||
|
"_path" text NOT NULL,
|
||||||
|
"id" varchar PRIMARY KEY NOT NULL,
|
||||||
|
"section_background" "enum_pages_blocks_fd_quiz_section_background" DEFAULT 'navy',
|
||||||
|
"anchor_id" varchar,
|
||||||
|
"block_name" varchar
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "pages_blocks_fd_quiz_locales" (
|
||||||
|
"heading" varchar DEFAULT 'Vilken tjänst passar dig?',
|
||||||
|
"description" varchar,
|
||||||
|
"trigger_label" varchar DEFAULT 'Starta quiz',
|
||||||
|
"next_label" varchar DEFAULT 'Nästa',
|
||||||
|
"back_label" varchar DEFAULT 'Tillbaka',
|
||||||
|
"result_heading" varchar DEFAULT 'Rekommenderat:',
|
||||||
|
"restart_label" varchar DEFAULT 'Börja om',
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"_locale" "_locales" NOT NULL,
|
||||||
|
"_parent_id" varchar NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "_pages_v_blocks_fd_link_cards_cards" (
|
||||||
|
"_order" integer NOT NULL,
|
||||||
|
"_parent_id" integer NOT NULL,
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"link_url" varchar,
|
||||||
|
"link_icon_id" integer,
|
||||||
|
"_uuid" varchar
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "_pages_v_blocks_fd_link_cards_cards_locales" (
|
||||||
|
"title" varchar,
|
||||||
|
"link_label" varchar,
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"_locale" "_locales" NOT NULL,
|
||||||
|
"_parent_id" integer NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "_pages_v_blocks_fd_link_cards" (
|
||||||
|
"_order" integer NOT NULL,
|
||||||
|
"_parent_id" integer NOT NULL,
|
||||||
|
"_path" text NOT NULL,
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"icon_id" integer,
|
||||||
|
"columns" "enum__pages_v_blocks_fd_link_cards_columns" DEFAULT '3',
|
||||||
|
"card_style" "enum__pages_v_blocks_fd_link_cards_card_style" DEFAULT 'outlined',
|
||||||
|
"section_background" "enum__pages_v_blocks_fd_link_cards_section_background" DEFAULT 'navy',
|
||||||
|
"anchor_id" varchar,
|
||||||
|
"_uuid" varchar,
|
||||||
|
"block_name" varchar
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "_pages_v_blocks_fd_link_cards_locales" (
|
||||||
|
"heading" varchar,
|
||||||
|
"description" varchar,
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"_locale" "_locales" NOT NULL,
|
||||||
|
"_parent_id" integer NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "_pages_v_blocks_fd_spec_cards_cards" (
|
||||||
|
"_order" integer NOT NULL,
|
||||||
|
"_parent_id" integer NOT NULL,
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"_uuid" varchar
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "_pages_v_blocks_fd_spec_cards_cards_locales" (
|
||||||
|
"title" varchar,
|
||||||
|
"description" varchar,
|
||||||
|
"spec_label" varchar,
|
||||||
|
"spec_value" varchar,
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"_locale" "_locales" NOT NULL,
|
||||||
|
"_parent_id" integer NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "_pages_v_blocks_fd_spec_cards" (
|
||||||
|
"_order" integer NOT NULL,
|
||||||
|
"_parent_id" integer NOT NULL,
|
||||||
|
"_path" text NOT NULL,
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"cta_link" varchar DEFAULT '/kontakt',
|
||||||
|
"secondary_cta_link" varchar,
|
||||||
|
"layout" "enum__pages_v_blocks_fd_spec_cards_layout" DEFAULT 'sideBySide',
|
||||||
|
"card_style" "enum__pages_v_blocks_fd_spec_cards_card_style" DEFAULT 'outlined',
|
||||||
|
"section_background" "enum__pages_v_blocks_fd_spec_cards_section_background" DEFAULT 'navy',
|
||||||
|
"anchor_id" varchar,
|
||||||
|
"_uuid" varchar,
|
||||||
|
"block_name" varchar
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "_pages_v_blocks_fd_spec_cards_locales" (
|
||||||
|
"heading" varchar,
|
||||||
|
"description" varchar,
|
||||||
|
"cta_text" varchar,
|
||||||
|
"secondary_cta_text" varchar,
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"_locale" "_locales" NOT NULL,
|
||||||
|
"_parent_id" integer NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "_pages_v_blocks_fd_quiz_outcomes" (
|
||||||
|
"_order" integer NOT NULL,
|
||||||
|
"_parent_id" integer NOT NULL,
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"key" varchar,
|
||||||
|
"cta_link" varchar DEFAULT '/kontakt',
|
||||||
|
"_uuid" varchar
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "_pages_v_blocks_fd_quiz_outcomes_locales" (
|
||||||
|
"title" varchar,
|
||||||
|
"description" varchar,
|
||||||
|
"cta_text" varchar DEFAULT 'Läs mer',
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"_locale" "_locales" NOT NULL,
|
||||||
|
"_parent_id" integer NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "_pages_v_blocks_fd_quiz_questions_options" (
|
||||||
|
"_order" integer NOT NULL,
|
||||||
|
"_parent_id" integer NOT NULL,
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"outcome_keys" varchar,
|
||||||
|
"_uuid" varchar
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "_pages_v_blocks_fd_quiz_questions_options_locales" (
|
||||||
|
"label" varchar,
|
||||||
|
"sublabel" varchar,
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"_locale" "_locales" NOT NULL,
|
||||||
|
"_parent_id" integer NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "_pages_v_blocks_fd_quiz_questions" (
|
||||||
|
"_order" integer NOT NULL,
|
||||||
|
"_parent_id" integer NOT NULL,
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"_uuid" varchar
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "_pages_v_blocks_fd_quiz_questions_locales" (
|
||||||
|
"question" varchar,
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"_locale" "_locales" NOT NULL,
|
||||||
|
"_parent_id" integer NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "_pages_v_blocks_fd_quiz" (
|
||||||
|
"_order" integer NOT NULL,
|
||||||
|
"_parent_id" integer NOT NULL,
|
||||||
|
"_path" text NOT NULL,
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"section_background" "enum__pages_v_blocks_fd_quiz_section_background" DEFAULT 'navy',
|
||||||
|
"anchor_id" varchar,
|
||||||
|
"_uuid" varchar,
|
||||||
|
"block_name" varchar
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "_pages_v_blocks_fd_quiz_locales" (
|
||||||
|
"heading" varchar DEFAULT 'Vilken tjänst passar dig?',
|
||||||
|
"description" varchar,
|
||||||
|
"trigger_label" varchar DEFAULT 'Starta quiz',
|
||||||
|
"next_label" varchar DEFAULT 'Nästa',
|
||||||
|
"back_label" varchar DEFAULT 'Tillbaka',
|
||||||
|
"result_heading" varchar DEFAULT 'Rekommenderat:',
|
||||||
|
"restart_label" varchar DEFAULT 'Börja om',
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"_locale" "_locales" NOT NULL,
|
||||||
|
"_parent_id" integer NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE "pages_blocks_fd_link_cards_cards" ADD CONSTRAINT "pages_blocks_fd_link_cards_cards_link_icon_id_media_id_fk" FOREIGN KEY ("link_icon_id") REFERENCES "public"."media"("id") ON DELETE set null ON UPDATE no action;
|
||||||
|
ALTER TABLE "pages_blocks_fd_link_cards_cards" ADD CONSTRAINT "pages_blocks_fd_link_cards_cards_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages_blocks_fd_link_cards"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "pages_blocks_fd_link_cards_cards_locales" ADD CONSTRAINT "pages_blocks_fd_link_cards_cards_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages_blocks_fd_link_cards_cards"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "pages_blocks_fd_link_cards" ADD CONSTRAINT "pages_blocks_fd_link_cards_icon_id_media_id_fk" FOREIGN KEY ("icon_id") REFERENCES "public"."media"("id") ON DELETE set null ON UPDATE no action;
|
||||||
|
ALTER TABLE "pages_blocks_fd_link_cards" ADD CONSTRAINT "pages_blocks_fd_link_cards_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "pages_blocks_fd_link_cards_locales" ADD CONSTRAINT "pages_blocks_fd_link_cards_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages_blocks_fd_link_cards"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "pages_blocks_fd_spec_cards_cards" ADD CONSTRAINT "pages_blocks_fd_spec_cards_cards_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages_blocks_fd_spec_cards"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "pages_blocks_fd_spec_cards_cards_locales" ADD CONSTRAINT "pages_blocks_fd_spec_cards_cards_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages_blocks_fd_spec_cards_cards"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "pages_blocks_fd_spec_cards" ADD CONSTRAINT "pages_blocks_fd_spec_cards_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "pages_blocks_fd_spec_cards_locales" ADD CONSTRAINT "pages_blocks_fd_spec_cards_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages_blocks_fd_spec_cards"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "pages_blocks_fd_quiz_outcomes" ADD CONSTRAINT "pages_blocks_fd_quiz_outcomes_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages_blocks_fd_quiz"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "pages_blocks_fd_quiz_outcomes_locales" ADD CONSTRAINT "pages_blocks_fd_quiz_outcomes_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages_blocks_fd_quiz_outcomes"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "pages_blocks_fd_quiz_questions_options" ADD CONSTRAINT "pages_blocks_fd_quiz_questions_options_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages_blocks_fd_quiz_questions"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "pages_blocks_fd_quiz_questions_options_locales" ADD CONSTRAINT "pages_blocks_fd_quiz_questions_options_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages_blocks_fd_quiz_questions_options"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "pages_blocks_fd_quiz_questions" ADD CONSTRAINT "pages_blocks_fd_quiz_questions_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages_blocks_fd_quiz"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "pages_blocks_fd_quiz_questions_locales" ADD CONSTRAINT "pages_blocks_fd_quiz_questions_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages_blocks_fd_quiz_questions"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "pages_blocks_fd_quiz" ADD CONSTRAINT "pages_blocks_fd_quiz_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "pages_blocks_fd_quiz_locales" ADD CONSTRAINT "pages_blocks_fd_quiz_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages_blocks_fd_quiz"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "_pages_v_blocks_fd_link_cards_cards" ADD CONSTRAINT "_pages_v_blocks_fd_link_cards_cards_link_icon_id_media_id_fk" FOREIGN KEY ("link_icon_id") REFERENCES "public"."media"("id") ON DELETE set null ON UPDATE no action;
|
||||||
|
ALTER TABLE "_pages_v_blocks_fd_link_cards_cards" ADD CONSTRAINT "_pages_v_blocks_fd_link_cards_cards_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_pages_v_blocks_fd_link_cards"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "_pages_v_blocks_fd_link_cards_cards_locales" ADD CONSTRAINT "_pages_v_blocks_fd_link_cards_cards_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_pages_v_blocks_fd_link_cards_cards"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "_pages_v_blocks_fd_link_cards" ADD CONSTRAINT "_pages_v_blocks_fd_link_cards_icon_id_media_id_fk" FOREIGN KEY ("icon_id") REFERENCES "public"."media"("id") ON DELETE set null ON UPDATE no action;
|
||||||
|
ALTER TABLE "_pages_v_blocks_fd_link_cards" ADD CONSTRAINT "_pages_v_blocks_fd_link_cards_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_pages_v"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "_pages_v_blocks_fd_link_cards_locales" ADD CONSTRAINT "_pages_v_blocks_fd_link_cards_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_pages_v_blocks_fd_link_cards"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "_pages_v_blocks_fd_spec_cards_cards" ADD CONSTRAINT "_pages_v_blocks_fd_spec_cards_cards_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_pages_v_blocks_fd_spec_cards"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "_pages_v_blocks_fd_spec_cards_cards_locales" ADD CONSTRAINT "_pages_v_blocks_fd_spec_cards_cards_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_pages_v_blocks_fd_spec_cards_cards"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "_pages_v_blocks_fd_spec_cards" ADD CONSTRAINT "_pages_v_blocks_fd_spec_cards_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_pages_v"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "_pages_v_blocks_fd_spec_cards_locales" ADD CONSTRAINT "_pages_v_blocks_fd_spec_cards_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_pages_v_blocks_fd_spec_cards"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "_pages_v_blocks_fd_quiz_outcomes" ADD CONSTRAINT "_pages_v_blocks_fd_quiz_outcomes_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_pages_v_blocks_fd_quiz"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "_pages_v_blocks_fd_quiz_outcomes_locales" ADD CONSTRAINT "_pages_v_blocks_fd_quiz_outcomes_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_pages_v_blocks_fd_quiz_outcomes"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "_pages_v_blocks_fd_quiz_questions_options" ADD CONSTRAINT "_pages_v_blocks_fd_quiz_questions_options_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_pages_v_blocks_fd_quiz_questions"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "_pages_v_blocks_fd_quiz_questions_options_locales" ADD CONSTRAINT "_pages_v_blocks_fd_quiz_questions_options_locales_parent__fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_pages_v_blocks_fd_quiz_questions_options"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "_pages_v_blocks_fd_quiz_questions" ADD CONSTRAINT "_pages_v_blocks_fd_quiz_questions_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_pages_v_blocks_fd_quiz"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "_pages_v_blocks_fd_quiz_questions_locales" ADD CONSTRAINT "_pages_v_blocks_fd_quiz_questions_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_pages_v_blocks_fd_quiz_questions"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "_pages_v_blocks_fd_quiz" ADD CONSTRAINT "_pages_v_blocks_fd_quiz_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_pages_v"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
ALTER TABLE "_pages_v_blocks_fd_quiz_locales" ADD CONSTRAINT "_pages_v_blocks_fd_quiz_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_pages_v_blocks_fd_quiz"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
CREATE INDEX "pages_blocks_fd_link_cards_cards_order_idx" ON "pages_blocks_fd_link_cards_cards" USING btree ("_order");
|
||||||
|
CREATE INDEX "pages_blocks_fd_link_cards_cards_parent_id_idx" ON "pages_blocks_fd_link_cards_cards" USING btree ("_parent_id");
|
||||||
|
CREATE INDEX "pages_blocks_fd_link_cards_cards_link_icon_idx" ON "pages_blocks_fd_link_cards_cards" USING btree ("link_icon_id");
|
||||||
|
CREATE UNIQUE INDEX "pages_blocks_fd_link_cards_cards_locales_locale_parent_id_un" ON "pages_blocks_fd_link_cards_cards_locales" USING btree ("_locale","_parent_id");
|
||||||
|
CREATE INDEX "pages_blocks_fd_link_cards_order_idx" ON "pages_blocks_fd_link_cards" USING btree ("_order");
|
||||||
|
CREATE INDEX "pages_blocks_fd_link_cards_parent_id_idx" ON "pages_blocks_fd_link_cards" USING btree ("_parent_id");
|
||||||
|
CREATE INDEX "pages_blocks_fd_link_cards_path_idx" ON "pages_blocks_fd_link_cards" USING btree ("_path");
|
||||||
|
CREATE INDEX "pages_blocks_fd_link_cards_icon_idx" ON "pages_blocks_fd_link_cards" USING btree ("icon_id");
|
||||||
|
CREATE UNIQUE INDEX "pages_blocks_fd_link_cards_locales_locale_parent_id_unique" ON "pages_blocks_fd_link_cards_locales" USING btree ("_locale","_parent_id");
|
||||||
|
CREATE INDEX "pages_blocks_fd_spec_cards_cards_order_idx" ON "pages_blocks_fd_spec_cards_cards" USING btree ("_order");
|
||||||
|
CREATE INDEX "pages_blocks_fd_spec_cards_cards_parent_id_idx" ON "pages_blocks_fd_spec_cards_cards" USING btree ("_parent_id");
|
||||||
|
CREATE UNIQUE INDEX "pages_blocks_fd_spec_cards_cards_locales_locale_parent_id_un" ON "pages_blocks_fd_spec_cards_cards_locales" USING btree ("_locale","_parent_id");
|
||||||
|
CREATE INDEX "pages_blocks_fd_spec_cards_order_idx" ON "pages_blocks_fd_spec_cards" USING btree ("_order");
|
||||||
|
CREATE INDEX "pages_blocks_fd_spec_cards_parent_id_idx" ON "pages_blocks_fd_spec_cards" USING btree ("_parent_id");
|
||||||
|
CREATE INDEX "pages_blocks_fd_spec_cards_path_idx" ON "pages_blocks_fd_spec_cards" USING btree ("_path");
|
||||||
|
CREATE UNIQUE INDEX "pages_blocks_fd_spec_cards_locales_locale_parent_id_unique" ON "pages_blocks_fd_spec_cards_locales" USING btree ("_locale","_parent_id");
|
||||||
|
CREATE INDEX "pages_blocks_fd_quiz_outcomes_order_idx" ON "pages_blocks_fd_quiz_outcomes" USING btree ("_order");
|
||||||
|
CREATE INDEX "pages_blocks_fd_quiz_outcomes_parent_id_idx" ON "pages_blocks_fd_quiz_outcomes" USING btree ("_parent_id");
|
||||||
|
CREATE UNIQUE INDEX "pages_blocks_fd_quiz_outcomes_locales_locale_parent_id_uniqu" ON "pages_blocks_fd_quiz_outcomes_locales" USING btree ("_locale","_parent_id");
|
||||||
|
CREATE INDEX "pages_blocks_fd_quiz_questions_options_order_idx" ON "pages_blocks_fd_quiz_questions_options" USING btree ("_order");
|
||||||
|
CREATE INDEX "pages_blocks_fd_quiz_questions_options_parent_id_idx" ON "pages_blocks_fd_quiz_questions_options" USING btree ("_parent_id");
|
||||||
|
CREATE UNIQUE INDEX "pages_blocks_fd_quiz_questions_options_locales_locale_parent" ON "pages_blocks_fd_quiz_questions_options_locales" USING btree ("_locale","_parent_id");
|
||||||
|
CREATE INDEX "pages_blocks_fd_quiz_questions_order_idx" ON "pages_blocks_fd_quiz_questions" USING btree ("_order");
|
||||||
|
CREATE INDEX "pages_blocks_fd_quiz_questions_parent_id_idx" ON "pages_blocks_fd_quiz_questions" USING btree ("_parent_id");
|
||||||
|
CREATE UNIQUE INDEX "pages_blocks_fd_quiz_questions_locales_locale_parent_id_uniq" ON "pages_blocks_fd_quiz_questions_locales" USING btree ("_locale","_parent_id");
|
||||||
|
CREATE INDEX "pages_blocks_fd_quiz_order_idx" ON "pages_blocks_fd_quiz" USING btree ("_order");
|
||||||
|
CREATE INDEX "pages_blocks_fd_quiz_parent_id_idx" ON "pages_blocks_fd_quiz" USING btree ("_parent_id");
|
||||||
|
CREATE INDEX "pages_blocks_fd_quiz_path_idx" ON "pages_blocks_fd_quiz" USING btree ("_path");
|
||||||
|
CREATE UNIQUE INDEX "pages_blocks_fd_quiz_locales_locale_parent_id_unique" ON "pages_blocks_fd_quiz_locales" USING btree ("_locale","_parent_id");
|
||||||
|
CREATE INDEX "_pages_v_blocks_fd_link_cards_cards_order_idx" ON "_pages_v_blocks_fd_link_cards_cards" USING btree ("_order");
|
||||||
|
CREATE INDEX "_pages_v_blocks_fd_link_cards_cards_parent_id_idx" ON "_pages_v_blocks_fd_link_cards_cards" USING btree ("_parent_id");
|
||||||
|
CREATE INDEX "_pages_v_blocks_fd_link_cards_cards_link_icon_idx" ON "_pages_v_blocks_fd_link_cards_cards" USING btree ("link_icon_id");
|
||||||
|
CREATE UNIQUE INDEX "_pages_v_blocks_fd_link_cards_cards_locales_locale_parent_id" ON "_pages_v_blocks_fd_link_cards_cards_locales" USING btree ("_locale","_parent_id");
|
||||||
|
CREATE INDEX "_pages_v_blocks_fd_link_cards_order_idx" ON "_pages_v_blocks_fd_link_cards" USING btree ("_order");
|
||||||
|
CREATE INDEX "_pages_v_blocks_fd_link_cards_parent_id_idx" ON "_pages_v_blocks_fd_link_cards" USING btree ("_parent_id");
|
||||||
|
CREATE INDEX "_pages_v_blocks_fd_link_cards_path_idx" ON "_pages_v_blocks_fd_link_cards" USING btree ("_path");
|
||||||
|
CREATE INDEX "_pages_v_blocks_fd_link_cards_icon_idx" ON "_pages_v_blocks_fd_link_cards" USING btree ("icon_id");
|
||||||
|
CREATE UNIQUE INDEX "_pages_v_blocks_fd_link_cards_locales_locale_parent_id_uniqu" ON "_pages_v_blocks_fd_link_cards_locales" USING btree ("_locale","_parent_id");
|
||||||
|
CREATE INDEX "_pages_v_blocks_fd_spec_cards_cards_order_idx" ON "_pages_v_blocks_fd_spec_cards_cards" USING btree ("_order");
|
||||||
|
CREATE INDEX "_pages_v_blocks_fd_spec_cards_cards_parent_id_idx" ON "_pages_v_blocks_fd_spec_cards_cards" USING btree ("_parent_id");
|
||||||
|
CREATE UNIQUE INDEX "_pages_v_blocks_fd_spec_cards_cards_locales_locale_parent_id" ON "_pages_v_blocks_fd_spec_cards_cards_locales" USING btree ("_locale","_parent_id");
|
||||||
|
CREATE INDEX "_pages_v_blocks_fd_spec_cards_order_idx" ON "_pages_v_blocks_fd_spec_cards" USING btree ("_order");
|
||||||
|
CREATE INDEX "_pages_v_blocks_fd_spec_cards_parent_id_idx" ON "_pages_v_blocks_fd_spec_cards" USING btree ("_parent_id");
|
||||||
|
CREATE INDEX "_pages_v_blocks_fd_spec_cards_path_idx" ON "_pages_v_blocks_fd_spec_cards" USING btree ("_path");
|
||||||
|
CREATE UNIQUE INDEX "_pages_v_blocks_fd_spec_cards_locales_locale_parent_id_uniqu" ON "_pages_v_blocks_fd_spec_cards_locales" USING btree ("_locale","_parent_id");
|
||||||
|
CREATE INDEX "_pages_v_blocks_fd_quiz_outcomes_order_idx" ON "_pages_v_blocks_fd_quiz_outcomes" USING btree ("_order");
|
||||||
|
CREATE INDEX "_pages_v_blocks_fd_quiz_outcomes_parent_id_idx" ON "_pages_v_blocks_fd_quiz_outcomes" USING btree ("_parent_id");
|
||||||
|
CREATE UNIQUE INDEX "_pages_v_blocks_fd_quiz_outcomes_locales_locale_parent_id_un" ON "_pages_v_blocks_fd_quiz_outcomes_locales" USING btree ("_locale","_parent_id");
|
||||||
|
CREATE INDEX "_pages_v_blocks_fd_quiz_questions_options_order_idx" ON "_pages_v_blocks_fd_quiz_questions_options" USING btree ("_order");
|
||||||
|
CREATE INDEX "_pages_v_blocks_fd_quiz_questions_options_parent_id_idx" ON "_pages_v_blocks_fd_quiz_questions_options" USING btree ("_parent_id");
|
||||||
|
CREATE UNIQUE INDEX "_pages_v_blocks_fd_quiz_questions_options_locales_locale_par" ON "_pages_v_blocks_fd_quiz_questions_options_locales" USING btree ("_locale","_parent_id");
|
||||||
|
CREATE INDEX "_pages_v_blocks_fd_quiz_questions_order_idx" ON "_pages_v_blocks_fd_quiz_questions" USING btree ("_order");
|
||||||
|
CREATE INDEX "_pages_v_blocks_fd_quiz_questions_parent_id_idx" ON "_pages_v_blocks_fd_quiz_questions" USING btree ("_parent_id");
|
||||||
|
CREATE UNIQUE INDEX "_pages_v_blocks_fd_quiz_questions_locales_locale_parent_id_u" ON "_pages_v_blocks_fd_quiz_questions_locales" USING btree ("_locale","_parent_id");
|
||||||
|
CREATE INDEX "_pages_v_blocks_fd_quiz_order_idx" ON "_pages_v_blocks_fd_quiz" USING btree ("_order");
|
||||||
|
CREATE INDEX "_pages_v_blocks_fd_quiz_parent_id_idx" ON "_pages_v_blocks_fd_quiz" USING btree ("_parent_id");
|
||||||
|
CREATE INDEX "_pages_v_blocks_fd_quiz_path_idx" ON "_pages_v_blocks_fd_quiz" USING btree ("_path");
|
||||||
|
CREATE UNIQUE INDEX "_pages_v_blocks_fd_quiz_locales_locale_parent_id_unique" ON "_pages_v_blocks_fd_quiz_locales" USING btree ("_locale","_parent_id");`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
|
||||||
|
await db.execute(sql`
|
||||||
|
DROP TABLE "pages_blocks_fd_link_cards_cards" CASCADE;
|
||||||
|
DROP TABLE "pages_blocks_fd_link_cards_cards_locales" CASCADE;
|
||||||
|
DROP TABLE "pages_blocks_fd_link_cards" CASCADE;
|
||||||
|
DROP TABLE "pages_blocks_fd_link_cards_locales" CASCADE;
|
||||||
|
DROP TABLE "pages_blocks_fd_spec_cards_cards" CASCADE;
|
||||||
|
DROP TABLE "pages_blocks_fd_spec_cards_cards_locales" CASCADE;
|
||||||
|
DROP TABLE "pages_blocks_fd_spec_cards" CASCADE;
|
||||||
|
DROP TABLE "pages_blocks_fd_spec_cards_locales" CASCADE;
|
||||||
|
DROP TABLE "pages_blocks_fd_quiz_outcomes" CASCADE;
|
||||||
|
DROP TABLE "pages_blocks_fd_quiz_outcomes_locales" CASCADE;
|
||||||
|
DROP TABLE "pages_blocks_fd_quiz_questions_options" CASCADE;
|
||||||
|
DROP TABLE "pages_blocks_fd_quiz_questions_options_locales" CASCADE;
|
||||||
|
DROP TABLE "pages_blocks_fd_quiz_questions" CASCADE;
|
||||||
|
DROP TABLE "pages_blocks_fd_quiz_questions_locales" CASCADE;
|
||||||
|
DROP TABLE "pages_blocks_fd_quiz" CASCADE;
|
||||||
|
DROP TABLE "pages_blocks_fd_quiz_locales" CASCADE;
|
||||||
|
DROP TABLE "_pages_v_blocks_fd_link_cards_cards" CASCADE;
|
||||||
|
DROP TABLE "_pages_v_blocks_fd_link_cards_cards_locales" CASCADE;
|
||||||
|
DROP TABLE "_pages_v_blocks_fd_link_cards" CASCADE;
|
||||||
|
DROP TABLE "_pages_v_blocks_fd_link_cards_locales" CASCADE;
|
||||||
|
DROP TABLE "_pages_v_blocks_fd_spec_cards_cards" CASCADE;
|
||||||
|
DROP TABLE "_pages_v_blocks_fd_spec_cards_cards_locales" CASCADE;
|
||||||
|
DROP TABLE "_pages_v_blocks_fd_spec_cards" CASCADE;
|
||||||
|
DROP TABLE "_pages_v_blocks_fd_spec_cards_locales" CASCADE;
|
||||||
|
DROP TABLE "_pages_v_blocks_fd_quiz_outcomes" CASCADE;
|
||||||
|
DROP TABLE "_pages_v_blocks_fd_quiz_outcomes_locales" CASCADE;
|
||||||
|
DROP TABLE "_pages_v_blocks_fd_quiz_questions_options" CASCADE;
|
||||||
|
DROP TABLE "_pages_v_blocks_fd_quiz_questions_options_locales" CASCADE;
|
||||||
|
DROP TABLE "_pages_v_blocks_fd_quiz_questions" CASCADE;
|
||||||
|
DROP TABLE "_pages_v_blocks_fd_quiz_questions_locales" CASCADE;
|
||||||
|
DROP TABLE "_pages_v_blocks_fd_quiz" CASCADE;
|
||||||
|
DROP TABLE "_pages_v_blocks_fd_quiz_locales" CASCADE;
|
||||||
|
DROP TYPE "public"."enum_pages_blocks_fd_link_cards_columns";
|
||||||
|
DROP TYPE "public"."enum_pages_blocks_fd_link_cards_card_style";
|
||||||
|
DROP TYPE "public"."enum_pages_blocks_fd_link_cards_section_background";
|
||||||
|
DROP TYPE "public"."enum_pages_blocks_fd_spec_cards_layout";
|
||||||
|
DROP TYPE "public"."enum_pages_blocks_fd_spec_cards_card_style";
|
||||||
|
DROP TYPE "public"."enum_pages_blocks_fd_spec_cards_section_background";
|
||||||
|
DROP TYPE "public"."enum_pages_blocks_fd_quiz_section_background";
|
||||||
|
DROP TYPE "public"."enum__pages_v_blocks_fd_link_cards_columns";
|
||||||
|
DROP TYPE "public"."enum__pages_v_blocks_fd_link_cards_card_style";
|
||||||
|
DROP TYPE "public"."enum__pages_v_blocks_fd_link_cards_section_background";
|
||||||
|
DROP TYPE "public"."enum__pages_v_blocks_fd_spec_cards_layout";
|
||||||
|
DROP TYPE "public"."enum__pages_v_blocks_fd_spec_cards_card_style";
|
||||||
|
DROP TYPE "public"."enum__pages_v_blocks_fd_spec_cards_section_background";
|
||||||
|
DROP TYPE "public"."enum__pages_v_blocks_fd_quiz_section_background";`)
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@ import * as migration_20260224_091812_add_anchor_links from './20260224_091812_a
|
|||||||
import * as migration_20260224_133833 from './20260224_133833';
|
import * as migration_20260224_133833 from './20260224_133833';
|
||||||
import * as migration_20260226_095439 from './20260226_095439';
|
import * as migration_20260226_095439 from './20260226_095439';
|
||||||
import * as migration_20260302_145030 from './20260302_145030';
|
import * as migration_20260302_145030 from './20260302_145030';
|
||||||
|
import * as migration_20260304_194657 from './20260304_194657';
|
||||||
|
|
||||||
export const migrations = [
|
export const migrations = [
|
||||||
{
|
{
|
||||||
@ -22,6 +23,11 @@ export const migrations = [
|
|||||||
{
|
{
|
||||||
up: migration_20260302_145030.up,
|
up: migration_20260302_145030.up,
|
||||||
down: migration_20260302_145030.down,
|
down: migration_20260302_145030.down,
|
||||||
name: '20260302_145030'
|
name: '20260302_145030',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
up: migration_20260304_194657.up,
|
||||||
|
down: migration_20260304_194657.down,
|
||||||
|
name: '20260304_194657'
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -188,6 +188,9 @@ export interface Page {
|
|||||||
| FDCtaBannerBlock
|
| FDCtaBannerBlock
|
||||||
| FDTestimonialBlock
|
| FDTestimonialBlock
|
||||||
| FDTeamBlock
|
| FDTeamBlock
|
||||||
|
| FDLinkCardsBlock
|
||||||
|
| FDSpecCardsBlock
|
||||||
|
| FDQuizBlock
|
||||||
)[];
|
)[];
|
||||||
meta?: {
|
meta?: {
|
||||||
title?: string | null;
|
title?: string | null;
|
||||||
@ -1506,6 +1509,116 @@ export interface FDTeamBlock {
|
|||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'fdTeam';
|
blockType: 'fdTeam';
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "FDLinkCardsBlock".
|
||||||
|
*/
|
||||||
|
export interface FDLinkCardsBlock {
|
||||||
|
icon?: (number | null) | Media;
|
||||||
|
heading: string;
|
||||||
|
description?: string | null;
|
||||||
|
cards?:
|
||||||
|
| {
|
||||||
|
title: string;
|
||||||
|
linkLabel: string;
|
||||||
|
linkUrl: string;
|
||||||
|
linkIcon?: (number | null) | Media;
|
||||||
|
id?: string | null;
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
|
columns?: ('2' | '3' | '4') | null;
|
||||||
|
cardStyle?: ('outlined' | 'navy' | 'gray' | 'yellow') | null;
|
||||||
|
sectionBackground?: ('white' | 'gray' | 'navy') | null;
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: 'fdLinkCards';
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "FDSpecCardsBlock".
|
||||||
|
*/
|
||||||
|
export interface FDSpecCardsBlock {
|
||||||
|
heading: string;
|
||||||
|
description?: string | null;
|
||||||
|
ctaText?: string | null;
|
||||||
|
ctaLink?: string | null;
|
||||||
|
secondaryCtaText?: string | null;
|
||||||
|
secondaryCtaLink?: string | null;
|
||||||
|
cards?:
|
||||||
|
| {
|
||||||
|
title: string;
|
||||||
|
description?: string | null;
|
||||||
|
specLabel?: string | null;
|
||||||
|
specValue?: string | null;
|
||||||
|
id?: string | null;
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
|
layout?: ('sideBySide' | 'sideBySideReverse' | 'fullWidth') | null;
|
||||||
|
cardStyle?: ('outlined' | 'navy' | 'gray' | 'white') | null;
|
||||||
|
sectionBackground?: ('white' | 'gray' | 'navy' | 'navyGradient') | null;
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: 'fdSpecCards';
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "FDQuizBlock".
|
||||||
|
*/
|
||||||
|
export interface FDQuizBlock {
|
||||||
|
heading: string;
|
||||||
|
description?: string | null;
|
||||||
|
triggerLabel?: string | null;
|
||||||
|
outcomes?:
|
||||||
|
| {
|
||||||
|
/**
|
||||||
|
* Används i frågornas alternativ. Bara små bokstäver, inga mellanslag.
|
||||||
|
*/
|
||||||
|
key: string;
|
||||||
|
title: string;
|
||||||
|
description?: string | null;
|
||||||
|
ctaText?: string | null;
|
||||||
|
ctaLink?: string | null;
|
||||||
|
id?: string | null;
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
|
questions?:
|
||||||
|
| {
|
||||||
|
question: string;
|
||||||
|
options?:
|
||||||
|
| {
|
||||||
|
label: string;
|
||||||
|
sublabel?: string | null;
|
||||||
|
/**
|
||||||
|
* Varje valt alternativ ger +1 poäng till angivna resultatnycklar.
|
||||||
|
*/
|
||||||
|
outcomeKeys: string;
|
||||||
|
id?: string | null;
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
|
id?: string | null;
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
|
nextLabel?: string | null;
|
||||||
|
backLabel?: string | null;
|
||||||
|
resultHeading?: string | null;
|
||||||
|
restartLabel?: string | null;
|
||||||
|
sectionBackground?: ('white' | 'gray' | 'navy') | null;
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
anchorId?: string | null;
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: 'fdQuiz';
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "posts".
|
* via the `definition` "posts".
|
||||||
@ -1886,6 +1999,9 @@ export interface PagesSelect<T extends boolean = true> {
|
|||||||
fdCtaBanner?: T | FDCtaBannerBlockSelect<T>;
|
fdCtaBanner?: T | FDCtaBannerBlockSelect<T>;
|
||||||
fdTestimonial?: T | FDTestimonialBlockSelect<T>;
|
fdTestimonial?: T | FDTestimonialBlockSelect<T>;
|
||||||
fdTeam?: T | FDTeamBlockSelect<T>;
|
fdTeam?: T | FDTeamBlockSelect<T>;
|
||||||
|
fdLinkCards?: T | FDLinkCardsBlockSelect<T>;
|
||||||
|
fdSpecCards?: T | FDSpecCardsBlockSelect<T>;
|
||||||
|
fdQuiz?: T | FDQuizBlockSelect<T>;
|
||||||
};
|
};
|
||||||
meta?:
|
meta?:
|
||||||
| T
|
| T
|
||||||
@ -2612,6 +2728,98 @@ export interface FDTeamBlockSelect<T extends boolean = true> {
|
|||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "FDLinkCardsBlock_select".
|
||||||
|
*/
|
||||||
|
export interface FDLinkCardsBlockSelect<T extends boolean = true> {
|
||||||
|
icon?: T;
|
||||||
|
heading?: T;
|
||||||
|
description?: T;
|
||||||
|
cards?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
title?: T;
|
||||||
|
linkLabel?: T;
|
||||||
|
linkUrl?: T;
|
||||||
|
linkIcon?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
|
columns?: T;
|
||||||
|
cardStyle?: T;
|
||||||
|
sectionBackground?: T;
|
||||||
|
anchorId?: T;
|
||||||
|
id?: T;
|
||||||
|
blockName?: T;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "FDSpecCardsBlock_select".
|
||||||
|
*/
|
||||||
|
export interface FDSpecCardsBlockSelect<T extends boolean = true> {
|
||||||
|
heading?: T;
|
||||||
|
description?: T;
|
||||||
|
ctaText?: T;
|
||||||
|
ctaLink?: T;
|
||||||
|
secondaryCtaText?: T;
|
||||||
|
secondaryCtaLink?: T;
|
||||||
|
cards?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
title?: T;
|
||||||
|
description?: T;
|
||||||
|
specLabel?: T;
|
||||||
|
specValue?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
|
layout?: T;
|
||||||
|
cardStyle?: T;
|
||||||
|
sectionBackground?: T;
|
||||||
|
anchorId?: T;
|
||||||
|
id?: T;
|
||||||
|
blockName?: T;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "FDQuizBlock_select".
|
||||||
|
*/
|
||||||
|
export interface FDQuizBlockSelect<T extends boolean = true> {
|
||||||
|
heading?: T;
|
||||||
|
description?: T;
|
||||||
|
triggerLabel?: T;
|
||||||
|
outcomes?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
key?: T;
|
||||||
|
title?: T;
|
||||||
|
description?: T;
|
||||||
|
ctaText?: T;
|
||||||
|
ctaLink?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
|
questions?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
question?: T;
|
||||||
|
options?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
label?: T;
|
||||||
|
sublabel?: T;
|
||||||
|
outcomeKeys?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
|
nextLabel?: T;
|
||||||
|
backLabel?: T;
|
||||||
|
resultHeading?: T;
|
||||||
|
restartLabel?: T;
|
||||||
|
sectionBackground?: T;
|
||||||
|
anchorId?: T;
|
||||||
|
id?: T;
|
||||||
|
blockName?: T;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "posts_select".
|
* via the `definition` "posts_select".
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user