From 12f624972155e0e718f1f3dbfd384a8e814c5006 Mon Sep 17 00:00:00 2001 From: Arina Yakovenko Date: Tue, 31 Mar 2026 12:22:21 +0200 Subject: [PATCH 01/45] fix: requests --- bo/src/components/admin/PageProducts.vue | 71 ++++-- bo/src/components/admin/ProductDetailView.vue | 228 +++++++++--------- bo/src/components/customer/PageProducts.vue | 2 +- bo/src/router/menu.ts | 3 +- bo/src/stores/product.ts | 2 +- 5 files changed, 170 insertions(+), 136 deletions(-) diff --git a/bo/src/components/admin/PageProducts.vue b/bo/src/components/admin/PageProducts.vue index 09cbe53..3ae8e8d 100644 --- a/bo/src/components/admin/PageProducts.vue +++ b/bo/src/components/admin/PageProducts.vue @@ -1,15 +1,20 @@ -
+ @@ -213,8 +213,8 @@ async function fetchProductList() { function goToProduct(productId: number) { router.push({ - name: 'product-detail', - params: { id: productId } + name: 'customer-product-details', + params: { product_id: productId } }) } @@ -352,6 +352,7 @@ const columns: TableColumn[] = [ return h(UButton, { onClick: () => { console.log('Clicked', row.original) + goToProduct(row.original.product_id) }, color: 'primary', variant: 'solid' diff --git a/bo/src/components/admin/ProductDetailView.vue b/bo/src/components/admin/ProductDetailView.vue index 19dd288..830fedf 100644 --- a/bo/src/components/admin/ProductDetailView.vue +++ b/bo/src/components/admin/ProductDetailView.vue @@ -1,33 +1,61 @@ @@ -132,118 +146,175 @@ import { ref, computed, onMounted, watch } from 'vue' import { useRoute } from 'vue-router' import { useProductStore } from '@/stores/product' -import { useEditable } from '@/composable/useConteditable' +import { useSettingsStore } from '@/stores/settings' import { langs } from '@/router/langs' -import type { Language } from '@/types' +import { useEditable } from '@/composable/useConteditable' import Default from '@/layouts/default.vue' const route = useRoute() -const activeTab = ref('description') const productStore = useProductStore() +const settingsStore = useSettingsStore() + +const activeTab = ref('description') const translating = ref(false) +const noTranslation = ref(false) -const isEditing = ref(false) - -const availableLangs = computed(() => langs) - -const selectedLanguage = ref('en') - -const currentLangId = ref(2) -const productID = ref(0) - -// Watch for language changes and refetch product description -watch(selectedLanguage, async (newLang: string) => { - if (productID.value) { - await fetchForLanguage(newLang) - } -}) - -const fetchForLanguage = async (langCode: string) => { - const lang = langs.find((l: Language) => l.iso_code === langCode) - if (lang && productID.value) { - await productStore.getProductDescription(lang.id, productID.value) - currentLangId.value = lang.id - } -} - -const translateToSelectedLanguage = async () => { - const targetLang = langs.find((l: Language) => l.iso_code === selectedLanguage.value) - if (targetLang && currentLangId.value && productID.value) { - translating.value = true - try { - await productStore.translateProductDescription(productID.value, currentLangId.value, targetLang.id) - currentLangId.value = targetLang.id - } finally { - translating.value = false - } - } -} - -onMounted(async () => { - const id = route.params.product_id - if (id) { - productID.value = Number(id) - await fetchForLanguage(selectedLanguage.value) - } -}) +const defaultLangId = ref(0) +const selectedLangId = ref(0) +const activeLangId = ref(0) +const productID = ref(0) const descriptionRef = ref(null) const usageRef = ref(null) const descriptionEdit = useEditable(descriptionRef) const usageEdit = useEditable(usageRef) +const isEditing = ref(false) -const originalDescription = ref('') const originalUsage = ref('') +const originalDescription = ref('') -const saveDescription = async () => { - descriptionEdit.disableEdit() - await productStore.saveProductDescription(productID.value) -} +const langOptions = computed(() => langs.filter(l => l.active !== false)) -const cancelDescriptionEdit = () => { - if (descriptionRef.value) { - descriptionRef.value.innerHTML = originalDescription.value +watch(selectedLangId, async (newLang) => { + if (!productID.value) return + + if (newLang === defaultLangId.value) { + noTranslation.value = false + activeLangId.value = defaultLangId.value + await productStore.getProductDescription(defaultLangId.value, productID.value) + await productStore.getDefaultProductDescription(defaultLangId.value, productID.value) + return } - descriptionEdit.disableEdit() + + 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 productName = computed(() => { + if (activeLangId.value !== 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) { + return productStore.productDescription?.description || '' + } + return productStore.defaultProductDescription?.description || '' +}) + +const displayedShortDescription = computed(() => { + if (activeLangId.value !== defaultLangId.value && !noTranslation.value) { + return productStore.productDescription?.description_short || '' + } + return productStore.defaultProductDescription?.description_short || '' +}) + +const displayedUsage = computed(() => { + if (activeLangId.value !== defaultLangId.value && !noTranslation.value) { + return productStore.productDescription?.usage || '' + } + return productStore.defaultProductDescription?.usage || '' +}) + +const goBackToDefault = async () => { + selectedLangId.value = defaultLangId.value + activeLangId.value = defaultLangId.value + noTranslation.value = false + await productStore.getProductDescription(defaultLangId.value, productID.value) + await productStore.getDefaultProductDescription(defaultLangId.value, productID.value) } +const translateOrSave = async () => { + if (!productID.value || selectedLangId.value === defaultLangId.value) return + + translating.value = true + try { + const res = await productStore.translateProductDescription(productID.value, defaultLangId.value, selectedLangId.value) + if (res) { + activeLangId.value = selectedLangId.value + noTranslation.value = false + } + } finally { + translating.value = false + } +} + +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() + isEditing.value = true +} +const saveUsage = async () => { + if (usageRef.value && productStore.productDescription) { + productStore.productDescription.usage = usageRef.value.innerHTML + } + usageEdit.disableEdit() + isEditing.value = false + await productStore.saveProductDescription(productID.value, activeLangId.value, 1) +} +const cancelUsageEdit = () => { + if (usageRef.value) usageRef.value.innerHTML = originalUsage.value + if (productStore.productDescription) productStore.productDescription.usage = originalUsage.value + usageEdit.disableEdit() + isEditing.value = false +} + +/* DESCRIPTION EDIT */ const enableDescriptionEdit = () => { - if (descriptionRef.value) { - originalDescription.value = descriptionRef.value.innerHTML - } + originalDescription.value = displayedDescription.value descriptionEdit.enableEdit() } - -const enableEdit = () => { - if (usageRef.value) { - originalUsage.value = usageRef.value.innerHTML +const saveDescription = async () => { + if (descriptionRef.value && productStore.productDescription) { + productStore.productDescription.description = descriptionRef.value.innerHTML } - isEditing.value = true - usageEdit.enableEdit() + descriptionEdit.disableEdit() + await productStore.saveProductDescription(productID.value, activeLangId.value, 1) +} +const cancelDescriptionEdit = () => { + if (descriptionRef.value) descriptionRef.value.innerHTML = originalDescription.value + if (productStore.productDescription) productStore.productDescription.description = originalDescription.value + descriptionEdit.disableEdit() } -const saveText = () => { - usageEdit.disableEdit() - isEditing.value = false - productStore.saveProductDescription(productID.value) -} - -const cancelEdit = () => { - if (usageRef.value) { - usageRef.value.innerHTML = originalUsage.value +onMounted(async () => { + const settings = await settingsStore.getSettings() + if (settings) { + defaultLangId.value = settings.app.shop_default_language + selectedLangId.value = defaultLangId.value + activeLangId.value = defaultLangId.value } - usageEdit.disableEdit() - isEditing.value = false -} - + 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) + } +}) \ No newline at end of file diff --git a/bo/src/stores/product.ts b/bo/src/stores/product.ts index fe533db..9380645 100644 --- a/bo/src/stores/product.ts +++ b/bo/src/stores/product.ts @@ -23,7 +23,8 @@ export interface ProductResponse { } export const useProductStore = defineStore('product', () => { - const productDescription = ref() + const productDescription = ref(null) + const defaultProductDescription = ref(null) const currentProduct = ref(null) const loading = ref(false) const error = ref(null) @@ -36,9 +37,7 @@ export const useProductStore = defineStore('product', () => { const response = await useFetchJson( `/api/v1/restricted/product-translation/get-product-description?productID=${productID}&productLangID=${langId}` ) - console.log(response, 'dfsfsdf'); productDescription.value = response.items - } catch (e: unknown) { error.value = e instanceof Error ? e.message : 'Failed to load product description' } finally { @@ -46,21 +45,31 @@ export const useProductStore = defineStore('product', () => { } } - async function saveProductDescription(productID?: number) { + 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 { const data = await useFetchJson( - `/api/v1/restricted/product-description/save-product-description?productID=${id}&productShopID=1&productLangID=1`, + `/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 - }) + 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 @@ -91,10 +100,12 @@ export const useProductStore = defineStore('product', () => { return { productDescription, + defaultProductDescription, currentProduct, loading, error, getProductDescription, + getDefaultProductDescription, clearCurrentProduct, saveProductDescription, translateProductDescription, diff --git a/bo/src/stores/settings.ts b/bo/src/stores/settings.ts index da907cb..5fff967 100644 --- a/bo/src/stores/settings.ts +++ b/bo/src/stores/settings.ts @@ -2,13 +2,19 @@ import { useFetchJson } from '@/composable/useFetchJson' import type { Resp } from '@/types' import type { Settings } from '@/types/settings' import { defineStore } from 'pinia' +import { ref } from 'vue' export const useSettingsStore = defineStore('settings', () => { - async function getSettings() { - const { items } = await useFetchJson>('/api/v1/settings',) - console.log(items); + const settings = ref(null) + const loaded = ref(false) + + async function getSettings(): Promise { + if (loaded.value && settings.value) return settings.value + const resp = await useFetchJson('/api/v1/settings') + settings.value = resp.items + loaded.value = true + return resp.items } - getSettings() - return {} + return { settings, loaded, getSettings } }) diff --git a/bo/src/types/product.d.ts b/bo/src/types/product.d.ts index 80d2e85..bfc9787 100644 --- a/bo/src/types/product.d.ts +++ b/bo/src/types/product.d.ts @@ -5,5 +5,6 @@ description_short: string meta_description: string available_now: string + delivery_in_stock?: string usage: string } \ No newline at end of file diff --git a/bo/src/types/settings.d.ts b/bo/src/types/settings.d.ts index 79069a2..4466473 100644 --- a/bo/src/types/settings.d.ts +++ b/bo/src/types/settings.d.ts @@ -12,6 +12,7 @@ export interface App { environment: string base_url: string password_regex: string + shop_default_language: number } export interface Server { From 729b54ca1a9045c6a70b94433906ece1ecb2436e Mon Sep 17 00:00:00 2001 From: Arina Yakovenko Date: Wed, 1 Apr 2026 10:52:08 +0200 Subject: [PATCH 03/45] fix: currency/county --- bo/components.d.ts | 7 +- bo/src/components/TopBar.vue | 76 ++++++++++--------- bo/src/components/TopBarLogin.vue | 4 +- bo/src/components/admin/PageProducts.vue | 2 +- bo/src/components/customer/PageProducts.vue | 2 +- .../{categoryMenu.vue => CategoryMenu.vue} | 0 .../inner/CountryCurrencySwitch.vue | 68 +++++++++++++++++ .../inner/{langSwitch.vue => LangSwitch.vue} | 0 .../{themeSwitch.vue => ThemeSwitch.vue} | 0 bo/src/plugins/02_i18n.ts | 3 +- bo/src/router/langs.ts | 31 +++++++- bo/src/types/lang.d.ts | 17 +++++ 12 files changed, 161 insertions(+), 49 deletions(-) rename bo/src/components/inner/{categoryMenu.vue => CategoryMenu.vue} (100%) create mode 100644 bo/src/components/inner/CountryCurrencySwitch.vue rename bo/src/components/inner/{langSwitch.vue => LangSwitch.vue} (100%) rename bo/src/components/inner/{themeSwitch.vue => ThemeSwitch.vue} (100%) diff --git a/bo/components.d.ts b/bo/components.d.ts index 51b00ed..5f92a0b 100644 --- a/bo/components.d.ts +++ b/bo/components.d.ts @@ -12,13 +12,14 @@ export {} declare module 'vue' { export interface GlobalComponents { CartDetails: typeof import('./src/components/customer/CartDetails.vue')['default'] - CategoryMenu: typeof import('./src/components/inner/categoryMenu.vue')['default'] + CategoryMenu: typeof import('./src/components/inner/CategoryMenu.vue')['default'] CategoryMenuListing: typeof import('./src/components/inner/categoryMenuListing.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'] En_PrivacyPolicyView: typeof import('./src/components/terms/en_PrivacyPolicyView.vue')['default'] En_TermsAndConditionsView: typeof import('./src/components/terms/en_TermsAndConditionsView.vue')['default'] - LangSwitch: typeof import('./src/components/inner/langSwitch.vue')['default'] + LangSwitch: typeof import('./src/components/inner/LangSwitch.vue')['default'] PageAddresses: typeof import('./src/components/customer/PageAddresses.vue')['default'] PageCarts: typeof import('./src/components/customer/PageCarts.vue')['default'] PageOrders: typeof import('./src/components/customer/PageOrders.vue')['default'] @@ -34,7 +35,7 @@ declare module 'vue' { ProductVariants: typeof import('./src/components/customer/components/ProductVariants.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] - ThemeSwitch: typeof import('./src/components/inner/themeSwitch.vue')['default'] + ThemeSwitch: typeof import('./src/components/inner/ThemeSwitch.vue')['default'] TopBar: typeof import('./src/components/TopBar.vue')['default'] TopBarLogin: typeof import('./src/components/TopBarLogin.vue')['default'] UAlert: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Alert.vue')['default'] diff --git a/bo/src/components/TopBar.vue b/bo/src/components/TopBar.vue index 38571bd..6f15a2f 100644 --- a/bo/src/components/TopBar.vue +++ b/bo/src/components/TopBar.vue @@ -1,13 +1,49 @@ + + - - diff --git a/bo/src/components/TopBarLogin.vue b/bo/src/components/TopBarLogin.vue index 7382dc8..d1820b9 100644 --- a/bo/src/components/TopBarLogin.vue +++ b/bo/src/components/TopBarLogin.vue @@ -1,6 +1,6 @@ diff --git a/bo/src/components/inner/langSwitch.vue b/bo/src/components/inner/LangSwitch.vue similarity index 100% rename from bo/src/components/inner/langSwitch.vue rename to bo/src/components/inner/LangSwitch.vue diff --git a/bo/src/components/inner/themeSwitch.vue b/bo/src/components/inner/ThemeSwitch.vue similarity index 100% rename from bo/src/components/inner/themeSwitch.vue rename to bo/src/components/inner/ThemeSwitch.vue diff --git a/bo/src/plugins/02_i18n.ts b/bo/src/plugins/02_i18n.ts index ae41822..ca1ef5b 100644 --- a/bo/src/plugins/02_i18n.ts +++ b/bo/src/plugins/02_i18n.ts @@ -1,11 +1,12 @@ import { useFetchJson } from '@/composable/useFetchJson' -import { initLangs, langs } from '@/router/langs' +import { initCountryCurrency, initLangs, langs } from '@/router/langs' import { watch } from 'vue' import { createI18n, type PathValue } from 'vue-i18n' // const x = await initLangs() +await initCountryCurrency() export const i18ninstall = createI18n({ legacy: false, // you must set `false`, to use Composition API locale: 'en', diff --git a/bo/src/router/langs.ts b/bo/src/router/langs.ts index 520516f..54a965c 100644 --- a/bo/src/router/langs.ts +++ b/bo/src/router/langs.ts @@ -1,12 +1,16 @@ import { useCookie } from "@/composable/useCookie" import { useFetchJson } from "@/composable/useFetchJson" -import type { Language } from "@/types" +import type { Country, Language } from "@/types" import { reactive, ref } from "vue" export const langs = reactive([] as Language[]) export const currentLang = ref() -const deflang = ref() +export const countries = reactive([] as Country[]) +export const currentCountry = ref() + +const defLang = ref() +const defCountry = ref() const cookie = useCookie() // Get available language codes for route matching // export const availableLocales = computed(() => langs.map((l) => l.lang_code)) @@ -22,8 +26,27 @@ export async function initLangs() { if (cc) { idfromcookie = langs.find((x) => x.id == parseInt(cc)) } - deflang.value = items.find((x) => x.is_default == true) - currentLang.value = idfromcookie ?? deflang.value + defLang.value = items.find((x) => x.is_default == true) + currentLang.value = idfromcookie ?? defLang.value + } catch (error) { + console.error('Failed to fetch languages:', error) + } +} + +// Initialize country/currency from API + +export async function initCountryCurrency() { + try { + const { items } = await useFetchJson('/api/v1/restricted/langs-and-countries/get-countries') + countries.push(...items) + + let idfromcookie = null + const cc = cookie.getCookie('country_id') + if (cc) { + idfromcookie = langs.find((x) => x.id == parseInt(cc)) + } + defCountry.value = items.find((x) => x.id === defLang.value?.id) + currentCountry.value = idfromcookie ?? defCountry.value } catch (error) { console.error('Failed to fetch languages:', error) } diff --git a/bo/src/types/lang.d.ts b/bo/src/types/lang.d.ts index 4ee9e08..b21a3e4 100644 --- a/bo/src/types/lang.d.ts +++ b/bo/src/types/lang.d.ts @@ -12,3 +12,20 @@ export interface Language { active: boolean flag: string } + +export interface Country { + id: number + name: string + flag: string + currency_id: string + ps_currency: { + id_currency: string + name: string + iso_code: string + numeric_iso_code: string + precision: number + conversion_rate: number + deleted: boolean + active: boolean + } +} From b48a143b4021e0f90540f38514beb052d82d5a3c Mon Sep 17 00:00:00 2001 From: Yakovenko Valeriia Date: Wed, 1 Apr 2026 10:52:34 +0200 Subject: [PATCH 04/45] 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 05/45] 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 From aa57d38bd6f8c56f82f1e2c43d7d101bc8c57db8 Mon Sep 17 00:00:00 2001 From: Arina Yakovenko Date: Wed, 1 Apr 2026 13:41:21 +0200 Subject: [PATCH 06/45] fix: selects --- bo/src/App.vue | 34 ++++++++++++ bo/src/app.config.ts | 13 +++-- bo/src/assets/error.svg | 1 + bo/src/assets/main.css | 33 ++++++----- bo/src/components/TopBar.vue | 35 ++++++------ bo/src/components/admin/PageProducts.vue | 16 ++++-- bo/src/components/admin/ProductDetailView.vue | 53 +++++++++--------- .../inner/CountryCurrencySwitch.vue | 55 +++++++------------ bo/src/components/inner/LangSwitch.vue | 54 ++++++++---------- bo/src/components/inner/ThemeSwitch.vue | 27 ++++++++- bo/src/router/index.ts | 4 +- bo/src/router/langs.ts | 11 ++++ 12 files changed, 197 insertions(+), 139 deletions(-) create mode 100644 bo/src/assets/error.svg diff --git a/bo/src/App.vue b/bo/src/App.vue index 6bc0c10..df9f0b4 100644 --- a/bo/src/App.vue +++ b/bo/src/App.vue @@ -7,3 +7,37 @@ import { RouterView } from 'vue-router' + + + diff --git a/bo/src/app.config.ts b/bo/src/app.config.ts index 0089c78..245e575 100644 --- a/bo/src/app.config.ts +++ b/bo/src/app.config.ts @@ -8,9 +8,9 @@ export const uiOptions: NuxtUIOptions = { } }, button: { - slots: { - base: 'border! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0!', - }, + // slots: { + // base: 'border! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0!', + // }, }, input: { slots: { @@ -40,9 +40,10 @@ export const uiOptions: NuxtUIOptions = { }, selectMenu: { slots: { - base: 'border! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0!', - content: 'border! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0! z-80 text-(--black)! dark:text-white!', - itemLeadingIcon: 'text-(--black)! dark:text-white!' + // base: 'border! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0!', + // content: 'border! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0! z-80', + // content: 'border! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0! z-80 text-(--black)! dark:text-white!', + // itemLeadingIcon: 'text-(--black)! dark:text-white!' } }, diff --git a/bo/src/assets/error.svg b/bo/src/assets/error.svg new file mode 100644 index 0000000..b2f2c75 --- /dev/null +++ b/bo/src/assets/error.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/bo/src/assets/main.css b/bo/src/assets/main.css index 77bddb8..6d9ebce 100644 --- a/bo/src/assets/main.css +++ b/bo/src/assets/main.css @@ -9,7 +9,7 @@ body { font-family: "Inter", sans-serif; } -.container{ +.container { max-width: 2100px; } @@ -30,7 +30,7 @@ body { /* text */ --accent-blue-dark: #3B82F6; - --accent-blue-light:#2563EB; + --accent-blue-light: #2563EB; --text-dark: #FFFEFB; /* placeholder */ @@ -52,22 +52,25 @@ body { --ui-bg-elevated: var(--color-gray-500); --ui-error: var(--dark-red); --border: var(--border-dark); - --tw-border-style: var(--border-dark); + --tw-border-style: var(--border-dark); } - .label-form { - @apply text-(--gray) dark:text-(--gray-dark) pl-0 md:pl-6 leading-none; - } +.label-form { + @apply text-(--gray) dark:text-(--gray-dark) pl-0 md:pl-6 leading-none; +} - .title { - @apply font-medium text-[19px] sm:text-xl md:text-[22px] leading-none text-(--black) dark:text-(--main-light); - } +.title { + @apply font-medium text-[19px] sm:text-xl md:text-[22px] leading-none text-(--black) dark:text-(--main-light); +} - .column-title { - @apply md:ml-[25px] mb-[25px] sm:mb-[30px]; - } +.column-title { + @apply md:ml-[25px] mb-[25px] sm:mb-[30px]; +} - .form-title { - @apply text-(--accent-green) dark:text-(--accent-green-dark) font-medium; - } +.form-title { + @apply text-(--accent-green) dark:text-(--accent-green-dark) font-medium; +} +.blue-button { + @apply bg-info! text-white! +} \ No newline at end of file diff --git a/bo/src/components/TopBar.vue b/bo/src/components/TopBar.vue index 6f15a2f..d009bd0 100644 --- a/bo/src/components/TopBar.vue +++ b/bo/src/components/TopBar.vue @@ -1,32 +1,32 @@ @@ -173,6 +173,7 @@ const UInput = resolveComponent('UInput') const UButton = resolveComponent('UButton') const UIcon = resolveComponent('UIcon') +import errorImg from '@/assets/error.svg' const columns: TableColumn[] = [ { accessorKey: 'product_id', @@ -209,9 +210,13 @@ const columns: TableColumn[] = [ cell: ({ row }) => { return h('img', { src: row.getValue('image_link') as string, - style: 'width:40px;height:40px;object-fit:cover;' + style: 'width:40px;height:40px;object-fit:cover;', + onError: (e: Event) => { + const target = e.target as HTMLImageElement + target.src = errorImg + } }) - } + }, }, { accessorKey: 'name', @@ -272,8 +277,9 @@ const columns: TableColumn[] = [ onClick: () => { goToProduct(row.original.product_id) }, - color: 'primary', - variant: 'solid' + class: 'cursor-pointer', + color: 'info', + variant: 'soft' }, () => 'Show product') }, } diff --git a/bo/src/components/admin/ProductDetailView.vue b/bo/src/components/admin/ProductDetailView.vue index 830fedf..fbbec2b 100644 --- a/bo/src/components/admin/ProductDetailView.vue +++ b/bo/src/components/admin/ProductDetailView.vue @@ -1,22 +1,19 @@ \ No newline at end of file + \ No newline at end of file diff --git a/bo/src/components/customer/CartDetails.vue b/bo/src/components/customer/CartDetails.vue index 4fef8a8..498752e 100644 --- a/bo/src/components/customer/CartDetails.vue +++ b/bo/src/components/customer/CartDetails.vue @@ -1,6 +1,5 @@ + \ No newline at end of file diff --git a/bo/src/components/customer/PageProduct.vue b/bo/src/components/customer/PageProduct.vue index 4a992d6..ad7c3e8 100644 --- a/bo/src/components/customer/PageProduct.vue +++ b/bo/src/components/customer/PageProduct.vue @@ -1,6 +1,5 @@ + \ No newline at end of file diff --git a/bo/src/views/HomeView.vue b/bo/src/views/HomeView.vue index 08cec22..b653373 100644 --- a/bo/src/views/HomeView.vue +++ b/bo/src/views/HomeView.vue @@ -1,9 +1,6 @@ + home View + \ No newline at end of file diff --git a/bo/src/views/RepoChartView.vue b/bo/src/views/RepoChartView.vue index c94fdc0..b8783a7 100644 --- a/bo/src/views/RepoChartView.vue +++ b/bo/src/views/RepoChartView.vue @@ -15,7 +15,6 @@ import { useAuthStore } from '@/stores/customer/auth' import { i18n } from '@/plugins/02_i18n' import type { TableColumn } from '@nuxt/ui' import { useI18n } from 'vue-i18n' -import Default from '@/layouts/default.vue' ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale) @@ -182,8 +181,7 @@ const columns = computed[]>(() => [ \ No newline at end of file + \ No newline at end of file diff --git a/bo/src/views/StorageView.vue b/bo/src/views/StorageView.vue index d055ed7..0a85dbe 100644 --- a/bo/src/views/StorageView.vue +++ b/bo/src/views/StorageView.vue @@ -7,6 +7,5 @@ \ No newline at end of file diff --git a/i18n/migrations/20260302163100_routes.sql b/i18n/migrations/20260302163100_routes.sql index 989c896..2ee9aec 100644 --- a/i18n/migrations/20260302163100_routes.sql +++ b/i18n/migrations/20260302163100_routes.sql @@ -10,17 +10,6 @@ CREATE TABLE IF NOT EXISTS b2b_routes ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; --- INSERT IGNORE INTO b2b_routes --- (name, path, component, meta, active) --- VALUES --- ('root', '', '', '{"trans": "route.root"}', 0), --- ('home', '', '/views/HomeView.vue', '{"trans": "route.home"}', 1), --- ('login', 'login', '/views/LoginView.vue', '{"guest":true}', 1), --- ('register', 'register', '/views/RegisterView.vue', '{"guest":true}', 1), --- ('password-recovery', 'password-recovery', '/views/PasswordRecoveryView.vue', '{"guest":true}', 1), --- ('reset-password', 'reset-password', '/views/ResetPasswordView.vue', '{"guest":true}', 1), --- ('verify-email', 'verify-email', '/views/VerifyEmailView.vue', '{"guest":true}', 1); - INSERT IGNORE INTO `b2b_routes` (`id`, `name`, `path`, `component`, `meta`, `active`) VALUES (1, 'root', '', '', '{"trans": "route.root"}', 0), (2, 'home', '', '/views/HomeView.vue', '{"trans": "route.home"}', 1), @@ -29,16 +18,59 @@ INSERT IGNORE INTO `b2b_routes` (`id`, `name`, `path`, `component`, `meta`, `act (5, 'password-recovery', 'password-recovery', '/views/PasswordRecoveryView.vue', '{"guest":true}', 1), (6, 'reset-password', 'reset-password', '/views/ResetPasswordForm.vue', '{"guest":true}', 1), (7, 'verify-email', 'verify-email', '/views/VerifyEmailView.vue', '{"guest":true}', 1), -(8, 'category', 'category/:category_id-:link_rewrite', '/views/CategoryView.vue', '{"guest":true}', 1), (9, 'admin-products-category', 'products/:category_id-:link_rewrite', '/components/admin/PageProducts.vue', '{ - "guest": true +"guest":true, +"name": "Products Category" }', 1), -(10, 'customer-addresses', 'addresses', '/components/customer/PageAddresses.vue', '{"guest":true}', 1), -(11, 'customer-carts', 'carts', '/components/customer/PageCarts.vue', '{"guest":true}', 1), -(12, 'customer-orders', 'orders', '/components/customer/PageOrders.vue', '{"guest":true}', 1), -(13, 'customer-statistic', 'statistic', '/components/customer/PageStatistic.vue', '{"guest":true}', 1), -(14, 'customer-product-details', 'products/:product_id/:link_rewrite', '/components/admin/ProductDetailView.vue', '{"guest":true}', 1), -(15, 'admin-products', 'products', '/components/admin/PageProducts.vue', '{"guest":true}', 1); +(10, 'customer-addresses', 'addresses', '/components/customer/PageAddresses.vue', '{ +"guest":true, +"name": "Addresses" +}', 1), +(11, 'customer-cart', 'cart/:id', '/components/customer/PageCart.vue', '{ +"guest":true, +"name": "Cart" +}', 1), +(12, 'customer-orders', 'orders', '/components/customer/PageOrders.vue', '{ +"guest":true, +"name": "Order" +}', 1), +(13, 'customer-statistic', 'statistic', '/components/customer/PageStatistic.vue', '{ +"guest":true, +"name": "Statistic" +} +', 1), +(14, 'admin-product-details', 'products/:product_id', '/components/admin/ProductDetailView.vue', '{ +"guest":true, +"name": "Products" +} +', 1), +(15, 'admin-products', 'products', '/components/admin/PageProducts.vue', '{ +"guest":true, +"name": "Products" +} +', 1), +(16, 'admin-users-list', 'users-list', '/components/admin/UsersList.vue', '{ +"guest":true, +"name": "Client List" +} +', 1), +(17, 'customer-management-profile', ':user_id/profile', '/components/customer-management/Profile.vue', '{ +"guest":true, +"name": "Profile" +} +', 1), +(18, 'admin-users-search', 'users-search', '/components/admin/UsersSearch.vue', '{ +"guest":true, +"name": "Search Clients" +} +', 1), +(19, 'customer-storage-file', 'file-storage', '/components/customer/StorageFileBrowser.vue', '{ +"guest":true, +"name": "File Storage" +}', 1), +(20, 'customer-products', 'products', '/components/customer/PageProducts.vue', '{ "guest":true, "name": "Products" }', 1), +(21, 'customer-product-details', 'products/:product_id', '/components/customer/PageProduct.vue', '{ "guest":true, "name": "Products" }', 1), +(22, 'customer-page-carts', 'carts', '/components/customer/PageCarts.vue', '{ "guest":true, "name": "Carts" }', 1); CREATE TABLE IF NOT EXISTS b2b_top_menu ( menu_id INT AUTO_INCREMENT NOT NULL, @@ -71,21 +103,6 @@ INSERT IGNORE INTO `b2b_top_menu` (`menu_id`, `label`, `parent_id`, `params`, `a }, "icon" : "quill:list" }', 1, '{"route":{"name":"admin-products","params":{"locale":""}}}', 1, 1), -(3, '{ - "name": "customer-carts", - "trans": { - "pl": { - "label": "Carts" - }, - "en": { - "label": "Carts" - }, - "de": { - "label": "Carts" - } - }, - "icon" : "proicons:cart" -}', 1, '{"route":{"name":"customer-carts","params":{"locale":""}}}', 1, 1), (4, '{ "name": "customer-addresses", "trans": { @@ -138,7 +155,138 @@ INSERT IGNORE INTO `b2b_top_menu` (`menu_id`, `label`, `parent_id`, `params`, `a } } }', 1, 1), -(9, '{"name":"carts","trans":{"pl":{"label":"Koszyki"},"en":{"label":"Carts"},"de":{"label":"Warenkörbe"}}}', 3, '{"route":{"name":"home","params":{"locale":""}}}', 1, 1); +(10, '{ + "name": "customer-storage-file", + "trans": { + "pl": { + "label": "File Storage" + }, + "en": { + "label": "File Storage" + }, + "de": { + "label": "File Storage" + } + }, + "icon": "carbon:volume-file-storage" +}', 1, '{ + "route": { + "name": "customer-storage-file", + "params": { + "locale": "" + } + } +}', 1, 1), +(12, '{ + "name": "admin-users-list", + "trans": { + "pl": { + "label": "Client List" + }, + "en": { + "label": "Client List" + }, + "de": { + "label": "Client List" + } + }, + "icon": "tdesign:user-list" +}', 1, '{ + "route": { + "name": "admin-users-list", + "params": { + "locale": "" + } + } +}', 1, 1), +(13, '{ + "name": "admin-users-search", + "trans": { + "pl": { + "label": "Search Clients" + }, + "en": { + "label": "Search Clients" + }, + "de": { + "label": "Search Clients" + } + }, + "icon": "material-symbols:search" +}', 1, '{ + "route": { + "name": "admin-users-search", + "params": { + "locale": "" + } + } +}', 1, 1), +(14, '{ + "name": "customer-management-profile", + "trans": { + "pl": { + "label": "Profile" + }, + "en": { + "label": "Profile" + }, + "de": { + "label": "Profile" + } + }, + "icon": "akar-icons:statistic-up" +}', 1, '{ + "route": { + "name": "customer-management-profile", + "params": { + "locale": "" + } + } +}', 1, 1), +(15, '{ + "name": "customer-products", + "trans": { + "pl": { + "label": "Products" + }, + "en": { + "label": "Products" + }, + "de": { + "label": "Products" + } + }, + "icon": "akar-icons:statistic-up" +}', 1, '{ + "route": { + "name": "customer-products", + "params": { + "locale": "" + } + } +}', 1, 1), +(16, '{ + "name": "customer-page-carts", + "trans": { + "pl": { + "label": "Carts" + }, + "en": { + "label": "Carts" + }, + "de": { + "label": "Carts" + } + }, + "icon": "proicons:cart1" +}', 1, '{ + "route": { + "name": "customer-page-carts", + "params": { + "locale": "" + } + } +}', 1, 1); -- +goose Down diff --git a/i18n/migrations/20260302163123_create_tables_data.sql b/i18n/migrations/20260302163123_create_tables_data.sql index 94c1233..fce9709 100644 --- a/i18n/migrations/20260302163123_create_tables_data.sql +++ b/i18n/migrations/20260302163123_create_tables_data.sql @@ -95,5 +95,24 @@ INSERT INTO `b2b_route_roles` (`route_id`, `role_id`) VALUES (7, '1'), (7, '2'), (7, '3'), -(7, '4'); +(7, '4'), +(9, '2'), +(9, '3'), +(10, '1'), +(11, '1'), +(12, '1'), +(13, '1'), +(14, '2'), +(14, '3'), +(15, '2'), +(15, '3'), +(16, '2'), +(16, '3'), +(17, '1'), +(18, '2'), +(18, '3'), +(19, '1'), +(20, '1'), +(21, '1'), +(22, '1'); -- +goose Down \ No newline at end of file From 5a0765426a0bbfefd02b65acdeb735ff86dcd5de Mon Sep 17 00:00:00 2001 From: Yakovenko Valeriia Date: Wed, 15 Apr 2026 13:53:05 +0200 Subject: [PATCH 41/45] fix: cart --- bo/src/components/customer/PageCart.vue | 3 --- bo/src/components/customer/PageCarts.vue | 4 +--- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/bo/src/components/customer/PageCart.vue b/bo/src/components/customer/PageCart.vue index f4f0583..e453be4 100644 --- a/bo/src/components/customer/PageCart.vue +++ b/bo/src/components/customer/PageCart.vue @@ -1,15 +1,12 @@ \ No newline at end of file + +store.fetchAddresses() + diff --git a/bo/src/components/customer/PageCarts.vue b/bo/src/components/customer/PageCarts.vue index 9d894b7..3c15233 100644 --- a/bo/src/components/customer/PageCarts.vue +++ b/bo/src/components/customer/PageCarts.vue @@ -1,69 +1,69 @@