All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 6s
Build & Deploy / 🧪 QA (push) Successful in 2m20s
Build & Deploy / 🏗️ Build (push) Successful in 4m22s
Build & Deploy / 🚀 Deploy (push) Successful in 23s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 5m16s
Build & Deploy / 🔔 Notify (push) Successful in 1s
- Fix PayloadRichText: migrate custom JSX converters to Lexical v3 nodesToJSX API - paragraph, heading, list, listitem, quote, link converters now use nodesToJSX - Resolves missing product texts since PayloadCMS migration - Fix mobile navigation: move overlay outside <header> to prevent fixed-position clipping - Header transform/backdrop-filter was containing the fixed overlay - Use bg-primary/95 backdrop-blur-3xl for premium blue background - Fix product image mobile layout: use md:-mt-32 responsive prefix - Negative margin only applies on md+ to avoid overlap on mobile - Improve mobile product page UX: - Breadcrumbs: flex-wrap, truncate, reduced separator spacing - Hero: reduced top padding pt-28 on mobile - Product image card: 4/3 aspect ratio and smaller padding on mobile - Section spacing: use responsive md: prefixes throughout - Data tables: 2-col grid on mobile, smaller card padding/radius - Tables: add right-edge scroll hint gradient on mobile
174 lines
7.0 KiB
TypeScript
174 lines
7.0 KiB
TypeScript
'use client';
|
|
|
|
import React, { useState } from 'react';
|
|
import { useTranslations } from 'next-intl';
|
|
|
|
interface KeyValueItem {
|
|
label: string;
|
|
value: string;
|
|
unit?: string;
|
|
}
|
|
|
|
interface VoltageTable {
|
|
voltageLabel: string;
|
|
metaItems: KeyValueItem[];
|
|
columns: Array<{ key: string; label: string }>;
|
|
rows: Array<{ configuration: string; cells: string[] }>;
|
|
}
|
|
|
|
interface ProductTechnicalDataProps {
|
|
data: {
|
|
technicalItems: KeyValueItem[];
|
|
voltageTables: VoltageTable[];
|
|
};
|
|
}
|
|
|
|
export default function ProductTechnicalData({ data }: ProductTechnicalDataProps) {
|
|
const t = useTranslations('Products');
|
|
const [expandedTables, setExpandedTables] = useState<Record<number, boolean>>({});
|
|
|
|
if (!data) return null;
|
|
const { technicalItems = [], voltageTables = [] } = data;
|
|
|
|
const toggleTable = (idx: number) => {
|
|
setExpandedTables((prev) => ({
|
|
...prev,
|
|
[idx]: !prev[idx],
|
|
}));
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-8 md:space-y-16">
|
|
{technicalItems.length > 0 && (
|
|
<div className="bg-white p-5 md:p-12 rounded-[20px] md:rounded-[32px] shadow-sm border border-neutral-dark/5">
|
|
<h3 className="text-2xl font-bold text-primary mb-8 flex items-center gap-3">
|
|
<div className="w-2 h-8 bg-accent rounded-full" />
|
|
General Data
|
|
</h3>
|
|
<dl className="grid grid-cols-2 sm:grid-cols-2 lg:grid-cols-3 gap-x-6 gap-y-6 md:gap-x-12 md:gap-y-8">
|
|
{technicalItems.map((item, idx) => (
|
|
<div key={idx} className="flex flex-col group">
|
|
<dt className="text-sm font-bold uppercase tracking-widest text-primary/40 mb-2 group-hover:text-accent transition-colors">
|
|
{item.label}
|
|
</dt>
|
|
<dd className="text-lg font-semibold text-text-primary">
|
|
{item.value}{' '}
|
|
{item.unit && (
|
|
<span className="text-sm font-normal text-text-secondary ml-1">
|
|
{item.unit}
|
|
</span>
|
|
)}
|
|
</dd>
|
|
</div>
|
|
))}
|
|
</dl>
|
|
</div>
|
|
)}
|
|
|
|
{voltageTables.map((table, idx) => {
|
|
const isExpanded = expandedTables[idx];
|
|
const hasManyRows = table.rows.length > 10;
|
|
|
|
return (
|
|
<div
|
|
key={idx}
|
|
className="bg-white p-5 md:p-12 rounded-[20px] md:rounded-[32px] shadow-sm border border-neutral-dark/5 overflow-hidden"
|
|
>
|
|
<h3 className="text-2xl font-bold text-primary mb-8 flex items-center gap-3">
|
|
<div className="w-2 h-8 bg-accent rounded-full" />
|
|
{table.voltageLabel !== 'Voltage unknown' &&
|
|
table.voltageLabel !== 'Spannung unbekannt'
|
|
? table.voltageLabel
|
|
: 'Technical Specifications'}
|
|
</h3>
|
|
|
|
{table.metaItems.length > 0 && (
|
|
<dl className="grid grid-cols-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 md:gap-8 mb-6 md:mb-12 bg-neutral-light/50 p-4 md:p-8 rounded-xl md:rounded-2xl border border-neutral-dark/5">
|
|
{table.metaItems.map((item, mIdx) => (
|
|
<div key={mIdx}>
|
|
<dt className="text-[10px] font-black uppercase tracking-[0.2em] text-primary/40 mb-1">
|
|
{item.label}
|
|
</dt>
|
|
<dd className="font-bold text-primary">
|
|
{item.value} {item.unit}
|
|
</dd>
|
|
</div>
|
|
))}
|
|
</dl>
|
|
)}
|
|
|
|
<div className="relative">
|
|
{/* Scroll hint gradient on right edge for mobile */}
|
|
<div className="pointer-events-none absolute right-0 top-0 h-full w-8 bg-gradient-to-l from-white to-transparent z-20 md:hidden" />
|
|
<div
|
|
id={`voltage-table-${idx}`}
|
|
className={`overflow-x-auto -mx-5 md:-mx-12 px-5 md:px-12 transition-all duration-500 ease-in-out ${
|
|
!isExpanded && hasManyRows ? 'max-h-[400px] overflow-y-hidden' : 'max-h-[none]'
|
|
}`}
|
|
>
|
|
<table className="min-w-full border-separate border-spacing-0">
|
|
<thead>
|
|
<tr>
|
|
<th
|
|
scope="col"
|
|
className="px-3 py-3 text-left text-[10px] font-black text-primary/40 uppercase tracking-[0.2em] sticky left-0 bg-white z-10 border-b border-neutral-dark/10"
|
|
>
|
|
Config.
|
|
</th>
|
|
{table.columns.map((col, cIdx) => (
|
|
<th
|
|
key={cIdx}
|
|
scope="col"
|
|
className="px-3 py-3 text-left text-[10px] font-black text-primary/40 uppercase tracking-[0.2em] whitespace-nowrap border-b border-neutral-dark/10"
|
|
>
|
|
{col.label}
|
|
</th>
|
|
))}
|
|
</tr>
|
|
</thead>
|
|
<tbody className="divide-y divide-neutral-dark/5">
|
|
{table.rows.map((row, rIdx) => (
|
|
<tr key={rIdx} className="hover:bg-neutral-light/50 transition-colors group">
|
|
<td className="px-3 py-2 text-xs font-bold text-primary sticky left-0 bg-white group-hover:bg-neutral-light/50 z-10 whitespace-nowrap">
|
|
{row.configuration}
|
|
</td>
|
|
{row.cells.map((cell: any, cellIdx: number) => (
|
|
<td
|
|
key={cellIdx}
|
|
className="px-3 py-2 text-xs text-text-secondary whitespace-nowrap"
|
|
>
|
|
{typeof cell === 'object' && cell !== null && 'value' in cell
|
|
? cell.value
|
|
: cell}
|
|
</td>
|
|
))}
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
{!isExpanded && hasManyRows && (
|
|
<div className="absolute bottom-0 left-0 right-0 h-32 bg-gradient-to-t from-white via-white/80 to-transparent z-20 pointer-events-none" />
|
|
)}
|
|
</div>
|
|
|
|
{hasManyRows && (
|
|
<div className="mt-8 flex justify-center">
|
|
<button
|
|
onClick={() => toggleTable(idx)}
|
|
aria-expanded={isExpanded}
|
|
aria-controls={`voltage-table-${idx}`}
|
|
className="px-8 py-3 rounded-full bg-primary text-white text-sm font-bold uppercase tracking-widest hover:bg-accent hover:text-primary transition-all duration-300 shadow-lg hover:shadow-accent/20"
|
|
>
|
|
{isExpanded ? t('showLess') : t('showMore')}
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|