'use client'
import React, { useState, useMemo } from 'react'
import type { FDVpsCalculatorBlock as FDVpsCalculatorBlockProps } from '@/payload-types'
const DEFAULT_PRICING = {
windows: 250,
cpuPerCore: 120,
ramPerGb: 100,
ssdPerGb: 4,
hddPerGb: 1,
adminFee: 200,
}
const formatKr = (n: number) => Math.round(n).toLocaleString('sv-SE') + ' kr'
function Toggle({ active, onToggle }: { active: boolean; onToggle: () => void }) {
return (
)
}
function ResourceRow({
label, value, onChange, min = 0, max = 999, step = 1, unit, priceLabel, isDark,
}: {
label: string; value: number; onChange: (v: number) => void
min?: number; max?: number; step?: number; unit: string; priceLabel: string; isDark: boolean
}) {
return (
)
}
export const FDVpsCalculatorBlockComponent: React.FC = ({
heading = 'Virtuell server — kalkylator',
description,
contactCtaText = 'Frågor? Kontakta oss',
contactCtaLink = '/kontakt',
orderCtaText = 'Beställ',
orderCtaLink = '/kontakt?subject=vps-bestallning',
sectionBackground = 'white',
pricingCpuPerCore,
pricingRamPerGb,
pricingSsdPerGb,
pricingHddPerGb,
pricingWindowsLicense,
discountPercent,
showAdminFee,
adminFeeAmount,
additionalServices = [],
anchorId,
}) => {
const pricing = {
windows: pricingWindowsLicense ?? DEFAULT_PRICING.windows,
cpuPerCore: pricingCpuPerCore ?? DEFAULT_PRICING.cpuPerCore,
ramPerGb: pricingRamPerGb ?? DEFAULT_PRICING.ramPerGb,
ssdPerGb: pricingSsdPerGb ?? DEFAULT_PRICING.ssdPerGb,
hddPerGb: pricingHddPerGb ?? DEFAULT_PRICING.hddPerGb,
}
const feeAmount = adminFeeAmount ?? DEFAULT_PRICING.adminFee
const discount = (discountPercent ?? 0) / 100
const [os, setOs] = useState<'linux' | 'windows'>('linux')
const [cpuCores, setCpuCores] = useState(2)
const [ramGb, setRamGb] = useState(4)
const [ssdGb, setSsdGb] = useState(50)
const [hddGb, setHddGb] = useState(0)
const [extraToggles, setExtraToggles] = useState>({})
const toggleExtra = (i: number) => setExtraToggles((p) => ({ ...p, [i]: !p[i] }))
const costs = useMemo(() => {
const disc = (v: number) => v * (1 - discount)
const licenseCost = os === 'windows' ? disc(pricing.windows) : 0
const cpuCost = disc(cpuCores * pricing.cpuPerCore)
const ramCost = disc(ramGb * pricing.ramPerGb)
const ssdCost = disc(ssdGb * pricing.ssdPerGb)
const hddCost = disc(hddGb * pricing.hddPerGb)
const feeCost = showAdminFee ? feeAmount : 0
const extraCosts = (additionalServices ?? []).map((svc, i) => extraToggles[i] ? (svc.price ?? 0) : 0)
const extraTotal = extraCosts.reduce((a, b) => a + b, 0)
const total = licenseCost + cpuCost + ramCost + ssdCost + hddCost + feeCost + extraTotal
return { licenseCost, cpuCost, ramCost, ssdCost, hddCost, feeCost, extraCosts, total }
}, [os, cpuCores, ramGb, ssdGb, hddGb, extraToggles, pricing, discount, feeAmount, showAdminFee, additionalServices])
const isDark = sectionBackground === 'navy'
const bgClass = isDark ? 'bg-fd-navy' : sectionBackground === 'gray' ? 'bg-fd-surface-alt dark:bg-fd-navy' : 'bg-white dark:bg-fd-navy'
// Use Tailwind classes for card styling instead of inline styles
const cardClass = isDark
? 'bg-white/5 border-[5px] border-white/10 rounded-[32px] md:rounded-[50px] lg:rounded-[70px]'
: 'bg-white border-[5px] border-[#e2e8f0] rounded-[32px] md:rounded-[50px] lg:rounded-[70px] dark:bg-white/5 dark:border-white/10'
const headingColor = isDark ? 'text-white' : 'text-fd-navy dark:text-white'
const descColor = isDark ? 'text-white/60' : 'text-fd-navy/60 dark:text-white/60'
const sectionLabel = isDark ? 'text-white/40' : 'text-fd-navy/35 dark:text-white/40'
const summaryLabel = isDark ? 'text-white' : 'text-fd-navy dark:text-white'
const categoryLabel = isDark ? 'text-white/40' : 'text-fd-navy/40 dark:text-white/40'
const dividerClass = isDark ? 'border-white/10' : 'border-fd-navy/8 dark:border-white/10'
const osActiveClass = 'bg-fd-yellow text-fd-navy border-fd-yellow font-joey-bold'
const osInactiveClass = isDark
? 'bg-transparent text-white/70 border-white/20 hover:border-white/50 font-joey'
: 'bg-transparent text-fd-navy/70 border-fd-navy/20 hover:border-fd-navy/50 font-joey dark:text-white/70 dark:border-white/20 dark:hover:border-white/50'
const discStr = discount > 0 ? ` (${discountPercent}% rabatt)` : ''
const baseRows = [
...(os === 'windows' ? [{ label: 'Licens (Windows)', cost: costs.licenseCost }] : []),
{ label: `CPU (${cpuCores} ${cpuCores === 1 ? 'kärna' : 'kärnor'})`, cost: costs.cpuCost },
{ label: `RAM (${ramGb} GB)`, cost: costs.ramCost },
{ label: `SSD NVMe (${ssdGb} GB)`, cost: costs.ssdCost },
...(hddGb > 0 ? [{ label: `HDD (${hddGb} GB)`, cost: costs.hddCost }] : []),
...(showAdminFee ? [{ label: 'Adminavgift', cost: costs.feeCost }] : []),
]
const tillvalRows = (additionalServices ?? []).flatMap((svc, i) =>
extraToggles[i] ? [{ label: svc.label ?? 'Tilläggstjänst', cost: costs.extraCosts[i] ?? 0 }] : []
)
const hasTillval = (additionalServices ?? []).length > 0
return (
{(heading || description) && (
{heading &&
{heading}
}
{description &&
{description}
}
{discount > 0 && (
{discountPercent}% rabatt på alla resurser
)}
)}
{/* Left: Config */}
Operativsystem
{(['linux', 'windows'] as const).map((opt) => (
))}
{showAdminFee && (
Adminavgift
{formatKr(feeAmount)}
)}
{hasTillval && (
Tillvalstjänster
{(additionalServices ?? []).map((svc, i) => (
{svc.label}
{svc.price != null && (
{svc.price} kr/mån
)}
toggleExtra(i)} />
))}
)}
{/* Right: Summary */}
Kostnadsöversikt
{baseRows.map((row, i) => (
{row.label}
{formatKr(row.cost)}
))}
{tillvalRows.length > 0 && (
<>
Tillval
{tillvalRows.map((row, i) => (
{row.label}
{formatKr(row.cost)}
))}
>
)}
Totalt per månad
{formatKr(costs.total)}
exkl. moms
)
}