fix: resolve layout, pdf datasheets data generation and excel 404 routing

This commit is contained in:
2026-03-03 12:38:55 +01:00
parent 34bb91c04b
commit 655f33091f
105 changed files with 1337 additions and 1077 deletions

View File

@@ -499,9 +499,14 @@ export default async function ProductPage({ params }: ProductPageProps) {
</h2> </h2>
<div className="h-1.5 w-24 bg-accent rounded-full" /> <div className="h-1.5 w-24 bg-accent rounded-full" />
</div> </div>
<div className="flex flex-col gap-4 max-w-2xl"> <div className="flex flex-row flex-wrap items-center gap-4 max-w-2xl">
<DatasheetDownload datasheetPath={datasheetPath} className="mt-0" /> <DatasheetDownload
{excelPath && <ExcelDownload excelPath={excelPath} className="mt-0" />} datasheetPath={datasheetPath}
className="mt-0 w-full sm:w-auto"
/>
{excelPath && (
<ExcelDownload excelPath={excelPath} className="mt-0 w-full sm:w-auto" />
)}
</div> </div>
</div> </div>
)} )}

View File

@@ -23,6 +23,14 @@ export async function requestBrochureAction(formData: FormData) {
const email = formData.get('email') as string; const email = formData.get('email') as string;
const locale = (formData.get('locale') as string) || 'en'; const locale = (formData.get('locale') as string) || 'en';
// Anti-spam Honeypot Check
const honeypot = formData.get('company_website') as string;
if (honeypot) {
logger.warn('Spam detected via honeypot in brochure request', { email });
// Silently succeed to fool the bot without doing actual work
return { success: true };
}
if (!email) { if (!email) {
logger.warn('Missing email in brochure request'); logger.warn('Missing email in brochure request');
return { success: false, error: 'Missing email address' }; return { success: false, error: 'Missing email address' };
@@ -77,7 +85,7 @@ export async function requestBrochureAction(formData: FormData) {
const html = await render( const html = await render(
React.createElement(BrochureDeliveryEmail, { React.createElement(BrochureDeliveryEmail, {
email, _email: email,
brochureUrl, brochureUrl,
locale: locale as 'en' | 'de', locale: locale as 'en' | 'de',
}), }),

View File

@@ -25,6 +25,14 @@ export async function sendContactFormAction(formData: FormData) {
// Track attempt // Track attempt
services.analytics.track('contact-form-attempt'); services.analytics.track('contact-form-attempt');
// Anti-spam Honeypot Check
const honeypot = formData.get('company_website') as string;
if (honeypot) {
logger.warn('Spam detected via honeypot in contact request', { email: formData.get('email') });
// Silently succeed to fool the bot without doing actual work
return { success: true };
}
const name = formData.get('name') as string; const name = formData.get('name') as string;
const email = formData.get('email') as string; const email = formData.get('email') as string;
const message = formData.get('message') as string; const message = formData.get('message') as string;

View File

@@ -139,6 +139,15 @@ export default function ContactForm() {
{t('form.title')} {t('form.title')}
</Heading> </Heading>
<form onSubmit={handleSubmit} className="grid grid-cols-1 md:grid-cols-2 gap-4 md:gap-8"> <form onSubmit={handleSubmit} className="grid grid-cols-1 md:grid-cols-2 gap-4 md:gap-8">
{/* Anti-spam Honeypot */}
<input
type="text"
name="company_website"
tabIndex={-1}
autoComplete="off"
style={{ display: 'none' }}
aria-hidden="true"
/>
<div className="space-y-1 md:space-y-2"> <div className="space-y-1 md:space-y-2">
<Label htmlFor="contact-name">{t('form.name')}</Label> <Label htmlFor="contact-name">{t('form.name')}</Label>
<Input <Input

View File

@@ -93,6 +93,16 @@ export default function FooterBrochureForm({ className }: Props) {
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="w-full md:w-auto flex flex-col sm:flex-row gap-3" className="w-full md:w-auto flex flex-col sm:flex-row gap-3"
> >
{/* Anti-spam Honeypot */}
<input
type="text"
name="company_website"
tabIndex={-1}
autoComplete="off"
style={{ display: 'none' }}
aria-hidden="true"
/>
<div className="relative w-full sm:w-64"> <div className="relative w-full sm:w-64">
<input <input
name="email" name="email"

View File

@@ -165,6 +165,16 @@ export default function RequestQuoteForm({ productName }: RequestQuoteFormProps)
return ( return (
<form onSubmit={handleSubmit} className="space-y-3 !mt-0"> <form onSubmit={handleSubmit} className="space-y-3 !mt-0">
{/* Anti-spam Honeypot */}
<input
type="text"
name="company_website"
tabIndex={-1}
autoComplete="off"
style={{ display: 'none' }}
aria-hidden="true"
/>
<div className="space-y-2 !mt-0"> <div className="space-y-2 !mt-0">
<div className="space-y-1 !mt-0"> <div className="space-y-1 !mt-0">
<label htmlFor={emailId} className="sr-only"> <label htmlFor={emailId} className="sr-only">

View File

@@ -102,7 +102,7 @@ export default async function middleware(request: NextRequest) {
export const config = { export const config = {
matcher: [ matcher: [
'/((?!api|_next/static|_next/image|favicon.ico|admin|manifest.webmanifest|.*\\.(?:svg|png|jpg|jpeg|gif|webp|pdf|txt|vcf|xml|webm|mp4|map)$).*)', '/((?!api|_next/static|_next/image|favicon.ico|admin|manifest.webmanifest|.*\\.(?:svg|png|jpg|jpeg|gif|webp|pdf|xlsx|txt|vcf|xml|webm|mp4|map)$).*)',
'/(de|en)/:path*', '/(de|en)/:path*',
], ],
}; };

View File

@@ -25,6 +25,18 @@ const nextConfig = {
}, },
}, },
...(isProd ? { output: 'standalone' } : {}), ...(isProd ? { output: 'standalone' } : {}),
async rewrites() {
return [
{
source: '/:locale/datasheets/:path*',
destination: '/datasheets/:path*',
},
{
source: '/:locale/brochure/:path*',
destination: '/brochure/:path*',
},
];
},
async headers() { async headers() {
const isProd = process.env.NODE_ENV === 'production'; const isProd = process.env.NODE_ENV === 'production';
const umamiDomain = new URL(process.env.UMAMI_API_ENDPOINT || 'https://analytics.infra.mintel.me').origin; const umamiDomain = new URL(process.env.UMAMI_API_ENDPOINT || 'https://analytics.infra.mintel.me').origin;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More