diff --git a/src/components/ChatAssistant.js b/src/components/ChatAssistant.js index 4a8b7c5..1edf3fa 100644 --- a/src/components/ChatAssistant.js +++ b/src/components/ChatAssistant.js @@ -52,7 +52,12 @@ class ChatAssistant extends Component { this.fileInputRef = React.createRef(); this.recordingTimer = null; } - + + buildPrivacyPromptHtml = () => { + const { t } = this.props; + return `${t('chat.privacyPromptBefore')}${t('chat.privacyPolicyLink')}${t('chat.privacyPromptAfter')}`; + }; + componentDidMount() { // Add socket listeners if socket is available and connected this.addSocketListeners(); @@ -69,7 +74,7 @@ class ChatAssistant extends Component { const privacyMessage = { id: 'privacy-prompt', sender: 'bot', - text: 'Bitte bestätigen Sie, dass Sie die Datenschutzbestimmungen gelesen haben und damit einverstanden sind. ', + text: this.buildPrivacyPromptHtml(), }; const updatedMessages = [privacyMessage, ...prevState.messages]; window.chatMessages = updatedMessages; @@ -84,6 +89,19 @@ class ChatAssistant extends Component { } componentDidUpdate(prevProps, prevState) { + if (prevProps.i18n?.language !== this.props.i18n?.language) { + this.setState((prev) => { + const idx = prev.messages.findIndex((m) => m.id === 'privacy-prompt'); + if (idx === -1) return null; + const updatedMessages = [...prev.messages]; + updatedMessages[idx] = { + ...updatedMessages[idx], + text: this.buildPrivacyPromptHtml(), + }; + window.chatMessages = updatedMessages; + return { messages: updatedMessages }; + }); + } if (prevState.messages !== this.state.messages || prevState.isTyping !== this.state.isTyping) { this.scrollToBottom(); } @@ -244,7 +262,7 @@ class ChatAssistant extends Component { }); } catch (err) { console.error("Error accessing microphone:", err); - alert("Could not access microphone. Please check your browser permissions."); + alert(this.props.t('chat.micPermissionDenied')); } }; @@ -359,7 +377,7 @@ class ChatAssistant extends Component { const newUserMessage = { id: Date.now(), sender: 'user', - text: `Uploaded image`, + text: `${this.props.t('chat.uploadedImageAlt')}`, isImage: true }; @@ -451,7 +469,7 @@ class ChatAssistant extends Component { } if (domNode.name === 'button' && domNode.attribs && domNode.attribs['data-confirm-privacy']) { - return ; + return ; } } }); @@ -505,12 +523,12 @@ class ChatAssistant extends Component { }} > - Assistent + {t('chat.assistantTitle')} 🧠 🛢 🌐 - + @@ -648,7 +666,7 @@ class ChatAssistant extends Component { autoFocus autoCapitalize="off" autoCorrect="off" - placeholder={isRecording ? "Aufnahme läuft..." : "Du kannst mich nach Cannabissorten fragen..."} + placeholder={isRecording ? t('chat.placeholderRecording') : t('chat.inputPlaceholder')} value={inputValue} onChange={this.handleInputChange} onKeyDown={this.handleKeyDown} @@ -670,7 +688,7 @@ class ChatAssistant extends Component { @@ -679,7 +697,7 @@ class ChatAssistant extends Component { @@ -690,7 +708,7 @@ class ChatAssistant extends Component { @@ -703,7 +721,7 @@ class ChatAssistant extends Component { onClick={this.handleSendMessage} disabled={isTyping || isRecording || inputsDisabled} > - Senden + {t('chat.send')} diff --git a/src/components/Product.js b/src/components/Product.js index 24beb6a..d0113cf 100644 --- a/src/components/Product.js +++ b/src/components/Product.js @@ -11,6 +11,11 @@ import { Link, useNavigate } from 'react-router-dom'; import { withI18n } from '../i18n/withTranslation.js'; import ZoomInIcon from '@mui/icons-material/ZoomIn'; import { STAR_POLYGON_POINTS } from '../utils/starPolygon.js'; +import { + PRODUCT_CARD_MOBILE_MAX_WIDTH_PX, + PRODUCT_CARD_WIDTH_SM_PX, + PRODUCT_CARD_WIDTH_XS_PX, +} from '../utils/productCardLayout.js'; // Helper function to find level 1 category ID from any category ID const findLevel1CategoryId = (categoryId) => { @@ -276,7 +281,16 @@ class Product extends Component { {isNew && (
{showThcBadge && ( @@ -460,7 +488,7 @@ class Product extends Component { { + if (!this._isMounted) return; + const next = getProductCarouselItemStridePx(); + if (next !== this.state.itemStride) { + this.translateX = 0; + this.updateTrackTransform(); + this.setState({ itemStride: next }); + } + }; + componentDidMount() { this._isMounted = true; + if (typeof window !== "undefined") { + window.addEventListener("resize", this.handleCarouselResize); + this.setState({ itemStride: getProductCarouselItemStridePx() }); + } const currentLanguage = this.props.languageContext?.currentLanguage || this.props.i18n.language; logCarousel("mount", { @@ -370,6 +391,9 @@ class ProductCarousel extends React.Component { componentWillUnmount() { this._isMounted = false; + if (typeof window !== "undefined") { + window.removeEventListener("resize", this.handleCarouselResize); + } this.stopAutoScroll(); this.clearInactivityTimer(); this.clearScrollbarTimer(); @@ -430,8 +454,9 @@ class ProductCarousel extends React.Component { this.translateX -= AUTO_SCROLL_SPEED; this.updateTrackTransform(); + const { itemStride } = this.state; const originalItemCount = this.originalProducts.length; - const maxScroll = ITEM_WIDTH * originalItemCount; + const maxScroll = itemStride * originalItemCount; // Check if we've scrolled past the first set of items if (Math.abs(this.translateX) >= maxScroll) { @@ -467,14 +492,15 @@ class ProductCarousel extends React.Component { if (this.originalProducts.length === 0) return; // direction: 1 = left (scroll content right), -1 = right (scroll content left) + const { itemStride } = this.state; const originalItemCount = this.originalProducts.length; - const maxScroll = ITEM_WIDTH * originalItemCount; + const maxScroll = itemStride * originalItemCount; - this.translateX += direction * ITEM_WIDTH; + this.translateX += direction * itemStride; // Handle wrap-around when scrolling left (positive translateX) if (this.translateX > 0) { - this.translateX = -(maxScroll - ITEM_WIDTH); + this.translateX = -(maxScroll - itemStride); } // Handle wrap-around when scrolling right (negative translateX beyond limit) else if (Math.abs(this.translateX) >= maxScroll) { @@ -494,9 +520,13 @@ class ProductCarousel extends React.Component { return null; } + const { itemStride } = this.state; const originalItemCount = this.originalProducts.length; - const viewportWidth = 1080; // carousel container max-width - const itemsInView = Math.floor(viewportWidth / ITEM_WIDTH); + const viewportWidth = + typeof window !== "undefined" + ? Math.min(1080, Math.max(0, window.innerWidth - 56)) + : 1080; + const itemsInView = Math.max(1, Math.floor(viewportWidth / itemStride)); // Calculate which item is currently at the left edge (first visible) let currentItemIndex; @@ -504,11 +534,11 @@ class ProductCarousel extends React.Component { if (this.translateX === 0) { currentItemIndex = 0; } else if (this.translateX > 0) { - const maxScroll = ITEM_WIDTH * originalItemCount; + const maxScroll = itemStride * originalItemCount; const effectivePosition = maxScroll + this.translateX; - currentItemIndex = Math.floor(effectivePosition / ITEM_WIDTH); + currentItemIndex = Math.floor(effectivePosition / itemStride); } else { - currentItemIndex = Math.floor(Math.abs(this.translateX) / ITEM_WIDTH); + currentItemIndex = Math.floor(Math.abs(this.translateX) / itemStride); } // Ensure we stay within bounds @@ -615,7 +645,7 @@ class ProductCarousel extends React.Component { top: '50%', left: '8px', transform: 'translateY(-50%)', - zIndex: 1200, + zIndex: 1000, backgroundColor: 'rgba(255, 255, 255, 0.9)', boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)', width: '48px', @@ -635,7 +665,7 @@ class ProductCarousel extends React.Component { top: '50%', right: '8px', transform: 'translateY(-50%)', - zIndex: 1200, + zIndex: 1000, backgroundColor: 'rgba(255, 255, 255, 0.9)', boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)', width: '48px', @@ -676,16 +706,19 @@ class ProductCarousel extends React.Component { }} > {products.map((product, index) => ( -
-
+ ))}
diff --git a/src/components/ProductList.js b/src/components/ProductList.js index 34594af..547bcaf 100644 --- a/src/components/ProductList.js +++ b/src/components/ProductList.js @@ -438,7 +438,11 @@ class ProductList extends Component {
- + {this.renderNoProductsMessage()} {products.map((product, index) => ( - {this.props.t ? this.props.t('navigation.home') : 'Startseite'} + {this.props.t ? this.props.t('sections.konfigurator') : 'Konfigurator'} {/* Thin text (positioned on top) */} - {this.props.t ? this.props.t('navigation.home') : 'Startseite'} + {this.props.t ? this.props.t('sections.konfigurator') : 'Konfigurator'} )} diff --git a/src/i18n/locales/ar/chat.js b/src/i18n/locales/ar/chat.js index c581dcb..ea11342 100644 --- a/src/i18n/locales/ar/chat.js +++ b/src/i18n/locales/ar/chat.js @@ -1,5 +1,18 @@ export default { - "privacyRead": "مقروء ومقبول", - "telegramAssistantIntro": "تقدر كمان توصل لمساعد Growheads على تيليجرام:", - "telegramAssistantLink": "t.me/Growheads_de_Bot" + "privacyRead": "قريت ووافقت", + "privacyPromptBefore": "من فضلك أكد إنك قرأت ", + "privacyPolicyLink": "سياسة الخصوصية", + "privacyPromptAfter": " ووافقت عليها. ", + "telegramAssistantIntro": "كمان تقدر تتواصل مع مساعد Growheads على Telegram:", + "telegramAssistantLink": "t.me/Growheads_de_Bot", + "assistantTitle": "المساعد", + "placeholderRecording": "جارٍ التسجيل…", + "inputPlaceholder": "تقدر تسألني عن سلالات القنب…", + "send": "إرسال", + "closeAria": "إغلاق المساعد", + "micStartAria": "ابدأ تسجيل الصوت", + "micStopAria": "إيقاف التسجيل", + "uploadImageAria": "رفع صورة", + "micPermissionDenied": "تعذر الوصول إلى الميكروفون. من فضلك راجع أذونات المتصفح.", + "uploadedImageAlt": "صورة مرفوعة" }; diff --git a/src/i18n/locales/bg/chat.js b/src/i18n/locales/bg/chat.js index d025f6c..152e208 100644 --- a/src/i18n/locales/bg/chat.js +++ b/src/i18n/locales/bg/chat.js @@ -1,5 +1,18 @@ export default { "privacyRead": "Прочетено и прието", + "privacyPromptBefore": "Моля, потвърдете, че сте прочели ", + "privacyPolicyLink": "политиката за поверителност", + "privacyPromptAfter": " и сте съгласни с нея. ", "telegramAssistantIntro": "Можете също да се свържете с асистента на Growheads в Telegram:", - "telegramAssistantLink": "t.me/Growheads_de_Bot" + "telegramAssistantLink": "t.me/Growheads_de_Bot", + "assistantTitle": "Асистент", + "placeholderRecording": "Записване…", + "inputPlaceholder": "Можете да ме питате за сортове канабис…", + "send": "Изпрати", + "closeAria": "Затвори асистента", + "micStartAria": "Стартиране на гласов запис", + "micStopAria": "Спиране на записа", + "uploadImageAria": "Качване на изображение", + "micPermissionDenied": "Не беше възможен достъп до микрофона. Моля, проверете разрешенията на браузъра си.", + "uploadedImageAlt": "Качено изображение" }; diff --git a/src/i18n/locales/cs/chat.js b/src/i18n/locales/cs/chat.js index eafb2e5..0a0debf 100644 --- a/src/i18n/locales/cs/chat.js +++ b/src/i18n/locales/cs/chat.js @@ -1,5 +1,18 @@ export default { "privacyRead": "Přečteno a přijato", + "privacyPromptBefore": "Prosím potvrďte, že jste si přečetli ", + "privacyPolicyLink": "zásady ochrany osobních údajů", + "privacyPromptAfter": " a souhlasíte s nimi. ", "telegramAssistantIntro": "Asistenta Growheads můžete také kontaktovat na Telegramu:", - "telegramAssistantLink": "t.me/Growheads_de_Bot" + "telegramAssistantLink": "t.me/Growheads_de_Bot", + "assistantTitle": "Asistent", + "placeholderRecording": "Nahrávání…", + "inputPlaceholder": "Můžete se mě zeptat na odrůdy konopí…", + "send": "Odeslat", + "closeAria": "Zavřít asistenta", + "micStartAria": "Spustit nahrávání hlasu", + "micStopAria": "Zastavit nahrávání", + "uploadImageAria": "Nahrát obrázek", + "micPermissionDenied": "Nepodařilo se získat přístup k mikrofonu. Zkontrolujte prosím oprávnění ve svém prohlížeči.", + "uploadedImageAlt": "Nahraný obrázek" }; diff --git a/src/i18n/locales/de/chat.js b/src/i18n/locales/de/chat.js index d61154c..3c4ad83 100644 --- a/src/i18n/locales/de/chat.js +++ b/src/i18n/locales/de/chat.js @@ -1,5 +1,18 @@ export default { "privacyRead": "Gelesen & Akzeptiert", + "privacyPromptBefore": "Bitte bestätigen Sie, dass Sie die ", + "privacyPolicyLink": "Datenschutzbestimmungen", + "privacyPromptAfter": " gelesen haben und damit einverstanden sind. ", "telegramAssistantIntro": "Du kannst den Growheads Assistenten auch per Telegram erreichen:", - "telegramAssistantLink": "t.me/Growheads_de_Bot" + "telegramAssistantLink": "t.me/Growheads_de_Bot", + "assistantTitle": "Assistent", + "placeholderRecording": "Aufnahme läuft...", + "inputPlaceholder": "Du kannst mich nach Cannabissorten fragen...", + "send": "Senden", + "closeAria": "Assistent schließen", + "micStartAria": "Sprachaufnahme starten", + "micStopAria": "Aufnahme stoppen", + "uploadImageAria": "Bild hochladen", + "micPermissionDenied": "Mikrofon-Zugriff nicht möglich. Bitte prüfen Sie die Browser-Berechtigungen.", + "uploadedImageAlt": "Hochgeladenes Bild" }; \ No newline at end of file diff --git a/src/i18n/locales/de/navigation.js b/src/i18n/locales/de/navigation.js index 71d87d4..519a8af 100644 --- a/src/i18n/locales/de/navigation.js +++ b/src/i18n/locales/de/navigation.js @@ -1,5 +1,6 @@ export default { "home": "Startseite", + "konfiguratorAria": "Zum Konfigurator", "new": "Neuheiten", "soon": "Demnächst", "aktionen": "Aktionen", diff --git a/src/i18n/locales/el/chat.js b/src/i18n/locales/el/chat.js index 4270629..b662050 100644 --- a/src/i18n/locales/el/chat.js +++ b/src/i18n/locales/el/chat.js @@ -1,5 +1,18 @@ export default { - "privacyRead": "Διαβάστηκε & Έγινε αποδεκτό", - "telegramAssistantIntro": "Μπορείτε επίσης να επικοινωνήσετε με τον βοηθό Growheads στο Telegram:", - "telegramAssistantLink": "t.me/Growheads_de_Bot" + "privacyRead": "Διαβάστηκε & Εγκρίθηκε", + "privacyPromptBefore": "Παρακαλώ επιβεβαιώστε ότι έχετε διαβάσει την ", + "privacyPolicyLink": "πολιτική απορρήτου", + "privacyPromptAfter": " και ότι συμφωνείτε με αυτήν. ", + "telegramAssistantIntro": "Μπορείτε επίσης να επικοινωνήσετε με τον βοηθό του Growheads στο Telegram:", + "telegramAssistantLink": "t.me/Growheads_de_Bot", + "assistantTitle": "Βοηθός", + "placeholderRecording": "Γίνεται εγγραφή…", + "inputPlaceholder": "Μπορείτε να με ρωτήσετε για ποικιλίες cannabis…", + "send": "Αποστολή", + "closeAria": "Κλείσιμο βοηθού", + "micStartAria": "Έναρξη ηχογράφησης φωνής", + "micStopAria": "Διακοπή εγγραφής", + "uploadImageAria": "Μεταφόρτωση εικόνας", + "micPermissionDenied": "Δεν ήταν δυνατή η πρόσβαση στο μικρόφωνο. Παρακαλώ ελέγξτε τα δικαιώματα του browser σας.", + "uploadedImageAlt": "Ανεβασμένη εικόνα" }; diff --git a/src/i18n/locales/en/chat.js b/src/i18n/locales/en/chat.js index 194d49e..35f251e 100644 --- a/src/i18n/locales/en/chat.js +++ b/src/i18n/locales/en/chat.js @@ -1,5 +1,18 @@ export default { - "privacyRead": "Read & Accepted", // Gelesen & Akzeptiert + "privacyRead": "Read & Accepted", + "privacyPromptBefore": "Please confirm that you have read the ", + "privacyPolicyLink": "privacy policy", + "privacyPromptAfter": " and agree to it. ", "telegramAssistantIntro": "You can also reach the Growheads assistant on Telegram:", - "telegramAssistantLink": "t.me/Growheads_de_Bot" + "telegramAssistantLink": "t.me/Growheads_de_Bot", + "assistantTitle": "Assistant", + "placeholderRecording": "Recording…", + "inputPlaceholder": "You can ask me about cannabis strains…", + "send": "Send", + "closeAria": "Close assistant", + "micStartAria": "Start voice recording", + "micStopAria": "Stop recording", + "uploadImageAria": "Upload image", + "micPermissionDenied": "Could not access the microphone. Please check your browser permissions.", + "uploadedImageAlt": "Uploaded image" }; diff --git a/src/i18n/locales/en/navigation.js b/src/i18n/locales/en/navigation.js index c111387..da6ae23 100644 --- a/src/i18n/locales/en/navigation.js +++ b/src/i18n/locales/en/navigation.js @@ -1,5 +1,6 @@ export default { "home": "Home", // Startseite + "konfiguratorAria": "Go to Configurator", // Zum Konfigurator "new": "New Arrivals", // Neuheiten "soon": "Coming Soon", // Demnächst "aktionen": "Promotions", // Aktionen diff --git a/src/i18n/locales/es/chat.js b/src/i18n/locales/es/chat.js index 3784740..ce006f2 100644 --- a/src/i18n/locales/es/chat.js +++ b/src/i18n/locales/es/chat.js @@ -1,5 +1,18 @@ export default { "privacyRead": "Leído y aceptado", - "telegramAssistantIntro": "También puedes contactar al asistente de Growheads en Telegram:", - "telegramAssistantLink": "t.me/Growheads_de_Bot" + "privacyPromptBefore": "Por favor, confirma que has leído la ", + "privacyPolicyLink": "política de privacidad", + "privacyPromptAfter": " y que estás de acuerdo con ella. ", + "telegramAssistantIntro": "También puedes contactar con el asistente de Growheads en Telegram:", + "telegramAssistantLink": "t.me/Growheads_de_Bot", + "assistantTitle": "Asistente", + "placeholderRecording": "Grabando…", + "inputPlaceholder": "Puedes preguntarme sobre cepas de cannabis…", + "send": "Enviar", + "closeAria": "Cerrar asistente", + "micStartAria": "Iniciar grabación de voz", + "micStopAria": "Detener grabación", + "uploadImageAria": "Subir imagen", + "micPermissionDenied": "No se pudo acceder al micrófono. Por favor, revisa los permisos de tu navegador.", + "uploadedImageAlt": "Imagen subida" }; diff --git a/src/i18n/locales/fr/chat.js b/src/i18n/locales/fr/chat.js index 9b0a3cf..815f85a 100644 --- a/src/i18n/locales/fr/chat.js +++ b/src/i18n/locales/fr/chat.js @@ -1,5 +1,18 @@ export default { "privacyRead": "Lu et accepté", - "telegramAssistantIntro": "Vous pouvez également joindre l'assistant Growheads sur Telegram :", - "telegramAssistantLink": "t.me/Growheads_de_Bot" + "privacyPromptBefore": "Veuillez confirmer que vous avez lu la ", + "privacyPolicyLink": "politique de confidentialité", + "privacyPromptAfter": " et que vous l'acceptez. ", + "telegramAssistantIntro": "Vous pouvez également contacter l'assistant Growheads sur Telegram :", + "telegramAssistantLink": "t.me/Growheads_de_Bot", + "assistantTitle": "Assistant", + "placeholderRecording": "Enregistrement…", + "inputPlaceholder": "Vous pouvez me demander des informations sur les variétés de cannabis…", + "send": "Envoyer", + "closeAria": "Fermer l'assistant", + "micStartAria": "Démarrer l'enregistrement vocal", + "micStopAria": "Arrêter l'enregistrement", + "uploadImageAria": "Télécharger une image", + "micPermissionDenied": "Impossible d'accéder au microphone. Veuillez vérifier les autorisations de votre navigateur.", + "uploadedImageAlt": "Image téléchargée" }; diff --git a/src/i18n/locales/hr/chat.js b/src/i18n/locales/hr/chat.js index da8fba4..0721a5e 100644 --- a/src/i18n/locales/hr/chat.js +++ b/src/i18n/locales/hr/chat.js @@ -1,5 +1,18 @@ export default { "privacyRead": "Pročitano i prihvaćeno", + "privacyPromptBefore": "Molimo potvrdite da ste pročitali ", + "privacyPolicyLink": "pravila privatnosti", + "privacyPromptAfter": " i da ih prihvaćate. ", "telegramAssistantIntro": "Također možete kontaktirati Growheads asistenta na Telegramu:", - "telegramAssistantLink": "t.me/Growheads_de_Bot" + "telegramAssistantLink": "t.me/Growheads_de_Bot", + "assistantTitle": "Asistent", + "placeholderRecording": "Snimanje…", + "inputPlaceholder": "Možete me pitati o sortama kanabisa…", + "send": "Pošalji", + "closeAria": "Zatvori asistenta", + "micStartAria": "Pokreni glasovno snimanje", + "micStopAria": "Zaustavi snimanje", + "uploadImageAria": "Prenesi sliku", + "micPermissionDenied": "Nije moguće pristupiti mikrofonu. Molimo provjerite dopuštenja u pregledniku.", + "uploadedImageAlt": "Prenesena slika" }; diff --git a/src/i18n/locales/hu/chat.js b/src/i18n/locales/hu/chat.js index 7f052eb..a49d319 100644 --- a/src/i18n/locales/hu/chat.js +++ b/src/i18n/locales/hu/chat.js @@ -1,5 +1,18 @@ export default { - "privacyRead": "Elolvasva és elfogadva", - "telegramAssistantIntro": "A Growheads asszisztenst Telegramon is elérheted:", - "telegramAssistantLink": "t.me/Growheads_de_Bot" + "privacyRead": "Elolvastam és elfogadtam", + "privacyPromptBefore": "Kérjük, erősítse meg, hogy elolvasta a ", + "privacyPolicyLink": "adatvédelmi szabályzatot", + "privacyPromptAfter": " és elfogadja azt. ", + "telegramAssistantIntro": "A Growheads asszisztenst Telegramon is elérheti:", + "telegramAssistantLink": "t.me/Growheads_de_Bot", + "assistantTitle": "Asszisztens", + "placeholderRecording": "Rögzítés…", + "inputPlaceholder": "Kérdezhet tőlem kannabiszfajtákról…", + "send": "Küldés", + "closeAria": "Asszisztens bezárása", + "micStartAria": "Hangrögzítés indítása", + "micStopAria": "Rögzítés leállítása", + "uploadImageAria": "Kép feltöltése", + "micPermissionDenied": "Nem sikerült hozzáférni a mikrofonhoz. Kérjük, ellenőrizze a böngésző engedélyeit.", + "uploadedImageAlt": "Feltöltött kép" }; diff --git a/src/i18n/locales/it/chat.js b/src/i18n/locales/it/chat.js index 0c69ef8..33cf6f3 100644 --- a/src/i18n/locales/it/chat.js +++ b/src/i18n/locales/it/chat.js @@ -1,5 +1,18 @@ export default { "privacyRead": "Letto e accettato", - "telegramAssistantIntro": "Puoi anche contattare l'assistente Growheads su Telegram:", - "telegramAssistantLink": "t.me/Growheads_de_Bot" + "privacyPromptBefore": "Conferma di aver letto la ", + "privacyPolicyLink": "informativa sulla privacy", + "privacyPromptAfter": " e di accettarla. ", + "telegramAssistantIntro": "Puoi أيضًا contattare l'assistente di Growheads su Telegram:", + "telegramAssistantLink": "t.me/Growheads_de_Bot", + "assistantTitle": "Assistente", + "placeholderRecording": "Registrazione…", + "inputPlaceholder": "Puoi chiedermi informazioni sulle varietà di cannabis…", + "send": "Invia", + "closeAria": "Chiudi assistente", + "micStartAria": "Avvia registrazione vocale", + "micStopAria": "Interrompi registrazione", + "uploadImageAria": "Carica immagine", + "micPermissionDenied": "Impossibile accedere al microfono. Controlla i permessi del browser.", + "uploadedImageAlt": "Immagine caricata" }; diff --git a/src/i18n/locales/pl/chat.js b/src/i18n/locales/pl/chat.js index a1376a0..5689749 100644 --- a/src/i18n/locales/pl/chat.js +++ b/src/i18n/locales/pl/chat.js @@ -1,5 +1,18 @@ export default { "privacyRead": "Przeczytano i zaakceptowano", + "privacyPromptBefore": "Proszę potwierdzić, że przeczytałeś(aś) ", + "privacyPolicyLink": "politykę prywatności", + "privacyPromptAfter": " i zgadzasz się na nią. ", "telegramAssistantIntro": "Możesz również skontaktować się z asystentem Growheads na Telegramie:", - "telegramAssistantLink": "t.me/Growheads_de_Bot" + "telegramAssistantLink": "t.me/Growheads_de_Bot", + "assistantTitle": "Asystent", + "placeholderRecording": "Nagrywanie…", + "inputPlaceholder": "Możesz zapytać mnie o odmiany konopi…", + "send": "Wyślij", + "closeAria": "Zamknij asystenta", + "micStartAria": "Rozpocznij nagrywanie głosu", + "micStopAria": "Zatrzymaj nagrywanie", + "uploadImageAria": "Prześlij obraz", + "micPermissionDenied": "Nie udało się uzyskać dostępu do mikrofonu. Sprawdź uprawnienia w przeglądarce.", + "uploadedImageAlt": "Przesłany obraz" }; diff --git a/src/i18n/locales/ro/chat.js b/src/i18n/locales/ro/chat.js index 8b66a68..0659866 100644 --- a/src/i18n/locales/ro/chat.js +++ b/src/i18n/locales/ro/chat.js @@ -1,5 +1,18 @@ export default { "privacyRead": "Citit și acceptat", - "telegramAssistantIntro": "Puteți, de asemenea, contacta asistentul Growheads pe Telegram:", - "telegramAssistantLink": "t.me/Growheads_de_Bot" + "privacyPromptBefore": "Te rugăm să confirmi că ai citit ", + "privacyPolicyLink": "politica de confidențialitate", + "privacyPromptAfter": " și ești de acord cu ea. ", + "telegramAssistantIntro": "Poți contacta și asistentul Growheads pe Telegram:", + "telegramAssistantLink": "t.me/Growheads_de_Bot", + "assistantTitle": "Asistent", + "placeholderRecording": "Înregistrare…", + "inputPlaceholder": "Mă poți întreba despre soiuri de cannabis…", + "send": "Trimite", + "closeAria": "Închide asistentul", + "micStartAria": "Începe înregistrarea vocală", + "micStopAria": "Oprește înregistrarea", + "uploadImageAria": "Încarcă imaginea", + "micPermissionDenied": "Nu s-a putut accesa microfonul. Te rugăm să verifici permisiunile din browser.", + "uploadedImageAlt": "Imagine încărcată" }; diff --git a/src/i18n/locales/ru/chat.js b/src/i18n/locales/ru/chat.js index fb79621..7e5e0f4 100644 --- a/src/i18n/locales/ru/chat.js +++ b/src/i18n/locales/ru/chat.js @@ -1,5 +1,18 @@ export default { "privacyRead": "Прочитано и принято", + "privacyPromptBefore": "Пожалуйста, подтвердите, что вы прочитали ", + "privacyPolicyLink": "политику конфиденциальности", + "privacyPromptAfter": " и согласны с ней. ", "telegramAssistantIntro": "Вы также можете связаться с помощником Growheads в Telegram:", - "telegramAssistantLink": "t.me/Growheads_de_Bot" + "telegramAssistantLink": "t.me/Growheads_de_Bot", + "assistantTitle": "Помощник", + "placeholderRecording": "Запись…", + "inputPlaceholder": "Вы можете спросить меня о сортах каннабиса…", + "send": "Отправить", + "closeAria": "Закрыть помощника", + "micStartAria": "Начать запись голоса", + "micStopAria": "Остановить запись", + "uploadImageAria": "Загрузить изображение", + "micPermissionDenied": "Не удалось получить доступ к микрофону. Пожалуйста, проверьте разрешения вашего браузера.", + "uploadedImageAlt": "Загруженное изображение" }; diff --git a/src/i18n/locales/sk/chat.js b/src/i18n/locales/sk/chat.js index cd1bf30..33e67ff 100644 --- a/src/i18n/locales/sk/chat.js +++ b/src/i18n/locales/sk/chat.js @@ -1,5 +1,18 @@ export default { - "privacyRead": "Prečítané a akceptované", - "telegramAssistantIntro": "Môžete tiež kontaktovať asistenta Growheads na Telegrame:", - "telegramAssistantLink": "t.me/Growheads_de_Bot" + "privacyRead": "Prečítané a prijaté", + "privacyPromptBefore": "Prosím potvrďte, že ste si prečítali ", + "privacyPolicyLink": "zásady ochrany osobných údajov", + "privacyPromptAfter": " a súhlasíte s nimi. ", + "telegramAssistantIntro": "Asistenta Growheads môžete tiež kontaktovať na Telegrame:", + "telegramAssistantLink": "t.me/Growheads_de_Bot", + "assistantTitle": "Asistent", + "placeholderRecording": "Nahráva sa…", + "inputPlaceholder": "Môžete sa ma opýtať na odrody kanabisu…", + "send": "Odoslať", + "closeAria": "Zavrieť asistenta", + "micStartAria": "Spustiť hlasové nahrávanie", + "micStopAria": "Zastaviť nahrávanie", + "uploadImageAria": "Nahrať obrázok", + "micPermissionDenied": "Nepodarilo sa získať prístup k mikrofónu. Skontrolujte, prosím, povolenia vo vašom prehliadači.", + "uploadedImageAlt": "Nahraný obrázok" }; diff --git a/src/i18n/locales/sl/chat.js b/src/i18n/locales/sl/chat.js index a0eb943..c22a0b7 100644 --- a/src/i18n/locales/sl/chat.js +++ b/src/i18n/locales/sl/chat.js @@ -1,5 +1,18 @@ export default { "privacyRead": "Prebrano in sprejeto", + "privacyPromptBefore": "Prosimo, potrdite, da ste prebrali ", + "privacyPolicyLink": "politiko zasebnosti", + "privacyPromptAfter": " in se z njo strinjate. ", "telegramAssistantIntro": "Pomočnika Growheads lahko dosežete tudi na Telegramu:", - "telegramAssistantLink": "t.me/Growheads_de_Bot" + "telegramAssistantLink": "t.me/Growheads_de_Bot", + "assistantTitle": "Pomočnik", + "placeholderRecording": "Snemanje…", + "inputPlaceholder": "Lahko me vprašate o sortah konoplje…", + "send": "Pošlji", + "closeAria": "Zapri pomočnika", + "micStartAria": "Začni glasovno snemanje", + "micStopAria": "Ustavi snemanje", + "uploadImageAria": "Naloži sliko", + "micPermissionDenied": "Dostop do mikrofona ni bil mogoč. Preverite dovoljenja v brskalniku.", + "uploadedImageAlt": "Naložena slika" }; diff --git a/src/i18n/locales/sq/chat.js b/src/i18n/locales/sq/chat.js index a717b13..3e15b48 100644 --- a/src/i18n/locales/sq/chat.js +++ b/src/i18n/locales/sq/chat.js @@ -1,5 +1,18 @@ export default { - "privacyRead": "Lexuar & Pranuar", - "telegramAssistantIntro": "Mund ta kontaktoni gjithashtu asistentin e Growheads në Telegram:", - "telegramAssistantLink": "t.me/Growheads_de_Bot" + "privacyRead": "Lexuar dhe pranuar", + "privacyPromptBefore": "Ju lutemi konfirmoni që e keni lexuar ", + "privacyPolicyLink": "politikën e privatësisë", + "privacyPromptAfter": " dhe pajtoheni me të. ", + "telegramAssistantIntro": "Mund të kontaktoni gjithashtu asistentin Growheads në Telegram:", + "telegramAssistantLink": "t.me/Growheads_de_Bot", + "assistantTitle": "Asistent", + "placeholderRecording": "Duke regjistruar…", + "inputPlaceholder": "Mund të më pyesni për varietete kanabisi…", + "send": "Dërgo", + "closeAria": "Mbyll asistentin", + "micStartAria": "Fillo regjistrimin e zërit", + "micStopAria": "Ndalo regjistrimin", + "uploadImageAria": "Ngarko imazh", + "micPermissionDenied": "Nuk mund të aksesohej mikrofoni. Ju lutemi kontrolloni lejet e shfletuesit tuaj.", + "uploadedImageAlt": "Imazh i ngarkuar" }; diff --git a/src/i18n/locales/sr/chat.js b/src/i18n/locales/sr/chat.js index d714235..2bcf4d5 100644 --- a/src/i18n/locales/sr/chat.js +++ b/src/i18n/locales/sr/chat.js @@ -1,5 +1,18 @@ export default { "privacyRead": "Pročitano i prihvaćeno", + "privacyPromptBefore": "Molimo potvrdite da ste pročitali ", + "privacyPolicyLink": "politiku privatnosti", + "privacyPromptAfter": " i da se slažete sa njom. ", "telegramAssistantIntro": "Takođe možete kontaktirati Growheads asistenta na Telegramu:", - "telegramAssistantLink": "t.me/Growheads_de_Bot" + "telegramAssistantLink": "t.me/Growheads_de_Bot", + "assistantTitle": "Asistent", + "placeholderRecording": "Snimanje…", + "inputPlaceholder": "Možete da me pitate o sortama kanabisa…", + "send": "Pošalji", + "closeAria": "Zatvori asistenta", + "micStartAria": "Započni glasovno snimanje", + "micStopAria": "Zaustavi snimanje", + "uploadImageAria": "Otpremi sliku", + "micPermissionDenied": "Nije moguće pristupiti mikrofonu. Proverite dozvole u pregledaču.", + "uploadedImageAlt": "Otpremana slika" }; diff --git a/src/i18n/locales/sv/chat.js b/src/i18n/locales/sv/chat.js index 44cf712..97cba8f 100644 --- a/src/i18n/locales/sv/chat.js +++ b/src/i18n/locales/sv/chat.js @@ -1,5 +1,18 @@ export default { - "privacyRead": "Läst & accepterat", + "privacyRead": "Läst & godkänt", + "privacyPromptBefore": "Bekräfta att du har läst ", + "privacyPolicyLink": "integritetspolicyn", + "privacyPromptAfter": " och godkänner den. ", "telegramAssistantIntro": "Du kan också nå Growheads-assistenten på Telegram:", - "telegramAssistantLink": "t.me/Growheads_de_Bot" + "telegramAssistantLink": "t.me/Growheads_de_Bot", + "assistantTitle": "Assistent", + "placeholderRecording": "Spelar in…", + "inputPlaceholder": "Du kan fråga mig om cannabissorter…", + "send": "Skicka", + "closeAria": "Stäng assistenten", + "micStartAria": "Starta röstinspelning", + "micStopAria": "Stoppa inspelning", + "uploadImageAria": "Ladda upp bild", + "micPermissionDenied": "Kunde inte komma åt mikrofonen. Kontrollera webbläsarens behörigheter.", + "uploadedImageAlt": "Uppladdad bild" }; diff --git a/src/i18n/locales/tr/chat.js b/src/i18n/locales/tr/chat.js index b2b33ab..ecfea63 100644 --- a/src/i18n/locales/tr/chat.js +++ b/src/i18n/locales/tr/chat.js @@ -1,5 +1,18 @@ export default { "privacyRead": "Okundu ve Kabul Edildi", + "privacyPromptBefore": "Lütfen ", + "privacyPolicyLink": "gizlilik politikasını", + "privacyPromptAfter": " okuduğunuzu ve kabul ettiğinizi onaylayın. ", "telegramAssistantIntro": "Growheads asistanına Telegram üzerinden de ulaşabilirsiniz:", - "telegramAssistantLink": "t.me/Growheads_de_Bot" + "telegramAssistantLink": "t.me/Growheads_de_Bot", + "assistantTitle": "Asistan", + "placeholderRecording": "Kaydediliyor…", + "inputPlaceholder": "Bana cannabis strains hakkında sorabilirsiniz…", + "send": "Gönder", + "closeAria": "Asistanı kapat", + "micStartAria": "Ses kaydını başlat", + "micStopAria": "Kaydı durdur", + "uploadImageAria": "Resim yükle", + "micPermissionDenied": "Mikrofona erişilemedi. Lütfen tarayıcı izinlerinizi kontrol edin.", + "uploadedImageAlt": "Yüklenen resim" }; diff --git a/src/i18n/locales/uk/chat.js b/src/i18n/locales/uk/chat.js index 77de775..e9c71e9 100644 --- a/src/i18n/locales/uk/chat.js +++ b/src/i18n/locales/uk/chat.js @@ -1,5 +1,18 @@ export default { "privacyRead": "Прочитано та прийнято", - "telegramAssistantIntro": "Ви також можете зв’язатися з асистентом Growheads у Telegram:", - "telegramAssistantLink": "t.me/Growheads_de_Bot" + "privacyPromptBefore": "Будь ласка, підтвердьте, що ви прочитали ", + "privacyPolicyLink": "політику конфіденційності", + "privacyPromptAfter": " і погоджуєтеся з нею. ", + "telegramAssistantIntro": "Ви також можете звернутися до асистента Growheads у Telegram:", + "telegramAssistantLink": "t.me/Growheads_de_Bot", + "assistantTitle": "Асистент", + "placeholderRecording": "Запис…", + "inputPlaceholder": "Ви можете запитати мене про сорти канабісу…", + "send": "Надіслати", + "closeAria": "Закрити асистента", + "micStartAria": "Почати голосовий запис", + "micStopAria": "Зупинити запис", + "uploadImageAria": "Завантажити зображення", + "micPermissionDenied": "Не вдалося отримати доступ до мікрофона. Будь ласка, перевірте дозволи вашого браузера.", + "uploadedImageAlt": "Завантажене зображення" }; diff --git a/src/i18n/locales/zh/chat.js b/src/i18n/locales/zh/chat.js index 187d696..e0d28c8 100644 --- a/src/i18n/locales/zh/chat.js +++ b/src/i18n/locales/zh/chat.js @@ -1,5 +1,18 @@ export default { "privacyRead": "已阅读并接受", - "telegramAssistantIntro": "你也可以在 Telegram 上联系 Growheads 助手:", - "telegramAssistantLink": "t.me/Growheads_de_Bot" + "privacyPromptBefore": "请确认您已阅读 ", + "privacyPolicyLink": "隐私政策", + "privacyPromptAfter": " 并同意。 ", + "telegramAssistantIntro": "您也可以在 Telegram 上联系 Growheads 助手:", + "telegramAssistantLink": "t.me/Growheads_de_Bot", + "assistantTitle": "助手", + "placeholderRecording": "录音中…", + "inputPlaceholder": "您可以向我询问关于 cannabis 品种的问题…", + "send": "发送", + "closeAria": "关闭助手", + "micStartAria": "开始语音录音", + "micStopAria": "停止录音", + "uploadImageAria": "上传图片", + "micPermissionDenied": "无法访问麦克风。请检查您的浏览器权限。", + "uploadedImageAlt": "已上传的图片" }; diff --git a/src/utils/productCardLayout.js b/src/utils/productCardLayout.js new file mode 100644 index 0000000..9c677a5 --- /dev/null +++ b/src/utils/productCardLayout.js @@ -0,0 +1,22 @@ +/** Matches MUI default `sm` breakpoint (600px). */ +export const PRODUCT_CARD_MOBILE_MAX_WIDTH_PX = 600; + +export const PRODUCT_CARD_GAP_PX = 16; +export const PRODUCT_CARD_WIDTH_XS_PX = 260; +export const PRODUCT_CARD_WIDTH_SM_PX = 250; + +export function isCompactProductCardViewport() { + return ( + typeof window !== "undefined" && + window.innerWidth < PRODUCT_CARD_MOBILE_MAX_WIDTH_PX + ); +} + +/** Card width + flex gap — must match Product carousel item stride for translate math. */ +export function getProductCarouselItemStridePx() { + return ( + (isCompactProductCardViewport() + ? PRODUCT_CARD_WIDTH_XS_PX + : PRODUCT_CARD_WIDTH_SM_PX) + PRODUCT_CARD_GAP_PX + ); +}