feat: footer cert marks + social icons

This commit is contained in:
Jeffrey 2026-02-20 13:22:07 +01:00
parent 14a383abd2
commit cab4779f7d
9 changed files with 463 additions and 84 deletions

View File

@ -1,9 +1,12 @@
import { getCachedGlobal } from '@/utilities/getGlobals'
import Link from 'next/link'
import React from 'react'
import type { Footer, Page } from '@/payload-types'
import type { Footer, Media, Page } from '@/payload-types'
import { CMSLink } from '@/components/Link'
import { Logo } from '@/components/Logo/Logo'
import { FDImage } from '@/components/FDImage'
import { SocialIconsRow } from '@/components/SocialIcons'
import type { SocialLinkData } from '@/components/SocialIcons'
/** Resolves the logo href from the logoLink group field */
function resolveLogoHref(logoLink: Footer['logoLink']): string {
@ -19,68 +22,130 @@ function resolveLogoHref(logoLink: Footer['logoLink']): string {
return logoLink.url || '/'
}
/** Builds a flat, ordered list of enabled social links from the socialLinks group */
export function buildSocialLinks(socialLinks: Footer['socialLinks']): SocialLinkData[] {
if (!socialLinks) return []
const result: SocialLinkData[] = []
if (socialLinks.linkedinEnabled && socialLinks.linkedinUrl)
result.push({ platform: 'linkedin', url: socialLinks.linkedinUrl, enabled: true })
if (socialLinks.instagramEnabled && socialLinks.instagramUrl)
result.push({ platform: 'instagram', url: socialLinks.instagramUrl, enabled: true })
if (socialLinks.facebookEnabled && socialLinks.facebookUrl)
result.push({ platform: 'facebook', url: socialLinks.facebookUrl, enabled: true })
if (socialLinks.youtubeEnabled && socialLinks.youtubeUrl)
result.push({ platform: 'youtube', url: socialLinks.youtubeUrl, enabled: true })
if (socialLinks.twitterEnabled && socialLinks.twitterUrl)
result.push({ platform: 'twitter', url: socialLinks.twitterUrl, enabled: true })
return result
}
export async function Footer() {
const footerData = await getCachedGlobal('footer', 1)() as unknown as Footer
const columns = footerData?.columns || []
const navItems = footerData?.navItems || []
const certMarks = footerData?.certMarks || []
const hasColumns = columns.length > 0
const socialLinks = buildSocialLinks(footerData?.socialLinks)
const logoHref = resolveLogoHref(footerData?.logoLink)
const bottomLeft = (footerData?.bottomLeftText || '© {year} Fiber Direkt. Alla rättigheter förbehållna.').replace('{year}', new Date().getFullYear().toString())
const bottomRight = footerData?.bottomRightText || 'Svenskt datacenter · ISO 27001 · ISO 14001'
const bottomLeft = (
footerData?.bottomLeftText || '© {year} Fiber Direkt. Alla rättigheter förbehållna.'
).replace('{year}', new Date().getFullYear().toString())
return (
<footer className="mt-auto bg-fd-navy text-white">
<div className="max-w-[1200px] mx-auto px-6 md:px-8 py-12 md:py-16 flex flex-col gap-10">
{/* Top section: Logo + columns or simple nav */}
<div className="flex flex-col lg:flex-row gap-10 lg:gap-16">
{/* Logo column */}
<div className="flex-shrink-0">
<Link className="flex items-center" href={logoHref}>
<Logo variant="white" className="max-w-[120px]" />
</Link>
</div>
{/* Sitemap columns */}
{hasColumns ? (
<div className="flex-1 grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-8 md:gap-10">
{columns.map((column, colIndex) => (
<div key={colIndex} className="flex flex-col gap-4">
<span className="font-joey-bold text-fd-yellow text-base md:text-lg">
{column.heading}
{/* ── Top bar: Logo (left) + Cert marks (right) ───────────────────── */}
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-6">
<Link className="flex items-center flex-shrink-0" href={logoHref}>
<Logo variant="white" className="max-w-[120px]" />
</Link>
{certMarks.length > 0 && (
<div className="flex items-center gap-4 flex-wrap">
{certMarks.map((mark, i) => {
const media = mark.image as Media | null | undefined
if (!media) return null
const imgEl = (
<FDImage
media={media}
size="thumbnail"
className="h-12 w-auto object-contain"
fallbackAlt={mark.alt || ''}
/>
)
return mark.linkUrl ? (
<a
key={i}
href={mark.linkUrl}
target="_blank"
rel="noopener noreferrer"
className="opacity-80 hover:opacity-100 transition-opacity"
>
{imgEl}
</a>
) : (
<span key={i} className="opacity-80">
{imgEl}
</span>
<nav className="flex flex-col gap-2.5">
{column.links?.map(({ link }, linkIndex) => (
<CMSLink
className="font-joey text-white/70 hover:text-white text-sm md:text-base transition-colors no-underline"
key={linkIndex}
{...link}
/>
))}
</nav>
</div>
))}
)
})}
</div>
) : (
<nav className="flex flex-col md:flex-row md:items-center gap-4 md:ml-auto">
{navItems.map(({ link }, i) => (
<CMSLink
className="font-joey text-white hover:text-fd-yellow transition-colors no-underline"
key={i}
{...link}
/>
))}
</nav>
)}
</div>
{/* Divider */}
{/* ── Sitemap columns ──────────────────────────────────────────────── */}
{hasColumns && (
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-8 md:gap-10">
{columns.map((column, colIndex) => (
<div key={colIndex} className="flex flex-col gap-4">
<span className="font-joey-bold text-fd-yellow text-base md:text-lg">
{column.heading}
</span>
<nav className="flex flex-col gap-2.5">
{column.links?.map(({ link }, linkIndex) => (
<CMSLink
className="font-joey text-white/70 hover:text-white text-sm md:text-base transition-colors no-underline"
key={linkIndex}
{...link}
/>
))}
</nav>
</div>
))}
</div>
)}
{/* ── Divider ──────────────────────────────────────────────────────── */}
<hr className="border-white/20" />
{/* Bottom row */}
<div className="flex flex-col md:flex-row md:justify-between md:items-center gap-4">
<p className="font-joey text-white/60 text-sm">{bottomLeft}</p>
<p className="font-joey text-white/60 text-sm">{bottomRight}</p>
{/* ── Bottom row: copyright · nav links · social icons ────────────── */}
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
{/* Left + center: copyright text and legal nav links */}
<div className="flex flex-col sm:flex-row sm:items-center gap-3 sm:gap-6 flex-wrap">
<p className="font-joey text-white/60 text-sm whitespace-nowrap">{bottomLeft}</p>
{navItems.length > 0 && (
<nav className="flex items-center gap-4 flex-wrap">
{navItems.map(({ link }, i) => (
<CMSLink
className="font-joey text-white/60 hover:text-white text-sm transition-colors no-underline underline-offset-2 hover:underline"
key={i}
{...link}
/>
))}
</nav>
)}
</div>
{/* Right: social media icons */}
<SocialIconsRow
links={socialLinks}
linkClassName="text-white/60 hover:text-white"
iconSize="w-5 h-5"
/>
</div>
</div>
</footer>

View File

@ -50,6 +50,134 @@ export const Footer: GlobalConfig = {
],
},
// ── Certifieringsmärken ───────────────────────────────────────────────────
{
name: 'certMarks',
type: 'array',
label: 'Certifieringsmärken',
maxRows: 6,
admin: {
description: 'Logotyper och certifieringsbadges som visas i övre högra hörnet av footern (t.ex. ISO, UC-sigill, Based in Sweden).',
initCollapsed: true,
},
fields: [
{
name: 'image',
type: 'upload',
label: 'Bild',
relationTo: 'media',
required: true,
admin: {
description: 'PNG eller SVG, helst med transparent bakgrund.',
},
},
{
name: 'alt',
type: 'text',
label: 'Alt-text',
admin: {
description: 'Beskriv certifieringen för tillgänglighet, t.ex. "ISO 27001 certifierad".',
},
},
{
name: 'linkUrl',
type: 'text',
label: 'Länk (valfri)',
admin: {
description: 'Länk till certifieringens webbsida (öppnas i ny flik).',
},
},
],
},
// ── Sociala medier ────────────────────────────────────────────────────────
{
name: 'socialLinks',
type: 'group',
label: 'Sociala medier',
admin: {
description: 'Ikoner visas längst ner i footern och i mobilmenyn. Aktivera och ange URL för varje plattform.',
},
fields: [
// LinkedIn
{
name: 'linkedinEnabled',
type: 'checkbox',
label: 'Visa LinkedIn',
defaultValue: false,
},
{
name: 'linkedinUrl',
type: 'text',
label: 'LinkedIn URL',
admin: {
condition: (_, siblingData) => Boolean(siblingData?.linkedinEnabled),
description: 'T.ex. https://www.linkedin.com/company/fiber-direkt',
},
},
// Instagram
{
name: 'instagramEnabled',
type: 'checkbox',
label: 'Visa Instagram',
defaultValue: false,
},
{
name: 'instagramUrl',
type: 'text',
label: 'Instagram URL',
admin: {
condition: (_, siblingData) => Boolean(siblingData?.instagramEnabled),
},
},
// Facebook
{
name: 'facebookEnabled',
type: 'checkbox',
label: 'Visa Facebook',
defaultValue: false,
},
{
name: 'facebookUrl',
type: 'text',
label: 'Facebook URL',
admin: {
condition: (_, siblingData) => Boolean(siblingData?.facebookEnabled),
},
},
// YouTube
{
name: 'youtubeEnabled',
type: 'checkbox',
label: 'Visa YouTube',
defaultValue: false,
},
{
name: 'youtubeUrl',
type: 'text',
label: 'YouTube URL',
admin: {
condition: (_, siblingData) => Boolean(siblingData?.youtubeEnabled),
},
},
// Twitter / X
{
name: 'twitterEnabled',
type: 'checkbox',
label: 'Visa X (Twitter)',
defaultValue: false,
},
{
name: 'twitterUrl',
type: 'text',
label: 'X (Twitter) URL',
admin: {
condition: (_, siblingData) => Boolean(siblingData?.twitterEnabled),
},
},
],
},
// ── Sitemap columns ───────────────────────────────────────────────────────
{
name: 'columns',
@ -91,13 +219,13 @@ export const Footer: GlobalConfig = {
],
},
// ── Simple nav (legacy) ───────────────────────────────────────────────────
// ── Simple nav (legacy / bottom links) ────────────────────────────────────
{
name: 'navItems',
type: 'array',
label: 'Enkel navigering (äldre)',
label: 'Nedre navigering',
admin: {
description: 'Enkla footer-länkar (visas om inga kolumner finns)',
description: 'Länkar i underfältet (visas som rad bredvid copyright, t.ex. Policys · Legal · Villkor)',
initCollapsed: true,
},
fields: [
@ -105,25 +233,19 @@ export const Footer: GlobalConfig = {
appearances: false,
}),
],
maxRows: 6,
maxRows: 8,
},
// ── Bottom text ───────────────────────────────────────────────────────────
{
name: 'bottomLeftText',
type: 'text',
label: 'Nedre vänster text',
label: 'Copyrighttext (nedre vänster)',
defaultValue: '© {year} Fiber Direkt. Alla rättigheter förbehållna.',
admin: {
description: 'Använd {year} för aktuellt årtal',
},
},
{
name: 'bottomRightText',
type: 'text',
label: 'Nedre höger text',
defaultValue: 'Svenskt datacenter · ISO 27001 · ISO 14001',
},
],
hooks: {
afterChange: [revalidateFooter],

View File

@ -5,12 +5,14 @@ import { usePathname } from 'next/navigation'
import React, { useEffect, useState } from 'react'
import type { Header, Page } from '@/payload-types'
import type { SocialLinkData } from '@/components/SocialIcons'
import { Logo } from '@/components/Logo/Logo'
import { HeaderNav } from './Nav'
interface HeaderClientProps {
data: Header
socialLinks?: SocialLinkData[]
}
function resolveLogoHref(logoLink: Header['logoLink']): string {
@ -26,7 +28,7 @@ function resolveLogoHref(logoLink: Header['logoLink']): string {
return logoLink.url || '/'
}
export const HeaderClient: React.FC<HeaderClientProps> = ({ data }) => {
export const HeaderClient: React.FC<HeaderClientProps> = ({ data, socialLinks = [] }) => {
const [theme, setTheme] = useState<string | null>(null)
const [isDark, setIsDark] = useState(false)
const { headerTheme, setHeaderTheme } = useHeaderTheme()
@ -59,9 +61,9 @@ export const HeaderClient: React.FC<HeaderClientProps> = ({ data }) => {
>
<div className="container py-5 flex justify-between items-center">
<Link href={logoHref}>
<Logo loading="eager" priority="high" variant={isDark ? "white" : "blue"} className="max-w-[80px] md:max-w-[100px]" />
<Logo loading="eager" priority="high" variant={isDark ? 'white' : 'blue'} className="max-w-[80px] md:max-w-[100px]" />
</Link>
<HeaderNav data={data} />
<HeaderNav data={data} socialLinks={socialLinks} />
</div>
</header>
)

View File

@ -2,10 +2,15 @@ import { HeaderClient } from './Component.client'
import { getCachedGlobal } from '@/utilities/getGlobals'
import React from 'react'
import type { Header } from '@/payload-types'
import type { Header, Footer } from '@/payload-types'
import { buildSocialLinks } from '@/Footer/Component'
export async function Header() {
const headerData = await getCachedGlobal("header", 1)() as unknown as Header
const headerData = await getCachedGlobal('header', 1)() as unknown as Header
return <HeaderClient data={headerData} />
// Also fetch footer social links so the mobile menu can show them
const footerData = await getCachedGlobal('footer', 1)() as unknown as Footer
const socialLinks = buildSocialLinks(footerData?.socialLinks)
return <HeaderClient data={headerData} socialLinks={socialLinks} />
}

View File

@ -4,6 +4,8 @@ import type { Header as HeaderType, Page } from '@/payload-types'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { MenuIcon, XIcon, ChevronDownIcon, ChevronRightIcon } from 'lucide-react'
import { SocialIconsRow } from '@/components/SocialIcons'
import type { SocialLinkData } from '@/components/SocialIcons'
type NavChild = NonNullable<NonNullable<NonNullable<HeaderType['navItems']>[number]['children']>[number]>
type NavItem = NonNullable<HeaderType['navItems']>[number]
@ -142,7 +144,10 @@ const MegaMenuPanel: React.FC<{
)
}
export const HeaderNav: React.FC<{ data: HeaderType }> = ({ data }) => {
export const HeaderNav: React.FC<{ data: HeaderType; socialLinks?: SocialLinkData[] }> = ({
data,
socialLinks = [],
}) => {
const navItems = (data?.navItems || []) as NavItem[]
const [mobileOpen, setMobileOpen] = useState(false)
const [openDropdown, setOpenDropdown] = useState<string | null>(null)
@ -280,7 +285,7 @@ export const HeaderNav: React.FC<{ data: HeaderType }> = ({ data }) => {
{mobileOpen ? <XIcon className="w-6 h-6" /> : <MenuIcon className="w-6 h-6" />}
</button>
{/* ── Mobile full-screen overlay — always navy, no dark: needed ── */}
{/* ── Mobile full-screen overlay ── */}
<div
className={`fixed inset-0 z-50 bg-fd-navy flex flex-col transition-transform duration-300 md:hidden ${
mobileOpen ? 'translate-x-0' : 'translate-x-full'
@ -289,6 +294,7 @@ export const HeaderNav: React.FC<{ data: HeaderType }> = ({ data }) => {
aria-modal="true"
aria-label="Navigeringsmeny"
>
{/* Header bar */}
<div className="flex items-center justify-between px-6 py-5 border-b border-white/10">
<Link href="/" onClick={closeMobile}>
<span className="font-joey-heavy text-fd-yellow text-xl">Fiber Direkt</span>
@ -302,6 +308,7 @@ export const HeaderNav: React.FC<{ data: HeaderType }> = ({ data }) => {
</button>
</div>
{/* Nav links */}
<nav className="flex-1 overflow-y-auto px-6 py-6 flex flex-col">
{navItems.map((item, i) => {
const hasChildren = item.children && item.children.length > 0
@ -347,6 +354,17 @@ export const HeaderNav: React.FC<{ data: HeaderType }> = ({ data }) => {
)
})}
</nav>
{/* ── Social icons row at bottom of mobile menu ── */}
{socialLinks.length > 0 && (
<div className="px-6 py-5 border-t border-white/10">
<SocialIconsRow
links={socialLinks}
linkClassName="text-white/50 hover:text-white"
iconSize="w-6 h-6"
/>
</div>
)}
</div>
</>
)

View File

@ -9,8 +9,8 @@ export const metadata: Metadata = {
export default function NotFound() {
return (
<main className="min-h-[calc(100vh-80px)] bg-white dark:bg-fd-navy flex items-center overflow-hidden">
<div className="max-w-[1200px] mx-auto px-6 md:px-8 w-full pt-8 pb-16 md:pt-12 md:pb-20">
<main className="w-full bg-white dark:bg-fd-navy lg:min-h-[calc(100vh-80px)] lg:flex lg:items-center overflow-hidden">
<div className="max-w-[1200px] mx-auto px-6 md:px-8 w-full py-16 md:py-20 lg:py-[99px]">
<div className="flex flex-col lg:flex-row items-center gap-12 lg:gap-20">
{/* ── Left: Text content ─────────────────────────────────────── */}
@ -48,10 +48,9 @@ export default function NotFound() {
</div>
</div>
{/* ── Right: Modem SVG illustration ──────────────────────────── */}
{/* ── Right: Modem SVG illustration — desktop only ───────────── */}
<div className="hidden lg:flex flex-1 items-center justify-center w-full max-w-[380px] lg:max-w-none">
<div className="relative w-full max-w-[460px]">
<svg
viewBox="0 0 1536 1024"
fill="none"
@ -67,7 +66,6 @@ export default function NotFound() {
className="dark:fill-white"
/>
</svg>
</div>
</div>

View File

@ -59,24 +59,29 @@ export const AnnouncementBarComponent: React.FC<Props> = ({
return (
<div
className={`w-full px-4 py-2 flex items-center justify-center gap-4 text-sm font-joey relative ${bgClass}`}
className={`w-full px-10 py-2 relative flex items-center justify-center ${bgClass}`}
role="status"
>
<span>{text}</span>
{buttonLabel && href && (
<a
href={href}
target={newTab ? '_blank' : undefined}
rel={newTab ? 'noopener noreferrer' : undefined}
className="underline font-joey-bold hover:opacity-70 transition-opacity"
>
{buttonLabel}
</a>
)}
{/* Centered content — px-10 leaves room for the dismiss button on both sides */}
<div className="flex items-center justify-center gap-3 text-sm font-joey text-center flex-wrap">
<span>{text}</span>
{buttonLabel && href && (
<a
href={href}
target={newTab ? '_blank' : undefined}
rel={newTab ? 'noopener noreferrer' : undefined}
className="underline font-joey-bold hover:opacity-70 transition-opacity whitespace-nowrap"
>
{buttonLabel}
</a>
)}
</div>
{/* Dismiss — truly absolute so it doesn't affect centering */}
{dismissible && (
<button
onClick={handleDismiss}
className="absolute right-4 opacity-60 hover:opacity-100 transition-opacity"
className="absolute right-4 top-1/2 -translate-y-1/2 opacity-60 hover:opacity-100 transition-opacity"
aria-label="Stäng notis"
>

View File

@ -0,0 +1,106 @@
import React from 'react'
interface IconProps {
className?: string
}
export const LinkedInIcon: React.FC<IconProps> = ({ className }) => (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" className={className} aria-hidden>
<path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z" fill="currentColor" />
<rect x="2" y="9" width="4" height="12" fill="currentColor" />
<circle cx="4" cy="4" r="2" fill="currentColor" />
</svg>
)
export const InstagramIcon: React.FC<IconProps> = ({ className }) => (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" className={className} aria-hidden>
<rect x="2" y="2" width="20" height="20" rx="5.5" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<circle cx="12" cy="12" r="4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<circle cx="17.5" cy="6.5" r="1" fill="currentColor" />
</svg>
)
export const FacebookIcon: React.FC<IconProps> = ({ className }) => (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" className={className} aria-hidden>
<path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z" fill="currentColor" />
</svg>
)
export const YouTubeIcon: React.FC<IconProps> = ({ className }) => (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" className={className} aria-hidden>
<rect x="1" y="4" width="22" height="16" rx="4" stroke="currentColor" strokeWidth="2" />
<polygon points="9.5,8.5 9.5,15.5 16.5,12" fill="currentColor" />
</svg>
)
export const XTwitterIcon: React.FC<IconProps> = ({ className }) => (
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" className={className} aria-hidden>
<path d="M13.8097 10.2033L22.448 0H20.401L12.9004 8.85942L6.90962 0H0L9.05919 13.397L0 24.0968H2.04712L9.96801 14.7409L16.2947 24.0968H23.2043L13.8092 10.2033H13.8097ZM11.0059 13.515L10.088 12.181L2.78473 1.5659H5.92899L11.8228 10.1326L12.7407 11.4667L20.402 22.6021H17.2577L11.0059 13.5155V13.515Z" fill="currentColor" />
</svg>
)
// ─── Types ────────────────────────────────────────────────────────────────────
export type SocialPlatform = 'linkedin' | 'instagram' | 'facebook' | 'youtube' | 'twitter'
const PLATFORM_ICONS: Record<SocialPlatform, React.FC<IconProps>> = {
linkedin: LinkedInIcon,
instagram: InstagramIcon,
facebook: FacebookIcon,
youtube: YouTubeIcon,
twitter: XTwitterIcon,
}
const PLATFORM_LABELS: Record<SocialPlatform, string> = {
linkedin: 'LinkedIn',
instagram: 'Instagram',
facebook: 'Facebook',
youtube: 'YouTube',
twitter: 'X (Twitter)',
}
// ─── SocialIconsRow ───────────────────────────────────────────────────────────
export interface SocialLinkData {
platform: SocialPlatform
url: string
enabled?: boolean | null
}
interface SocialIconsRowProps {
links: SocialLinkData[]
className?: string
/** Classes on each <a> — controls color via currentColor */
linkClassName?: string
iconSize?: string
}
export const SocialIconsRow: React.FC<SocialIconsRowProps> = ({
links,
className = '',
linkClassName = 'text-white/60 hover:text-white',
iconSize = 'w-5 h-5',
}) => {
const activeLinks = links.filter((l) => l.enabled && l.url)
if (activeLinks.length === 0) return null
return (
<div className={`flex items-center gap-4 ${className}`}>
{activeLinks.map(({ platform, url }) => {
const Icon = PLATFORM_ICONS[platform]
return (
<a
key={platform}
href={url}
target="_blank"
rel="noopener noreferrer"
aria-label={PLATFORM_LABELS[platform]}
className={`transition-colors duration-200 ${linkClassName}`}
>
<Icon className={iconSize} />
</a>
)
})}
</div>
)
}

View File

@ -2780,6 +2780,44 @@ export interface Footer {
*/
url?: string | null;
};
/**
* Logotyper och certifieringsbadges som visas i övre högra hörnet av footern (t.ex. ISO, UC-sigill, Based in Sweden).
*/
certMarks?:
| {
/**
* PNG eller SVG, helst med transparent bakgrund.
*/
image: number | Media;
/**
* Beskriv certifieringen för tillgänglighet, t.ex. "ISO 27001 certifierad".
*/
alt?: string | null;
/**
* Länk till certifieringens webbsida (öppnas i ny flik).
*/
linkUrl?: string | null;
id?: string | null;
}[]
| null;
/**
* Ikoner visas längst ner i footern och i mobilmenyn. Aktivera och ange URL för varje plattform.
*/
socialLinks?: {
linkedinEnabled?: boolean | null;
/**
* T.ex. https://www.linkedin.com/company/fiber-direkt
*/
linkedinUrl?: string | null;
instagramEnabled?: boolean | null;
instagramUrl?: string | null;
facebookEnabled?: boolean | null;
facebookUrl?: string | null;
youtubeEnabled?: boolean | null;
youtubeUrl?: string | null;
twitterEnabled?: boolean | null;
twitterUrl?: string | null;
};
/**
* Footer-kolumner med rubriker och länkar (sitemap-stil)
*/
@ -2813,7 +2851,7 @@ export interface Footer {
}[]
| null;
/**
* Enkla footer-länkar (visas om inga kolumner finns)
* Länkar i underfältet (visas som rad bredvid copyright, t.ex. Policys · Legal · Villkor)
*/
navItems?:
| {
@ -2839,7 +2877,6 @@ export interface Footer {
* Använd {year} för aktuellt årtal
*/
bottomLeftText?: string | null;
bottomRightText?: string | null;
updatedAt?: string | null;
createdAt?: string | null;
}
@ -3014,6 +3051,28 @@ export interface FooterSelect<T extends boolean = true> {
reference?: T;
url?: T;
};
certMarks?:
| T
| {
image?: T;
alt?: T;
linkUrl?: T;
id?: T;
};
socialLinks?:
| T
| {
linkedinEnabled?: T;
linkedinUrl?: T;
instagramEnabled?: T;
instagramUrl?: T;
facebookEnabled?: T;
facebookUrl?: T;
youtubeEnabled?: T;
youtubeUrl?: T;
twitterEnabled?: T;
twitterUrl?: T;
};
columns?:
| T
| {
@ -3049,7 +3108,6 @@ export interface FooterSelect<T extends boolean = true> {
id?: T;
};
bottomLeftText?: T;
bottomRightText?: T;
updatedAt?: T;
createdAt?: T;
globalType?: T;