feat: Remove Telegraf link from footer and add ManufacturerCarousel to SharedCarousel component
This commit is contained in:
@@ -352,9 +352,6 @@ class Footer extends Component {
|
|||||||
<Typography variant="body2" sx={{ fontSize: { xs: '11px', md: '14px' }, lineHeight: 1.5 }}>
|
<Typography variant="body2" sx={{ fontSize: { xs: '11px', md: '14px' }, lineHeight: 1.5 }}>
|
||||||
© {new Date().getFullYear()} <StyledDomainLink href="https://growheads.de" target="_blank" rel="noopener noreferrer">GrowHeads.de</StyledDomainLink>
|
© {new Date().getFullYear()} <StyledDomainLink href="https://growheads.de" target="_blank" rel="noopener noreferrer">GrowHeads.de</StyledDomainLink>
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2" sx={{ fontSize: { xs: '9px', md: '9px' }, lineHeight: 1.5, mt: 1 }}>
|
|
||||||
<StyledDomainLink href="https://telegraf.growheads.de" target="_blank" rel="noreferrer">Telegraf - sicherer Chat mit unseren Mitarbeitern</StyledDomainLink>
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
189
src/components/ManufacturerCarousel.js
Normal file
189
src/components/ManufacturerCarousel.js
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import { withTranslation } from 'react-i18next';
|
||||||
|
import { withLanguage } from '../i18n/withTranslation.js';
|
||||||
|
|
||||||
|
const ITEM_WIDTH = 140 + 16; // 140px width + 16px gap
|
||||||
|
const AUTO_SCROLL_SPEED = 0.4;
|
||||||
|
|
||||||
|
class ManufacturerCarousel extends React.Component {
|
||||||
|
_isMounted = false;
|
||||||
|
originalItems = [];
|
||||||
|
animationFrame = null;
|
||||||
|
translateX = 0;
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
items: [], // [{ id, name, src }]
|
||||||
|
};
|
||||||
|
this.carouselTrackRef = React.createRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this._isMounted = true;
|
||||||
|
this.loadImages();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this._isMounted = false;
|
||||||
|
if (this.animationFrame) {
|
||||||
|
cancelAnimationFrame(this.animationFrame);
|
||||||
|
this.animationFrame = null;
|
||||||
|
}
|
||||||
|
// Revoke object URLs to avoid memory leaks
|
||||||
|
for (const item of this.originalItems) {
|
||||||
|
if (item.src) URL.revokeObjectURL(item.src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadImages = () => {
|
||||||
|
window.socketManager.emit('getHerstellerImages', {}, (res) => {
|
||||||
|
if (!this._isMounted || !res?.success || !res.manufacturers?.length) return;
|
||||||
|
|
||||||
|
const items = res.manufacturers
|
||||||
|
.filter(m => m.imageBuffer)
|
||||||
|
.map(m => {
|
||||||
|
const blob = new Blob([m.imageBuffer], { type: 'image/avif' });
|
||||||
|
return { id: m.id, name: m.name || '', src: URL.createObjectURL(blob) };
|
||||||
|
})
|
||||||
|
.sort(() => Math.random() - 0.5);
|
||||||
|
|
||||||
|
if (items.length === 0) return;
|
||||||
|
|
||||||
|
this.originalItems = items;
|
||||||
|
this.setState({ items: [...items, ...items] }, () => {
|
||||||
|
this.startAutoScroll();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
startAutoScroll = () => {
|
||||||
|
if (!this.animationFrame) {
|
||||||
|
this.animationFrame = requestAnimationFrame(this.tick);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
tick = () => {
|
||||||
|
if (!this._isMounted || this.originalItems.length === 0) return;
|
||||||
|
|
||||||
|
this.translateX -= AUTO_SCROLL_SPEED;
|
||||||
|
|
||||||
|
const maxScroll = ITEM_WIDTH * this.originalItems.length;
|
||||||
|
if (Math.abs(this.translateX) >= maxScroll) {
|
||||||
|
this.translateX = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.carouselTrackRef.current) {
|
||||||
|
this.carouselTrackRef.current.style.transform = `translateX(${this.translateX}px)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.animationFrame = requestAnimationFrame(this.tick);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { t } = this.props;
|
||||||
|
const { items } = this.state;
|
||||||
|
|
||||||
|
if (!items || items.length === 0) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{ mt: 4 }}>
|
||||||
|
<Typography
|
||||||
|
variant="h4"
|
||||||
|
component="div"
|
||||||
|
sx={{
|
||||||
|
fontFamily: 'SwashingtonCP',
|
||||||
|
textShadow: '3px 3px 10px rgba(0, 0, 0, 0.4)',
|
||||||
|
textAlign: 'center',
|
||||||
|
mb: 2,
|
||||||
|
color: 'primary.main',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('product.manufacturer')}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'relative',
|
||||||
|
overflow: 'hidden',
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: '1200px',
|
||||||
|
margin: '0 auto',
|
||||||
|
padding: '0 20px',
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Fade edges */}
|
||||||
|
<div style={{
|
||||||
|
position: 'absolute', top: 0, left: 0,
|
||||||
|
width: '60px', height: '100%',
|
||||||
|
background: 'linear-gradient(to right, #c8e6c9, transparent)',
|
||||||
|
zIndex: 2, pointerEvents: 'none',
|
||||||
|
}} />
|
||||||
|
<div style={{
|
||||||
|
position: 'absolute', top: 0, right: 0,
|
||||||
|
width: '60px', height: '100%',
|
||||||
|
background: 'linear-gradient(to left, #c8e6c9, transparent)',
|
||||||
|
zIndex: 2, pointerEvents: 'none',
|
||||||
|
}} />
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'relative',
|
||||||
|
overflow: 'hidden',
|
||||||
|
padding: '12px 0',
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: '1080px',
|
||||||
|
margin: '0 auto',
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
ref={this.carouselTrackRef}
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: '16px',
|
||||||
|
alignItems: 'center',
|
||||||
|
width: 'fit-content',
|
||||||
|
transform: 'translateX(0px)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{items.map((item, index) => (
|
||||||
|
<div
|
||||||
|
key={`${item.id}-${index}`}
|
||||||
|
style={{
|
||||||
|
flex: '0 0 140px',
|
||||||
|
width: '140px',
|
||||||
|
height: '140px',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
overflow: 'hidden',
|
||||||
|
userSelect: 'none',
|
||||||
|
pointerEvents: 'none',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={item.src}
|
||||||
|
alt={item.name}
|
||||||
|
draggable={false}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
objectFit: 'contain',
|
||||||
|
display: 'block',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withTranslation()(withLanguage(ManufacturerCarousel));
|
||||||
@@ -7,6 +7,7 @@ import ChevronLeft from "@mui/icons-material/ChevronLeft";
|
|||||||
import ChevronRight from "@mui/icons-material/ChevronRight";
|
import ChevronRight from "@mui/icons-material/ChevronRight";
|
||||||
import CategoryBox from "./CategoryBox.js";
|
import CategoryBox from "./CategoryBox.js";
|
||||||
import ProductCarousel from "./ProductCarousel.js";
|
import ProductCarousel from "./ProductCarousel.js";
|
||||||
|
import ManufacturerCarousel from "./ManufacturerCarousel.js";
|
||||||
import { withTranslation } from 'react-i18next';
|
import { withTranslation } from 'react-i18next';
|
||||||
import { withLanguage } from '../i18n/withTranslation.js';
|
import { withLanguage } from '../i18n/withTranslation.js';
|
||||||
|
|
||||||
@@ -419,6 +420,9 @@ class SharedCarousel extends React.Component {
|
|||||||
|
|
||||||
{/* Product Carousel for "neu" category */}
|
{/* Product Carousel for "neu" category */}
|
||||||
<ProductCarousel categoryId="neu" />
|
<ProductCarousel categoryId="neu" />
|
||||||
|
|
||||||
|
{/* Manufacturer logo carousel */}
|
||||||
|
<ManufacturerCarousel />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user