From b48a143b4021e0f90540f38514beb052d82d5a3c Mon Sep 17 00:00:00 2001 From: Yakovenko Valeriia Date: Wed, 1 Apr 2026 10:52:34 +0200 Subject: [PATCH 1/2] fix: default lang --- bo/src/components/admin/PageProducts.vue | 2 +- bo/src/components/admin/ProductDetailView.vue | 121 +++++++----------- bo/src/stores/product.ts | 19 +-- bo/src/stores/settings.ts | 3 +- 4 files changed, 54 insertions(+), 91 deletions(-) diff --git a/bo/src/components/admin/PageProducts.vue b/bo/src/components/admin/PageProducts.vue index 450954a..ca13d3a 100644 --- a/bo/src/components/admin/PageProducts.vue +++ b/bo/src/components/admin/PageProducts.vue @@ -7,7 +7,7 @@ - --> + diff --git a/bo/src/components/admin/ProductDetailView.vue b/bo/src/components/admin/ProductDetailView.vue index 830fedf..df573e5 100644 --- a/bo/src/components/admin/ProductDetailView.vue +++ b/bo/src/components/admin/ProductDetailView.vue @@ -1,22 +1,19 @@ @@ -158,8 +151,6 @@ const settingsStore = useSettingsStore() const activeTab = ref('description') const translating = ref(false) const noTranslation = ref(false) - -const defaultLangId = ref(0) const selectedLangId = ref(0) const activeLangId = ref(0) const productID = ref(0) @@ -179,72 +170,59 @@ const langOptions = computed(() => langs.filter(l => l.active !== false)) watch(selectedLangId, async (newLang) => { if (!productID.value) return - if (newLang === defaultLangId.value) { + if (newLang === productStore.defaultLangId.value) { noTranslation.value = false - activeLangId.value = defaultLangId.value - await productStore.getProductDescription(defaultLangId.value, productID.value) - await productStore.getDefaultProductDescription(defaultLangId.value, productID.value) + activeLangId.value = productStore.defaultLangId.value + await productStore.getProductDescription(productStore.defaultLangId.value, productID.value) return } - - await productStore.getProductDescription(newLang, productID.value) - const desc = productStore.productDescription - - if (!desc || (!desc.description && !desc.description_short && !desc.usage)) { - noTranslation.value = true - activeLangId.value = defaultLangId.value - await productStore.getDefaultProductDescription(defaultLangId.value, productID.value) - } else { - noTranslation.value = false - activeLangId.value = newLang - } }) -const isViewingExistingTranslation = computed(() => activeLangId.value !== defaultLangId.value && !noTranslation.value) +const isViewingExistingTranslation = computed(() => activeLangId.value !== productStore.defaultLangId.value && !noTranslation.value) const productName = computed(() => { - if (activeLangId.value !== defaultLangId.value && !noTranslation.value) { + if (activeLangId.value !== productStore.defaultLangId.value && !noTranslation.value) { return productStore.productDescription?.name || '' } return productStore.defaultProductDescription?.name || productStore.productDescription?.name || '' }) const displayedDescription = computed(() => { - if (activeLangId.value !== defaultLangId.value && !noTranslation.value) { + if (activeLangId.value !== productStore.defaultLangId.value && !noTranslation.value) { return productStore.productDescription?.description || '' } return productStore.defaultProductDescription?.description || '' }) const displayedShortDescription = computed(() => { - if (activeLangId.value !== defaultLangId.value && !noTranslation.value) { + if (activeLangId.value !== productStore.defaultLangId.value && !noTranslation.value) { return productStore.productDescription?.description_short || '' } return productStore.defaultProductDescription?.description_short || '' }) const displayedUsage = computed(() => { - if (activeLangId.value !== defaultLangId.value && !noTranslation.value) { + if (activeLangId.value !== productStore.defaultLangId.value && !noTranslation.value) { return productStore.productDescription?.usage || '' } return productStore.defaultProductDescription?.usage || '' }) const goBackToDefault = async () => { - selectedLangId.value = defaultLangId.value - activeLangId.value = defaultLangId.value + selectedLangId.value = productStore.defaultLangId.value + activeLangId.value = productStore.defaultLangId.value noTranslation.value = false - await productStore.getProductDescription(defaultLangId.value, productID.value) - await productStore.getDefaultProductDescription(defaultLangId.value, productID.value) + await productStore.getProductDescription(productStore.defaultLangId.value, productID.value) + await productStore.getDefaultProductDescription(productStore.defaultLangId.value, productID.value) } const translateOrSave = async () => { - if (!productID.value || selectedLangId.value === defaultLangId.value) return + if (!productID.value || selectedLangId.value === productStore.defaultLangId.value) return translating.value = true try { - const res = await productStore.translateProductDescription(productID.value, defaultLangId.value, selectedLangId.value) + const res = await productStore.translateProductDescription(productID.value, productStore.defaultLangId.value, selectedLangId.value) if (res) { activeLangId.value = selectedLangId.value noTranslation.value = false @@ -256,7 +234,7 @@ const translateOrSave = async () => { const btnClass = (tab: string) => ['cursor-pointer px-4 py-2', activeTab.value === tab ? 'bg-blue-500 text-white' : ''] -/* USAGE EDIT */ + const enableUsageEdit = () => { originalUsage.value = displayedUsage.value usageEdit.enableEdit() @@ -277,7 +255,6 @@ const cancelUsageEdit = () => { isEditing.value = false } -/* DESCRIPTION EDIT */ const enableDescriptionEdit = () => { originalDescription.value = displayedDescription.value descriptionEdit.enableEdit() @@ -298,23 +275,15 @@ const cancelDescriptionEdit = () => { onMounted(async () => { const settings = await settingsStore.getSettings() if (settings) { - defaultLangId.value = settings.app.shop_default_language - selectedLangId.value = defaultLangId.value - activeLangId.value = defaultLangId.value + productStore.defaultLangId.value = settings.app.shop_default_language + selectedLangId.value = productStore.defaultLangId.value + activeLangId.value = productStore.defaultLangId.value } const id = route.params.product_id if (id) { productID.value = Number(id) - await productStore.getProductDescription(defaultLangId.value, productID.value) - await productStore.getDefaultProductDescription(defaultLangId.value, productID.value) + await productStore.getProductDescription(productStore.defaultLangId.value, productID.value) + await productStore.getDefaultProductDescription(productStore.defaultLangId.value, productID.value) } }) - - \ No newline at end of file diff --git a/bo/src/stores/product.ts b/bo/src/stores/product.ts index 9380645..9fa8164 100644 --- a/bo/src/stores/product.ts +++ b/bo/src/stores/product.ts @@ -1,3 +1,4 @@ +import { useSettingsStore } from '../stores/settings' import { defineStore } from 'pinia' import { ref } from 'vue' import { useFetchJson } from '@/composable/useFetchJson' @@ -28,6 +29,9 @@ export const useProductStore = defineStore('product', () => { const currentProduct = ref(null) const loading = ref(false) const error = ref(null) + const settingsStore = useSettingsStore() + + const defaultLangId = settingsStore.shopDefaultLanguage async function getProductDescription(langId = 1, productID: number) { loading.value = true @@ -45,17 +49,6 @@ export const useProductStore = defineStore('product', () => { } } - async function getDefaultProductDescription(langId: number, productID: number) { - try { - const response = await useFetchJson( - `/api/v1/restricted/product-translation/get-product-description?productID=${productID}&productLangID=${langId}` - ) - defaultProductDescription.value = response.items - } catch (e: unknown) { - console.error('Failed to load default product description:', e) - } - } - async function saveProductDescription(productID?: number, langId?: number, shopId = 1) { const id = productID || 1 try { @@ -83,7 +76,7 @@ export const useProductStore = defineStore('product', () => { error.value = null try { - const response = await useFetchJson(`/api/v1/restricted/product-description/translate-product-description?productID=${productID}&productShopID=1&productFromLangID=${fromLangId}&productToLangID=${toLangId}&model=OpenAI`) + const response = await useFetchJson(`/api/v1/restricted/product-description/translate-product-description?productID=${productID}&productShopID=1&productFromLangID=${fromLangId}&productToLangID=${defaultLangId}&model=OpenAI`) productDescription.value = response.items return response.items } catch (e: any) { @@ -104,8 +97,8 @@ export const useProductStore = defineStore('product', () => { currentProduct, loading, error, + defaultLangId, getProductDescription, - getDefaultProductDescription, clearCurrentProduct, saveProductDescription, translateProductDescription, diff --git a/bo/src/stores/settings.ts b/bo/src/stores/settings.ts index 5fff967..784e085 100644 --- a/bo/src/stores/settings.ts +++ b/bo/src/stores/settings.ts @@ -7,6 +7,7 @@ import { ref } from 'vue' export const useSettingsStore = defineStore('settings', () => { const settings = ref(null) const loaded = ref(false) + const shopDefaultLanguage= settings?.app?.shop_default_language async function getSettings(): Promise { if (loaded.value && settings.value) return settings.value @@ -16,5 +17,5 @@ export const useSettingsStore = defineStore('settings', () => { return resp.items } - return { settings, loaded, getSettings } + return { settings, loaded, shopDefaultLanguage, getSettings } }) From f9ae1e491ec986e96aa966a79c899c68b69dd0a8 Mon Sep 17 00:00:00 2001 From: Yakovenko Valeriia Date: Wed, 1 Apr 2026 13:40:56 +0200 Subject: [PATCH 2/2] fix: translate --- bo/components.d.ts | 1 + bo/src/components/admin/ProductDetailView.vue | 603 ++++++++++++------ bo/src/stores/product.ts | 202 ++++-- bo/src/stores/settings.ts | 4 +- 4 files changed, 579 insertions(+), 231 deletions(-) diff --git a/bo/components.d.ts b/bo/components.d.ts index 5f92a0b..1aebac6 100644 --- a/bo/components.d.ts +++ b/bo/components.d.ts @@ -14,6 +14,7 @@ declare module 'vue' { CartDetails: typeof import('./src/components/customer/CartDetails.vue')['default'] CategoryMenu: typeof import('./src/components/inner/CategoryMenu.vue')['default'] CategoryMenuListing: typeof import('./src/components/inner/categoryMenuListing.vue')['default'] + copy: typeof import('./src/components/admin/ProductDetailView copy.vue')['default'] CountryCurrencySwitch: typeof import('./src/components/inner/CountryCurrencySwitch.vue')['default'] Cs_PrivacyPolicyView: typeof import('./src/components/terms/cs_PrivacyPolicyView.vue')['default'] Cs_TermsAndConditionsView: typeof import('./src/components/terms/cs_TermsAndConditionsView.vue')['default'] diff --git a/bo/src/components/admin/ProductDetailView.vue b/bo/src/components/admin/ProductDetailView.vue index df573e5..840dd43 100644 --- a/bo/src/components/admin/ProductDetailView.vue +++ b/bo/src/components/admin/ProductDetailView.vue @@ -1,49 +1,32 @@ + + + + + + \ No newline at end of file diff --git a/bo/src/stores/product.ts b/bo/src/stores/product.ts index 9fa8164..27e3ad7 100644 --- a/bo/src/stores/product.ts +++ b/bo/src/stores/product.ts @@ -1,8 +1,8 @@ -import { useSettingsStore } from '../stores/settings' import { defineStore } from 'pinia' import { ref } from 'vue' import { useFetchJson } from '@/composable/useFetchJson' import type { ProductDescription } from '@/types/product' +import { useSettingsStore } from './settings' export interface Product { id: number @@ -24,24 +24,20 @@ export interface ProductResponse { } export const useProductStore = defineStore('product', () => { - const productDescription = ref(null) - const defaultProductDescription = ref(null) - const currentProduct = ref(null) + const loading = ref(false) const error = ref(null) - const settingsStore = useSettingsStore() + const productDescription = ref() - const defaultLangId = settingsStore.shopDefaultLanguage - - async function getProductDescription(langId = 1, productID: number) { + async function getProductDescription(langId: number, productID: number) { loading.value = true error.value = null - try { const response = await useFetchJson( `/api/v1/restricted/product-translation/get-product-description?productID=${productID}&productLangID=${langId}` ) productDescription.value = response.items + console.log(productDescription, 'dfsfsdf'); } catch (e: unknown) { error.value = e instanceof Error ? e.message : 'Failed to load product description' } finally { @@ -49,34 +45,12 @@ export const useProductStore = defineStore('product', () => { } } - async function saveProductDescription(productID?: number, langId?: number, shopId = 1) { - const id = productID || 1 - try { - const data = await useFetchJson( - `/api/v1/restricted/product-description/save-product-description?productID=${id}&productShopID=${shopId}&productLangID=${langId ?? 1}`, - { - method: 'POST', - body: JSON.stringify({ - description: productDescription.value?.description, - description_short: productDescription.value?.description_short, - meta_description: productDescription.value?.meta_description, - available_now: productDescription.value?.available_now, - usage: productDescription.value?.usage - }) - } - ) - return data - } catch (e) { - console.error(e) - } - } - - async function translateProductDescription(productID: number, fromLangId: number, toLangId: number) { + async function translateProductDescription(productID: number, toLangId: number, defaultLangId: number, model: string = 'OpenAI') { loading.value = true error.value = null try { - const response = await useFetchJson(`/api/v1/restricted/product-description/translate-product-description?productID=${productID}&productShopID=1&productFromLangID=${fromLangId}&productToLangID=${defaultLangId}&model=OpenAI`) + const response = await useFetchJson(`/api/v1/restricted/product-translation/translate-product-description?productID=${productID}&productFromLangID=${defaultLangId}&productToLangID=${toLangId}&model=${model}`) productDescription.value = response.items return response.items } catch (e: any) { @@ -87,20 +61,166 @@ export const useProductStore = defineStore('product', () => { } } - function clearCurrentProduct() { - currentProduct.value = null + + function stripHtml(html: string) { + const div = document.createElement('div') + div.innerHTML = html + return div.textContent || div.innerText || '' + } + + async function saveProductDescription(productID?: number, langId?: number) { + const id = productID || 1 + const lang = langId || 1 + try { + const data = await useFetchJson( + `/api/v1/restricted/product-translation/save-product-description?productID=${id}&productLangID=${lang}`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + name: stripHtml(productDescription.value?.name || ''), + description: stripHtml(productDescription.value?.description || ''), + description_short: stripHtml(productDescription.value?.description_short || ''), + meta_title: stripHtml(productDescription.value?.meta_title || ''), + meta_description: stripHtml(productDescription.value?.meta_description || ''), + available_now: stripHtml(productDescription.value?.available_now || ''), + available_later: stripHtml(productDescription.value?.available_later || ''), + usage: stripHtml(productDescription.value?.usage || '') + }) + } + ) + return data + } catch (e) { + console.error(e) + } } return { productDescription, - defaultProductDescription, - currentProduct, loading, error, - defaultLangId, - getProductDescription, - clearCurrentProduct, - saveProductDescription, translateProductDescription, + getProductDescription, + saveProductDescription + } }) + + + +// import { defineStore } from 'pinia' +// import { ref } from 'vue' +// import { useFetchJson } from '@/composable/useFetchJson' +// import type { ProductDescription } from '@/types/product' +// import { useSettingsStore } from './settings' + +// export interface Product { +// id: number +// image: string +// name: string +// code: string +// inStock: boolean +// priceFrom: number +// priceTo: number +// count: number +// description?: string +// howToUse?: string +// productDetails?: string +// } + +// export interface ProductResponse { +// items: Product[] +// items_count: number +// } + +// export const useProductStore = defineStore('product', () => { +// const productDescription = ref() +// const currentProduct = ref(null) +// const loading = ref(false) +// const error = ref(null) + +// async function getProductDescription(langId = 1, productID: number) { +// loading.value = true +// error.value = null + +// try { +// const response = await useFetchJson( +// `/api/v1/restricted/product-translation/get-product-description?productID=${productID}&productLangID=${langId}` +// ) +// productDescription.value = response.items +// console.log(productDescription, 'dfsfsdf'); + +// } catch (e: unknown) { +// error.value = e instanceof Error ? e.message : 'Failed to load product description' +// } finally { +// loading.value = false +// } +// } +// function stripHtml(html: string) { +// const div = document.createElement('div') +// div.innerHTML = html +// return div.textContent || div.innerText || '' +// } +// async function saveProductDescription(productID?: number, langId?: number) { +// const id = productID || 1 +// const lang = langId || 1 +// try { +// const data = await useFetchJson( +// `/api/v1/restricted/product-translation/save-product-description?productID=${id}&productLangID=${lang}`, +// { +// method: 'POST', +// headers: { +// 'Content-Type': 'application/json' +// }, +// body: JSON.stringify({ +// name: stripHtml(productDescription.value?.name || ''), +// description: stripHtml(productDescription.value?.description || ''), +// description_short: stripHtml(productDescription.value?.description_short || ''), +// meta_title: stripHtml(productDescription.value?.meta_title || ''), +// meta_description: stripHtml(productDescription.value?.meta_description || ''), +// available_now: stripHtml(productDescription.value?.available_now || ''), +// available_later: stripHtml(productDescription.value?.available_later || ''), +// usage: stripHtml(productDescription.value?.usage || '') +// }) +// } +// ) +// return data +// } catch (e) { +// console.error(e) +// } +// } + +// const defaultLangId = ref(1) +// async function translateProductDescription(productID: number, fromLangId: number, defaultLangId: number, model: string = 'OpenAI') { +// loading.value = true +// error.value = null + +// try { +// const response = await useFetchJson(`/api/v1/restricted/product-translation/translate-product-description?productID=${productID}&productFromLangID=${fromLangId}&productToLangID=${defaultLangId}&model=${model}`) +// productDescription.value = response.items +// return response.items +// } catch (e: any) { +// error.value = e?.message || 'Failed to translate product description' +// console.error('Failed to translate product description:', e) +// } finally { +// loading.value = false +// } +// } + +// function clearCurrentProduct() { +// currentProduct.value = null +// } + +// return { +// productDescription, +// currentProduct, +// loading, +// error, +// getProductDescription, +// clearCurrentProduct, +// saveProductDescription, +// translateProductDescription, +// } +// }) \ No newline at end of file diff --git a/bo/src/stores/settings.ts b/bo/src/stores/settings.ts index 784e085..6231629 100644 --- a/bo/src/stores/settings.ts +++ b/bo/src/stores/settings.ts @@ -2,12 +2,12 @@ import { useFetchJson } from '@/composable/useFetchJson' import type { Resp } from '@/types' import type { Settings } from '@/types/settings' import { defineStore } from 'pinia' -import { ref } from 'vue' +import { computed, ref } from 'vue' export const useSettingsStore = defineStore('settings', () => { const settings = ref(null) const loaded = ref(false) - const shopDefaultLanguage= settings?.app?.shop_default_language + const shopDefaultLanguage = computed(() => settings.value?.app?.shop_default_language ?? 1) async function getSettings(): Promise { if (loaded.value && settings.value) return settings.value