/* ProductGrid — grid + elevated in-place product focus (no modal) */ const ProductGrid = ({ lang, categoryId, onSelect, onBack, selectedProduct, onClearProduct, onVideoChange }) => { const [showVideo, setShowVideo] = React.useState(null); // null | 'usage' | 'assembly' const [phase, setPhase] = React.useState(selectedProduct ? 'focus' : 'grid'); // 'grid' | 'fading' | 'focus' const [currentSlide, setCurrentSlide] = React.useState(1); // Pagination state React.useEffect(() => { if (onVideoChange) onVideoChange(!!showVideo); }, [showVideo]); // Reset slide when category changes React.useEffect(() => { setCurrentSlide(1); }, [categoryId]); const cat = window.CATALOG; const category = cat.categories[categoryId]; const allProducts = cat.getProductsByCategory(categoryId) || []; // Group products by slide and get current slide's products const productsBySlide = {}; allProducts.forEach(p => { const slideNum = p.slide || 1; if (!productsBySlide[slideNum]) productsBySlide[slideNum] = []; productsBySlide[slideNum].push(p); }); const slideNumbers = Object.keys(productsBySlide).map(Number).sort((a, b) => a - b); const totalSlides = slideNumbers.length; const products = productsBySlide[currentSlide] || []; const isPlaceholder = !!(category && category.placeholder) || allProducts.length === 0; const categoryName = lang === 'el' ? category.label_el : category.label_en; // Drive a brief "fading" phase so cards smoothly disappear before the focus view enters. // Use a ref-backed timer so the cleanup tied to dep changes doesn't cancel our own scheduled flip. const phaseTimerRef = React.useRef(null); React.useEffect(() => { if (phaseTimerRef.current) { clearTimeout(phaseTimerRef.current); phaseTimerRef.current = null; } if (selectedProduct) { setPhase('fading'); phaseTimerRef.current = setTimeout(() => { setPhase('focus'); phaseTimerRef.current = null; }, 280); } else { setPhase('grid'); setShowVideo(null); } return () => { if (phaseTimerRef.current) { clearTimeout(phaseTimerRef.current); phaseTimerRef.current = null; } }; }, [selectedProduct]); // Reset video sub-view when product changes React.useEffect(() => { setShowVideo(null); }, [selectedProduct ? selectedProduct.id : null]); // Distribute images evenly: divide container into N equal slots, center each image in its slot const getImageStyle = (imgIdx, totalImages) => { const xPercent = ((imgIdx + 0.5) / totalImages) * 100; const maxW = `${Math.round(86 / totalImages)}%`; return { left: `${xPercent}%`, transform: 'translate(-50%, -50%)', maxWidth: maxW, opacity: 1, }; }; /* ---------- GRID VIEW (no product selected, or fading out) ---------- */ if (!selectedProduct || phase === 'fading') { return ( {categoryName} {isPlaceholder ? ( {t('no_products', lang)} {t('placeholder_text', lang)} ) : ( <> {products.map((p, idx) => { const pname = lang === 'el' ? (p.name_el || p.code) : (p.name_en || p.code); const isTarget = phase === 'fading' && selectedProduct && p.id === selectedProduct.id; const images = p.images || [p.hero]; return ( { e.stopPropagation(); if (phase === 'grid') onSelect(p.id); }} style={{ '--card-i': idx }} > {/* Stack all images in clockwise pattern for depth effect */} {images.map((img, imgIdx) => ( ))} {p.code} {pname} ); })} {/* Pagination controls */} {totalSlides > 1 && ( {slideNumbers.map(slideNum => ( setCurrentSlide(slideNum)} > {slideNum} ))} )} > )} ); } /* ---------- FOCUS VIEW (product selected) ---------- */ const product = selectedProduct; const productName = lang === 'el' ? (product.name_el || product.code) : (product.name_en || product.code); const catInfo = window.CATALOG.categories[product.category]; const catName = lang === 'el' ? catInfo.label_el : catInfo.label_en; const isAccessory = catInfo.folder === 'accessories'; const isPlaceholderProduct = !!product.placeholder; // Group specs by section const grouped = {}; product.specs.forEach(s => { const sec = s.section || '—'; if (!grouped[sec]) grouped[sec] = []; grouped[sec].push(s); }); // Friendly section titles const sectionLabel = (sec) => { const map = { 'ΠΡΟΔΙΑΓΡΑΦΕΣ': lang === 'el' ? 'Προδιαγραφές' : 'Specifications', 'ΔΙΑΣΤΑΣΕΙΣ': lang === 'el' ? 'Διαστάσεις' : 'Dimensions', 'ΥΠΟΛΟΓΙΣΤΗΣ': lang === 'el' ? 'Υπολογιστής' : 'Computer', 'ΠΛΗΡΟΦΟΡΙΕΣ': lang === 'el' ? 'Πληροφορίες' : 'Information', }; return map[sec] || sec; }; // Forward-compatible per-spec translation: prefer label_el/label_en if present. const specLabel = (s) => lang === 'el' ? (s.label_el || s.label) : (s.label_en || s.label); const specValue = (s) => lang === 'el' ? (s.value_el || s.value) : (s.value_en || s.value); // Distribute spec sections to left/right columns, balancing by item count const sectionList = Object.keys(grouped).filter(s => s !== '—'); const leftSections = []; const rightSections = []; let leftWeight = 0; let rightWeight = 0; // Stable preferred sides so the layout is predictable across products const preferredLeft = new Set(['ΠΡΟΔΙΑΓΡΑΦΕΣ', 'ΠΛΗΡΟΦΟΡΙΕΣ']); const preferredRight = new Set(['ΔΙΑΣΤΑΣΕΙΣ', 'ΥΠΟΛΟΓΙΣΤΗΣ']); sectionList.forEach(sec => { const w = grouped[sec].length; if (preferredLeft.has(sec)) { leftSections.push(sec); leftWeight += w; } else if (preferredRight.has(sec)) { rightSections.push(sec); rightWeight += w; } else if (leftWeight <= rightWeight) { leftSections.push(sec); leftWeight += w; } else { rightSections.push(sec); rightWeight += w; } }); // Video availability const hasUsageVideo = ('video_usage' in product) || ('video_howto' in product); const hasAssemblyVideo = ('video_assembly' in product) && !isAccessory; /* ---------- VIDEO SUB-VIEW ---------- */ if (showVideo) { const videoUrl = showVideo === 'assembly' ? product.video_assembly : (product.video_usage || product.video_howto); const videoTitle = showVideo === 'assembly' ? (lang === 'el' ? 'Βίντεο Συναρμολόγησης' : 'Assembly Video') : (lang === 'el' ? 'Βίντεο Χρήσης' : 'How-To Video'); return ( {catName} {product.code} {videoTitle} setShowVideo(null)}> {lang === 'el' ? 'Επιστροφή' : 'Return'} {videoUrl ? ( {t('video_unsupported', lang)} ) : ( {lang === 'el' ? 'Το βίντεο θα προστεθεί σύντομα' : 'Video coming soon'} )} ); } /* ---------- MAIN FOCUS LAYOUT ---------- */ return ( {/* Top bar — title group on the left, action buttons on the right */} ENERGETICS • {catName} {productName} {t('code_label', lang)} {product.code} {hasUsageVideo && ( setShowVideo('usage')}> {lang === 'el' ? 'Βίντεο Χρήσης' : 'How to Use'} )} {hasAssemblyVideo && ( setShowVideo('assembly')}> {lang === 'el' ? 'Συναρμολόγηση' : 'Assembly'} )} {lang === 'el' ? 'Πίσω' : 'Back'} {/* Three-column main: specs L | product | specs R */} {/* Left specs */} {/* Center — product hero */} {product.hero_folded && product.hero_computer ? ( {product.hero_tag || product.code} {product.hero_folded_tag || (lang === 'el' ? 'Διπλωμένο' : 'Folded')} {product.hero_computer_tag || (lang === 'el' ? 'Υπολογιστής' : 'Computer')} ) : product.hero_folded ? ( {product.hero_tag || product.code} {product.hero_folded_tag || (lang === 'el' ? 'Διπλωμένο' : 'Folded')} ) : ( )} {/* Right specs */} ); }; window.ProductGrid = ProductGrid;
{t('placeholder_text', lang)}
{lang === 'el' ? 'Το βίντεο θα προστεθεί σύντομα' : 'Video coming soon'}