chore(qa): resolve all lingering eslint warnings after branch merge
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 9s
CI - Lint, Typecheck & Test / quality-assurance (pull_request) Failing after 1m57s
Build & Deploy / 🧪 QA (push) Failing after 2m3s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 9s
CI - Lint, Typecheck & Test / quality-assurance (pull_request) Failing after 1m57s
Build & Deploy / 🧪 QA (push) Failing after 2m3s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
This commit is contained in:
@@ -2,7 +2,7 @@ import { notFound, redirect } from 'next/navigation';
|
|||||||
import { Container, Badge, Heading } from '@/components/ui';
|
import { Container, Badge, Heading } from '@/components/ui';
|
||||||
import { getTranslations, setRequestLocale } from 'next-intl/server';
|
import { getTranslations, setRequestLocale } from 'next-intl/server';
|
||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
import { getPageBySlug, getAllPages } from '@/lib/pages';
|
import { getPageBySlug } from '@/lib/pages';
|
||||||
import { mapSlugToFileSlug, mapFileSlugToTranslated } from '@/lib/slugs';
|
import { mapSlugToFileSlug, mapFileSlugToTranslated } from '@/lib/slugs';
|
||||||
import PayloadRichText from '@/components/PayloadRichText';
|
import PayloadRichText from '@/components/PayloadRichText';
|
||||||
import { SITE_URL } from '@/lib/schema';
|
import { SITE_URL } from '@/lib/schema';
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ interface BlogIndexProps {
|
|||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function generateMetadata({ params }: BlogIndexProps) {
|
export async function generateMetadata({ params }: BlogIndexProps): Promise<Metadata> {
|
||||||
const { locale } = await params;
|
const { locale } = await params;
|
||||||
const t = await getTranslations({ locale, namespace: 'Blog.meta' });
|
const t = await getTranslations({ locale, namespace: 'Blog.meta' });
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Container, Heading, Section } from '@/components/ui';
|
|||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
import { getTranslations, setRequestLocale } from 'next-intl/server';
|
import { getTranslations, setRequestLocale } from 'next-intl/server';
|
||||||
import { SITE_URL } from '@/lib/schema';
|
import { SITE_URL } from '@/lib/schema';
|
||||||
import { getOGImageMetadata } from '@/lib/metadata';
|
|
||||||
import { Suspense } from 'react';
|
import { Suspense } from 'react';
|
||||||
import ContactMap from '@/components/ContactMap';
|
import ContactMap from '@/components/ContactMap';
|
||||||
import ObfuscatedEmail from '@/components/ObfuscatedEmail';
|
import ObfuscatedEmail from '@/components/ObfuscatedEmail';
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export default async function NotFound() {
|
|||||||
}
|
}
|
||||||
suggestedUrl = '/' + pathParts.join('/');
|
suggestedUrl = '/' + pathParts.join('/');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch {
|
||||||
// Ignore Payload errors in 404
|
// Ignore Payload errors in 404
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import JsonLd from '@/components/JsonLd';
|
import JsonLd from '@/components/JsonLd';
|
||||||
import { SITE_URL } from '@/lib/schema';
|
import { SITE_URL } from '@/lib/schema';
|
||||||
import ProductSidebar from '@/components/ProductSidebar';
|
import ProductSidebar from '@/components/ProductSidebar';
|
||||||
import ProductTabs from '@/components/ProductTabs';
|
|
||||||
import ExcelDownload from '@/components/ExcelDownload';
|
import ExcelDownload from '@/components/ExcelDownload';
|
||||||
import ProductTechnicalData from '@/components/ProductTechnicalData';
|
|
||||||
import RelatedProducts from '@/components/RelatedProducts';
|
import RelatedProducts from '@/components/RelatedProducts';
|
||||||
import DatasheetDownload from '@/components/DatasheetDownload';
|
import DatasheetDownload from '@/components/DatasheetDownload';
|
||||||
import { Badge, Card, Container, Heading, Section } from '@/components/ui';
|
import { Badge, Card, Container, Heading, Section } from '@/components/ui';
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { getTranslations, setRequestLocale } from 'next-intl/server';
|
|||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
import JsonLd from '@/components/JsonLd';
|
import JsonLd from '@/components/JsonLd';
|
||||||
import { getBreadcrumbSchema, SITE_URL } from '@/lib/schema';
|
import { getBreadcrumbSchema, SITE_URL } from '@/lib/schema';
|
||||||
import { Section, Container, Heading, Badge, Button } from '@/components/ui';
|
import { Section, Container, Heading, Badge } from '@/components/ui';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import Reveal from '@/components/Reveal';
|
import Reveal from '@/components/Reveal';
|
||||||
import Gallery from '@/components/team/Gallery';
|
import Gallery from '@/components/team/Gallery';
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default function Lightbox({ isOpen, images, initialIndex, onClose }: Ligh
|
|||||||
const previousFocusRef = useRef<HTMLElement | null>(null);
|
const previousFocusRef = useRef<HTMLElement | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMounted(true); // eslint-disable-line react-hooks/set-state-in-effect
|
setMounted(true);
|
||||||
return () => setMounted(false);
|
return () => setMounted(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ export default function Lightbox({ isOpen, images, initialIndex, onClose }: Ligh
|
|||||||
if (photoParam !== null) {
|
if (photoParam !== null) {
|
||||||
const index = parseInt(photoParam, 10);
|
const index = parseInt(photoParam, 10);
|
||||||
if (!isNaN(index) && index >= 0 && index < images.length) {
|
if (!isNaN(index) && index >= 0 && index < images.length) {
|
||||||
setCurrentIndex(index); // eslint-disable-line react-hooks/set-state-in-effect
|
setCurrentIndex(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [searchParams, images.length]);
|
}, [searchParams, images.length]);
|
||||||
@@ -139,7 +139,7 @@ export default function Lightbox({ isOpen, images, initialIndex, onClose }: Ligh
|
|||||||
if (!mounted) return null;
|
if (!mounted) return null;
|
||||||
|
|
||||||
return createPortal(
|
return createPortal(
|
||||||
<LazyMotion strict features={() => import('@/lib/framer-features').then(res => res.default)}>
|
<LazyMotion strict features={() => import('@/lib/framer-features').then((res) => res.default)}>
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export default function ObfuscatedEmail({ email, className = '', children }: Obf
|
|||||||
const [mounted, setMounted] = useState(false);
|
const [mounted, setMounted] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||||
setMounted(true);
|
setMounted(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export default function ObfuscatedPhone({ phone, className = '', children }: Obf
|
|||||||
const [mounted, setMounted] = useState(false);
|
const [mounted, setMounted] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||||
setMounted(true);
|
setMounted(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -786,8 +786,8 @@ const jsxConverters: JSXConverters = {
|
|||||||
</Section>
|
</Section>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
imageGallery: ({ node }: any) => <Gallery />,
|
imageGallery: () => <Gallery />,
|
||||||
'block-imageGallery': ({ node }: any) => <Gallery />,
|
'block-imageGallery': () => <Gallery />,
|
||||||
categoryGrid: ({ node }: any) => {
|
categoryGrid: ({ node }: any) => {
|
||||||
const cats = node.fields.categories || [];
|
const cats = node.fields.categories || [];
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -28,13 +28,13 @@ export default function TrackedLink({
|
|||||||
}: TrackedLinkProps) {
|
}: TrackedLinkProps) {
|
||||||
const { trackEvent } = useAnalytics();
|
const { trackEvent } = useAnalytics();
|
||||||
|
|
||||||
const handleClick = (e: React.MouseEvent) => {
|
const handleClick = () => {
|
||||||
try {
|
try {
|
||||||
trackEvent(eventName, {
|
trackEvent(eventName, {
|
||||||
href,
|
href,
|
||||||
...eventProperties,
|
...eventProperties,
|
||||||
});
|
});
|
||||||
} catch (_e) {
|
} catch {
|
||||||
// Analytics tracking should not block navigation, so we catch and ignore errors.
|
// Analytics tracking should not block navigation, so we catch and ignore errors.
|
||||||
}
|
}
|
||||||
if (onClick) onClick();
|
if (onClick) onClick();
|
||||||
|
|||||||
1932
lib/pdf-brochure.tsx
1932
lib/pdf-brochure.tsx
File diff suppressed because it is too large
Load Diff
@@ -4,10 +4,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export interface FormattedTechnicalValue {
|
export interface FormattedTechnicalValue {
|
||||||
original: string;
|
original: string;
|
||||||
isList: boolean;
|
isList: boolean;
|
||||||
parts: string[];
|
parts: string[];
|
||||||
displayValue: string;
|
displayValue: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -15,92 +15,90 @@ export interface FormattedTechnicalValue {
|
|||||||
* Detects if it's a list (separated by / or ,) and tries to clean it up.
|
* Detects if it's a list (separated by / or ,) and tries to clean it up.
|
||||||
*/
|
*/
|
||||||
export function formatTechnicalValue(value: string | null | undefined): FormattedTechnicalValue {
|
export function formatTechnicalValue(value: string | null | undefined): FormattedTechnicalValue {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return { original: '', isList: false, parts: [], displayValue: '' };
|
return { original: '', isList: false, parts: [], displayValue: '' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const str = String(value).trim();
|
||||||
|
|
||||||
|
// Detect list separators
|
||||||
|
let parts: string[] = [];
|
||||||
|
if (str.includes(' / ')) {
|
||||||
|
parts = str.split(' / ').map((p) => p.trim());
|
||||||
|
} else if (str.includes(' /')) {
|
||||||
|
parts = str.split(' /').map((p) => p.trim());
|
||||||
|
} else if (str.includes('/ ')) {
|
||||||
|
parts = str.split('/ ').map((p) => p.trim());
|
||||||
|
} else if (str.split('/').length > 2) {
|
||||||
|
// Check if it's actually many standards separated by / without spaces
|
||||||
|
// e.g. EN123/EN456/EN789
|
||||||
|
const split = str.split('/');
|
||||||
|
if (split.length > 3) {
|
||||||
|
parts = split.map((p) => p.trim());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const str = String(value).trim();
|
// If no parts found yet, try comma
|
||||||
|
if (parts.length === 0 && str.includes(', ')) {
|
||||||
|
parts = str.split(', ').map((p) => p.trim());
|
||||||
|
}
|
||||||
|
|
||||||
// Detect list separators
|
// Filter out empty parts
|
||||||
let parts: string[] = [];
|
parts = parts.filter(Boolean);
|
||||||
if (str.includes(' / ')) {
|
|
||||||
parts = str.split(' / ').map(p => p.trim());
|
// If we have parts, let's see if we can simplify them
|
||||||
} else if (str.includes(' /')) {
|
if (parts.length > 2) {
|
||||||
parts = str.split(' /').map(p => p.trim());
|
// Find common prefix to condense repetitive standards
|
||||||
} else if (str.includes('/ ')) {
|
let commonPrefix = '';
|
||||||
parts = str.split('/ ').map(p => p.trim());
|
const first = parts[0];
|
||||||
} else if (str.split('/').length > 2) {
|
const last = parts[parts.length - 1];
|
||||||
// Check if it's actually many standards separated by / without spaces
|
let i = 0;
|
||||||
// e.g. EN123/EN456/EN789
|
while (i < first.length && first.charAt(i) === last.charAt(i)) {
|
||||||
const split = str.split('/');
|
i++;
|
||||||
if (split.length > 3) {
|
}
|
||||||
parts = split.map(p => p.trim());
|
commonPrefix = first.substring(0, i);
|
||||||
|
|
||||||
|
// If a meaningful prefix exists (e.g., "EN 60 332-1-")
|
||||||
|
if (commonPrefix.length > 4) {
|
||||||
|
const suffixParts: string[] = [];
|
||||||
|
|
||||||
|
for (let idx = 0; idx < parts.length; idx++) {
|
||||||
|
if (idx === 0) {
|
||||||
|
suffixParts.push(parts[idx]);
|
||||||
|
} else {
|
||||||
|
const suffix = parts[idx].substring(commonPrefix.length).trim();
|
||||||
|
if (suffix) {
|
||||||
|
suffixParts.push(suffix);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no parts found yet, try comma
|
// Condense into a single string like "EN 60 332-1-2 / -3 / -4"
|
||||||
if (parts.length === 0 && str.includes(', ')) {
|
// Wait, returning a single string might still wrap badly.
|
||||||
parts = str.split(', ').map(p => p.trim());
|
// Instead, we return them as chunks or just a condensed string.
|
||||||
}
|
const condensedString = suffixParts[0] + ' / -' + suffixParts.slice(1).join(' / -');
|
||||||
|
|
||||||
// Filter out empty parts
|
return {
|
||||||
parts = parts.filter(Boolean);
|
|
||||||
|
|
||||||
// If we have parts, let's see if we can simplify them
|
|
||||||
if (parts.length > 2) {
|
|
||||||
// Find common prefix to condense repetitive standards
|
|
||||||
let commonPrefix = '';
|
|
||||||
const first = parts[0];
|
|
||||||
const last = parts[parts.length - 1];
|
|
||||||
let i = 0;
|
|
||||||
while (i < first.length && first.charAt(i) === last.charAt(i)) {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
commonPrefix = first.substring(0, i);
|
|
||||||
|
|
||||||
// If a meaningful prefix exists (e.g., "EN 60 332-1-")
|
|
||||||
if (commonPrefix.length > 4) {
|
|
||||||
// Trim trailing spaces/dashes before comparing words
|
|
||||||
const basePrefix = commonPrefix.trim();
|
|
||||||
const suffixParts: string[] = [];
|
|
||||||
|
|
||||||
for (let idx = 0; idx < parts.length; idx++) {
|
|
||||||
if (idx === 0) {
|
|
||||||
suffixParts.push(parts[idx]);
|
|
||||||
} else {
|
|
||||||
const suffix = parts[idx].substring(commonPrefix.length).trim();
|
|
||||||
if (suffix) {
|
|
||||||
suffixParts.push(suffix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Condense into a single string like "EN 60 332-1-2 / -3 / -4"
|
|
||||||
// Wait, returning a single string might still wrap badly.
|
|
||||||
// Instead, we return them as chunks or just a condensed string.
|
|
||||||
const condensedString = suffixParts[0] + ' / -' + suffixParts.slice(1).join(' / -');
|
|
||||||
|
|
||||||
return {
|
|
||||||
original: str,
|
|
||||||
isList: false, // Turn off badge rendering to use text block instead
|
|
||||||
parts: [condensedString],
|
|
||||||
displayValue: condensedString
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no common prefix, return as list so UI can render badges
|
|
||||||
return {
|
|
||||||
original: str,
|
|
||||||
isList: true,
|
|
||||||
parts,
|
|
||||||
displayValue: parts.join(', ')
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
original: str,
|
original: str,
|
||||||
isList: false,
|
isList: false, // Turn off badge rendering to use text block instead
|
||||||
parts: [str],
|
parts: [condensedString],
|
||||||
displayValue: str
|
displayValue: condensedString,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no common prefix, return as list so UI can render badges
|
||||||
|
return {
|
||||||
|
original: str,
|
||||||
|
isList: true,
|
||||||
|
parts,
|
||||||
|
displayValue: parts.join(', '),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
original: str,
|
||||||
|
isList: false,
|
||||||
|
parts: [str],
|
||||||
|
displayValue: str,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
2
next-env.d.ts
vendored
2
next-env.d.ts
vendored
@@ -1,6 +1,6 @@
|
|||||||
/// <reference types="next" />
|
/// <reference types="next" />
|
||||||
/// <reference types="next/image-types/global" />
|
/// <reference types="next/image-types/global" />
|
||||||
import "./.next/dev/types/routes.d.ts";
|
import "./.next/types/routes.d.ts";
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
// NOTE: This file should not be edited
|
||||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
|
/* eslint-disable @next/next/no-img-element */
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export default function Icon() {
|
export default function Icon() {
|
||||||
return (
|
return (
|
||||||
<img
|
<img
|
||||||
src="/logo-blue.svg"
|
src="/logo-blue.svg"
|
||||||
alt="KLZ"
|
alt="KLZ"
|
||||||
className="klz-admin-icon"
|
className="klz-admin-icon"
|
||||||
style={{ maxWidth: '100%', height: 'auto', maxHeight: '32px', display: 'block' }}
|
style={{ maxWidth: '100%', height: 'auto', maxHeight: '32px', display: 'block' }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
|
/* eslint-disable @next/next/no-img-element */
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export default function Logo() {
|
export default function Logo() {
|
||||||
return (
|
return (
|
||||||
<img
|
<img
|
||||||
src="/logo-blue.svg"
|
src="/logo-blue.svg"
|
||||||
alt="KLZ Cables"
|
alt="KLZ Cables"
|
||||||
className="klz-admin-logo"
|
className="klz-admin-logo"
|
||||||
style={{ maxWidth: '100%', height: 'auto', maxHeight: '40px', display: 'block' }}
|
style={{ maxWidth: '100%', height: 'auto', maxHeight: '40px', display: 'block' }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user