fix: product description
This commit is contained in:
@@ -11,7 +11,6 @@ body {
|
|||||||
|
|
||||||
.container{
|
.container{
|
||||||
max-width: 2100px;
|
max-width: 2100px;
|
||||||
margin: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@theme {
|
@theme {
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ const authStore = useAuthStore()
|
|||||||
<span class="font-semibold text-gray-900 dark:text-white">TimeTracker</span>
|
<span class="font-semibold text-gray-900 dark:text-white">TimeTracker</span>
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<!-- Right Side Actions -->
|
<!-- Right Side Actions -->
|
||||||
|
<RouterLink :to="{ name: 'product-detail' }">
|
||||||
|
product detail
|
||||||
|
</RouterLink>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<!-- Language Switcher -->
|
<!-- Language Switcher -->
|
||||||
<LangSwitch />
|
<LangSwitch />
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ const router = createRouter({
|
|||||||
children: [
|
children: [
|
||||||
{ path: '', component: () => import('../views/RepoChartView.vue'), name: 'home' },
|
{ path: '', component: () => import('../views/RepoChartView.vue'), name: 'home' },
|
||||||
{ path: 'products', component: () => import('../views/customer/ProductsView.vue'), name: 'products' },
|
{ path: 'products', component: () => import('../views/customer/ProductsView.vue'), name: 'products' },
|
||||||
{ path: 'products/:id', component: () => import('../views/customer/ProductDetailView.vue'), name: 'product-detail' },
|
{ path: 'products-datail/', component: () => import('../views/customer/ProductDetailView.vue'), name: 'product-detail' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { ref, computed } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useFetchJson } from '@/composable/useFetchJson'
|
import { useFetchJson } from '@/composable/useFetchJson'
|
||||||
|
|
||||||
export interface Product {
|
export interface Product {
|
||||||
@@ -22,31 +22,26 @@ export interface ProductResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useProductStore = defineStore('product', () => {
|
export const useProductStore = defineStore('product', () => {
|
||||||
const products = ref<Product[]>([])
|
const productDescription = ref()
|
||||||
const currentProduct = ref<Product | null>(null)
|
const currentProduct = ref<Product | null>(null)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const error = ref<string | null>(null)
|
const error = ref<string | null>(null)
|
||||||
|
|
||||||
// Fetch all products
|
// Fetch all products
|
||||||
async function fetchProducts() {
|
async function getProductDescription() {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
error.value = null
|
error.value = null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await useFetchJson<ProductResponse>('/api/v1/restricted/product-description', {
|
const response = await useFetchJson('/api/v1/restricted/product-description/get-product-description?productID=51&productShopID=1&productLangID=1')
|
||||||
method: 'GET',
|
productDescription.value = response
|
||||||
})
|
|
||||||
console.log(data)
|
|
||||||
const response = (data as any).items || data
|
|
||||||
products.value = response.items || response || []
|
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
error.value = e?.message || 'Failed to load products'
|
error.value = e?.message || 'Failed to load product description'
|
||||||
console.error('Failed to fetch products:', e)
|
console.error('Failed to fetch product description:', e)
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch single product by ID
|
// Fetch single product by ID
|
||||||
async function fetchProductById(id: number) {
|
async function fetchProductById(id: number) {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
@@ -74,11 +69,11 @@ console.log(data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
products,
|
productDescription,
|
||||||
currentProduct,
|
currentProduct,
|
||||||
loading,
|
loading,
|
||||||
error,
|
error,
|
||||||
fetchProducts,
|
getProductDescription,
|
||||||
fetchProductById,
|
fetchProductById,
|
||||||
clearCurrentProduct,
|
clearCurrentProduct,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import { useAuthStore } from '@/stores/auth'
|
|||||||
import { i18n } from '@/plugins/02_i18n'
|
import { i18n } from '@/plugins/02_i18n'
|
||||||
import type { TableColumn } from '@nuxt/ui'
|
import type { TableColumn } from '@nuxt/ui'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import ProductDetailView from './customer/ProductDetailView.vue'
|
||||||
|
import ProductsView from './customer/ProductsView.vue'
|
||||||
|
|
||||||
ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale)
|
ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale)
|
||||||
|
|
||||||
|
|||||||
@@ -1,304 +1,42 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
|
||||||
import { useAuthStore } from '@/stores/auth'
|
|
||||||
import { useProductStore, type Product } from '@/stores/product'
|
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
import type { TableColumn } from '@nuxt/ui'
|
|
||||||
import { h } from 'vue'
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
const router = useRouter()
|
|
||||||
const authStore = useAuthStore()
|
|
||||||
const productStore = useProductStore()
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
// Get product from route params
|
|
||||||
const productId = computed(() => Number(route.params.id))
|
|
||||||
const product = computed(() => productStore.currentProduct)
|
|
||||||
|
|
||||||
// Fetch product on mount
|
|
||||||
onMounted(() => {
|
|
||||||
if (productId.value) {
|
|
||||||
productStore.fetchProductById(productId.value)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Clear product when leaving
|
|
||||||
onUnmounted(() => {
|
|
||||||
productStore.clearCurrentProduct()
|
|
||||||
})
|
|
||||||
|
|
||||||
// Active tab for the four buttons
|
|
||||||
const activeTab = ref<'description' | 'howToUse' | 'productDetails' | 'documents'>('description')
|
|
||||||
|
|
||||||
// Mock variants (in real app, this would come from API)
|
|
||||||
const variants = computed(() => {
|
|
||||||
if (!product.value) return []
|
|
||||||
// Create mock variants based on the product
|
|
||||||
return [
|
|
||||||
{ ...product.value, id: product.value.id * 10 + 1, name: `${product.value.name} - Standard`, priceFrom: product.value.priceFrom, priceTo: product.value.priceFrom + 50, count: product.value.count },
|
|
||||||
{ ...product.value, id: product.value.id * 10 + 2, name: `${product.value.name} - Premium`, priceFrom: product.value.priceFrom + 100, priceTo: product.value.priceTo, count: Math.floor(product.value.count / 2) },
|
|
||||||
{ ...product.value, id: product.value.id * 10 + 3, name: `${product.value.name} - Bundle`, priceFrom: product.value.priceTo + 50, priceTo: product.value.priceTo + 150, count: Math.floor(product.value.count / 3) },
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
// Pagination for variants table
|
|
||||||
const page = ref(1)
|
|
||||||
const pageSize = 5
|
|
||||||
|
|
||||||
const totalItems = computed(() => variants.value.length)
|
|
||||||
|
|
||||||
const paginatedVariants = computed(() => {
|
|
||||||
const start = (page.value - 1) * pageSize
|
|
||||||
const end = start + pageSize
|
|
||||||
return variants.value.slice(start, end)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Table columns for variants
|
|
||||||
const columns = computed<TableColumn<any>[]>(() => [
|
|
||||||
{
|
|
||||||
accessorKey: 'image',
|
|
||||||
header: () => h('div', { class: 'text-center' }, t('products.image')),
|
|
||||||
cell: ({ row }) => h('img', {
|
|
||||||
src: row.getValue('image'),
|
|
||||||
alt: 'Product',
|
|
||||||
class: 'w-12 h-12 object-cover rounded'
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: 'name',
|
|
||||||
header: t('products.product_name'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: 'code',
|
|
||||||
header: t('products.product_code'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: 'inStock',
|
|
||||||
header: t('products.in_stock'),
|
|
||||||
cell: ({ row }) => {
|
|
||||||
const inStock = row.getValue('inStock')
|
|
||||||
return h('span', {
|
|
||||||
class: inStock ? 'text-green-600 font-medium' : 'text-red-600 font-medium'
|
|
||||||
}, inStock ? t('products.yes') : t('products.no'))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: 'price',
|
|
||||||
header: t('products.price'),
|
|
||||||
cell: ({ row }) => {
|
|
||||||
const priceFromVal = row.original.priceFrom
|
|
||||||
const priceToVal = row.original.priceTo
|
|
||||||
return `$${priceFromVal} - $${priceToVal}`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: 'count',
|
|
||||||
header: t('products.count'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'actions',
|
|
||||||
header: '',
|
|
||||||
cell: ({ row }) => h('div', { class: 'flex gap-2' }, [
|
|
||||||
h('button', {
|
|
||||||
class: 'px-3 py-1.5 text-sm font-medium bg-primary text-white rounded-lg hover:bg-blue-600 transition-colors',
|
|
||||||
onClick: () => addToCart(row.original)
|
|
||||||
}, t('products.add_to_cart')),
|
|
||||||
h('button', {
|
|
||||||
class: 'px-3 py-1.5 text-sm font-medium bg-gray-200 dark:bg-gray-700 text-black dark:text-white rounded-lg hover:bg-gray-300 dark:hover:bg-gray-600 transition-colors',
|
|
||||||
onClick: () => incrementCount(row.original)
|
|
||||||
}, '+')
|
|
||||||
])
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
// Actions
|
|
||||||
function addToCart(product: Product) {
|
|
||||||
console.log('Add to cart:', product)
|
|
||||||
}
|
|
||||||
|
|
||||||
function incrementCount(product: Product) {
|
|
||||||
product.count++
|
|
||||||
}
|
|
||||||
|
|
||||||
function goBack() {
|
|
||||||
router.push({ name: 'products' })
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<div class="container my-10">
|
||||||
<div class="p-6 bg-white dark:bg-(--black) min-h-screen font-sans">
|
<div id="textDescriptionRef" ref="textDescriptionRef" v-html="productStore.productDescription.description"></div>
|
||||||
<!-- Back Button -->
|
<button @click="create">create</button>
|
||||||
<button
|
<button @click="save">Save</button>
|
||||||
@click="goBack"
|
|
||||||
class="mb-4 px-4 py-2 text-sm font-medium text-black dark:text-white bg-gray-200 dark:bg-gray-700 rounded-lg hover:bg-gray-300 dark:hover:bg-gray-600 transition-colors"
|
|
||||||
>
|
|
||||||
← {{ t('products.back_to_list') }}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Loading State -->
|
|
||||||
<div v-if="productStore.loading" class="mb-4 p-3 bg-blue-100 text-blue-700 rounded">
|
|
||||||
{{ t('products.loading') }}...
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Error State -->
|
|
||||||
<div v-if="productStore.error" class="mb-4 p-3 bg-red-100 text-red-700 rounded">
|
|
||||||
{{ productStore.error }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="!authStore.isAuthenticated" class="mb-4 p-3 bg-yellow-100 text-yellow-700 rounded">
|
|
||||||
{{ t('products.login_to_view') }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="authStore.isAuthenticated && product && !productStore.loading">
|
|
||||||
<!-- Product Header: Image and Title -->
|
|
||||||
<div class="flex flex-col md:flex-row gap-8 mb-6">
|
|
||||||
<!-- Product Image -->
|
|
||||||
<div class="w-full md:w-1/3">
|
|
||||||
<img
|
|
||||||
:src="product.image"
|
|
||||||
:alt="product.name"
|
|
||||||
class="w-full h-auto rounded-lg shadow-lg object-cover"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Product Info -->
|
|
||||||
<div class="w-full md:w-2/3 flex flex-col justify-center">
|
|
||||||
<h1 class="text-3xl font-bold mb-4 text-black dark:text-white">{{ product.name }}</h1>
|
|
||||||
|
|
||||||
<!-- Description -->
|
|
||||||
<p class="text-gray-600 dark:text-gray-300 mb-4">{{ product.description }}</p>
|
|
||||||
|
|
||||||
<!-- Product Code -->
|
|
||||||
<div class="mb-4">
|
|
||||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">{{ t('products.product_code') }}: </span>
|
|
||||||
<span class="text-lg font-semibold text-black dark:text-white">{{ product.code }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Price -->
|
|
||||||
<div class="mb-4">
|
|
||||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">{{ t('products.price') }}: </span>
|
|
||||||
<span class="text-2xl font-bold text-primary">${{ product.priceFrom }} - ${{ product.priceTo }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Stock Status -->
|
|
||||||
<div class="mb-4">
|
|
||||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">{{ t('products.in_stock') }}: </span>
|
|
||||||
<span :class="product.inStock ? 'text-green-600 font-medium' : 'text-red-600 font-medium'">
|
|
||||||
{{ product.inStock ? t('products.yes') : t('products.no') }}
|
|
||||||
</span>
|
|
||||||
<span v-if="product.inStock" class="ml-2 text-gray-500">({{ product.count }} {{ t('products.available') }})</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- First Divider -->
|
|
||||||
<hr class="border-gray-300 dark:border-gray-600 my-6" />
|
|
||||||
|
|
||||||
<!-- Four Buttons -->
|
|
||||||
<div class="flex flex-wrap gap-2 mb-6">
|
|
||||||
<button
|
|
||||||
@click="activeTab = 'description'"
|
|
||||||
:class="[
|
|
||||||
'px-4 py-2 text-sm font-medium rounded-lg transition-colors',
|
|
||||||
activeTab === 'description'
|
|
||||||
? 'bg-primary text-white'
|
|
||||||
: 'bg-gray-200 dark:bg-gray-700 text-black dark:text-white hover:bg-gray-300 dark:hover:bg-gray-600'
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
{{ t('products.description') }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
@click="activeTab = 'howToUse'"
|
|
||||||
:class="[
|
|
||||||
'px-4 py-2 text-sm font-medium rounded-lg transition-colors',
|
|
||||||
activeTab === 'howToUse'
|
|
||||||
? 'bg-primary text-white'
|
|
||||||
: 'bg-gray-200 dark:bg-gray-700 text-black dark:text-white hover:bg-gray-300 dark:hover:bg-gray-600'
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
{{ t('products.how_to_use') }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
@click="activeTab = 'productDetails'"
|
|
||||||
:class="[
|
|
||||||
'px-4 py-2 text-sm font-medium rounded-lg transition-colors',
|
|
||||||
activeTab === 'productDetails'
|
|
||||||
? 'bg-primary text-white'
|
|
||||||
: 'bg-gray-200 dark:bg-gray-700 text-black dark:text-white hover:bg-gray-300 dark:hover:bg-gray-600'
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
{{ t('products.product_details') }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
@click="activeTab = 'documents'"
|
|
||||||
:class="[
|
|
||||||
'px-4 py-2 text-sm font-medium rounded-lg transition-colors',
|
|
||||||
activeTab === 'documents'
|
|
||||||
? 'bg-primary text-white'
|
|
||||||
: 'bg-gray-200 dark:bg-gray-700 text-black dark:text-white hover:bg-gray-300 dark:hover:bg-gray-600'
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
{{ t('products.documents') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Tab Content -->
|
|
||||||
<div class="mb-6 p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
|
|
||||||
<div v-if="activeTab === 'description'" class="text-black dark:text-white">
|
|
||||||
<h3 class="text-lg font-semibold mb-2">{{ t('products.description') }}</h3>
|
|
||||||
<p>{{ product.description }}</p>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="activeTab === 'howToUse'" class="text-black dark:text-white">
|
|
||||||
<h3 class="text-lg font-semibold mb-2">{{ t('products.how_to_use') }}</h3>
|
|
||||||
<p>{{ product.howToUse }}</p>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="activeTab === 'productDetails'" class="text-black dark:text-white">
|
|
||||||
<h3 class="text-lg font-semibold mb-2">{{ t('products.product_details') }}</h3>
|
|
||||||
<p>{{ product.productDetails }}</p>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="activeTab === 'documents'" class="text-black dark:text-white">
|
|
||||||
<h3 class="text-lg font-semibold mb-2">{{ t('products.documents') }}</h3>
|
|
||||||
<ul class="list-disc list-inside">
|
|
||||||
<li><a href="#" class="text-primary hover:underline">User Manual.pdf</a></li>
|
|
||||||
<li><a href="#" class="text-primary hover:underline">Technical Specifications.pdf</a></li>
|
|
||||||
<li><a href="#" class="text-primary hover:underline">Warranty Information.pdf</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Second Divider -->
|
|
||||||
<hr class="border-gray-300 dark:border-gray-600 my-6" />
|
|
||||||
|
|
||||||
<!-- Product Variants Section -->
|
|
||||||
<div>
|
|
||||||
<h2 class="text-2xl font-bold mb-4 text-black dark:text-white">{{ t('products.product_variants') }}</h2>
|
|
||||||
|
|
||||||
<!-- Variants Table -->
|
|
||||||
<div class="border border-(--border-light) dark:border-(--border-dark) rounded overflow-hidden">
|
|
||||||
<UTable
|
|
||||||
:data="paginatedVariants"
|
|
||||||
:columns="columns"
|
|
||||||
class="dark:text-white! text-dark"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Pagination -->
|
|
||||||
<div class="pt-4 flex justify-center items-center dark:text-white! text-dark">
|
|
||||||
<UPagination
|
|
||||||
v-model:page="page"
|
|
||||||
:page-count="pageSize"
|
|
||||||
:total="totalItems"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-else-if="authStore.isAuthenticated && !product && !productStore.loading && !productStore.error" class="text-center py-10">
|
|
||||||
<p class="text-gray-500 dark:text-gray-400">{{ t('products.product_not_found') }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { useProductStore } from '@/stores/product'
|
||||||
|
|
||||||
|
|
||||||
|
const productStore = useProductStore()
|
||||||
|
const textDescriptionRef = ref<HTMLElement | null>(null)
|
||||||
|
|
||||||
|
await productStore.getProductDescription()
|
||||||
|
|
||||||
|
const save = () => {
|
||||||
|
const myDiv = document.getElementById("textDescriptionRef");
|
||||||
|
Array.from(myDiv.children).forEach(item => {
|
||||||
|
item.setAttribute('contenteditable', 'false')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const create = () => {
|
||||||
|
const myDiv = document.getElementById("textDescriptionRef");
|
||||||
|
Array.from(myDiv.children).forEach(item => {
|
||||||
|
item.setAttribute('contenteditable', 'true')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.images {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 70px;
|
||||||
|
margin: 20px 0 20px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -12,7 +12,6 @@ const authStore = useAuthStore()
|
|||||||
const productStore = useProductStore()
|
const productStore = useProductStore()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
// Search filters
|
|
||||||
const searchName = ref('')
|
const searchName = ref('')
|
||||||
const searchCode = ref('')
|
const searchCode = ref('')
|
||||||
const priceFromFilter = ref<number | null>(null)
|
const priceFromFilter = ref<number | null>(null)
|
||||||
@@ -24,28 +23,30 @@ const pageSize = 5
|
|||||||
|
|
||||||
// Fetch products on mount
|
// Fetch products on mount
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
productStore.fetchProducts()
|
productStore.getProductDescription()
|
||||||
})
|
})
|
||||||
|
|
||||||
// Filtered products
|
// Filtered products
|
||||||
const filteredProducts = computed(() => {
|
// const filteredProducts = computed(() => {
|
||||||
return productStore.products.filter(product => {
|
// console.log(productStore.products);
|
||||||
const matchesName = product.name.toLowerCase().includes(searchName.value.toLowerCase())
|
|
||||||
const matchesCode = product.code.toLowerCase().includes(searchCode.value.toLowerCase())
|
|
||||||
const matchesPriceFrom = priceFromFilter.value === null || product.priceFrom >= priceFromFilter.value
|
|
||||||
const matchesPriceTo = priceToFilter.value === null || product.priceTo <= priceToFilter.value
|
|
||||||
|
|
||||||
return matchesName && matchesCode && matchesPriceFrom && matchesPriceTo
|
// return productStore.products.filter(product => {
|
||||||
})
|
// const matchesName = product.name.toLowerCase().includes(searchName.value.toLowerCase())
|
||||||
})
|
// const matchesCode = product.code.toLowerCase().includes(searchCode.value.toLowerCase())
|
||||||
|
// const matchesPriceFrom = priceFromFilter.value === null || product.priceFrom >= priceFromFilter.value
|
||||||
|
// const matchesPriceTo = priceToFilter.value === null || product.priceTo <= priceToFilter.value
|
||||||
|
|
||||||
const totalItems = computed(() => filteredProducts.value.length)
|
// return matchesName && matchesCode && matchesPriceFrom && matchesPriceTo
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
|
||||||
const paginatedProducts = computed(() => {
|
// const totalItems = computed(() => filteredProducts.value.length)
|
||||||
const start = (page.value - 1) * pageSize
|
|
||||||
const end = start + pageSize
|
// const paginatedProducts = computed(() => {
|
||||||
return filteredProducts.value.slice(start, end)
|
// const start = (page.value - 1) * pageSize
|
||||||
})
|
// const end = start + pageSize
|
||||||
|
// return filteredProducts.value.slice(start, end)
|
||||||
|
// })
|
||||||
|
|
||||||
// Reset page when filters change
|
// Reset page when filters change
|
||||||
function resetPage() {
|
function resetPage() {
|
||||||
@@ -154,6 +155,7 @@ function clearFilters() {
|
|||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="p-6 bg-white dark:bg-(--black) min-h-screen font-sans">
|
<div class="p-6 bg-white dark:bg-(--black) min-h-screen font-sans">
|
||||||
|
<div v-html="productStore.products"></div>
|
||||||
<h1 class="text-2xl font-bold mb-6 text-black dark:text-white">{{ t('products.title') }}</h1>
|
<h1 class="text-2xl font-bold mb-6 text-black dark:text-white">{{ t('products.title') }}</h1>
|
||||||
|
|
||||||
<div v-if="!authStore.isAuthenticated" class="mb-4 p-3 bg-yellow-100 text-yellow-700 rounded">
|
<div v-if="!authStore.isAuthenticated" class="mb-4 p-3 bg-yellow-100 text-yellow-700 rounded">
|
||||||
@@ -220,34 +222,33 @@ function clearFilters() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Products Table -->
|
<!-- Products Table -->
|
||||||
<div class="border border-(--border-light) dark:border-(--border-dark) rounded overflow-hidden">
|
<!-- <div class="border border-(--border-light) dark:border-(--border-dark) rounded overflow-hidden">
|
||||||
<UTable
|
<UTable
|
||||||
:data="paginatedProducts"
|
:data="paginatedProducts"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
class="dark:text-white! text-dark"
|
class="dark:text-white! text-dark"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<!-- Empty State -->
|
<!-- Empty State -->
|
||||||
<div v-if="filteredProducts.length === 0" class="text-center py-10 text-gray-500 dark:text-gray-400">
|
<!-- <div v-if="filteredProducts.length === 0" class="text-center py-10 text-gray-500 dark:text-gray-400">
|
||||||
{{ t('products.no_products') }}
|
{{ t('products.no_products') }}
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<!-- Pagination -->
|
<!-- Pagination -->
|
||||||
<div v-if="filteredProducts.length > 0" class="pt-4 flex justify-center items-center dark:text-white! text-dark">
|
<!-- <div v-if="filteredProducts.length > 0" class="pt-4 flex justify-center items-center dark:text-white! text-dark">
|
||||||
<UPagination
|
<UPagination
|
||||||
v-model:page="page"
|
v-model:page="page"
|
||||||
:page-count="pageSize"
|
:page-count="pageSize"
|
||||||
:total="totalItems"
|
:total="totalItems"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<!-- Results count -->
|
<!-- Results count -->
|
||||||
<div v-if="filteredProducts.length > 0" class="text-sm text-gray-600 dark:text-gray-400 text-center">
|
<!-- <div v-if="filteredProducts.length > 0" class="text-sm text-gray-600 dark:text-gray-400 text-center">
|
||||||
{{ t('products.showing') }} {{ paginatedProducts.length }} {{ t('products.of') }} {{ totalItems }} {{ t('products.products') }}
|
{{ t('products.showing') }} {{ paginatedProducts.length }} {{ t('products.of') }} {{ totalItems }} {{ t('products.products') }}
|
||||||
</div>
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user