Compare commits
2 Commits
product-pr
...
test
| Author | SHA1 | Date | |
|---|---|---|---|
| 99fe11fbeb | |||
|
|
1ea50af96a |
@@ -1,6 +1,8 @@
|
||||
package restricted
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"git.ma-al.com/goc_daniel/b2b/app/config"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/listProductsService"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/i18n"
|
||||
@@ -50,7 +52,20 @@ func (h *ListProductsHandler) GetListing(c fiber.Ctx) error {
|
||||
// "override_currency": c.Query("override_currency", ""),
|
||||
// }
|
||||
|
||||
listing, err := h.listProductsService.GetListing(paging, filters)
|
||||
id_shop_attribute := c.Query("shopID")
|
||||
id_shop, err := strconv.Atoi(id_shop_attribute)
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||
}
|
||||
|
||||
id_lang, err := strconv.Atoi(c.Cookies("lang_id", "2"))
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||
}
|
||||
|
||||
listing, err := h.listProductsService.GetListing(uint(id_shop), uint(id_lang), paging, filters)
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
|
||||
@@ -61,6 +61,13 @@ type Product struct {
|
||||
State uint `gorm:"column:state" json:"state" form:"state"`
|
||||
DeliveryDays uint `gorm:"column:delivery_days" json:"delivery_days" form:"delivery_days"`
|
||||
}
|
||||
type ProductInList struct {
|
||||
ID uint `gorm:"column:id_product;primaryKey" json:"product_id" form:"product_id"`
|
||||
Name string `gorm:"column:name;default:'no name'" json:"name" form:"name"`
|
||||
Price float64 `gorm:"column:price;default:0.0" json:"price" form:"price"`
|
||||
ActiveAsProduct uint `gorm:"column:active;default:0" json:"active_as_product" form:"active_as_product"`
|
||||
ActiveInShop uint `gorm:"column:active;default:0" json:"active_in_shop" form:"active_in_shop"`
|
||||
}
|
||||
|
||||
type ProductFilters struct {
|
||||
Sort string `json:"sort,omitempty" query:"sort,omitempty" example:"price,asc;name,desc"` // sort rule
|
||||
|
||||
@@ -17,8 +17,8 @@ func New() *ListProductsService {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ListProductsService) GetListing(p find.Paging, filters *filters.FiltersList) (find.Found[model.Product], error) {
|
||||
var products find.Found[model.Product]
|
||||
func (s *ListProductsService) GetListing(id_shop uint, id_lang uint, p find.Paging, filters *filters.FiltersList) (find.Found[model.ProductInList], error) {
|
||||
var products find.Found[model.ProductInList]
|
||||
|
||||
// currencyIso := c.Cookies("currency_iso", "")
|
||||
// countryIso := c.Cookies("country_iso", "")
|
||||
@@ -30,7 +30,7 @@ func (s *ListProductsService) GetListing(p find.Paging, filters *filters.Filters
|
||||
// countryIso = overrides["override_country"]
|
||||
// }
|
||||
|
||||
products, err := s.listProductsRepo.GetListing(p, filters)
|
||||
products, err := s.listProductsRepo.GetListing(id_shop, id_lang, p, filters)
|
||||
if err != nil {
|
||||
return products, err
|
||||
}
|
||||
|
||||
1
bo/components.d.ts
vendored
1
bo/components.d.ts
vendored
@@ -19,6 +19,7 @@ declare module 'vue' {
|
||||
PageAddresses: typeof import('./src/components/customer/PageAddresses.vue')['default']
|
||||
PageCart: typeof import('./src/components/customer/PageCart.vue')['default']
|
||||
PageProductCardFull: typeof import('./src/components/customer/PageProductCardFull.vue')['default']
|
||||
PageProductsList: typeof import('./src/components/customer/PageProductsList.vue')['default']
|
||||
Pl_PrivacyPolicyView: typeof import('./src/components/terms/pl_PrivacyPolicyView.vue')['default']
|
||||
Pl_TermsAndConditionsView: typeof import('./src/components/terms/pl_TermsAndConditionsView.vue')['default']
|
||||
ProductCustomization: typeof import('./src/components/customer/components/ProductCustomization.vue')['default']
|
||||
|
||||
@@ -31,6 +31,9 @@ const authStore = useAuthStore()
|
||||
<RouterLink :to="{ name: 'cart' }">
|
||||
Cart
|
||||
</RouterLink>
|
||||
<RouterLink :to="{ name: 'products-list' }">
|
||||
Products List
|
||||
</RouterLink>
|
||||
<div class="flex items-center gap-2">
|
||||
<!-- Language Switcher -->
|
||||
<LangSwitch />
|
||||
|
||||
@@ -14,9 +14,9 @@ const authStore = useAuthStore()
|
||||
<!-- Logo -->
|
||||
<RouterLink :to="{ name: 'home' }" class="flex items-center gap-2">
|
||||
<div class="w-8 h-8 rounded-lg bg-primary text-white flex items-center justify-center">
|
||||
<UIcon name="i-heroicons-clock" class="w-5 h-5" />
|
||||
<UIcon name="carbon:ibm-webmethods-b2b-integration" class="w-5 h-5" />
|
||||
</div>
|
||||
<span class="font-semibold text-gray-900 dark:text-white">TimeTracker</span>
|
||||
<span class="font-semibold text-gray-900 dark:text-white">B2B</span>
|
||||
</RouterLink>
|
||||
<!-- Right Side Actions -->
|
||||
<div class="flex items-center gap-2">
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="container mx-auto mt-10">
|
||||
<div class="flex flex-col mb-6">
|
||||
<div class="flex flex-col gap-5 mb-6">
|
||||
<h1 class="text-2xl font-bold text-black dark:text-white">{{ t('Addresses') }}</h1>
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex md:flex-row flex-col justify-between items-start md:items-center gap-5 md:gap-0">
|
||||
<div class="flex gap-2 items-center">
|
||||
<UInput v-model="searchQuery" type="text" :placeholder="t('Search address')"
|
||||
class="bg-white dark:bg-gray-800 text-black dark:text-white absolute" />
|
||||
@@ -16,7 +16,6 @@
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="paginatedAddresses.length" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<div v-for="address in paginatedAddresses" :key="address.id"
|
||||
class="border border-(--border-light) dark:border-(--border-dark) rounded-md p-4 bg-(--second-light) dark:bg-(--main-dark) hover:shadow-md transition-shadow">
|
||||
@@ -35,11 +34,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-center py-8 text-gray-500 dark:text-gray-400">{{ t('No addresses found') }}</div>
|
||||
|
||||
<div class="mt-6 flex justify-center">
|
||||
<UPagination v-model:page="page" :total="totalItems" :page-size="pageSize" />
|
||||
</div>
|
||||
|
||||
<UModal v-model:open="showModal" :overlay="true" class="max-w-md mx-auto">
|
||||
<template #content>
|
||||
<div class="p-6 flex flex-col gap-6">
|
||||
@@ -74,7 +71,6 @@
|
||||
</div>
|
||||
</template>
|
||||
</UModal>
|
||||
|
||||
<UModal v-model:open="showDeleteConfirm" :overlay="true" class="max-w-md mx-auto">
|
||||
<template #content>
|
||||
<div class="p-6 flex flex-col gap-3">
|
||||
@@ -106,7 +102,6 @@ import { useI18n } from 'vue-i18n'
|
||||
|
||||
const addressStore = useAddressStore()
|
||||
const { t } = useI18n()
|
||||
|
||||
const searchQuery = ref('')
|
||||
const showModal = ref(false)
|
||||
const isEditing = ref(false)
|
||||
@@ -122,17 +117,14 @@ const totalItems = computed(() => addressStore.totalItems)
|
||||
const pageSize = addressStore.pageSize
|
||||
|
||||
watch(page, (newPage) => addressStore.setPage(newPage))
|
||||
|
||||
watch(searchQuery, (val) => {
|
||||
addressStore.setSearchQuery(val)
|
||||
})
|
||||
|
||||
function openCreateModal() {
|
||||
resetForm()
|
||||
isEditing.value = false
|
||||
showModal.value = true
|
||||
}
|
||||
|
||||
function openEditModal(address: any) {
|
||||
formData.value = {
|
||||
street: address.street,
|
||||
@@ -144,17 +136,14 @@ function openEditModal(address: any) {
|
||||
editingAddressId.value = address.id
|
||||
showModal.value = true
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
formData.value = { street: '', zipCode: '', city: '', country: '' }
|
||||
editingAddressId.value = null
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
showModal.value = false
|
||||
resetForm()
|
||||
}
|
||||
|
||||
function validate() {
|
||||
const errors = []
|
||||
if (!formData.value.street) errors.push({ name: 'street', message: 'Street required' })
|
||||
@@ -163,7 +152,6 @@ function validate() {
|
||||
if (!formData.value.country) errors.push({ name: 'country', message: 'Country required' })
|
||||
return errors.length ? errors : null
|
||||
}
|
||||
|
||||
function saveAddress() {
|
||||
if (validate()) return
|
||||
if (isEditing.value && editingAddressId.value) {
|
||||
@@ -173,12 +161,18 @@ function saveAddress() {
|
||||
}
|
||||
closeModal()
|
||||
}
|
||||
|
||||
// const Lera = ref('')
|
||||
// function run (){
|
||||
// if(Lera.value==='lera'){
|
||||
// console.log('Leraa okokok')
|
||||
// }else{
|
||||
// console.log('LEra nonono')
|
||||
// }
|
||||
// }
|
||||
function confirmDelete(id: number) {
|
||||
addressToDelete.value = id
|
||||
showDeleteConfirm.value = true
|
||||
}
|
||||
|
||||
function deleteAddress() {
|
||||
if (addressToDelete.value) {
|
||||
addressStore.deleteAddress(addressToDelete.value)
|
||||
|
||||
@@ -3,11 +3,14 @@
|
||||
<h1 class="text-2xl font-bold text-black dark:text-white mb-8">{{ t('Shopping Cart') }}</h1>
|
||||
<div class="flex flex-col lg:flex-row gap-8 mb-8">
|
||||
<div class="flex-1">
|
||||
<div class="bg-(--second-light) dark:bg-(--main-dark) rounded-lg border border-(--border-light) dark:border-(--border-dark) overflow-hidden">
|
||||
<h2 class="text-lg font-semibold text-black dark:text-white p-4 border-b border-(--border-light) dark:border-(--border-dark)">
|
||||
<div
|
||||
class="bg-(--second-light) dark:bg-(--main-dark) rounded-lg border border-(--border-light) dark:border-(--border-dark) overflow-hidden">
|
||||
<h2
|
||||
class="text-lg font-semibold text-black dark:text-white p-4 border-b border-(--border-light) dark:border-(--border-dark)">
|
||||
{{ t('Selected Products') }}
|
||||
</h2>
|
||||
<div class="hidden md:grid grid-cols-12 gap-4 p-4 bg-(--second-light) dark:bg-(--main-dark) text-sm font-medium text-gray-600 dark:text-gray-300 border-b border-(--border-light) dark:border-(--border-dark)">
|
||||
<div
|
||||
class="hidden md:grid grid-cols-12 gap-4 p-4 bg-(--second-light) dark:bg-(--main-dark) text-sm font-medium text-gray-600 dark:text-gray-300 border-b border-(--border-light) dark:border-(--border-dark)">
|
||||
<div class="col-span-4">{{ t('Product') }}</div>
|
||||
<div class="col-span-2 text-right">{{ t('Price') }}</div>
|
||||
<div class="col-span-3 text-center">{{ t('Quantity') }}</div>
|
||||
@@ -15,10 +18,11 @@
|
||||
<div class="col-span-1 text-center">{{ t('Actions') }}</div>
|
||||
</div>
|
||||
<div v-if="cartStore.items.length > 0">
|
||||
<div v-for="item in cartStore.items" :key="item.id"
|
||||
<div v-for="item in cartStore.items" :key="item.id"
|
||||
class="grid grid-cols-1 md:grid-cols-12 gap-4 p-4 border-b border-(--border-light) dark:border-(--border-dark) items-center">
|
||||
<div class="col-span-4 flex items-center gap-4">
|
||||
<div class="w-16 h-16 bg-(--second-light) dark:bg-(--main-dark) rounded flex items-center justify-center overflow-hidden">
|
||||
<div
|
||||
class="w-16 h-16 bg-(--second-light) dark:bg-(--main-dark) rounded flex items-center justify-center overflow-hidden">
|
||||
<img v-if="item.image" :src="item.image" :alt="item.name" class="w-full h-full object-cover" />
|
||||
<UIcon v-else name="mdi:package-variant" class="text-2xl text-gray-400" />
|
||||
</div>
|
||||
@@ -29,22 +33,18 @@
|
||||
<span class="text-black dark:text-white">${{ item.price.toFixed(2) }}</span>
|
||||
</div>
|
||||
<div class="col-span-3 flex items-center justify-center">
|
||||
<div class="flex items-center border border-(--border-light) dark:border-(--border-dark) rounded">
|
||||
<button @click="decreaseQuantity(item)" class="px-3 py-1 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors">
|
||||
<UIcon name="mdi:minus" />
|
||||
</button>
|
||||
<span class="px-3 py-1 text-black dark:text-white min-w-[40px] text-center">{{ item.quantity }}</span>
|
||||
<button @click="increaseQuantity(item)" class="px-3 py-1 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors">
|
||||
<UIcon name="mdi:plus" />
|
||||
</button>
|
||||
</div>
|
||||
<UInputNumber v-model="item.quantity" :min="1"
|
||||
@update:model-value="(val: number) => cartStore.updateQuantity(item.id, val)" />
|
||||
</div>
|
||||
<div class="col-span-2 text-right">
|
||||
<span class="md:hidden text-gray-500 dark:text-gray-400 text-sm">{{ t('Total') }}: </span>
|
||||
<span class="text-black dark:text-white font-medium">${{ (item.price * item.quantity).toFixed(2) }}</span>
|
||||
<span class="text-black dark:text-white font-medium">${{ (item.price * item.quantity).toFixed(2)
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="col-span-1 flex justify-center">
|
||||
<button @click="removeItem(item.id)" class="p-2 text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20 rounded transition-colors" :title="t('Remove')">
|
||||
<button @click="removeItem(item.id)"
|
||||
class="p-2 text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20 rounded transition-colors"
|
||||
:title="t('Remove')">
|
||||
<UIcon name="material-symbols:delete" class="text-[20px]" />
|
||||
</button>
|
||||
</div>
|
||||
@@ -53,14 +53,16 @@
|
||||
<div v-else class="p-8 text-center">
|
||||
<UIcon name="mdi:cart-outline" class="text-6xl text-gray-300 dark:text-gray-600 mb-4" />
|
||||
<p class="text-gray-500 dark:text-gray-400">{{ t('Your cart is empty') }}</p>
|
||||
<RouterLink :to="{ name: 'product-card-full' }" class="inline-block mt-4 text-(--accent-blue-light) dark:text-(--accent-blue-dark) hover:underline">
|
||||
<RouterLink :to="{ name: 'product-card-full' }"
|
||||
class="inline-block mt-4 text-(--accent-blue-light) dark:text-(--accent-blue-dark) hover:underline">
|
||||
{{ t('Continue Shopping') }}
|
||||
</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lg:w-80">
|
||||
<div class="bg-(--second-light) dark:bg-(--main-dark) rounded-lg border border-(--border-light) dark:border-(--border-dark) p-6 sticky top-24">
|
||||
<div
|
||||
class="bg-(--second-light) dark:bg-(--main-dark) rounded-lg border border-(--border-light) dark:border-(--border-dark) p-6 sticky top-24">
|
||||
<h2 class="text-lg font-semibold text-black dark:text-white mb-4">{{ t('Order Summary') }}</h2>
|
||||
<div class="space-y-3 border-b border-(--border-light) dark:border-(--border-dark) pb-4 mb-4">
|
||||
<div class="flex justify-between">
|
||||
@@ -74,13 +76,15 @@
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">{{ t('VAT') }} ({{ (cartStore.vatRate * 100).toFixed(0) }}%)</span>
|
||||
<span class="text-gray-600 dark:text-gray-400">{{ t('VAT') }} ({{ (cartStore.vatRate * 100).toFixed(0)
|
||||
}}%)</span>
|
||||
<span class="text-black dark:text-white">${{ cartStore.vatAmount.toFixed(2) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-between mb-6">
|
||||
<span class="text-black dark:text-white font-semibold text-lg">{{ t('Total') }}</span>
|
||||
<span class="text-(--accent-blue-light) dark:text-(--accent-blue-dark) font-bold text-lg">${{ cartStore.orderTotal.toFixed(2) }}</span>
|
||||
<span class="text-(--accent-blue-light) dark:text-(--accent-blue-dark) font-bold text-lg">${{
|
||||
cartStore.orderTotal.toFixed(2) }}</span>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3">
|
||||
<UButton block color="primary" @click="placeOrder" :disabled="!canPlaceOrder"
|
||||
@@ -97,19 +101,19 @@
|
||||
</div>
|
||||
<div class="flex flex-col lg:flex-row gap-8">
|
||||
<div class="flex-1">
|
||||
<div class="bg-(--second-light) dark:bg-(--main-dark) rounded-lg border border-(--border-light) dark:border-(--border-dark) p-6">
|
||||
<div
|
||||
class="bg-(--second-light) dark:bg-(--main-dark) rounded-lg border border-(--border-light) dark:border-(--border-dark) p-6">
|
||||
<h2 class="text-lg font-semibold text-black dark:text-white mb-4">{{ t('Select Delivery Address') }}</h2>
|
||||
<div class="mb-4">
|
||||
<UInput v-model="addressSearchQuery" type="text" :placeholder="t('Search address')"
|
||||
class="w-full bg-white dark:bg-gray-800 text-black dark:text-white" />
|
||||
</div>
|
||||
<div v-if="addressStore.filteredAddresses.length > 0" class="space-y-3">
|
||||
<label v-for="address in addressStore.filteredAddresses" :key="address.id"
|
||||
class="flex items-start gap-3 p-4 border rounded-lg cursor-pointer transition-colors"
|
||||
:class="cartStore.selectedAddressId === address.id
|
||||
? 'border-(--accent-blue-light) dark:border-(--accent-blue-dark) bg-blue-50 dark:bg-blue-900/20'
|
||||
<label v-for="address in addressStore.filteredAddresses" :key="address.id"
|
||||
class="flex items-start gap-3 p-4 border rounded-lg cursor-pointer transition-colors" :class="cartStore.selectedAddressId === address.id
|
||||
? 'border-(--accent-blue-light) dark:border-(--accent-blue-dark) bg-blue-50 dark:bg-blue-900/20'
|
||||
: 'border-(--border-light) dark:border-(--border-dark) hover:border-gray-400'">
|
||||
<input type="radio" :value="address.id" v-model="selectedAddress"
|
||||
<input type="radio" :value="address.id" v-model="selectedAddress"
|
||||
class="mt-1 w-4 h-4 text-(--accent-blue-light) dark:text-(--accent-blue-dark)" />
|
||||
<div class="flex-1">
|
||||
<p class="text-black dark:text-white font-medium">{{ address.street }}</p>
|
||||
@@ -121,22 +125,23 @@
|
||||
<div v-else class="text-center py-6">
|
||||
<UIcon name="mdi:map-marker-outline" class="text-4xl text-gray-400 mb-2" />
|
||||
<p class="text-gray-500 dark:text-gray-400">{{ t('No addresses found') }}</p>
|
||||
<RouterLink :to="{ name: 'addresses' }" class="inline-block mt-2 text-(--accent-blue-light) dark:text-(--accent-blue-dark) hover:underline">
|
||||
<RouterLink :to="{ name: 'addresses' }"
|
||||
class="inline-block mt-2 text-(--accent-blue-light) dark:text-(--accent-blue-dark) hover:underline">
|
||||
{{ t('Add Address') }}
|
||||
</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="bg-(--second-light) dark:bg-(--main-dark) rounded-lg border border-(--border-light) dark:border-(--border-dark) p-6">
|
||||
<div
|
||||
class="bg-(--second-light) dark:bg-(--main-dark) rounded-lg border border-(--border-light) dark:border-(--border-dark) p-6">
|
||||
<h2 class="text-lg font-semibold text-black dark:text-white mb-4">{{ t('Delivery Method') }}</h2>
|
||||
<div class="space-y-3">
|
||||
<label v-for="method in cartStore.deliveryMethods" :key="method.id"
|
||||
class="flex items-center gap-3 p-4 border rounded-lg cursor-pointer transition-colors"
|
||||
:class="cartStore.selectedDeliveryMethodId === method.id
|
||||
? 'border-(--accent-blue-light) dark:border-(--accent-blue-dark) bg-blue-50 dark:bg-blue-900/20'
|
||||
<label v-for="method in cartStore.deliveryMethods" :key="method.id"
|
||||
class="flex items-center gap-3 p-4 border rounded-lg cursor-pointer transition-colors" :class="cartStore.selectedDeliveryMethodId === method.id
|
||||
? 'border-(--accent-blue-light) dark:border-(--accent-blue-dark) bg-blue-50 dark:bg-blue-900/20'
|
||||
: 'border-(--border-light) dark:border-(--border-dark) hover:border-gray-400'">
|
||||
<input type="radio" :value="method.id" v-model="selectedDeliveryMethod"
|
||||
<input type="radio" :value="method.id" v-model="selectedDeliveryMethod"
|
||||
class="w-4 h-4 text-(--accent-blue-light) dark:text-(--accent-blue-dark)" />
|
||||
<div class="flex-1">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -157,7 +162,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { useCartStore, type CartItem } from '@/stores/cart'
|
||||
import { useCartStore } from '@/stores/cart'
|
||||
import { useAddressStore } from '@/stores/address'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
@@ -186,19 +191,10 @@ watch(selectedDeliveryMethod, (newValue) => {
|
||||
})
|
||||
|
||||
const canPlaceOrder = computed(() => {
|
||||
return cartStore.items.length > 0 &&
|
||||
cartStore.selectedAddressId !== null &&
|
||||
cartStore.selectedDeliveryMethodId !== null
|
||||
return cartStore.items.length > 0 &&
|
||||
cartStore.selectedAddressId !== null &&
|
||||
cartStore.selectedDeliveryMethodId !== null
|
||||
})
|
||||
|
||||
function increaseQuantity(item: CartItem) {
|
||||
cartStore.updateQuantity(item.id, item.quantity + 1)
|
||||
}
|
||||
|
||||
function decreaseQuantity(item: CartItem) {
|
||||
cartStore.updateQuantity(item.id, item.quantity - 1)
|
||||
}
|
||||
|
||||
function removeItem(itemId: number) {
|
||||
cartStore.removeItem(itemId)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="container mt-14 mx-auto">
|
||||
<div class="flex justify-between gap-8 mb-6">
|
||||
<div class="flex md:flex-row flex-col justify-between gap-8 mb-6">
|
||||
<div class="flex-1">
|
||||
<div class="bg-gray-100 dark:bg-gray-800 rounded-lg p-8 flex items-center justify-center min-h-[300px]">
|
||||
<img :src="selectedColor?.image || productData.image" :alt="productData.name"
|
||||
@@ -29,7 +29,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-between items-end mb-8">
|
||||
<div class="flex md:flex-row flex-col justify-between md:items-end items-start gap-5 md:gap-0 md:mb-8 mb-4">
|
||||
<div class="flex flex-col gap-3">
|
||||
<span class="text-sm text-(--accent-blue-light) dark:text-(--accent-blue-dark) ">Colors:</span>
|
||||
<div class="flex gap-2">
|
||||
@@ -49,10 +49,10 @@
|
||||
</div>
|
||||
<ProductCustomization />
|
||||
<hr class="border-t border-(--border-light) dark:border-(--border-dark) mb-8" />
|
||||
<div class="mb-6 w-[55%]">
|
||||
<div class="flex justify-between items-center gap-10 mb-8">
|
||||
<div class="mb-6 w-[100%] xl:w-[60%]">
|
||||
<div class="grid grid-cols-2 lg:grid-cols-4 gap-10 mb-8">
|
||||
<UButton v-for="tab in tabs" :key="tab.id" @click="activeTab = tab.id" :class="[
|
||||
'px-15 py-2 cursor-pointer',
|
||||
'px-15 py-2 cursor-pointer sm:text-nowrap flex items-center! justify-center!',
|
||||
activeTab === tab.id
|
||||
? 'bg-blue-600 hover:text-black hover:dark:text-white text-white'
|
||||
: 'text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700'
|
||||
|
||||
89
bo/src/components/customer/PageProductsList.vue
Normal file
89
bo/src/components/customer/PageProductsList.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div class="container mx-auto p-6">
|
||||
<h1 class="text-2xl font-bold mb-6 text-gray-900 dark:text-white">Products</h1>
|
||||
<div v-if="loading" class="text-center py-8">
|
||||
<span class="text-gray-600 dark:text-gray-400">Loading products...</span>
|
||||
</div>
|
||||
<div v-else-if="error" class="mb-4 p-3 bg-red-100 text-red-700 rounded">
|
||||
{{ error }}
|
||||
</div>
|
||||
<div v-else class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-800">
|
||||
<tr>
|
||||
<th
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Product ID
|
||||
</th>
|
||||
<th
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Reference
|
||||
</th>
|
||||
<th
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Price
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-900 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<tr v-for="product in products" :key="product.product_id"
|
||||
class="hover:bg-gray-50 dark:hover:bg-gray-800">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-white">
|
||||
{{ product.product_id }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-white">
|
||||
{{ product.reference }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-white">
|
||||
{{ product.price.toFixed(2) }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div v-if="products.length === 0" class="text-center py-8 text-gray-500 dark:text-gray-400">
|
||||
No products found
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useFetchJson } from '@/composable/useFetchJson'
|
||||
|
||||
interface Product {
|
||||
product_id: number
|
||||
reference: string
|
||||
price: number
|
||||
}
|
||||
|
||||
interface ApiResponse {
|
||||
items: Product[]
|
||||
count: number
|
||||
message: string
|
||||
}
|
||||
|
||||
const products = ref<Product[]>([])
|
||||
const loading = ref(true)
|
||||
const error = ref<string | null>(null)
|
||||
|
||||
async function fetchProducts() {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const response = await useFetchJson<ApiResponse>('/api/v1/restricted/list-products/get-listing?p=3&elems') as unknown as { items: Product[] }
|
||||
products.value = response.items || []
|
||||
} catch (e: any) {
|
||||
error.value = e.message || 'Failed to load products'
|
||||
console.error('Error fetching products:', e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchProducts()
|
||||
})
|
||||
</script>
|
||||
@@ -4,7 +4,7 @@
|
||||
<p class="text-[24px] font-bold">Product customization</p>
|
||||
<p class="text-[15px]">Don't forget to save your customization to be able to add to cart</p>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 gap-10">
|
||||
<div class="grid grid-cols-2 sm:grid-cols-2 md:grid-cols-3 gap-5 md:gap-10 ">
|
||||
<UInput label="Podaj kolor kanapy narożnej" placeholder="Podaj kolor kanapy narożnej" class="dark:text-white text-black"/>
|
||||
<UInput label="Podaj kolor fotela" placeholder="Podaj kolor fotela" class="dark:text-white text-black"/>
|
||||
<UInput label="Podaj kolor kwadratu" placeholder="Podaj kolor kwadratu" class="dark:text-white text-black"/>
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import TopBar from '@/components/TopBar.vue';
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="h-screen grid grid-rows-[auto_1fr_auto]">
|
||||
<!-- <header class="w-full bg-gray-100 text-primary shadow border-b-gray-300 p-4 mb-8">Header</header> -->
|
||||
|
||||
@@ -9,11 +9,7 @@ function isAuthenticated(): boolean {
|
||||
if (typeof document === 'undefined') return false
|
||||
return document.cookie.split('; ').some((c) => c === 'is_authenticated=1')
|
||||
}
|
||||
|
||||
|
||||
await getSettings()
|
||||
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.VITE_BASE_URL),
|
||||
routes: [
|
||||
@@ -34,6 +30,7 @@ const router = createRouter({
|
||||
{ path: 'product-card-full/', component: () => import('../components/customer/PageProductCardFull.vue'), name: 'product-card-full' },
|
||||
{ path: 'addresses', component: () => import('../components/customer/PageAddresses.vue'), name: 'addresses' },
|
||||
{ path: 'cart', component: () => import('../components/customer/PageCart.vue'), name: 'cart' },
|
||||
{ path: 'products-list', component: () => import('../components/customer/PageProductsList.vue'), name: 'products-list' },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -55,7 +52,6 @@ const router = createRouter({
|
||||
router.beforeEach((to, from, next) => {
|
||||
const locale = to.params.locale as string
|
||||
const localeLang = langs.find((x) => x.iso_code == locale)
|
||||
|
||||
if (locale && langs.length > 0) {
|
||||
const authStore = useAuthStore()
|
||||
console.log(authStore.isAuthenticated, to, from)
|
||||
@@ -77,7 +73,6 @@ router.beforeEach((to, from, next) => {
|
||||
if (!locale && to.path !== '/') {
|
||||
return next(`/${currentLang.value?.iso_code}${to.path}`)
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
|
||||
@@ -95,7 +95,6 @@ export const useAddressStore = defineStore('address', () => {
|
||||
id: existing.id,
|
||||
...normalize(formData)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
function deleteAddress(id: number): boolean {
|
||||
|
||||
@@ -63,7 +63,6 @@ const PrivacyComponent = computed(() =>
|
||||
<UButton @click="showTherms = false" class="mx-auto px-12">{{ $t('general.close') }}</UButton>
|
||||
</template>
|
||||
</UDrawer>
|
||||
<!-- PrivacyPolicyView -->
|
||||
<UDrawer v-model:open="showPrivacy" :overlay="false">
|
||||
<template #body>
|
||||
<component :is="PrivacyComponent" />
|
||||
@@ -76,9 +75,9 @@ const PrivacyComponent = computed(() =>
|
||||
<div class="text-center mb-15">
|
||||
<div
|
||||
class="inline-flex items-center justify-center w-16 h-16 rounded-2xl bg-primary-500 text-white mb-4 shadow-lg shadow-primary-500/30">
|
||||
<UIcon name="i-heroicons-clock" class="w-8 h-8" />
|
||||
<UIcon name="carbon:ibm-webmethods-b2b-integration" class="w-8 h-8" />
|
||||
</div>
|
||||
<h1 class="text-3xl font-bold text-gray-900 dark:text-white">TimeTracker</h1>
|
||||
<h1 class="text-3xl font-bold text-gray-900 dark:text-white">B2B</h1>
|
||||
</div>
|
||||
<div class="w-full max-w-md flex flex-col gap-4">
|
||||
<UForm :validate="validate" @submit="handleLogin" class="space-y-5">
|
||||
@@ -94,11 +93,12 @@ const PrivacyComponent = computed(() =>
|
||||
|
||||
<UFormField :label="$t('general.password')" name="password" required class="w-full dark:text-white text-black">
|
||||
<UInput v-model="password" :placeholder="$t('general.enter_your_password')"
|
||||
:type="showPassword ? 'text' : 'password'" class="w-full placeholder:text-(--placeholder)" :ui="{ trailing: 'pe-1' }">
|
||||
:type="showPassword ? 'text' : 'password'" class="w-full placeholder:text-(--placeholder)"
|
||||
:ui="{ trailing: 'pe-1' }">
|
||||
<template #trailing>
|
||||
<UIcon color="neutral" variant="link" size="sm" :name="showPassword ? 'i-lucide-eye-off' : 'i-lucide-eye'"
|
||||
:aria-label="showPassword ? 'Hide password' : 'Show password'" :aria-pressed="showPassword"
|
||||
aria-controls="password" @click="showPassword = !showPassword" class="mr-2"/>
|
||||
aria-controls="password" @click="showPassword = !showPassword" class="mr-2" />
|
||||
</template>
|
||||
</UInput>
|
||||
</UFormField>
|
||||
@@ -114,15 +114,11 @@ const PrivacyComponent = computed(() =>
|
||||
{{ $t('general.sign_in') }}
|
||||
</UButton>
|
||||
</UForm>
|
||||
|
||||
<!-- Divider -->
|
||||
<div class="flex items-center gap-3 my-1">
|
||||
<div class="flex-1 h-px bg-gray-200 dark:dark:hover:bg-(--gray-dark)" />
|
||||
<span class="text-xs text-gray-400 dark:text-gray-500">{{ $t('general.or') }}</span>
|
||||
<div class="flex-1 h-px bg-gray-200 dark:dark:hover:bg-(--gray-dark)" />
|
||||
</div>
|
||||
|
||||
<!-- Google Sign In -->
|
||||
<UButton type="button" color="neutral" variant="outline" size="lg" block :disabled="authStore.loading"
|
||||
@click="authStore.loginWithGoogle()"
|
||||
class="flex items-center justify-center gap-2 dark:text-white text-black cursor-pointer">
|
||||
|
||||
@@ -40,9 +40,9 @@ function validate(): FormError[] {
|
||||
<div class="text-center mb-15">
|
||||
<div
|
||||
class="inline-flex items-center justify-center w-16 h-16 rounded-2xl bg-primary-500 text-white mb-4 shadow-lg shadow-primary-500/30">
|
||||
<UIcon name="i-heroicons-clock" class="w-8 h-8" />
|
||||
<UIcon name="carbon:ibm-webmethods-b2b-integration" class="w-8 h-8" />
|
||||
</div>
|
||||
<h1 class="text-3xl font-bold text-gray-900 dark:text-white">TimeTracker</h1>
|
||||
<h1 class="text-3xl font-bold text-gray-900 dark:text-white">B2B</h1>
|
||||
</div>
|
||||
<div class="w-full max-w-md flex flex-col gap-4">
|
||||
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
<div class="text-center mb-15">
|
||||
<div
|
||||
class="inline-flex items-center justify-center w-16 h-16 rounded-2xl bg-primary-500 text-white mb-4 shadow-lg shadow-primary-500/30">
|
||||
<UIcon name="i-heroicons-clock" class="w-8 h-8" />
|
||||
<UIcon name="carbon:ibm-webmethods-b2b-integration" class="w-8 h-8" />
|
||||
</div>
|
||||
<h1 class="text-3xl font-bold text-gray-900 dark:text-white">TimeTracker</h1>
|
||||
<h1 class="text-3xl font-bold text-gray-900 dark:text-white">B2B</h1>
|
||||
</div>
|
||||
<div class="w-full max-w-md">
|
||||
<UForm :validate="validate" @submit="handleRegister" class="flex flex-col gap-3">
|
||||
|
||||
@@ -181,7 +181,7 @@ const columns = computed<TableColumn<IssueTimeSummary>[]>(() => [
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="container mx-auto">
|
||||
<div class="p-6 bg-(--main-light) dark:bg-(--black) font-sans">
|
||||
<h1 class="text-2xl font-bold mb-6 text-black dark:text-white">{{ $t('repo_chart.repository_work_chart') }}
|
||||
</h1>
|
||||
|
||||
@@ -53,9 +53,9 @@ function validate(): FormError[] {
|
||||
<div class="text-center mb-15">
|
||||
<div
|
||||
class="inline-flex items-center justify-center w-16 h-16 rounded-2xl bg-primary-500 text-white mb-4 shadow-lg shadow-primary-500/30">
|
||||
<UIcon name="i-heroicons-clock" class="w-8 h-8" />
|
||||
<UIcon name="carbon:ibm-webmethods-b2b-integration" class="w-8 h-8" />
|
||||
</div>
|
||||
<h1 class="text-3xl font-bold text-gray-900 dark:text-white">TimeTracker</h1>
|
||||
<h1 class="text-3xl font-bold text-gray-900 dark:text-white">B2B</h1>
|
||||
</div>
|
||||
<div class="w-full max-w-md flex flex-col gap-4">
|
||||
|
||||
|
||||
@@ -73,9 +73,9 @@ function goToLogin() {
|
||||
<div class="text-center mb-8">
|
||||
<div
|
||||
class="inline-flex items-center justify-center w-16 h-16 rounded-2xl bg-primary-500 text-white mb-4 shadow-lg shadow-primary-500/30">
|
||||
<UIcon name="i-heroicons-clock" class="w-8 h-8" />
|
||||
<UIcon name="carbon:ibm-webmethods-b2b-integration" class="w-8 h-8" />
|
||||
</div>
|
||||
<h1 class="text-3xl font-bold text-gray-900 dark:text-white">TimeTracker</h1>
|
||||
<h1 class="text-3xl font-bold text-gray-900 dark:text-white">B2B</h1>
|
||||
</div>
|
||||
|
||||
<UCard class="shadow-xl shadow-gray-200/50 dark:shadow-gray-900/50">
|
||||
|
||||
@@ -1,224 +0,0 @@
|
||||
-- +goose Up
|
||||
DELIMITER //
|
||||
|
||||
DROP PROCEDURE IF EXISTS get_full_product //
|
||||
|
||||
CREATE PROCEDURE get_full_product(
|
||||
IN p_id_product INT UNSIGNED,
|
||||
IN p_id_shop INT UNSIGNED,
|
||||
IN p_id_lang INT UNSIGNED,
|
||||
IN p_id_customer INT UNSIGNED,
|
||||
IN p_id_group INT UNSIGNED,
|
||||
IN p_id_currency INT UNSIGNED,
|
||||
IN p_id_country INT UNSIGNED,
|
||||
IN p_quantity INT UNSIGNED
|
||||
)
|
||||
BEGIN
|
||||
|
||||
DECLARE v_tax_rate DECIMAL(10,4) DEFAULT 0;
|
||||
|
||||
SELECT COALESCE(t.rate, 0.0000) INTO v_tax_rate
|
||||
FROM ps_tax_rule tr
|
||||
INNER JOIN ps_tax t
|
||||
ON t.id_tax = tr.id_tax
|
||||
WHERE tr.id_tax_rules_group = (
|
||||
SELECT ps.id_tax_rules_group
|
||||
FROM ps_product_shop ps
|
||||
WHERE ps.id_product = p_id_product
|
||||
AND ps.id_shop = p_id_shop
|
||||
LIMIT 1
|
||||
)
|
||||
AND tr.id_country = p_id_country
|
||||
ORDER BY
|
||||
tr.id_state DESC,
|
||||
tr.zipcode_from != '' DESC,
|
||||
tr.id_tax_rule DESC
|
||||
LIMIT 1;
|
||||
|
||||
/* FINAL JSON */
|
||||
SELECT JSON_OBJECT(
|
||||
|
||||
/* ================= PRODUCT ================= */
|
||||
'id_product', p.id_product,
|
||||
'reference', p.reference,
|
||||
'name', pl.name,
|
||||
'description', pl.description,
|
||||
'short_description', pl.description_short,
|
||||
|
||||
/* ================= PRICE ================= */
|
||||
'price', JSON_OBJECT(
|
||||
'base', COALESCE(ps.price, p.price),
|
||||
|
||||
'final_tax_excl',
|
||||
(
|
||||
COALESCE(ps.price, p.price)
|
||||
- IFNULL(
|
||||
CASE
|
||||
WHEN sp.reduction_type = 'amount' THEN sp.reduction
|
||||
WHEN sp.reduction_type = 'percentage' THEN COALESCE(ps.price, p.price) * sp.reduction
|
||||
ELSE 0
|
||||
END, 0
|
||||
)
|
||||
),
|
||||
|
||||
'final_tax_incl',
|
||||
(
|
||||
(
|
||||
COALESCE(ps.price, p.price)
|
||||
- IFNULL(
|
||||
CASE
|
||||
WHEN sp.reduction_type = 'amount' THEN sp.reduction
|
||||
WHEN sp.reduction_type = 'percentage' THEN COALESCE(ps.price, p.price) * sp.reduction
|
||||
ELSE 0
|
||||
END, 0
|
||||
)
|
||||
) * (1 + v_tax_rate / 100)
|
||||
)
|
||||
),
|
||||
|
||||
/* ================= META ================= */
|
||||
'active', COALESCE(ps.active, p.active),
|
||||
'visibility', COALESCE(ps.visibility, p.visibility),
|
||||
'manufacturer', m.name,
|
||||
'category', cl.name,
|
||||
|
||||
/* ================= IMAGE ================= */
|
||||
'cover_image', JSON_OBJECT(
|
||||
'id', i.id_image,
|
||||
'legend', il.legend
|
||||
),
|
||||
|
||||
/* ================= FEATURES ================= */
|
||||
'features', (
|
||||
SELECT JSON_ARRAYAGG(
|
||||
JSON_OBJECT(
|
||||
'name', fl.name,
|
||||
'value', fvl.value
|
||||
)
|
||||
)
|
||||
FROM ps_feature_product fp
|
||||
JOIN ps_feature_lang fl
|
||||
ON fl.id_feature = fp.id_feature AND fl.id_lang = p_id_lang
|
||||
JOIN ps_feature_value_lang fvl
|
||||
ON fvl.id_feature_value = fp.id_feature_value AND fvl.id_lang = p_id_lang
|
||||
WHERE fp.id_product = p.id_product
|
||||
),
|
||||
|
||||
/* ================= COMBINATIONS ================= */
|
||||
'combinations', (
|
||||
SELECT JSON_ARRAYAGG(
|
||||
JSON_OBJECT(
|
||||
'id_product_attribute', pa.id_product_attribute,
|
||||
'reference', pa.reference,
|
||||
|
||||
'price', JSON_OBJECT(
|
||||
'impact', COALESCE(pas.price, pa.price),
|
||||
|
||||
'final_tax_excl',
|
||||
(
|
||||
COALESCE(ps.price, p.price)
|
||||
+ COALESCE(pas.price, pa.price)
|
||||
),
|
||||
|
||||
'final_tax_incl',
|
||||
(
|
||||
(
|
||||
COALESCE(ps.price, p.price)
|
||||
+ COALESCE(pas.price, pa.price)
|
||||
) * (1 + v_tax_rate / 100)
|
||||
)
|
||||
),
|
||||
|
||||
'stock', IFNULL(sa.quantity, 0),
|
||||
|
||||
'default_on', pas.default_on,
|
||||
|
||||
/* ATTRIBUTES JSON */
|
||||
'attributes', (
|
||||
SELECT JSON_ARRAYAGG(
|
||||
JSON_OBJECT(
|
||||
'group', agl.name,
|
||||
'attribute', al.name
|
||||
)
|
||||
)
|
||||
FROM ps_product_attribute_combination pac
|
||||
JOIN ps_attribute a ON a.id_attribute = pac.id_attribute
|
||||
JOIN ps_attribute_lang al
|
||||
ON al.id_attribute = a.id_attribute AND al.id_lang = p_id_lang
|
||||
JOIN ps_attribute_group_lang agl
|
||||
ON agl.id_attribute_group = a.id_attribute_group AND agl.id_lang = p_id_lang
|
||||
WHERE pac.id_product_attribute = pa.id_product_attribute
|
||||
),
|
||||
|
||||
/* IMAGES */
|
||||
'images', (
|
||||
SELECT JSON_ARRAYAGG(img.id_image)
|
||||
FROM ps_product_attribute_image pai
|
||||
JOIN ps_image img ON img.id_image = pai.id_image
|
||||
WHERE pai.id_product_attribute = pa.id_product_attribute
|
||||
)
|
||||
|
||||
)
|
||||
)
|
||||
FROM ps_product_attribute pa
|
||||
JOIN ps_product_attribute_shop pas
|
||||
ON pas.id_product_attribute = pa.id_product_attribute
|
||||
AND pas.id_shop = p_id_shop
|
||||
LEFT JOIN ps_stock_available sa
|
||||
ON sa.id_product = pa.id_product
|
||||
AND sa.id_product_attribute = pa.id_product_attribute
|
||||
AND sa.id_shop = p_id_shop
|
||||
WHERE pa.id_product = p.id_product
|
||||
)
|
||||
|
||||
) AS product_json
|
||||
|
||||
FROM ps_product p
|
||||
|
||||
LEFT JOIN ps_product_shop ps
|
||||
ON ps.id_product = p.id_product AND ps.id_shop = p_id_shop
|
||||
|
||||
LEFT JOIN ps_product_lang pl
|
||||
ON pl.id_product = p.id_product
|
||||
AND pl.id_lang = p_id_lang
|
||||
AND pl.id_shop = p_id_shop
|
||||
|
||||
LEFT JOIN ps_category_lang cl
|
||||
ON cl.id_category = COALESCE(ps.id_category_default, p.id_category_default)
|
||||
AND cl.id_lang = p_id_lang
|
||||
AND cl.id_shop = p_id_shop
|
||||
|
||||
LEFT JOIN ps_manufacturer m
|
||||
ON m.id_manufacturer = p.id_manufacturer
|
||||
|
||||
LEFT JOIN ps_image i
|
||||
ON i.id_product = p.id_product AND i.cover = 1
|
||||
|
||||
LEFT JOIN ps_image_lang il
|
||||
ON il.id_image = i.id_image AND il.id_lang = p_id_lang
|
||||
|
||||
/* SPECIFIC PRICE */
|
||||
LEFT JOIN (
|
||||
SELECT sp1.*
|
||||
FROM ps_specific_price sp1
|
||||
WHERE sp1.id_product = p_id_product
|
||||
AND (sp1.id_customer = 0 OR sp1.id_customer = p_id_customer)
|
||||
AND (sp1.id_group = 0 OR sp1.id_group = p_id_group)
|
||||
AND (sp1.id_currency = 0 OR sp1.id_currency = p_id_currency)
|
||||
AND sp1.from_quantity <= p_quantity
|
||||
ORDER BY
|
||||
sp1.id_customer DESC,
|
||||
sp1.id_group DESC,
|
||||
sp1.from_quantity DESC,
|
||||
sp1.id_specific_price DESC
|
||||
LIMIT 1
|
||||
) sp ON sp.id_product = p.id_product
|
||||
|
||||
WHERE p.id_product = p_id_product
|
||||
|
||||
LIMIT 1;
|
||||
|
||||
END //
|
||||
|
||||
DELIMITER ;
|
||||
-- +goose Down
|
||||
431
package-lock.json
generated
431
package-lock.json
generated
@@ -4,6 +4,7 @@
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "b2b",
|
||||
"dependencies": {
|
||||
"@nuxt/ui": "^4.5.1",
|
||||
"chart.js": "^4.5.1",
|
||||
@@ -1006,6 +1007,395 @@
|
||||
"integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz",
|
||||
"integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz",
|
||||
"integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz",
|
||||
"integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz",
|
||||
"integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz",
|
||||
"integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz",
|
||||
"integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz",
|
||||
"integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz",
|
||||
"integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loong64-musl": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-ppc64-musl": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-openbsd-x64": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz",
|
||||
"integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-openharmony-arm64": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz",
|
||||
"integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openharmony"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz",
|
||||
"integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz",
|
||||
"integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz",
|
||||
"integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@standard-schema/spec": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
|
||||
@@ -1975,13 +2365,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@unhead/vue": {
|
||||
"version": "2.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@unhead/vue/-/vue-2.1.10.tgz",
|
||||
"integrity": "sha512-VP78Onh2HNezLPfhYjfHqn4dxlcQsE6PJgTTs61NksO/thvilNswtgBq0N0MWCLtn43N5akEPGW2y2zxM3PWgQ==",
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@unhead/vue/-/vue-2.1.12.tgz",
|
||||
"integrity": "sha512-zEWqg0nZM8acpuTZE40wkeUl8AhIe0tU0OkilVi1D4fmVjACrwoh5HP6aNqJ8kUnKsoy6D+R3Vi/O+fmdNGO7g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hookable": "^6.0.1",
|
||||
"unhead": "2.1.10"
|
||||
"unhead": "2.1.12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/harlan-zw"
|
||||
@@ -2801,6 +3191,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fuse.js": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.1.0.tgz",
|
||||
@@ -2846,9 +3251,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/h3": {
|
||||
"version": "1.15.5",
|
||||
"resolved": "https://registry.npmjs.org/h3/-/h3-1.15.5.tgz",
|
||||
"integrity": "sha512-xEyq3rSl+dhGX2Lm0+eFQIAzlDN6Fs0EcC4f7BNUmzaRX/PTzeuM+Tr2lHB8FoXggsQIeXLj8EDVgs5ywxyxmg==",
|
||||
"version": "1.15.8",
|
||||
"resolved": "https://registry.npmjs.org/h3/-/h3-1.15.8.tgz",
|
||||
"integrity": "sha512-iOH6Vl8mGd9nNfu9C0IZ+GuOAfJHcyf3VriQxWaSWIB76Fg4BnFuk4cxBxjmQSSxJS664+pgjP6e7VBnUzFfcg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cookie-es": "^1.2.2",
|
||||
@@ -4257,9 +4662,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/unhead": {
|
||||
"version": "2.1.10",
|
||||
"resolved": "https://registry.npmjs.org/unhead/-/unhead-2.1.10.tgz",
|
||||
"integrity": "sha512-We8l9uNF8zz6U8lfQaVG70+R/QBfQx1oPIgXin4BtZnK2IQpz6yazQ0qjMNVBDw2ADgF2ea58BtvSK+XX5AS7g==",
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/unhead/-/unhead-2.1.12.tgz",
|
||||
"integrity": "sha512-iTHdWD9ztTunOErtfUFk6Wr11BxvzumcYJ0CzaSCBUOEtg+DUZ9+gnE99i8QkLFT2q1rZD48BYYGXpOZVDLYkA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hookable": "^6.0.1"
|
||||
@@ -4269,9 +4674,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/unhead/node_modules/hookable": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/hookable/-/hookable-6.0.1.tgz",
|
||||
"integrity": "sha512-uKGyY8BuzN/a5gvzvA+3FVWo0+wUjgtfSdnmjtrOVwQCZPHpHDH2WRO3VZSOeluYrHoDCiXFffZXs8Dj1ULWtw==",
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/hookable/-/hookable-6.1.0.tgz",
|
||||
"integrity": "sha512-ZoKZSJgu8voGK2geJS+6YtYjvIzu9AOM/KZXsBxr83uhLL++e9pEv/dlgwgy3dvHg06kTz6JOh1hk3C8Ceiymw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unifont": {
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
type UIListProductsRepo interface {
|
||||
GetListing(p find.Paging, filt *filters.FiltersList) (find.Found[model.Product], error)
|
||||
GetListing(id_shop uint, id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error)
|
||||
}
|
||||
|
||||
type ListProductsRepo struct{}
|
||||
@@ -17,12 +17,18 @@ func New() UIListProductsRepo {
|
||||
return &ListProductsRepo{}
|
||||
}
|
||||
|
||||
func (repo *ListProductsRepo) GetListing(p find.Paging, filt *filters.FiltersList) (find.Found[model.Product], error) {
|
||||
var listing []model.Product
|
||||
func (repo *ListProductsRepo) GetListing(id_shop uint, id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error) {
|
||||
var listing []model.ProductInList
|
||||
var total int64
|
||||
|
||||
// Apply filters here
|
||||
q := db.DB.Table("ps_product")
|
||||
q := db.DB.Table("ps_product").
|
||||
Select("ps_product.id_product AS id_product", "ps_product_lang.name AS name", "ps_product_shop.price AS price", "ps_product.active AS active_as_product", "ps_product_shop.active AS active_in_shop").
|
||||
Joins("LEFT JOIN ps_product_shop ON ps_product.id_product = ps_product_shop.id_product").
|
||||
Joins("LEFT JOIN ps_product_lang ON ps_product.id_product = ps_product_lang.id_product").
|
||||
Where("ps_product_shop.id_shop = ?", id_shop).
|
||||
Where("ps_product_lang.id_shop = ?", id_shop).
|
||||
Where("ps_product_lang.id_lang = ?", id_lang)
|
||||
|
||||
// var resultIDs []uint
|
||||
// q := db.DB.
|
||||
@@ -40,7 +46,7 @@ func (repo *ListProductsRepo) GetListing(p find.Paging, filt *filters.FiltersLis
|
||||
|
||||
err := q.Count(&total).Error
|
||||
if err != nil {
|
||||
return find.Found[model.Product]{}, err
|
||||
return find.Found[model.ProductInList]{}, err
|
||||
}
|
||||
|
||||
err = q.
|
||||
@@ -48,10 +54,10 @@ func (repo *ListProductsRepo) GetListing(p find.Paging, filt *filters.FiltersLis
|
||||
Offset(p.Offset()).
|
||||
Scan(&listing).Error
|
||||
if err != nil {
|
||||
return find.Found[model.Product]{}, err
|
||||
return find.Found[model.ProductInList]{}, err
|
||||
}
|
||||
|
||||
return find.Found[model.Product]{
|
||||
return find.Found[model.ProductInList]{
|
||||
Items: listing,
|
||||
Count: uint(total),
|
||||
}, nil
|
||||
|
||||
Reference in New Issue
Block a user