const catalog = window.EUROWET_CATALOG || { categories: {}, products: [] }; const translations = { ro: { brandSub: "Materiale de produs pentru parteneri", navCatalog: "Catalog", navCategories: "Categorii", navSource: "Sursă", navInquiry: "Cerere", heroEyebrow: "Catalog partener", heroTitle: "Fotografii, descrieri și categorii de produs într-un singur spațiu", heroText: "Catalog bilingv distinct, construit din materiale Eurowet autorizate, cu filtrare rapidă, detalii de produs și selecție pentru cereri.", browseProducts: "Vezi produsele", prepareInquiry: "Pregătește cererea", metricProducts: "produse", metricImages: "fotografii", metricCategories: "categorii", sourceNotice: "Materialele sunt organizate din fișiere de produs autorizate de partener. Designul, structura catalogului și interacțiunile sunt personalizate și intenționat diferite de eurowet.pl.", categoryEyebrow: "Împărțire produse", categoryTitle: "Categorii pregătite din setul de materiale", filterEyebrow: "Filtre", filterTitle: "Caută produs", searchPlaceholder: "Caută după nume, categorie sau descriere", categoryFilter: "Categorie", animalFilter: "Animal", materialFilter: "Material", onlyEnglish: "Descriere engleză extrasă", resetFilters: "Resetează filtre", catalogEyebrow: "Catalog", sortLabel: "Sortare", sortName: "Nume A-Z", sortCategory: "Categorie", sortSource: "Cu sursă mai întâi", detailsEyebrow: "Detalii produs", inquiryEyebrow: "Cerere", inquiryTitle: "Produse selectate", emptyInquiry: "Adaugă produse pentru a pregăti o cerere de ofertă.", copyInquiry: "Copiază lista", all: "Toate", multi: "Mai multe", dog: "Câini", cat: "Pisici", small: "Animale mici", livestock: "Fermă", productsFound: "{count} produse", oneProductFound: "1 produs", noResults: "Nu există produse pentru filtrele selectate.", details: "Detalii", add: "Adaugă", added: "Adăugat la cerere", removed: "Eliminat din cerere", sourceDescription: "Descriere sursă", englishDescription: "Descriere engleză", shortDescription: "Descriere scurtă", sourceFiles: "Fișiere sursă", openFile: "Deschide fișier", noExtractedText: "Textul extras nu este disponibil; folosește fișierul sursă.", copied: "Lista de produse a fost copiată", copyFallback: "Selectează și copiază lista din panoul cererii.", }, en: { brandSub: "Partner product materials", navCatalog: "Catalog", navCategories: "Categories", navSource: "Source", navInquiry: "Inquiry", heroEyebrow: "Partner catalogue", heroTitle: "Product photos, descriptions and categories in one workspace", heroText: "A distinct bilingual catalogue built from authorised Eurowet materials, with fast filtering, product detail panels and inquiry selection.", browseProducts: "Browse products", prepareInquiry: "Prepare inquiry", metricProducts: "products", metricImages: "photos", metricCategories: "categories", sourceNotice: "Materials are organised from authorised partner product files. The design, catalogue structure and interaction model are custom and intentionally different from eurowet.pl.", categoryEyebrow: "Product division", categoryTitle: "Categories prepared from the material set", filterEyebrow: "Filters", filterTitle: "Find product", searchPlaceholder: "Search by name, category or description", categoryFilter: "Category", animalFilter: "Animal", materialFilter: "Material", onlyEnglish: "English description extracted", resetFilters: "Reset filters", catalogEyebrow: "Catalogue", sortLabel: "Sort", sortName: "Name A-Z", sortCategory: "Category", sortSource: "With source first", detailsEyebrow: "Product details", inquiryEyebrow: "Inquiry", inquiryTitle: "Selected products", emptyInquiry: "Add products to prepare a partner offer request.", copyInquiry: "Copy product list", all: "All", multi: "Multiple", dog: "Dogs", cat: "Cats", small: "Small animals", livestock: "Livestock", productsFound: "{count} products", oneProductFound: "1 product", noResults: "No products match the selected filters.", details: "Details", add: "Add", added: "Added to inquiry", removed: "Removed from inquiry", sourceDescription: "Source description", englishDescription: "English description", shortDescription: "Short description", sourceFiles: "Source files", openFile: "Open file", noExtractedText: "Extracted text is not available; use the source file.", copied: "Product list copied", copyFallback: "Select and copy the list from the inquiry panel.", }, }; const state = { lang: localStorage.getItem("vetbridgeLang") || "ro", query: "", category: "all", animal: "all", onlyEnglish: false, sort: "name", inquiry: [], }; const categoryIcons = { dermo: "sparkles", "eye-ear": "eye", oral: "smile-plus", digestive: "activity", supplements: "pill", repellents: "shield", hygiene: "package-check", }; const els = { productGrid: document.querySelector("#productGrid"), categoryCards: document.querySelector("#categoryCards"), categoryFilters: document.querySelector("#categoryFilters"), animalFilters: document.querySelector("#animalFilters"), searchInput: document.querySelector("#searchInput"), onlyEnglish: document.querySelector("#onlyEnglish"), sortSelect: document.querySelector("#sortSelect"), resultCount: document.querySelector("#resultCount"), previewStack: document.querySelector("#previewStack"), detailsDrawer: document.querySelector(".details-drawer"), drawerTitle: document.querySelector("#drawerTitle"), drawerContent: document.querySelector("#drawerContent"), inquiryPanel: document.querySelector(".inquiry-panel"), inquiryItems: document.querySelector("#inquiryItems"), emptyInquiry: document.querySelector("#emptyInquiry"), inquiryCount: document.querySelector(".inquiry-count"), overlay: document.querySelector(".overlay"), toast: document.querySelector(".toast"), }; function t(key) { return translations[state.lang][key] || translations.en[key] || key; } function interpolate(template, values) { return Object.entries(values).reduce( (text, [key, value]) => text.replaceAll(`{${key}}`, value), template, ); } function categoryLabel(key) { return catalog.categories[key]?.[state.lang] || catalog.categories[key]?.en || key; } function animalLabel(key) { return t(key); } function refreshIcons() { if (window.lucide) window.lucide.createIcons(); } function applyTranslations() { document.documentElement.lang = state.lang; document.querySelectorAll("[data-i18n]").forEach((node) => { node.textContent = t(node.dataset.i18n); }); document.querySelectorAll("[data-i18n-placeholder]").forEach((node) => { node.setAttribute("placeholder", t(node.dataset.i18nPlaceholder)); }); document.querySelectorAll("[data-i18n-aria-label]").forEach((node) => { node.setAttribute("aria-label", t(node.dataset.i18nAriaLabel)); }); document.querySelectorAll(".lang-btn").forEach((button) => { button.classList.toggle("active", button.dataset.lang === state.lang); button.setAttribute("aria-pressed", String(button.dataset.lang === state.lang)); }); } function getVisibleProducts() { const query = state.query.trim().toLowerCase(); return catalog.products .filter((product) => { const categoryMatch = state.category === "all" || product.category === state.category; const animalMatch = state.animal === "all" || product.animals.includes(state.animal); const englishMatch = !state.onlyEnglish || Boolean(product.details.en); const queryMatch = !query || [ product.name, categoryLabel(product.category), product.description[state.lang], product.description.en, product.description.ro, product.details.en, product.details.source, ] .join(" ") .toLowerCase() .includes(query); return categoryMatch && animalMatch && englishMatch && queryMatch; }) .sort((a, b) => { if (state.sort === "category") { return categoryLabel(a.category).localeCompare(categoryLabel(b.category), state.lang); } if (state.sort === "source") { return Number(Boolean(b.details.en)) - Number(Boolean(a.details.en)) || a.name.localeCompare(b.name); } return a.name.localeCompare(b.name, state.lang); }); } function renderPreview() { const featured = catalog.products .filter((product) => product.image) .slice(0, 4) .map((product) => `${product.name}`) .join(""); els.previewStack.innerHTML = featured; } function renderCategoryCards() { const counts = catalog.products.reduce((acc, product) => { acc[product.category] = (acc[product.category] || 0) + 1; return acc; }, {}); els.categoryCards.innerHTML = Object.keys(catalog.categories) .map( (key) => `
${categoryLabel(key)} ${interpolate(t("productsFound"), { count: counts[key] || 0 })}
`, ) .join(""); } function renderFilterChips() { const categories = [ { key: "all", label: t("all") }, ...Object.keys(catalog.categories).map((key) => ({ key, label: categoryLabel(key) })), ]; els.categoryFilters.innerHTML = categories .map( (item) => ` `, ) .join(""); const animals = ["all", "dog", "cat", "small", "livestock", "multi"]; els.animalFilters.innerHTML = animals .map( (key) => ` `, ) .join(""); } function renderProducts() { const products = getVisibleProducts(); els.resultCount.textContent = products.length === 1 ? t("oneProductFound") : interpolate(t("productsFound"), { count: products.length }); if (!products.length) { els.productGrid.innerHTML = `
${t("noResults")}
`; return; } els.productGrid.innerHTML = products .map( (product) => `
${product.name}
${categoryLabel(product.category)}
${product.animals.map((animal) => `${animalLabel(animal)}`).join("")}

${product.name}

${product.description[state.lang]}

`, ) .join(""); refreshIcons(); } function sourceLink(path, label) { if (!path) return ""; return ` ${label} `; } function openDetails(productId) { const product = catalog.products.find((item) => item.id === productId); if (!product) return; els.drawerTitle.textContent = product.name; const englishText = product.details.en || t("noExtractedText"); const sourceText = product.details.source || t("noExtractedText"); els.drawerContent.innerHTML = `
${product.name}
${categoryLabel(product.category)} ${product.animals.map((animal) => `${animalLabel(animal)}`).join("")}

${t("shortDescription")}

${product.description[state.lang]}

${t("englishDescription")}

${englishText}

${t("sourceDescription")}

${sourceText}

${t("sourceFiles")}

`; els.detailsDrawer.classList.add("open"); els.detailsDrawer.setAttribute("aria-hidden", "false"); els.overlay.classList.add("open"); refreshIcons(); } function closePanels() { els.detailsDrawer.classList.remove("open"); els.inquiryPanel.classList.remove("open"); els.detailsDrawer.setAttribute("aria-hidden", "true"); els.inquiryPanel.setAttribute("aria-hidden", "true"); els.overlay.classList.remove("open"); } function openInquiry() { els.inquiryPanel.classList.add("open"); els.inquiryPanel.setAttribute("aria-hidden", "false"); els.overlay.classList.add("open"); } function addToInquiry(productId) { if (!state.inquiry.includes(productId)) state.inquiry.push(productId); renderInquiry(); showToast(t("added")); } function removeFromInquiry(productId) { state.inquiry = state.inquiry.filter((id) => id !== productId); renderInquiry(); showToast(t("removed")); } function renderInquiry() { const products = state.inquiry .map((id) => catalog.products.find((product) => product.id === id)) .filter(Boolean); els.inquiryCount.textContent = products.length; els.emptyInquiry.style.display = products.length ? "none" : "grid"; els.inquiryItems.innerHTML = products .map( (product) => `
${product.name}
${product.name} ${categoryLabel(product.category)}
`, ) .join(""); refreshIcons(); } function showToast(message) { els.toast.textContent = message; els.toast.classList.add("show"); window.clearTimeout(showToast.timer); showToast.timer = window.setTimeout(() => els.toast.classList.remove("show"), 2200); } async function copyInquiry() { const products = state.inquiry .map((id) => catalog.products.find((product) => product.id === id)) .filter(Boolean); const text = products.map((product, index) => `${index + 1}. ${product.name} - ${categoryLabel(product.category)}`).join("\n"); try { await navigator.clipboard.writeText(text); showToast(t("copied")); } catch { showToast(t("copyFallback")); } } function rerenderAll() { applyTranslations(); renderCategoryCards(); renderFilterChips(); renderProducts(); renderInquiry(); refreshIcons(); } document.querySelectorAll(".lang-btn").forEach((button) => { button.addEventListener("click", () => { state.lang = button.dataset.lang; localStorage.setItem("vetbridgeLang", state.lang); rerenderAll(); }); }); els.searchInput.addEventListener("input", (event) => { state.query = event.target.value; renderProducts(); }); els.categoryFilters.addEventListener("click", (event) => { const button = event.target.closest("[data-category]"); if (!button) return; state.category = button.dataset.category; renderFilterChips(); renderProducts(); }); els.animalFilters.addEventListener("click", (event) => { const button = event.target.closest("[data-animal]"); if (!button) return; state.animal = button.dataset.animal; renderFilterChips(); renderProducts(); }); els.onlyEnglish.addEventListener("change", (event) => { state.onlyEnglish = event.target.checked; renderProducts(); }); els.sortSelect.addEventListener("change", (event) => { state.sort = event.target.value; renderProducts(); }); document.querySelector("#resetFilters").addEventListener("click", () => { state.query = ""; state.category = "all"; state.animal = "all"; state.onlyEnglish = false; els.searchInput.value = ""; els.onlyEnglish.checked = false; renderFilterChips(); renderProducts(); }); els.productGrid.addEventListener("click", (event) => { const details = event.target.closest("[data-details]"); const add = event.target.closest("[data-add]"); if (details) openDetails(details.dataset.details); if (add) addToInquiry(add.dataset.add); }); els.inquiryItems.addEventListener("click", (event) => { const button = event.target.closest("[data-remove]"); if (button) removeFromInquiry(button.dataset.remove); }); document.querySelectorAll(".inquiry-toggle, .inquiry-toggle-inline").forEach((button) => { button.addEventListener("click", openInquiry); }); document.querySelector(".close-details").addEventListener("click", closePanels); document.querySelector(".close-inquiry").addEventListener("click", closePanels); els.overlay.addEventListener("click", closePanels); document.querySelector("#copyInquiry").addEventListener("click", copyInquiry); document.querySelector("#metricProducts").textContent = catalog.products.length; document.querySelector("#metricImages").textContent = catalog.products.filter((product) => product.image).length; document.querySelector("#metricCategories").textContent = Object.keys(catalog.categories).length; renderPreview(); rerenderAll(); window.addEventListener("load", refreshIcons);