Add CSS animations for rotating stars in MainPageLayout: Implemented new animations for star graphics to enhance visual appeal. Updated SharedCarousel to support dynamic language changes and improved category fetching logic. Enhanced localization files with new text for indoor season prompts in multiple languages.
This commit is contained in:
@@ -23,6 +23,39 @@ const MainPageLayout = () => {
|
||||
const isAktionen = currentPath === "/aktionen";
|
||||
const isFiliale = currentPath === "/filiale";
|
||||
|
||||
// Add CSS animations for rotating stars
|
||||
React.useEffect(() => {
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@keyframes rotateClockwise {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes rotateCounterClockwise {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(-360deg); }
|
||||
}
|
||||
|
||||
.star-rotate-slow-cw {
|
||||
animation: rotateClockwise 60s linear infinite;
|
||||
}
|
||||
|
||||
.star-rotate-slow-ccw {
|
||||
animation: rotateCounterClockwise 45s linear infinite;
|
||||
}
|
||||
|
||||
.star-rotate-medium-cw {
|
||||
animation: rotateClockwise 30s linear infinite;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
return () => {
|
||||
document.head.removeChild(style);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Get navigation config based on current page
|
||||
const getNavigationConfig = () => {
|
||||
if (isHome) {
|
||||
@@ -359,6 +392,7 @@ const MainPageLayout = () => {
|
||||
viewBox="0 0 60 60"
|
||||
width="168"
|
||||
height="168"
|
||||
className="star-rotate-slow-cw"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '-9px',
|
||||
@@ -378,6 +412,7 @@ const MainPageLayout = () => {
|
||||
viewBox="0 0 60 60"
|
||||
width="159"
|
||||
height="159"
|
||||
className="star-rotate-slow-ccw"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '-4.5px',
|
||||
@@ -397,6 +432,7 @@ const MainPageLayout = () => {
|
||||
viewBox="0 0 60 60"
|
||||
width="150"
|
||||
height="150"
|
||||
className="star-rotate-medium-cw"
|
||||
>
|
||||
<polygon
|
||||
points="30,0 38,20 60,22 43,37 48,60 30,48 12,60 17,37 0,22 22,20"
|
||||
@@ -451,6 +487,96 @@ const MainPageLayout = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Multi-pointed star for stecklinge box - bottom right */}
|
||||
{index === 1 && pageType === "home" && (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: '-65px',
|
||||
right: '-65px',
|
||||
width: '180px',
|
||||
height: '180px',
|
||||
zIndex: 999,
|
||||
pointerEvents: 'auto',
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
>
|
||||
{/* Background star - slightly larger and rotated */}
|
||||
<svg
|
||||
viewBox="0 0 60 60"
|
||||
width="168"
|
||||
height="168"
|
||||
className="star-rotate-slow-ccw"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '-9px',
|
||||
left: '-9px',
|
||||
transform: 'rotate(20deg)'
|
||||
}}
|
||||
>
|
||||
<polygon
|
||||
points="30,0 38,20 60,22 43,37 48,60 30,48 12,60 17,37 0,22 22,20"
|
||||
fill="#5F9EA0"
|
||||
stroke="none"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
{/* Middle star - medium size with different rotation */}
|
||||
<svg
|
||||
viewBox="0 0 60 60"
|
||||
width="159"
|
||||
height="159"
|
||||
className="star-rotate-medium-cw"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '-4.5px',
|
||||
left: '-4.5px',
|
||||
transform: 'rotate(-25deg)'
|
||||
}}
|
||||
>
|
||||
<polygon
|
||||
points="30,0 38,20 60,22 43,37 48,60 30,48 12,60 17,37 0,22 22,20"
|
||||
fill="#7FCDCD"
|
||||
stroke="none"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
{/* Foreground star - main star with text */}
|
||||
<svg
|
||||
viewBox="0 0 60 60"
|
||||
width="150"
|
||||
height="150"
|
||||
className="star-rotate-slow-cw"
|
||||
>
|
||||
<polygon
|
||||
points="30,0 38,20 60,22 43,37 48,60 30,48 12,60 17,37 0,22 22,20"
|
||||
fill="#AFEEEE"
|
||||
stroke="none"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
{/* Text positioned in the center of the star */}
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '42%',
|
||||
left: '45%',
|
||||
transform: 'translate(-50%, -50%) rotate(10deg)',
|
||||
color: 'white',
|
||||
fontWeight: '900',
|
||||
fontSize: '20px',
|
||||
textShadow: '0px 3px 6px rgba(0,0,0,0.5)',
|
||||
zIndex: 1000,
|
||||
textAlign: 'center',
|
||||
lineHeight: '1.1',
|
||||
width: '135px'
|
||||
}}
|
||||
>
|
||||
{t('sections.indoorSeason')}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={`animated-border-card ${index === 0 ? 'seeds-card' : 'cutlings-card'}`}>
|
||||
<Paper
|
||||
component={Link}
|
||||
|
||||
@@ -38,28 +38,52 @@ const getProductCache = () => {
|
||||
};
|
||||
|
||||
// Initialize categories
|
||||
const initializeCategories = () => {
|
||||
const initializeCategories = (language = 'en') => {
|
||||
const productCache = getProductCache();
|
||||
|
||||
if (productCache && productCache[`categoryTree_209_${language}`]) {
|
||||
const cached = productCache[`categoryTree_209_${language}`];
|
||||
if (cached.categoryTree) {
|
||||
return processCategoryTree(cached.categoryTree);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to old cache format if language-specific cache doesn't exist
|
||||
if (productCache && productCache["categoryTree_209"]) {
|
||||
const cached = productCache["categoryTree_209"];
|
||||
if (cached.categoryTree) {
|
||||
return processCategoryTree(cached.categoryTree);
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
const SharedCarousel = () => {
|
||||
const { carouselRef, filteredCategories, setFilteredCategories, moveCarousel } = useCarousel();
|
||||
const context = useContext(SocketContext);
|
||||
const { t } = useTranslation();
|
||||
const { t, i18n } = useTranslation();
|
||||
const [rootCategories, setRootCategories] = useState([]);
|
||||
const [currentLanguage, setCurrentLanguage] = useState(i18n.language);
|
||||
|
||||
useEffect(() => {
|
||||
const initialCategories = initializeCategories();
|
||||
const initialCategories = initializeCategories(currentLanguage);
|
||||
setRootCategories(initialCategories);
|
||||
}, []);
|
||||
}, [currentLanguage]);
|
||||
|
||||
// Listen for language changes
|
||||
useEffect(() => {
|
||||
const handleLanguageChange = (lng) => {
|
||||
setCurrentLanguage(lng);
|
||||
// Clear categories to force refetch
|
||||
setRootCategories([]);
|
||||
};
|
||||
|
||||
i18n.on('languageChanged', handleLanguageChange);
|
||||
return () => {
|
||||
i18n.off('languageChanged', handleLanguageChange);
|
||||
};
|
||||
}, [i18n]);
|
||||
|
||||
useEffect(() => {
|
||||
// Only fetch from socket if we don't already have categories
|
||||
@@ -68,12 +92,30 @@ const SharedCarousel = () => {
|
||||
context && context.socket && context.socket.connected &&
|
||||
typeof window !== "undefined"
|
||||
) {
|
||||
context.socket.emit("categoryList", { categoryId: 209, language: 'en', requestTranslation: true }, (response) => {
|
||||
if (response && response.categoryTree) {
|
||||
// Store in cache
|
||||
context.socket.emit("categoryList", { categoryId: 209, language: currentLanguage, requestTranslation: true }, (response) => {
|
||||
if (response && response.success) {
|
||||
// Use translated data if available, otherwise fall back to original
|
||||
const categoryTreeToUse = response.translation || response.categoryTree;
|
||||
|
||||
if (categoryTreeToUse) {
|
||||
// Store in cache with language-specific key
|
||||
try {
|
||||
if (!window.productCache) window.productCache = {};
|
||||
window.productCache[`categoryTree_209_${currentLanguage}`] = {
|
||||
categoryTree: categoryTreeToUse,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
setRootCategories(categoryTreeToUse.children || []);
|
||||
}
|
||||
} else if (response && response.categoryTree) {
|
||||
// Fallback for old response format
|
||||
// Store in cache with language-specific key
|
||||
try {
|
||||
if (!window.productCache) window.productCache = {};
|
||||
window.productCache["categoryTree_209"] = {
|
||||
window.productCache[`categoryTree_209_${currentLanguage}`] = {
|
||||
categoryTree: response.categoryTree,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
@@ -84,7 +126,7 @@ const SharedCarousel = () => {
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [context, context?.socket?.connected, rootCategories.length]);
|
||||
}, [context, context?.socket?.connected, rootCategories.length, currentLanguage]);
|
||||
|
||||
useEffect(() => {
|
||||
const filtered = rootCategories.filter(
|
||||
|
||||
@@ -350,7 +350,6 @@ i18n
|
||||
.init({
|
||||
resources,
|
||||
fallbackLng: 'de', // German as fallback since it's your primary language
|
||||
lng: 'de', // Default language
|
||||
debug: process.env.NODE_ENV === 'development',
|
||||
|
||||
// Language detection options
|
||||
|
||||
@@ -6,5 +6,6 @@ export default {
|
||||
"address1": "Trachenberger Straße 14",
|
||||
"address2": "01129 Dresden",
|
||||
"showUsPhoto": "ورينا أجمل صورة عندك",
|
||||
"selectSeedRate": "اختار البذرة واضغط للتقييم"
|
||||
"selectSeedRate": "اختار البذرة واضغط تقييم",
|
||||
"indoorSeason": "موسم الزراعة الداخلية بدأ"
|
||||
};
|
||||
|
||||
@@ -6,5 +6,6 @@ export default {
|
||||
"address1": "Trachenberger Straße 14",
|
||||
"address2": "01129 Dresden",
|
||||
"showUsPhoto": "Покажи ни най-красивата си снимка",
|
||||
"selectSeedRate": "Избери семе, кликни за оценка"
|
||||
"selectSeedRate": "Избери семе, кликни за оценка",
|
||||
"indoorSeason": "Вътрешният сезон започва"
|
||||
};
|
||||
|
||||
@@ -6,5 +6,6 @@ export default {
|
||||
"address1": "Trachenberger Straße 14",
|
||||
"address2": "01129 Dresden",
|
||||
"showUsPhoto": "Ukaž nám svou nejkrásnější fotku",
|
||||
"selectSeedRate": "Vyber semeno, klikni pro hodnocení"
|
||||
"selectSeedRate": "Vyber semeno, klikni na hodnocení",
|
||||
"indoorSeason": "Začíná indoor sezóna"
|
||||
};
|
||||
|
||||
@@ -6,5 +6,6 @@ export default {
|
||||
"address1": "Trachenberger Straße 14",
|
||||
"address2": "01129 Dresden",
|
||||
"showUsPhoto": "Zeig uns dein schönstes Foto",
|
||||
"selectSeedRate": "Wähle Seed aus, klicke Bewerten"
|
||||
"selectSeedRate": "Wähle Seed aus, klicke Bewerten",
|
||||
"indoorSeason": "Die Indoorsaison beginnt"
|
||||
};
|
||||
@@ -5,6 +5,7 @@ export default {
|
||||
"thcTest": "Τεστ THC",
|
||||
"address1": "Trachenberger Straße 14",
|
||||
"address2": "01129 Dresden",
|
||||
"showUsPhoto": "Δείξε μας τη πιο όμορφη φωτογραφία σου",
|
||||
"selectSeedRate": "Επίλεξε σπόρο, κάνε κλικ για αξιολόγηση"
|
||||
"showUsPhoto": "Δείξε μας την πιο όμορφη φωτογραφία σου",
|
||||
"selectSeedRate": "Επίλεξε σπόρο, κάνε κλικ για αξιολόγηση",
|
||||
"indoorSeason": "Η εσωτερική σεζόν ξεκινά"
|
||||
};
|
||||
|
||||
@@ -6,5 +6,6 @@ export default {
|
||||
"address1": "Trachenberger Straße 14", // Trachenberger Straße 14
|
||||
"address2": "01129 Dresden", // 01129 Dresden
|
||||
"showUsPhoto": "Show us your most beautiful photo", // Zeig uns dein schönstes Foto
|
||||
"selectSeedRate": "Select seed, click to rate" // Wähle Seed aus, klicke Bewerten
|
||||
"selectSeedRate": "Select seed, click rate", // Wähle Seed aus, klicke Bewerten
|
||||
"indoorSeason": "The indoor season begins" // Die Indoorsaison beginnt
|
||||
};
|
||||
|
||||
@@ -6,5 +6,6 @@ export default {
|
||||
"address1": "Trachenberger Straße 14",
|
||||
"address2": "01129 Dresden",
|
||||
"showUsPhoto": "Muéstranos tu foto más hermosa",
|
||||
"selectSeedRate": "Selecciona semilla, haz clic para calificar"
|
||||
"selectSeedRate": "Selecciona semilla, haz clic para valorar",
|
||||
"indoorSeason": "Comienza la temporada de interior"
|
||||
};
|
||||
|
||||
@@ -6,5 +6,6 @@ export default {
|
||||
"address1": "Trachenberger Straße 14",
|
||||
"address2": "01129 Dresden",
|
||||
"showUsPhoto": "Montre-nous ta plus belle photo",
|
||||
"selectSeedRate": "Sélectionne une graine, clique pour évaluer"
|
||||
"selectSeedRate": "Sélectionne une graine, clique pour noter",
|
||||
"indoorSeason": "La saison en intérieur commence"
|
||||
};
|
||||
|
||||
@@ -6,5 +6,6 @@ export default {
|
||||
"address1": "Trachenberger Straße 14",
|
||||
"address2": "01129 Dresden",
|
||||
"showUsPhoto": "Pokaži nam svoju najljepšu fotografiju",
|
||||
"selectSeedRate": "Odaberi sjeme, klikni za ocjenu"
|
||||
"selectSeedRate": "Odaberi sjeme, klikni ocjenu",
|
||||
"indoorSeason": "Počinje sezona uzgoja u zatvorenom"
|
||||
};
|
||||
|
||||
@@ -6,5 +6,6 @@ export default {
|
||||
"address1": "Trachenberger Straße 14",
|
||||
"address2": "01129 Dresden",
|
||||
"showUsPhoto": "Mutasd meg nekünk a legszebb fotódat",
|
||||
"selectSeedRate": "Válassz magot, kattints az értékeléshez"
|
||||
"selectSeedRate": "Válassz magot, kattints az értékelésre",
|
||||
"indoorSeason": "Kezdődik a beltéri szezon"
|
||||
};
|
||||
|
||||
@@ -6,5 +6,6 @@ export default {
|
||||
"address1": "Trachenberger Straße 14",
|
||||
"address2": "01129 Dresden",
|
||||
"showUsPhoto": "Mostraci la tua foto più bella",
|
||||
"selectSeedRate": "Seleziona il seme, clicca per valutare"
|
||||
"selectSeedRate": "Seleziona il seme, clicca per valutare",
|
||||
"indoorSeason": "Inizia la stagione indoor"
|
||||
};
|
||||
|
||||
@@ -6,5 +6,6 @@ export default {
|
||||
"address1": "Trachenberger Straße 14",
|
||||
"address2": "01129 Dresden",
|
||||
"showUsPhoto": "Pokaż nam swoje najpiękniejsze zdjęcie",
|
||||
"selectSeedRate": "Wybierz nasiono, kliknij, aby ocenić"
|
||||
"selectSeedRate": "Wybierz nasiono, kliknij ocenę",
|
||||
"indoorSeason": "Sezon indoor się zaczyna"
|
||||
};
|
||||
|
||||
@@ -6,5 +6,6 @@ export default {
|
||||
"address1": "Trachenberger Straße 14",
|
||||
"address2": "01129 Dresden",
|
||||
"showUsPhoto": "Arată-ne cea mai frumoasă fotografie a ta",
|
||||
"selectSeedRate": "Selectează sămânța, fă clic pentru a evalua"
|
||||
"selectSeedRate": "Selectează sămânța, apasă pentru evaluare",
|
||||
"indoorSeason": "Sezonul indoor începe"
|
||||
};
|
||||
|
||||
@@ -6,5 +6,6 @@ export default {
|
||||
"address1": "Trachenberger Straße 14",
|
||||
"address2": "01129 Dresden",
|
||||
"showUsPhoto": "Покажи нам свою самую красивую фотографию",
|
||||
"selectSeedRate": "Выберите семя, нажмите для оценки"
|
||||
"selectSeedRate": "Выберите семя, нажмите оценить",
|
||||
"indoorSeason": "Начинается сезон для выращивания в помещении"
|
||||
};
|
||||
|
||||
@@ -6,5 +6,6 @@ export default {
|
||||
"address1": "Trachenberger Straße 14",
|
||||
"address2": "01129 Dresden",
|
||||
"showUsPhoto": "Ukážte nám svoju najkrajšiu fotku",
|
||||
"selectSeedRate": "Vyberte semienko, kliknite na hodnotenie"
|
||||
"selectSeedRate": "Vyberte semienko, kliknite na hodnotenie",
|
||||
"indoorSeason": "Začína sezóna pestovania v interiéri"
|
||||
};
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
export default {
|
||||
"seeds": "Semena",
|
||||
"stecklinge": "Rezalci",
|
||||
"oilPress": "Izposodi si stiskalnico olja",
|
||||
"oilPress": "Izposodi si stiskalnico za olje",
|
||||
"thcTest": "THC test",
|
||||
"address1": "Trachenberger Straße 14",
|
||||
"address2": "01129 Dresden",
|
||||
"showUsPhoto": "Pokaži nam svojo najlepšo fotografijo",
|
||||
"selectSeedRate": "Izberi seme, klikni za oceno"
|
||||
"selectSeedRate": "Izberi seme, klikni oceno",
|
||||
"indoorSeason": "Začne se sezona gojenja v zaprtih prostorih"
|
||||
};
|
||||
|
||||
@@ -6,5 +6,6 @@ export default {
|
||||
"address1": "Trachenberger Straße 14",
|
||||
"address2": "01129 Dresden",
|
||||
"showUsPhoto": "Pokaži nam svoju najlepšu fotografiju",
|
||||
"selectSeedRate": "Izaberi seme, klikni da oceniš"
|
||||
"selectSeedRate": "Izaberi seme, klikni oceni",
|
||||
"indoorSeason": "Počinje sezona za uzgoj u zatvorenom"
|
||||
};
|
||||
|
||||
@@ -6,5 +6,6 @@ export default {
|
||||
"address1": "Trachenberger Straße 14",
|
||||
"address2": "01129 Dresden",
|
||||
"showUsPhoto": "Visa oss ditt vackraste foto",
|
||||
"selectSeedRate": "Välj frö, klicka för att betygsätta"
|
||||
"selectSeedRate": "Välj frö, klicka betyg",
|
||||
"indoorSeason": "Inomhussäsongen börjar"
|
||||
};
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
export default {
|
||||
"seeds": "Tohumlar",
|
||||
"stecklinge": "Çelikler",
|
||||
"oilPress": "Yağ presi ödünç al",
|
||||
"oilPress": "Yağ presini ödünç al",
|
||||
"thcTest": "THC testi",
|
||||
"address1": "Trachenberger Straße 14",
|
||||
"address2": "01129 Dresden",
|
||||
"showUsPhoto": "Bize en güzel fotoğrafını göster",
|
||||
"selectSeedRate": "Tohumu seç, değerlendirmek için tıkla"
|
||||
"showUsPhoto": "En güzel fotoğrafını göster",
|
||||
"selectSeedRate": "Tohumu seç, oy ver",
|
||||
"indoorSeason": "Kapalı sezon başlıyor"
|
||||
};
|
||||
|
||||
@@ -6,5 +6,6 @@ export default {
|
||||
"address1": "Trachenberger Straße 14",
|
||||
"address2": "01129 Dresden",
|
||||
"showUsPhoto": "Покажи нам своє найкрасивіше фото",
|
||||
"selectSeedRate": "Виберіть насіння, натисніть, щоб оцінити"
|
||||
"selectSeedRate": "Виберіть насіння, натисніть оцінити",
|
||||
"indoorSeason": "Починається сезон для вирощування в приміщенні"
|
||||
};
|
||||
|
||||
@@ -6,5 +6,6 @@ export default {
|
||||
"address1": "Trachenberger Straße 14",
|
||||
"address2": "01129 Dresden",
|
||||
"showUsPhoto": "展示你最美的照片",
|
||||
"selectSeedRate": "选择种子,点击评分"
|
||||
"selectSeedRate": "选择种子,点击评分",
|
||||
"indoorSeason": "室内季节开始了"
|
||||
};
|
||||
|
||||
@@ -28,14 +28,15 @@ export class LanguageProvider extends Component {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// Listen for language changes from i18n
|
||||
if (this.props.i18n) {
|
||||
this.props.i18n.on('languageChanged', this.handleLanguageChanged);
|
||||
// Set initial language state and HTML attribute
|
||||
this.setState({ currentLanguage: this.props.i18n.language });
|
||||
document.documentElement.lang = this.props.i18n.language;
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// Clean up listener
|
||||
if (this.props.i18n) {
|
||||
this.props.i18n.off('languageChanged', this.handleLanguageChanged);
|
||||
}
|
||||
@@ -47,6 +48,13 @@ export class LanguageProvider extends Component {
|
||||
// Update HTML lang attribute for SEO
|
||||
document.documentElement.lang = lng;
|
||||
|
||||
// Ensure language is saved to localStorage
|
||||
try {
|
||||
localStorage.setItem('i18nextLng', lng);
|
||||
} catch (error) {
|
||||
console.warn('Could not save language to localStorage:', error);
|
||||
}
|
||||
|
||||
// Update config if available
|
||||
if (window.shopConfig) {
|
||||
// Language code mapping for all supported languages
|
||||
|
||||
Reference in New Issue
Block a user