diff --git a/bo/src/components/admin/PageProducts.vue b/bo/src/components/admin/PageProducts.vue index 46cb4ae..8d3c1af 100644 --- a/bo/src/components/admin/PageProducts.vue +++ b/bo/src/components/admin/PageProducts.vue @@ -14,7 +14,7 @@ -
+ @@ -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 {