This commit is contained in:
Iliyan Angelov
2025-10-13 01:49:06 +03:00
parent 76c857b4f5
commit 5ad9cbe3a6
97 changed files with 5752 additions and 2376 deletions

View File

@@ -1,207 +0,0 @@
'use client';
import Image from 'next/image';
import { useState } from 'react';
interface OptimizedImageProps {
src: string;
alt: string;
width?: number;
height?: number;
className?: string;
priority?: boolean;
fill?: boolean;
sizes?: string;
quality?: number;
style?: React.CSSProperties;
objectFit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
loading?: 'lazy' | 'eager';
}
/**
* OptimizedImage Component
*
* An enterprise-grade optimized image component that provides:
* - Automatic lazy loading
* - Responsive images with srcset
* - WebP/AVIF format support
* - Blur placeholder while loading
* - Error handling with fallback
* - Performance optimization
*
* @example
* <OptimizedImage
* src="/images/hero.jpg"
* alt="Hero banner showcasing our services"
* width={1200}
* height={600}
* priority={true}
* />
*/
export default function OptimizedImage({
src,
alt,
width,
height,
className = '',
priority = false,
fill = false,
sizes,
quality = 85,
style,
objectFit = 'cover',
loading,
}: OptimizedImageProps) {
const [imgSrc, setImgSrc] = useState(src);
const [isLoading, setIsLoading] = useState(true);
const [hasError, setHasError] = useState(false);
// Fallback image for errors
const fallbackImage = '/images/placeholder.png';
// Handle image load
const handleLoad = () => {
setIsLoading(false);
};
// Handle image error
const handleError = () => {
setHasError(true);
setIsLoading(false);
if (imgSrc !== fallbackImage) {
setImgSrc(fallbackImage);
}
};
// SEO-friendly alt text validation
const seoAlt = alt || 'GNX Soft - Enterprise Software Solutions';
// Validate alt text for SEO
if (process.env.NODE_ENV === 'development' && !alt) {
console.warn(
`OptimizedImage: Missing alt text for image "${src}". Alt text is crucial for SEO and accessibility.`
);
}
// Common image props
const imageProps = {
src: imgSrc,
alt: seoAlt,
className: `${className} ${isLoading ? 'image-loading' : 'image-loaded'}`,
onLoad: handleLoad,
onError: handleError,
quality,
loading: loading || (priority ? 'eager' : 'lazy'),
style: {
...style,
objectFit: objectFit as any,
},
};
// Use fill layout for responsive images
if (fill) {
return (
<div className={`optimized-image-wrapper ${hasError ? 'has-error' : ''}`}>
<Image
{...imageProps}
fill
sizes={sizes || '100vw'}
priority={priority}
/>
<style jsx>{`
.optimized-image-wrapper {
position: relative;
overflow: hidden;
}
.optimized-image-wrapper.has-error {
background: #f3f4f6;
}
:global(.image-loading) {
filter: blur(10px);
transform: scale(1.1);
transition: filter 0.3s ease, transform 0.3s ease;
}
:global(.image-loaded) {
filter: blur(0);
transform: scale(1);
}
`}</style>
</div>
);
}
// Standard layout with explicit dimensions
return (
<div className={`optimized-image-container ${hasError ? 'has-error' : ''}`}>
<Image
{...imageProps}
width={width}
height={height}
sizes={sizes}
priority={priority}
/>
<style jsx>{`
.optimized-image-container {
position: relative;
display: inline-block;
}
.optimized-image-container.has-error {
background: #f3f4f6;
border-radius: 4px;
}
:global(.image-loading) {
filter: blur(10px);
transform: scale(1.05);
transition: filter 0.4s ease, transform 0.4s ease;
}
:global(.image-loaded) {
filter: blur(0);
transform: scale(1);
}
`}</style>
</div>
);
}
/**
* Usage Examples:
*
* 1. Hero Image (Priority Loading):
* <OptimizedImage
* src="/images/hero.jpg"
* alt="Enterprise software development solutions"
* width={1920}
* height={1080}
* priority={true}
* sizes="100vw"
* />
*
* 2. Service Card Image (Lazy Loading):
* <OptimizedImage
* src="/images/service/custom-software.jpg"
* alt="Custom software development service icon"
* width={400}
* height={300}
* sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
* />
*
* 3. Background Image (Fill):
* <OptimizedImage
* src="/images/background.jpg"
* alt="Technology background pattern"
* fill={true}
* sizes="100vw"
* objectFit="cover"
* />
*
* 4. Logo (High Priority):
* <OptimizedImage
* src="/images/logo.png"
* alt="GNX Soft company logo"
* width={200}
* height={50}
* priority={true}
* quality={100}
* />
*/

View File

@@ -1,57 +0,0 @@
'use client';
import { ReactNode, CSSProperties } from 'react';
interface ProtectedImageProps {
src: string;
alt: string;
className?: string;
style?: CSSProperties;
width?: number;
height?: number;
showWatermark?: boolean;
priority?: boolean;
children?: ReactNode;
}
/**
* Protected Image Component
* Wraps images with protection against downloading and copying
*/
export default function ProtectedImage({
src,
alt,
className = '',
style = {},
width,
height,
showWatermark = false,
children
}: ProtectedImageProps) {
const wrapperClass = `protected-image-wrapper ${showWatermark ? 'watermarked-image' : ''} ${className}`;
return (
<div className={wrapperClass} style={style}>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={src}
alt={alt}
width={width}
height={height}
draggable="false"
onContextMenu={(e) => e.preventDefault()}
onDragStart={(e) => e.preventDefault()}
style={{
WebkitUserSelect: 'none',
MozUserSelect: 'none',
msUserSelect: 'none',
userSelect: 'none',
pointerEvents: 'none'
}}
/>
{children}
</div>
);
}

View File

@@ -49,55 +49,116 @@ export const CookieConsentBanner: React.FC = () => {
return (
<AnimatePresence>
{/* Fullscreen overlay to center the banner */}
<motion.div
initial={{ y: 100, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: 100, opacity: 0 }}
transition={{ duration: 0.3, ease: 'easeOut' }}
className="cookie-consent-banner"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.2 }}
style={{
position: 'fixed',
inset: 0,
zIndex: 10000,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: 'rgba(17, 24, 39, 0.45)',
backdropFilter: 'blur(4px)',
}}
>
<div className="cookie-consent-banner__container">
<div className="cookie-consent-banner__content">
<div className="cookie-consent-banner__icon">
<i className="fas fa-cookie-bite"></i>
{/* Centered enterprise-style card */}
<motion.div
initial={{ opacity: 0, scale: 0.96, y: 8 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.98, y: 8 }}
transition={{ duration: 0.25, ease: 'easeOut' }}
className="cookie-consent-banner"
style={{
width: 'min(680px, 92vw)',
background: '#0b1220',
color: '#e5e7eb',
border: '1px solid rgba(255,255,255,0.08)',
borderRadius: 16,
boxShadow: '0 25px 70px rgba(0,0,0,0.45)',
padding: 24,
}}
>
<div className="cookie-consent-banner__container" style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
<div className="cookie-consent-banner__content" style={{ display: 'flex', gap: 16 }}>
<div
className="cookie-consent-banner__icon"
style={{
width: 48,
height: 48,
borderRadius: 12,
display: 'grid',
placeItems: 'center',
background: 'linear-gradient(135deg, rgba(199, 213, 236, 0.39), rgba(147,197,253,0.08))',
color: '#93c5fd',
flex: '0 0 auto',
}}
>
<i className="fas fa-cookie-bite"></i>
</div>
<div className="cookie-consent-banner__text" style={{ display: 'grid', gap: 6 }}>
<h3 style={{ margin: 0, fontSize: 20, fontWeight: 700 }}>Cookie Preferences</h3>
<p style={{ margin: 0, lineHeight: 1.6, color: '#ffffff' }}>
We use only essential functional cookies to ensure our website works properly. We do not collect
personal data or use tracking cookies. Your privacy is important to us.
</p>
{config.showPrivacyNotice && (
<div className="cookie-consent-banner__links" style={{ marginTop: 6 }}>
<a
href={config.privacyPolicyUrl}
target="_blank"
rel="noopener noreferrer"
style={{ color: '#93c5fd', textDecoration: 'underline' }}
>
Privacy Policy
</a>
</div>
)}
</div>
</div>
<div className="cookie-consent-banner__text">
<h3>Cookie Preferences</h3>
<p>
We use only essential functional cookies to ensure our website works properly.
We do not collect personal data or use tracking cookies. Your privacy is important to us.
</p>
{config.showPrivacyNotice && (
<div className="cookie-consent-banner__links">
<a href={config.privacyPolicyUrl} target="_blank" rel="noopener noreferrer">
Privacy Policy
</a>
<a href={config.cookiePolicyUrl} target="_blank" rel="noopener noreferrer">
Cookie Policy
</a>
</div>
)}
<div
className="cookie-consent-banner__actions"
style={{ display: 'flex', justifyContent: 'flex-end', gap: 12, marginTop: 8 }}
>
<button
type="button"
className="cookie-consent-banner__btn cookie-consent-banner__btn--secondary"
onClick={showSettings}
style={{
padding: '10px 14px',
borderRadius: 10,
border: '1px solid rgba(255,255,255,0.12)',
background: 'transparent',
color: '#e5e7eb',
fontWeight: 600,
}}
>
<i className="fas fa-cog" style={{ marginRight: 8 }}></i>
Settings
</button>
<button
type="button"
className="cookie-consent-banner__btn cookie-consent-banner__btn--primary"
onClick={acceptNecessary}
style={{
padding: '10px 14px',
borderRadius: 10,
border: '1px solid rgba(59,130,246,0.35)',
background: 'linear-gradient(135deg, rgba(59,130,246,0.25), rgba(37,99,235,0.35))',
color: '#ffffff',
fontWeight: 700,
}}
>
<i className="fas fa-check" style={{ marginRight: 8 }}></i>
Accept Functional Only
</button>
</div>
</div>
<div className="cookie-consent-banner__actions">
<button
type="button"
className="cookie-consent-banner__btn cookie-consent-banner__btn--secondary"
onClick={showSettings}
>
<i className="fas fa-cog"></i>
Settings
</button>
<button
type="button"
className="cookie-consent-banner__btn cookie-consent-banner__btn--primary"
onClick={acceptNecessary}
>
<i className="fas fa-check"></i>
Accept Functional Only
</button>
</div>
</div>
</motion.div>
</motion.div>
</AnimatePresence>
);

View File

@@ -64,8 +64,8 @@ const defaultPreferences: CookiePreferences = {
const defaultConfig: CookieConsentConfig = {
version: '2.0',
companyName: 'Your Company Name',
privacyPolicyUrl: '/privacy-policy',
cookiePolicyUrl: '/cookie-policy',
privacyPolicyUrl: '/policy?type=privacy',
cookiePolicyUrl: '/policy?type=privacy',
dataControllerEmail: 'privacy@yourcompany.com',
retentionPeriod: 365, // 1 year
enableAuditLog: true,
@@ -137,7 +137,6 @@ export const CookieConsentProvider: React.FC<{
}
}
} catch (error) {
console.warn('Failed to load cookie preferences:', error);
}
// Show banner if no valid consent found
@@ -171,7 +170,6 @@ export const CookieConsentProvider: React.FC<{
}));
}
} catch (error) {
console.warn('Failed to save cookie preferences:', error);
}
};
@@ -271,7 +269,6 @@ export const CookieConsentProvider: React.FC<{
try {
localStorage.removeItem(CONSENT_STORAGE_KEY);
} catch (error) {
console.warn('Failed to reset cookie preferences:', error);
}
setState({
@@ -288,7 +285,6 @@ export const CookieConsentProvider: React.FC<{
try {
localStorage.removeItem(CONSENT_STORAGE_KEY);
} catch (error) {
console.warn('Failed to withdraw consent:', error);
}
const auditEntry = config.enableAuditLog ? createAuditLogEntry('consent_withdrawn', defaultPreferences) : null;
@@ -386,7 +382,6 @@ export const useFunctional = () => {
try {
localStorage.setItem(`user-preference-${key}`, JSON.stringify(value));
} catch (error) {
console.warn('Failed to save user preference:', error);
}
}
};
@@ -397,7 +392,6 @@ export const useFunctional = () => {
const saved = localStorage.getItem(`user-preference-${key}`);
return saved ? JSON.parse(saved) : defaultValue;
} catch (error) {
console.warn('Failed to load user preference:', error);
return defaultValue;
}
}
@@ -406,7 +400,6 @@ export const useFunctional = () => {
const rememberUserAction = (action: string, data?: any) => {
if (isEnabled) {
console.log('User Action Remembered:', action, data);
// Implement your user action tracking logic here
}
};

View File

@@ -26,7 +26,6 @@ export const useFunctional = () => {
try {
localStorage.setItem(`user-preference-${key}`, JSON.stringify(value));
} catch (error) {
console.warn('Failed to save user preference:', error);
}
}
};
@@ -37,7 +36,6 @@ export const useFunctional = () => {
const saved = localStorage.getItem(`user-preference-${key}`);
return saved ? JSON.parse(saved) : defaultValue;
} catch (error) {
console.warn('Failed to load user preference:', error);
return defaultValue;
}
}
@@ -46,7 +44,6 @@ export const useFunctional = () => {
const rememberUserAction = (action: string, data?: any) => {
if (canShow) {
console.log('User Action Remembered:', action, data);
// Implement your user action tracking logic here
}
};

View File

@@ -24,7 +24,6 @@ const Header = () => {
// Find the Services menu item and update its submenu with API data
const servicesIndex = baseNavigation.findIndex(item => item.title === "Services");
if (servicesIndex !== -1 && apiServices.length > 0) {
console.log('Replacing services with API data:', apiServices);
baseNavigation[servicesIndex] = {
...baseNavigation[servicesIndex],
submenu: apiServices.map(service => ({
@@ -37,8 +36,6 @@ const Header = () => {
updated_at: service.updated_at
}))
} as any;
} else {
console.log('Using static services data. API services:', apiServices.length, 'Services index:', servicesIndex);
}
return baseNavigation;