Revert "refactor: Update webpack configuration to improve git commit hash retrieval and enhance lazy loading of components for better performance"
This reverts commit 52c9888a6a.
This commit is contained in:
@@ -7,8 +7,6 @@ const generateHomepageMetaTags = (baseUrl, config) => {
|
|||||||
const canonicalUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
|
const canonicalUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<link rel="preload" as="image" href="/assets/images/konfigurator.avif" fetchpriority="high">
|
|
||||||
|
|
||||||
<!-- SEO Meta Tags -->
|
<!-- SEO Meta Tags -->
|
||||||
<meta name="description" content="${description}">
|
<meta name="description" content="${description}">
|
||||||
<meta name="keywords" content="${keywords}">
|
<meta name="keywords" content="${keywords}">
|
||||||
|
|||||||
@@ -32,10 +32,11 @@ import i18n from './i18n/index.js';
|
|||||||
|
|
||||||
import Header from "./components/Header.js";
|
import Header from "./components/Header.js";
|
||||||
import Footer from "./components/Footer.js";
|
import Footer from "./components/Footer.js";
|
||||||
const IdleMainPagesSlideshow = lazy(() => import(/* webpackChunkName: "idle-slideshow" */ "./components/IdleMainPagesSlideshow.js"));
|
import MainPageLayout from "./components/MainPageLayout.js";
|
||||||
const MainPageLayout = lazy(() => import(/* webpackChunkName: "main-page-layout" */ "./components/MainPageLayout.js"));
|
import IdleMainPagesSlideshow from "./components/IdleMainPagesSlideshow.js";
|
||||||
const Content = lazy(() => import(/* webpackChunkName: "content" */ "./components/Content.js"));
|
|
||||||
const ProductDetail = lazy(() => import(/* webpackChunkName: "product-detail" */ "./components/ProductDetail.js"));
|
import Content from "./components/Content.js";
|
||||||
|
import ProductDetail from "./components/ProductDetail.js";
|
||||||
|
|
||||||
// Lazy load rarely-accessed pages
|
// Lazy load rarely-accessed pages
|
||||||
const ProfilePage = lazy(() => import(/* webpackChunkName: "profile" */ "./pages/ProfilePage.js"));
|
const ProfilePage = lazy(() => import(/* webpackChunkName: "profile" */ "./pages/ProfilePage.js"));
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ import {
|
|||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import Footer from './components/Footer.js';
|
import Footer from './components/Footer.js';
|
||||||
import { Logo, CategoryList } from './components/header/index.js';
|
import { Logo, CategoryList } from './components/header/index.js';
|
||||||
import MainPageLayout from './components/MainPageLayout.js';
|
|
||||||
import { CarouselProvider } from './contexts/CarouselContext.js';
|
|
||||||
|
|
||||||
|
|
||||||
class PrerenderHome extends React.Component {
|
class PrerenderHome extends React.Component {
|
||||||
@@ -124,15 +122,6 @@ class PrerenderHome extends React.Component {
|
|||||||
),
|
),
|
||||||
React.createElement(CategoryList, { categoryId: 209, activeCategoryId: null })
|
React.createElement(CategoryList, { categoryId: 209, activeCategoryId: null })
|
||||||
),
|
),
|
||||||
React.createElement(
|
|
||||||
Box,
|
|
||||||
{ component: 'main', sx: { flexGrow: 1 } },
|
|
||||||
React.createElement(
|
|
||||||
CarouselProvider,
|
|
||||||
null,
|
|
||||||
React.createElement(MainPageLayout)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
React.createElement(Footer)
|
React.createElement(Footer)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { Link } from 'react-router-dom';
|
|||||||
import MuiLink from '@mui/material/Link';
|
import MuiLink from '@mui/material/Link';
|
||||||
import { alpha } from '@mui/material/styles';
|
import { alpha } from '@mui/material/styles';
|
||||||
import TelegramIcon from '@mui/icons-material/Telegram';
|
import TelegramIcon from '@mui/icons-material/Telegram';
|
||||||
import { isUserLoggedIn } from '../utils/authSession.js';
|
import { isUserLoggedIn } from './LoginComponent.js';
|
||||||
import { withTranslation } from '../i18n/withTranslation.js';
|
import { withTranslation } from '../i18n/withTranslation.js';
|
||||||
|
|
||||||
const TELEGRAM_ASSISTANT_URL = 'https://t.me/Growheads_de_Bot';
|
const TELEGRAM_ASSISTANT_URL = 'https://t.me/Growheads_de_Bot';
|
||||||
|
|||||||
@@ -6,37 +6,13 @@ import MenuItem from '@mui/material/MenuItem';
|
|||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import { withI18n } from '../i18n/withTranslation.js';
|
import { withI18n } from '../i18n/withTranslation.js';
|
||||||
|
|
||||||
const LANGUAGE_FLAG_EMOJIS = {
|
|
||||||
ar: '🇪🇬',
|
|
||||||
bg: '🇧🇬',
|
|
||||||
cs: '🇨🇿',
|
|
||||||
de: '🇩🇪',
|
|
||||||
el: '🇬🇷',
|
|
||||||
en: '🇺🇸',
|
|
||||||
es: '🇪🇸',
|
|
||||||
fr: '🇫🇷',
|
|
||||||
hr: '🇭🇷',
|
|
||||||
hu: '🇭🇺',
|
|
||||||
it: '🇮🇹',
|
|
||||||
pl: '🇵🇱',
|
|
||||||
ro: '🇷🇴',
|
|
||||||
ru: '🇷🇺',
|
|
||||||
sk: '🇸🇰',
|
|
||||||
sl: '🇸🇮',
|
|
||||||
sq: '🇦🇱',
|
|
||||||
sr: '🇷🇸',
|
|
||||||
sv: '🇸🇪',
|
|
||||||
tr: '🇹🇷',
|
|
||||||
uk: '🇺🇦',
|
|
||||||
zh: '🇨🇳'
|
|
||||||
};
|
|
||||||
|
|
||||||
class LanguageSwitcher extends Component {
|
class LanguageSwitcher extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
anchorEl: null
|
anchorEl: null,
|
||||||
|
loadedFlags: {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +36,72 @@ class LanguageSwitcher extends Component {
|
|||||||
this.handleClose();
|
this.handleClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Lazy load flag components
|
||||||
|
loadFlagComponent = async (lang) => {
|
||||||
|
if (this.state.loadedFlags[lang]) {
|
||||||
|
return this.state.loadedFlags[lang];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const flagMap = {
|
||||||
|
'ar': () => import('country-flag-icons/react/3x2').then(m => m.EG),
|
||||||
|
'bg': () => import('country-flag-icons/react/3x2').then(m => m.BG),
|
||||||
|
'cs': () => import('country-flag-icons/react/3x2').then(m => m.CZ),
|
||||||
|
'de': () => import('country-flag-icons/react/3x2').then(m => m.DE),
|
||||||
|
'el': () => import('country-flag-icons/react/3x2').then(m => m.GR),
|
||||||
|
'en': () => import('country-flag-icons/react/3x2').then(m => m.US),
|
||||||
|
'es': () => import('country-flag-icons/react/3x2').then(m => m.ES),
|
||||||
|
'fr': () => import('country-flag-icons/react/3x2').then(m => m.FR),
|
||||||
|
'hr': () => import('country-flag-icons/react/3x2').then(m => m.HR),
|
||||||
|
'hu': () => import('country-flag-icons/react/3x2').then(m => m.HU),
|
||||||
|
'it': () => import('country-flag-icons/react/3x2').then(m => m.IT),
|
||||||
|
'pl': () => import('country-flag-icons/react/3x2').then(m => m.PL),
|
||||||
|
'ro': () => import('country-flag-icons/react/3x2').then(m => m.RO),
|
||||||
|
'ru': () => import('country-flag-icons/react/3x2').then(m => m.RU),
|
||||||
|
'sk': () => import('country-flag-icons/react/3x2').then(m => m.SK),
|
||||||
|
'sl': () => import('country-flag-icons/react/3x2').then(m => m.SI),
|
||||||
|
'sq': () => import('country-flag-icons/react/3x2').then(m => m.AL),
|
||||||
|
'sr': () => import('country-flag-icons/react/3x2').then(m => m.RS),
|
||||||
|
'sv': () => import('country-flag-icons/react/3x2').then(m => m.SE),
|
||||||
|
'tr': () => import('country-flag-icons/react/3x2').then(m => m.TR),
|
||||||
|
'uk': () => import('country-flag-icons/react/3x2').then(m => m.UA),
|
||||||
|
'zh': () => import('country-flag-icons/react/3x2').then(m => m.CN)
|
||||||
|
};
|
||||||
|
|
||||||
|
const flagLoader = flagMap[lang];
|
||||||
|
if (flagLoader) {
|
||||||
|
const FlagComponent = await flagLoader();
|
||||||
|
this.setState(prevState => ({
|
||||||
|
loadedFlags: {
|
||||||
|
...prevState.loadedFlags,
|
||||||
|
[lang]: FlagComponent
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
return FlagComponent;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Failed to load flag for language: ${lang}`, error);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
getLanguageFlag = (lang) => {
|
getLanguageFlag = (lang) => {
|
||||||
|
const FlagComponent = this.state.loadedFlags[lang];
|
||||||
|
|
||||||
|
if (FlagComponent) {
|
||||||
|
return (
|
||||||
|
<FlagComponent
|
||||||
|
style={{
|
||||||
|
width: '20px',
|
||||||
|
height: '14px',
|
||||||
|
borderRadius: '2px',
|
||||||
|
border: '1px solid #ddd'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loading placeholder or fallback
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
component="span"
|
component="span"
|
||||||
@@ -70,17 +111,35 @@ class LanguageSwitcher extends Component {
|
|||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
minWidth: '20px',
|
minWidth: '20px',
|
||||||
height: '14px',
|
height: '14px',
|
||||||
fontSize: '14px',
|
backgroundColor: '#f5f5f5',
|
||||||
lineHeight: 1,
|
color: '#666',
|
||||||
|
fontSize: '8px',
|
||||||
|
fontWeight: 'bold',
|
||||||
borderRadius: '2px',
|
borderRadius: '2px',
|
||||||
overflow: 'hidden'
|
fontFamily: 'monospace',
|
||||||
|
border: '1px solid #ddd'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{LANGUAGE_FLAG_EMOJIS[lang] || this.getLanguageLabel(lang)}
|
{this.getLanguageLabel(lang)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Load flags when menu opens
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
const { anchorEl } = this.state;
|
||||||
|
const { languageContext } = this.props;
|
||||||
|
|
||||||
|
if (anchorEl && !prevState.anchorEl && languageContext) {
|
||||||
|
// Menu just opened, lazy load flags for all languages (not just available ones)
|
||||||
|
languageContext.allLanguages.forEach(lang => {
|
||||||
|
if (!this.state.loadedFlags[lang]) {
|
||||||
|
this.loadFlagComponent(lang);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getLanguageLabel = (lang) => {
|
getLanguageLabel = (lang) => {
|
||||||
const labels = {
|
const labels = {
|
||||||
'ar': 'EG',
|
'ar': 'EG',
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import { withRouter } from './withRouter.js';
|
|||||||
import GoogleLoginButton from './GoogleLoginButton.js';
|
import GoogleLoginButton from './GoogleLoginButton.js';
|
||||||
import CartSyncDialog from './CartSyncDialog.js';
|
import CartSyncDialog from './CartSyncDialog.js';
|
||||||
import { localAndArchiveServer, mergeCarts } from '../utils/cartUtils.js';
|
import { localAndArchiveServer, mergeCarts } from '../utils/cartUtils.js';
|
||||||
import { isUserLoggedIn } from '../utils/authSession.js';
|
|
||||||
import config from '../config.js';
|
import config from '../config.js';
|
||||||
import { withI18n } from '../i18n/withTranslation.js';
|
import { withI18n } from '../i18n/withTranslation.js';
|
||||||
import {
|
import {
|
||||||
@@ -54,6 +53,24 @@ const persistSessionAuth = (response) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Function to check if user is logged in
|
||||||
|
export const isUserLoggedIn = () => {
|
||||||
|
const storedUser = sessionStorage.getItem('user');
|
||||||
|
if (storedUser) {
|
||||||
|
try {
|
||||||
|
const parsedUser = JSON.parse(storedUser);
|
||||||
|
console.log('Parsed User:', parsedUser);
|
||||||
|
return { isLoggedIn: true, user: parsedUser, isAdmin: !!parsedUser.admin };
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error parsing user from sessionStorage:', error);
|
||||||
|
sessionStorage.removeItem('user');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log('isUserLoggedIn', false);
|
||||||
|
return { isLoggedIn: false, user: null, isAdmin: false };
|
||||||
|
};
|
||||||
|
|
||||||
// Hilfsfunktion zum Vergleich zweier Cart-Arrays
|
// Hilfsfunktion zum Vergleich zweier Cart-Arrays
|
||||||
function cartsAreIdentical(cartA, cartB) {
|
function cartsAreIdentical(cartA, cartB) {
|
||||||
console.log('Vergleiche Carts:', {cartA, cartB});
|
console.log('Vergleiche Carts:', {cartA, cartB});
|
||||||
|
|||||||
@@ -275,15 +275,7 @@ const ContentBox = ({ box, index, pageType, starHovered, setStarHovered, opacity
|
|||||||
>
|
>
|
||||||
<Box sx={{ height: "100%", bgcolor: box.bgcolor, position: "relative", display: "flex", alignItems: "center", justifyContent: "center" }}>
|
<Box sx={{ height: "100%", bgcolor: box.bgcolor, position: "relative", display: "flex", alignItems: "center", justifyContent: "center" }}>
|
||||||
{opacity === 1 && (
|
{opacity === 1 && (
|
||||||
<img
|
<img src={box.image} alt={box.title} style={{ maxWidth: "100%", maxHeight: "100%", objectFit: "contain", position: "absolute", top: "50%", left: "50%", transform: "translate(-50%, -50%)" }} />
|
||||||
src={box.image}
|
|
||||||
alt={box.title}
|
|
||||||
loading={pageType === "home" && index === 1 ? "eager" : undefined}
|
|
||||||
fetchPriority={pageType === "home" && index === 1 ? "high" : undefined}
|
|
||||||
width={pageType === "home" && index === 1 ? 768 : undefined}
|
|
||||||
height={pageType === "home" && index === 1 ? 650 : undefined}
|
|
||||||
style={{ maxWidth: "100%", maxHeight: "100%", objectFit: "contain", position: "absolute", top: "50%", left: "50%", transform: "translate(-50%, -50%)" }}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
<Box sx={{ position: "absolute", bottom: 0, left: 0, right: 0, bgcolor: "rgba(27, 94, 32, 0.8)", p: 1 }}>
|
<Box sx={{ position: "absolute", bottom: 0, left: 0, right: 0, bgcolor: "rgba(27, 94, 32, 0.8)", p: 1 }}>
|
||||||
<Typography sx={{ fontSize: "1.6rem", color: "white", fontFamily: "SwashingtonCP" }}>{box.title}</Typography>
|
<Typography sx={{ fontSize: "1.6rem", color: "white", fontFamily: "SwashingtonCP" }}>{box.title}</Typography>
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ const LoginComponent = lazy(() => import(/* webpackChunkName: "login" */ "../Log
|
|||||||
|
|
||||||
import CartDropdown from '../CartDropdown.js';
|
import CartDropdown from '../CartDropdown.js';
|
||||||
import LanguageSwitcher from '../LanguageSwitcher.js';
|
import LanguageSwitcher from '../LanguageSwitcher.js';
|
||||||
|
import { isUserLoggedIn } from '../LoginComponent.js';
|
||||||
import { withI18n } from '../../i18n/withTranslation.js';
|
import { withI18n } from '../../i18n/withTranslation.js';
|
||||||
import { isUserLoggedIn } from '../../utils/authSession.js';
|
|
||||||
|
|
||||||
function getBadgeNumber() {
|
function getBadgeNumber() {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { isUserLoggedIn } from "../../utils/authSession.js";
|
import { isUserLoggedIn } from "../LoginComponent.js";
|
||||||
|
|
||||||
class OrderProcessingService {
|
class OrderProcessingService {
|
||||||
constructor(getContext, setState) {
|
constructor(getContext, setState) {
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
export const isUserLoggedIn = () => {
|
|
||||||
const storedUser = sessionStorage.getItem('user');
|
|
||||||
if (storedUser) {
|
|
||||||
try {
|
|
||||||
const parsedUser = JSON.parse(storedUser);
|
|
||||||
console.log('Parsed User:', parsedUser);
|
|
||||||
return { isLoggedIn: true, user: parsedUser, isAdmin: !!parsedUser.admin };
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error parsing user from sessionStorage:', error);
|
|
||||||
sessionStorage.removeItem('user');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('isUserLoggedIn', false);
|
|
||||||
return { isLoggedIn: false, user: null, isAdmin: false };
|
|
||||||
};
|
|
||||||
@@ -5,31 +5,17 @@ import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
|
|||||||
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
||||||
import ESLintPlugin from 'eslint-webpack-plugin';
|
import ESLintPlugin from 'eslint-webpack-plugin';
|
||||||
import { cpSync } from 'fs';
|
import { cpSync } from 'fs';
|
||||||
import { execFileSync } from 'child_process';
|
import { execSync } from 'child_process';
|
||||||
import webpack from 'webpack';
|
import webpack from 'webpack';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
|
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
|
||||||
|
|
||||||
// Git hash for meta tag / currentHash.json — avoid execSync (spawns /bin/sh; fails with EPERM in some sandboxes/CI)
|
// Get git commit hash
|
||||||
const getGitCommitHash = () => {
|
const getGitCommitHash = () => {
|
||||||
const fromEnv =
|
|
||||||
process.env.GIT_COMMIT ||
|
|
||||||
process.env.VERCEL_GIT_COMMIT_SHA ||
|
|
||||||
process.env.CI_COMMIT_SHA ||
|
|
||||||
process.env.GITHUB_SHA ||
|
|
||||||
'';
|
|
||||||
if (fromEnv) return String(fromEnv).trim();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return execFileSync('git', ['rev-parse', 'HEAD'], {
|
return execSync('git rev-parse HEAD').toString().trim();
|
||||||
encoding: 'utf8',
|
|
||||||
maxBuffer: 1024 * 1024,
|
|
||||||
}).trim();
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(
|
console.error('Failed to get git commit hash:', e);
|
||||||
'Git commit hash unavailable (set GIT_COMMIT or run build in a git repo):',
|
|
||||||
e && e.message ? e.message : e
|
|
||||||
);
|
|
||||||
return 'unknown';
|
return 'unknown';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -315,36 +301,9 @@ export default {
|
|||||||
priority: 20,
|
priority: 20,
|
||||||
reuseExistingChunk: true,
|
reuseExistingChunk: true,
|
||||||
},
|
},
|
||||||
// Keep Stripe checkout code out of the initial vendor bundle
|
|
||||||
stripe: {
|
|
||||||
test: /[\\/]node_modules[\\/]@stripe[\\/]/,
|
|
||||||
name: 'stripe',
|
|
||||||
priority: 19,
|
|
||||||
chunks: 'async',
|
|
||||||
reuseExistingChunk: true,
|
|
||||||
enforce: true,
|
|
||||||
},
|
|
||||||
// Lazy HTML parsing/sanitizing should stay async with the product detail/chat flows
|
|
||||||
htmlParser: {
|
|
||||||
test: /[\\/]node_modules[\\/](html-react-parser|sanitize-html|htmlparser2|domhandler|dom-serializer|entities|react-property|parse-srcset|postcss)[\\/]/,
|
|
||||||
name: 'html-parser-vendor',
|
|
||||||
priority: 18,
|
|
||||||
chunks: 'async',
|
|
||||||
reuseExistingChunk: true,
|
|
||||||
enforce: true,
|
|
||||||
},
|
|
||||||
// Girocode/QR code generation is only needed in checkout/profile flows
|
|
||||||
payments: {
|
|
||||||
test: /[\\/]node_modules[\\/](qrcode|sepa-payment-qr-code|iban)[\\/]/,
|
|
||||||
name: 'payments',
|
|
||||||
priority: 17,
|
|
||||||
chunks: 'async',
|
|
||||||
reuseExistingChunk: true,
|
|
||||||
enforce: true,
|
|
||||||
},
|
|
||||||
// socket.io-client and its dependencies — always async, never initial
|
// socket.io-client and its dependencies — always async, never initial
|
||||||
socketio: {
|
socketio: {
|
||||||
test: /[\\/]node_modules[\\/](socket\.io-client|engine\.io-client|engine\.io-parser|@socket\.io|socket\.io-parser|socket\.io-msgpack-parser)[\\/]/,
|
test: /[\\/]node_modules[\\/](socket\.io-client|engine\.io-client|@socket\.io|socket\.io-parser|socket\.io-msgpack-parser)[\\/]/,
|
||||||
name: 'socketio',
|
name: 'socketio',
|
||||||
priority: 15,
|
priority: 15,
|
||||||
chunks: 'async',
|
chunks: 'async',
|
||||||
|
|||||||
Reference in New Issue
Block a user