Compare commits
3 Commits
2bfe97f6fe
...
551b5ef77d
| Author | SHA1 | Date | |
|---|---|---|---|
| 551b5ef77d | |||
| a7f69c854a | |||
| 7e8e9897e1 |
@@ -3,21 +3,50 @@
|
||||
<p class="cursor-pointer" @click="backFromProduct()">Back to products</p>
|
||||
<div class="container my-10 mx-auto ">
|
||||
<div
|
||||
class="flex items-end justify-between gap-4 mb-6 bg-(--second-light) dark:bg-(--main-dark) border border-(--border-light) dark:border-(--border-dark) p-4 rounded-md">
|
||||
<div class="flex items-center gap-3" v-if="!isTranslations">
|
||||
<p class="text-red-500 text-md whitespace-nowrap">Translate from Polish to</p>
|
||||
<USelect v-model="toLangId" :items="availableLangs" value-key="id" label-key="name"
|
||||
placeholder="Select language" class="w-48">
|
||||
<template #selected="{ modelValue }">
|
||||
{{ modelValue }}
|
||||
</template>
|
||||
<template #item-label="{ item }">
|
||||
<div class="flex items-center gap-2">
|
||||
<span>{{ item.flag }}</span>
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</USelect>
|
||||
class=" gap-4 mb-6 bg-(--second-light) dark:bg-(--main-dark) border border-(--border-light) dark:border-(--border-dark) p-4 rounded-md">
|
||||
<div v-if="!isShowProductView" class="flex items-end justify-between">
|
||||
<div class="flex items-center gap-3" v-if="!isTranslations">
|
||||
<p class="text-red-500 text-md whitespace-nowrap">Translate from Polish to</p>
|
||||
<USelect v-model="toLangId" :items="availableLangs" value-key="id" label-key="name"
|
||||
placeholder="Select language" class="w-48">
|
||||
<template #selected="{ modelValue }">
|
||||
{{ modelValue }}
|
||||
</template>
|
||||
<template #item-label="{ item }">
|
||||
<div class="flex items-center gap-2">
|
||||
<span>{{ item.flag }}</span>
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</USelect>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-3 items-end justify-end">
|
||||
<UButton @click="() => {
|
||||
fetchForLanguage(toLangId)
|
||||
isShowProductView = true
|
||||
}" color="primary" v-if="isTranslations === false"
|
||||
class="text-white bg-(--accent-blue-light) dark:bg-(--accent-blue-dark) px-12!">
|
||||
Show product
|
||||
</UButton>
|
||||
<UButton @click="translateToSelectedLanguage" color="primary" :loading="translating"
|
||||
v-if="isTranslations === false"
|
||||
class="text-white bg-(--accent-blue-light) dark:bg-(--accent-blue-dark) px-12!">
|
||||
Translate
|
||||
</UButton>
|
||||
<div v-else class="flex gap-3 items-end justify-end">
|
||||
<UButton @click="() => {
|
||||
toLangId = settingStore.shopDefaultLanguage
|
||||
isTranslations = false
|
||||
}" color="primary" class="text-white bg-(--accent-blue-light) dark:bg-(--accent-blue-dark) px-12!">
|
||||
Cancel
|
||||
</UButton>
|
||||
<UButton color="primary" @click="productStore.saveProductDescription(productID, toLangId)"
|
||||
class="text-white bg-(--accent-blue-light) dark:bg-(--accent-blue-dark) px-12!">
|
||||
Save
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<UButton @click="isShowProductView = false" color="primary"
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
<template>
|
||||
<div class="flex flex-col">
|
||||
<p class="text-sm">Country/Currency:</p>
|
||||
<USelectMenu v-model="country" :items="countries"
|
||||
class="w-44 bg-(--main-light) dark:bg-(--black) rounded-md hover:none! text-sm!" valueKey="id"
|
||||
:searchInput="false">
|
||||
<template #default="{ modelValue }">
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="font-medium dark:text-white text-black whitespace-nowrap">{{ modelValue.name }} / {{
|
||||
currentCountry?.ps_currency.iso_code }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #item-leading="{ item }">
|
||||
<div class="flex items-center rounded-md cursor-pointer transition-colors">
|
||||
<span class="ml-2 dark:text-white text-black font-medium">{{ item.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</div>
|
||||
<USelectMenu v-model="country" :items="countries" value-key="id"
|
||||
class="w-48 bg-(--main-light) dark:bg-(--black) rounded-md shadow-sm" :searchInput="false">
|
||||
<template #default>
|
||||
<div class="flex flex-col items-start leading-tight">
|
||||
<span class="text-xs text-gray-400">
|
||||
Country/Currency
|
||||
</span>
|
||||
<span class="font-medium dark:text-white text-black">
|
||||
{{ country?.name }} / {{ country?.ps_currency.iso_code }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #item-leading="{ item }">
|
||||
<div class="flex items-center gap-2 cursor-pointer">
|
||||
<span class="text-lg">{{ item.flag }}</span>
|
||||
<span class="font-medium dark:text-white text-black">
|
||||
{{ item.name }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { countries, currentCountry, switchLocalization } from '@/router/langs'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useCookie } from '@/composable/useCookie'
|
||||
import { computed, watch } from 'vue'
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
const cookie = useCookie()
|
||||
|
||||
const country = computed({
|
||||
get() {
|
||||
return currentCountry.value
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
<template>
|
||||
<div class="flex flex-col">
|
||||
<p class="text-sm">Language:</p>
|
||||
<USelectMenu v-model="locale" :items="langs"
|
||||
class="w-40 bg-(--main-light) dark:bg-(--black) rounded-md shadow-sm hover:none!" valueKey="iso_code"
|
||||
:searchInput="false">
|
||||
<template #default="{ modelValue }">
|
||||
<div class="flex items-center gap-1">
|
||||
<!-- <span class="text-md dark:text-white text-black">{{langs.find(x => x.iso_code == modelValue)?.flag}}</span> -->
|
||||
<span class="font-medium dark:text-white text-black">{{langs.find(x => x.iso_code ==
|
||||
modelValue)?.name}}</span>
|
||||
<USelectMenu v-model="locale" :items="langs" value-key="iso_code"
|
||||
class="w-48 bg-(--main-light) dark:bg-(--black) rounded-md shadow-sm" :searchInput="false">
|
||||
<template #default>
|
||||
<div class="flex items-center gap-2">
|
||||
<!-- <span class="text-lg">{{ selectedLang?.flag }}</span> -->
|
||||
|
||||
<div class="flex flex-col leading-tight items-start">
|
||||
<span class="text-xs text-gray-400">
|
||||
Language
|
||||
</span>
|
||||
<span class="font-medium dark:text-white text-black">
|
||||
{{ selectedLang?.name || 'Select language' }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #item-leading="{ item }">
|
||||
<div class="flex items-center rounded-md cursor-pointer transition-colors">
|
||||
<!-- <span class="text-md ">{{ item.flag }}</span> -->
|
||||
<span class="ml-2 dark:text-white text-black font-medium">{{ item.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #item-leading="{ item }">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-lg">{{ item.flag }}</span>
|
||||
<span class="font-medium dark:text-white text-black">
|
||||
{{ item.name }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -31,6 +37,10 @@ import { i18n } from '@/plugins/02_i18n'
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
const selectedLang = computed(() =>
|
||||
langs.find(item => item.iso_code === locale.value)
|
||||
)
|
||||
|
||||
const cookie = useCookie()
|
||||
const locale = computed({
|
||||
get() {
|
||||
|
||||
@@ -1,19 +1,61 @@
|
||||
<!-- <script setup lang="ts">
|
||||
import TopBar from '@/components/TopBar.vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="h-screen grid grid-rows-[auto_1fr_auto]">
|
||||
<main class="pt-20 pb-10">
|
||||
<TopBar />
|
||||
<div class=" px-4">
|
||||
<div class="flex flex-1">
|
||||
<USidebar v-model:open="open" collapsible="icon" rail :ui="{
|
||||
container: 'h-full',
|
||||
inner: 'bg-elevated/25 divide-transparent',
|
||||
body: 'py-0'
|
||||
}">
|
||||
<template #header>
|
||||
<UDropdownMenu :items="teamsItems" :content="{ align: 'start', collisionPadding: 12 }"
|
||||
:ui="{ content: 'w-(--reka-dropdown-menu-trigger-width) min-w-48' }">
|
||||
<UButton v-bind="selectedTeam" trailing-icon="i-lucide-chevrons-up-down" color="neutral" variant="ghost"
|
||||
square class="w-full data-[state=open]:bg-elevated overflow-hidden" :ui="{
|
||||
trailingIcon: 'text-dimmed ms-auto'
|
||||
}" />
|
||||
</UDropdownMenu>
|
||||
</template>
|
||||
|
||||
<template #default="{ state }">
|
||||
<UNavigationMenu :key="state" :items="menuItems" orientation="vertical"
|
||||
:ui="{ link: 'p-1.5 overflow-hidden' }" />
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<UDropdownMenu :items="userItems" :content="{ align: 'center', collisionPadding: 12 }"
|
||||
:ui="{ content: 'w-(--reka-dropdown-menu-trigger-width) min-w-48' }">
|
||||
<UButton v-bind="user" :label="user?.name" trailing-icon="i-lucide-chevrons-up-down" color="neutral"
|
||||
variant="ghost" square class="w-full data-[state=open]:bg-elevated overflow-hidden" :ui="{
|
||||
trailingIcon: 'text-dimmed ms-auto'
|
||||
}" />
|
||||
</UDropdownMenu>
|
||||
</template>
|
||||
</USidebar>
|
||||
|
||||
<div class="flex-1 flex flex-col">
|
||||
<div class="h-(--ui-header-height) shrink-0 flex items-center justify-between px-4 border-b border-default">
|
||||
<UButton icon="i-lucide-panel-left" color="neutral" variant="ghost" aria-label="Toggle sidebar"
|
||||
@click="open = !open" />
|
||||
<div class="flex items-center gap-12">
|
||||
<div class="flex items-center gap-2">
|
||||
<CountryCurrencySwitch />
|
||||
<LangSwitch />
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<ThemeSwitch />
|
||||
<button v-if="authStore.isAuthenticated" @click="authStore.logout()"
|
||||
class="px-3 py-1.5 text-sm font-medium text-black dark:text-white hover:text-black dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-600 rounded-lg transition-colors border border-(--border-light) dark:border-(--border-dark) whitespace-nowrap">
|
||||
{{ $t('general.logout') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 p-4">
|
||||
<slot />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</template> -->
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
@@ -23,7 +65,7 @@ import { defineShortcuts, extractShortcuts } from '@nuxt/ui/runtime/composables/
|
||||
import { LabelTrans, TopMenuItem } from '@/types'
|
||||
|
||||
const open = ref(true)
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const colorMode = useColorMode()
|
||||
|
||||
const teams = ref([
|
||||
@@ -113,6 +155,9 @@ function getItems(state: 'collapsed' | 'expanded') {
|
||||
import { useRouter } from 'vue-router'
|
||||
import { currentLang } from '@/router/langs'
|
||||
import { useFetchJson } from '@/composable/useFetchJson'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import CountryCurrencySwitch from '@/components/inner/CountryCurrencySwitch.vue'
|
||||
import LangSwitch from '@/components/inner/LangSwitch.vue'
|
||||
|
||||
|
||||
const router = useRouter()
|
||||
@@ -246,49 +291,3 @@ const userItems = computed<DropdownMenuItem[][]>(() => [
|
||||
|
||||
defineShortcuts(extractShortcuts(teamsItems.value))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-1">
|
||||
<USidebar v-model:open="open" collapsible="icon" rail :ui="{
|
||||
container: 'h-full',
|
||||
inner: 'bg-elevated/25 divide-transparent',
|
||||
body: 'py-0'
|
||||
}">
|
||||
<template #header>
|
||||
<UDropdownMenu :items="teamsItems" :content="{ align: 'start', collisionPadding: 12 }"
|
||||
:ui="{ content: 'w-(--reka-dropdown-menu-trigger-width) min-w-48' }">
|
||||
<UButton v-bind="selectedTeam" trailing-icon="i-lucide-chevrons-up-down" color="neutral" variant="ghost"
|
||||
square class="w-full data-[state=open]:bg-elevated overflow-hidden" :ui="{
|
||||
trailingIcon: 'text-dimmed ms-auto'
|
||||
}" />
|
||||
</UDropdownMenu>
|
||||
</template>
|
||||
|
||||
<template #default="{ state }">
|
||||
<UNavigationMenu :key="state" :items="menuItems" orientation="vertical"
|
||||
:ui="{ link: 'p-1.5 overflow-hidden' }" />
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<UDropdownMenu :items="userItems" :content="{ align: 'center', collisionPadding: 12 }"
|
||||
:ui="{ content: 'w-(--reka-dropdown-menu-trigger-width) min-w-48' }">
|
||||
<UButton v-bind="user" :label="user?.name" trailing-icon="i-lucide-chevrons-up-down" color="neutral"
|
||||
variant="ghost" square class="w-full data-[state=open]:bg-elevated overflow-hidden" :ui="{
|
||||
trailingIcon: 'text-dimmed ms-auto'
|
||||
}" />
|
||||
</UDropdownMenu>
|
||||
</template>
|
||||
</USidebar>
|
||||
|
||||
<div class="flex-1 flex flex-col">
|
||||
<div class="h-(--ui-header-height) shrink-0 flex items-center px-4 border-b border-default">
|
||||
<UButton icon="i-lucide-panel-left" color="neutral" variant="ghost" aria-label="Toggle sidebar"
|
||||
@click="open = !open" />
|
||||
</div>
|
||||
|
||||
<div class="flex-1 p-4">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -45,6 +45,7 @@ export const useProductStore = defineStore('product', () => {
|
||||
}
|
||||
}
|
||||
|
||||
const translat = ref()
|
||||
const settingStore = useSettingsStore()
|
||||
async function translateProductDescription(productID: number, toLangId: number, model: string = 'Google') {
|
||||
loading.value = true
|
||||
@@ -53,6 +54,7 @@ export const useProductStore = defineStore('product', () => {
|
||||
try {
|
||||
const response = await useFetchJson<ProductDescription>(`/api/v1/restricted/product-translation/translate-product-description?productID=${productID}&productFromLangID=${settingStore.shopDefaultLanguage}&productToLangID=${toLangId}&model=${model}`)
|
||||
productDescription.value = response.items
|
||||
saveProductDescription(productID, toLangId)
|
||||
return response.items
|
||||
} catch (e: any) {
|
||||
error.value = e?.message || 'Failed to translate product description'
|
||||
@@ -62,16 +64,46 @@ export const useProductStore = defineStore('product', () => {
|
||||
}
|
||||
}
|
||||
|
||||
function fixHtml(html: string) {
|
||||
return html
|
||||
// 1. fix img
|
||||
.replace(/<img([^>]*?)>/g, '<img$1 />')
|
||||
|
||||
function stripHtml(html: string) {
|
||||
const div = document.createElement('div')
|
||||
div.innerHTML = html
|
||||
return div.textContent || div.innerText || ''
|
||||
// 2. escape text only
|
||||
.replace(/>([^<]+)</g, (match, text) => {
|
||||
const escaped = text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
|
||||
return `>${escaped}<`
|
||||
})
|
||||
}
|
||||
|
||||
async function saveProductDescription(productID?: number, langId?: number) {
|
||||
function fixAll(obj: any): any {
|
||||
if (typeof obj === 'string') {
|
||||
return fixHtml(obj)
|
||||
}
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map(fixAll)
|
||||
}
|
||||
|
||||
if (typeof obj === 'object' && obj !== null) {
|
||||
const result: any = {}
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
result[key] = fixAll(value)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
async function saveProductDescription(productID?: number, langId?: number | null) {
|
||||
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}`,
|
||||
@@ -81,14 +113,14 @@ export const useProductStore = defineStore('product', () => {
|
||||
'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 || ''),
|
||||
name: productDescription.value?.name || '',
|
||||
description: productDescription.value?.description || '',
|
||||
description_short: productDescription.value?.description_short || '',
|
||||
meta_title: productDescription.value?.meta_title || '',
|
||||
meta_description: productDescription.value?.meta_description || '',
|
||||
available_now: productDescription.value?.available_now || '',
|
||||
available_later: productDescription.value?.available_later || '',
|
||||
usage: productDescription.value?.usage || '',
|
||||
// delivery_in_stock: stripHtml(productDescription.value?.delivery_in_stock || '')
|
||||
})
|
||||
}
|
||||
@@ -103,126 +135,10 @@ export const useProductStore = defineStore('product', () => {
|
||||
productDescription,
|
||||
loading,
|
||||
error,
|
||||
translat,
|
||||
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<Product | null>(null)
|
||||
// const loading = ref(false)
|
||||
// const error = ref<string | null>(null)
|
||||
|
||||
// async function getProductDescription(langId = 1, productID: number) {
|
||||
// loading.value = true
|
||||
// error.value = null
|
||||
|
||||
// try {
|
||||
// const response = await useFetchJson<ProductDescription>(
|
||||
// `/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<ProductDescription>(`/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,
|
||||
// }
|
||||
// })
|
||||
})
|
||||
Reference in New Issue
Block a user