1041 lines
30 KiB
Vue
1041 lines
30 KiB
Vue
<template>
|
|
<Teleport v-if="isHeaderMounted" to="#actions">
|
|
<div class="q-gutter-x-sm row">
|
|
<VnSearchBar :search-term="search" @on-search-error="items = []" />
|
|
<QBtn
|
|
:icon="viewTypeButtonContent.icon"
|
|
:label="viewTypeButtonContent.label"
|
|
@click="onViewModeClick()"
|
|
rounded
|
|
no-caps
|
|
>
|
|
<QTooltip>
|
|
{{ viewTypeButtonContent.label }}
|
|
</QTooltip>
|
|
</QBtn>
|
|
<QBtn
|
|
icon="shopping_cart_checkout"
|
|
:label="t('shoppingCart')"
|
|
:to="{ name: 'basket' }"
|
|
rounded
|
|
no-caps
|
|
>
|
|
<QTooltip>
|
|
{{ t('shoppingCart') }}
|
|
</QTooltip>
|
|
</QBtn>
|
|
</div>
|
|
</Teleport>
|
|
<div style="padding-bottom: 5em">
|
|
<QDrawer v-model="rightDrawerOpen" side="right" :width="250">
|
|
<div class="q-pa-md">
|
|
<div class="basket-info q-gutter-y-sm">
|
|
<span v-if="order?.nickname">{{ order.nickname }}</span>
|
|
<span v-if="order?.sent">
|
|
{{
|
|
formatDateTitle(order.sent, {
|
|
shortDay: true,
|
|
shortMonth: true,
|
|
includeOfString: true
|
|
})
|
|
}}
|
|
</span>
|
|
<QBtn
|
|
rounded
|
|
no-caps
|
|
:to="{
|
|
name: 'checkout',
|
|
params: { id: appStore.basketOrderId },
|
|
query: { continue: 'catalog' }
|
|
}"
|
|
>
|
|
{{ t('modify') }}
|
|
</QBtn>
|
|
</div>
|
|
<div class="q-mt-md">
|
|
<div class="q-mb-xs text-grey-7">
|
|
{{ t('category') }}
|
|
<QIcon
|
|
v-if="category"
|
|
style="font-size: 1.3em"
|
|
name="cancel"
|
|
class="cursor-pointer"
|
|
:title="t('deleteFilter')"
|
|
@click="removeCategory()"
|
|
/>
|
|
</div>
|
|
<div class="categories">
|
|
<QBtn
|
|
flat
|
|
round
|
|
class="category q-pa-sm"
|
|
v-for="cat in categories"
|
|
:class="{ active: category == cat.id }"
|
|
:key="cat.id"
|
|
@click="selectedCategory = cat.id"
|
|
>
|
|
<img :src="`statics/category/${cat.code}.svg`" />
|
|
<QTooltip>{{ cat.name }}</QTooltip>
|
|
</QBtn>
|
|
</div>
|
|
</div>
|
|
<div class="q-mt-md" v-if="selectedCategory">
|
|
<div class="q-mb-xs text-grey-7">
|
|
{{ t('filterBy') }}
|
|
</div>
|
|
<VnSelect
|
|
v-model="selectedType"
|
|
option-value="id"
|
|
option-label="name"
|
|
:options="itemFamilies"
|
|
:disable="!category"
|
|
:label="t('family')"
|
|
/>
|
|
<VnSelect
|
|
v-model="selectedColor"
|
|
option-value="id"
|
|
option-label="name"
|
|
:options="itemColors"
|
|
:disable="!category"
|
|
:label="t('color')"
|
|
/>
|
|
<VnSelect
|
|
v-model="selectedProducer"
|
|
option-value="id"
|
|
option-label="name"
|
|
:options="itemProducers"
|
|
:disable="!category"
|
|
:label="t('producer')"
|
|
/>
|
|
<VnSelect
|
|
v-model="selectedOrigin"
|
|
option-value="id"
|
|
option-label="name"
|
|
:options="itemOrigins"
|
|
:disable="!category"
|
|
:label="t('origin')"
|
|
/>
|
|
<VnSelect
|
|
v-model="selectedSubcategory"
|
|
option-value="id"
|
|
option-label="name"
|
|
:options="itemSubcategories"
|
|
:disable="!category"
|
|
:label="t('category')"
|
|
/>
|
|
<VnSelect
|
|
v-if="isSomeFilterSelected"
|
|
v-model="selectedOrderBy"
|
|
:options="orderByOptions"
|
|
option-value="value"
|
|
option-label="label"
|
|
:is-clearable="false"
|
|
:label="t('orderBy')"
|
|
/>
|
|
</div>
|
|
<span
|
|
v-else
|
|
class="flex full-width justify-center q-pt-lg text-h6 text-grey-7"
|
|
>
|
|
Elige una categoria
|
|
</span>
|
|
</div>
|
|
</QDrawer>
|
|
<div
|
|
:class="
|
|
viewMode === 'grid'
|
|
? 'q-pa-md row justify-center q-gutter-md'
|
|
: 'column items-center'
|
|
"
|
|
>
|
|
<QSpinner
|
|
v-if="loading"
|
|
color="primary"
|
|
size="3em"
|
|
:thickness="2"
|
|
/>
|
|
<div
|
|
v-else-if="!items || !items.length || !isSomeFilterSelected"
|
|
class="text-subtitle1 text-grey-7 q-pa-md"
|
|
>
|
|
<QIcon name="refresh" size="sm" class="q-mr-sm"></QIcon>
|
|
<span>{{ t('pleaseSetFilter') }}</span>
|
|
</div>
|
|
<CatalogCard
|
|
v-else
|
|
v-for="_item in items"
|
|
:key="_item.id"
|
|
:item="_item"
|
|
:view-mode="viewMode"
|
|
@click="showItem(_item)"
|
|
/>
|
|
</div>
|
|
<QDialog v-model="showItemDialog">
|
|
<QCard style="width: 25em" class="column">
|
|
<div class="q-pa-md" style="display: flex">
|
|
<VnImg
|
|
storage="catalog"
|
|
size="200x200"
|
|
:id="'asd'"
|
|
width="112px"
|
|
height="112px"
|
|
rounded="bottom"
|
|
class="q-mr-md"
|
|
/>
|
|
<div class="column">
|
|
<div class="text-subtitle2">
|
|
{{ selectedItem.item }}
|
|
</div>
|
|
<span class="text-subtitle2 text-grey-7 text-uppercase">
|
|
{{ selectedItem.subName }}
|
|
</span>
|
|
<span class="text-grey-7"> #{{ selectedItem.id }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tags q-px-md text-caption">
|
|
<div
|
|
v-for="(tag, index) in selectedItem.tags"
|
|
:key="index"
|
|
class="full-width row"
|
|
>
|
|
<span class="text-grey-7 col-5">
|
|
{{ tag.name }}
|
|
</span>
|
|
<span class="col">{{ tag.value }}</span>
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-for="(lot, index) in selectedItem.lots"
|
|
:key="index"
|
|
class="column"
|
|
>
|
|
<QSeparator />
|
|
<div class="row items-center q-px-md q-py-xs">
|
|
<span class="col">{{ currency(lot.price) }}</span>
|
|
<span class="col"> x{{ lot.grouping }}</span>
|
|
<QBtn
|
|
icon="add"
|
|
round
|
|
flat
|
|
dense
|
|
@click="onAddLotClick(lot)"
|
|
>
|
|
<QTooltip>{{ t('add') }}</QTooltip>
|
|
</QBtn>
|
|
</div>
|
|
</div>
|
|
<div class="row items-center justify-between q-pa-md bg-black">
|
|
<QBtn
|
|
icon="delete"
|
|
round
|
|
dense
|
|
flat
|
|
color="white"
|
|
@click="resetAmounts()"
|
|
>
|
|
<QTooltip>{{ t('delete') }}</QTooltip>
|
|
</QBtn>
|
|
<span class="text-center text-white">{{ amount }}</span>
|
|
<QBtn
|
|
icon="check"
|
|
round
|
|
dense
|
|
flat
|
|
color="white"
|
|
@click="onConfirmClick()"
|
|
>
|
|
<QTooltip>{{ t('confirm') }}</QTooltip>
|
|
</QBtn>
|
|
</div>
|
|
</QCard>
|
|
</QDialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { useI18n } from 'vue-i18n';
|
|
import { inject, onBeforeMount, ref, computed, watch } from 'vue';
|
|
import { useRoute, useRouter } from 'vue-router';
|
|
|
|
import VnImg from 'src/components/ui/VnImg.vue';
|
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
|
import CatalogCard from 'src/pages/Ecomerce/CatalogCard.vue';
|
|
import VnSearchBar from 'src/components/ui/VnSearchBar.vue';
|
|
|
|
import { useAppStore } from 'stores/app';
|
|
import { storeToRefs } from 'pinia';
|
|
import { formatDateTitle, currency } from 'src/lib/filters.js';
|
|
import useNotify from 'src/composables/useNotify.js';
|
|
import debounce from 'src/utils/debouncer.js';
|
|
|
|
const jApi = inject('jApi');
|
|
const { t } = useI18n();
|
|
const appStore = useAppStore();
|
|
const route = useRoute();
|
|
const router = useRouter();
|
|
const { isHeaderMounted, rightDrawerOpen, basketOrderId } =
|
|
storeToRefs(appStore);
|
|
const { notify } = useNotify();
|
|
|
|
const order = ref(null);
|
|
const items = ref([]);
|
|
const selectedItem = ref(null);
|
|
const showItemDialog = ref(false);
|
|
const loading = ref(false);
|
|
const isGuest = ref(false); // TODO: Integrate isGuest logic
|
|
const viewMode = ref('grid');
|
|
|
|
// Filters options
|
|
const categories = ref([]);
|
|
const itemColors = ref([]);
|
|
const itemFamilies = ref([]);
|
|
const itemProducers = ref([]);
|
|
const itemOrigins = ref([]);
|
|
const itemSubcategories = ref([]);
|
|
const search = ref(null);
|
|
// Filters values
|
|
const category = ref(null);
|
|
const type = ref(null);
|
|
const color = ref(null);
|
|
const producer = ref(null);
|
|
const origin = ref(null);
|
|
const subcategory = ref(null);
|
|
// Order by options
|
|
const orderBy = ref('i.relevancy DESC, longName');
|
|
const orderByOptions = ref([
|
|
{
|
|
label: t('relevancy'),
|
|
value: 'i.relevancy DESC, longName'
|
|
},
|
|
{
|
|
label: t('name'),
|
|
value: 'longName ASC'
|
|
},
|
|
{
|
|
label: t('lowerSize'),
|
|
value: 'size ASC'
|
|
},
|
|
{
|
|
label: t('higherSize'),
|
|
value: 'size DESC'
|
|
},
|
|
{
|
|
label: t('lowerPrice'),
|
|
value: 'price ASC'
|
|
},
|
|
{
|
|
label: t('higherPrice'),
|
|
value: 'price DESC'
|
|
},
|
|
{
|
|
label: t('available'),
|
|
value: 'available'
|
|
},
|
|
{ label: t('color'), value: 'ink ASC' },
|
|
{
|
|
label: t('producer'),
|
|
value: 'producer ASC'
|
|
},
|
|
{
|
|
label: t('origin'),
|
|
value: 'origin ASC'
|
|
},
|
|
{
|
|
label: t('category'),
|
|
value: 'category ASC'
|
|
}
|
|
]);
|
|
|
|
const selectedCategory = computed({
|
|
get() {
|
|
return category.value;
|
|
},
|
|
async set(value) {
|
|
category.value = value;
|
|
onCategoryChange();
|
|
if (!value) return;
|
|
debouncedGetFilters();
|
|
debouncedGetItems();
|
|
refreshTitle();
|
|
}
|
|
});
|
|
|
|
const selectedType = computed({
|
|
get() {
|
|
return type.value;
|
|
},
|
|
set(value) {
|
|
type.value = value;
|
|
router.push({
|
|
params: { category: category.value, type: type.value },
|
|
query: { ...route.query }
|
|
});
|
|
if (!selectedCategory.value) return;
|
|
debouncedGetFilters();
|
|
debouncedGetItems();
|
|
refreshTitle();
|
|
}
|
|
});
|
|
|
|
const selectedColor = computed({
|
|
get() {
|
|
return color.value;
|
|
},
|
|
set(value) {
|
|
color.value = value;
|
|
if (!selectedCategory.value) return;
|
|
debouncedGetFilters();
|
|
debouncedGetItems();
|
|
}
|
|
});
|
|
|
|
const selectedProducer = computed({
|
|
get() {
|
|
return producer.value;
|
|
},
|
|
set(value) {
|
|
producer.value = value;
|
|
if (!value || !selectedCategory.value) return;
|
|
debouncedGetFilters();
|
|
debouncedGetItems();
|
|
}
|
|
});
|
|
|
|
const selectedOrigin = computed({
|
|
get() {
|
|
return origin.value;
|
|
},
|
|
set(value) {
|
|
origin.value = value;
|
|
if (!value || !selectedCategory.value) return;
|
|
debouncedGetFilters();
|
|
debouncedGetItems();
|
|
}
|
|
});
|
|
|
|
const selectedSubcategory = computed({
|
|
get() {
|
|
return subcategory.value;
|
|
},
|
|
set(value) {
|
|
subcategory.value = value;
|
|
if (!value && !selectedCategory.value) return;
|
|
debouncedGetFilters();
|
|
debouncedGetItems();
|
|
}
|
|
});
|
|
|
|
const selectedOrderBy = computed({
|
|
get() {
|
|
return orderBy.value;
|
|
},
|
|
set(value) {
|
|
if (value) {
|
|
orderBy.value = value;
|
|
} else {
|
|
orderBy.value = 'i.relevancy DESC, longName';
|
|
}
|
|
debouncedGetItems();
|
|
}
|
|
});
|
|
|
|
const queryFilter = computed(() => {
|
|
const filters = [];
|
|
|
|
// Filtro principal
|
|
if (selectedCategory.value) {
|
|
filters.push(`(t.categoryFk = ${selectedCategory.value})`);
|
|
}
|
|
|
|
// Filtro búsqueda
|
|
if (search.value) {
|
|
filters.push(
|
|
`((i.longName LIKE '%${search.value}%') OR (i.subname LIKE '%${search.value}%'))`
|
|
);
|
|
}
|
|
|
|
const otherFilters = {
|
|
'i.typeFk': selectedType.value,
|
|
'i.inkFk': selectedColor?.value ? `'${selectedColor.value}'` : null,
|
|
'i.producerFk': selectedProducer.value,
|
|
'i.originFk': selectedOrigin.value
|
|
};
|
|
|
|
// Añadir solo aquellos filtros que tengan valor
|
|
Object.entries(otherFilters).forEach(([key, value]) => {
|
|
if (value) {
|
|
filters.push(`(${key} = ${value})`);
|
|
}
|
|
});
|
|
|
|
// Concatenar todos los filtros
|
|
return filters.join(' AND ');
|
|
});
|
|
|
|
const isSomeFilterSelected = computed(() => {
|
|
return (
|
|
search.value ||
|
|
selectedType.value ||
|
|
selectedColor.value ||
|
|
selectedProducer.value ||
|
|
selectedOrigin.value ||
|
|
selectedSubcategory.value
|
|
);
|
|
});
|
|
|
|
const viewTypeButtonContent = computed(() => {
|
|
return {
|
|
label: t(viewMode.value === 'list' ? 'gridView' : 'listView'),
|
|
icon: viewMode.value === 'list' ? 'grid_on' : 'view_list'
|
|
};
|
|
});
|
|
|
|
const getFilters = async () => {
|
|
const promises = [
|
|
getItemFamilies(),
|
|
getItemColors(),
|
|
getProducers(),
|
|
getOrigins(),
|
|
getSubcategories()
|
|
];
|
|
|
|
await Promise.allSettled(promises);
|
|
};
|
|
|
|
const getItems = async () => {
|
|
try {
|
|
if (!basketOrderId.value || !isSomeFilterSelected.value) return;
|
|
loading.value = true;
|
|
|
|
const res = await jApi.execQuery(
|
|
`DROP TEMPORARY TABLE IF EXISTS tmp.item;
|
|
CREATE TEMPORARY TABLE tmp.item
|
|
(INDEX (itemFk))
|
|
ENGINE = MEMORY
|
|
SELECT i.id itemFk
|
|
FROM vn.item i
|
|
JOIN vn.itemType t ON t.id = i.typeFk
|
|
WHERE (${queryFilter.value});
|
|
CALL myOrder_calcCatalogFull(#orderId);
|
|
SELECT i.id, i.longName item, i.subName,
|
|
i.tag5, i.value5, i.tag6, i.value6,
|
|
i.tag7, i.value7, i.tag8, i.value8,
|
|
i.relevancy, i.size, i.category, b.minQuantity,
|
|
k.name ink, p.name producer, o.name origin,
|
|
b.available, b.price, b.grouping,
|
|
i.image, im.updated
|
|
FROM tmp.ticketCalculateItem b
|
|
JOIN vn.item i ON i.id = b.itemFk
|
|
LEFT JOIN vn.ink k ON k.id = i.inkFk
|
|
LEFT JOIN vn.producer p ON p.id = i.producerFk
|
|
LEFT JOIN vn.origin o ON o.id = i.originFk
|
|
LEFT JOIN image im ON im.collectionFk = 'catalog'
|
|
AND im.name = i.image
|
|
WHERE b.available > 0
|
|
ORDER BY ${selectedOrderBy.value}
|
|
LIMIT 5000;
|
|
DROP TEMPORARY TABLE tmp.item;
|
|
CALL vn.ticketCalculatePurge();`,
|
|
{ orderId: basketOrderId.value }
|
|
);
|
|
items.value = res.results[3].data;
|
|
await onItemsFetched();
|
|
loading.value = false;
|
|
} catch (error) {
|
|
console.error('Error getting items:', error);
|
|
}
|
|
};
|
|
|
|
const debouncedGetItems = debounce(getItems, 400);
|
|
const debouncedGetFilters = debounce(getFilters, 400);
|
|
|
|
const getOrder = async () => {
|
|
try {
|
|
const [data] = await jApi.query(
|
|
`SELECT o.id, o.sent, ad.nickname, o.addressFk, o.agencyModeFk
|
|
FROM myOrder o
|
|
LEFT JOIN myAddress ad ON ad.id = o.addressFk
|
|
WHERE o.id = #orderId`,
|
|
{ orderId: basketOrderId.value }
|
|
);
|
|
order.value = data;
|
|
} catch (error) {
|
|
console.error('Error getting order:', error);
|
|
}
|
|
};
|
|
|
|
const getCategories = async () => {
|
|
try {
|
|
categories.value = await jApi.query(
|
|
`SELECT c.id, l.name, c.color, c.code
|
|
FROM vn.itemCategory c
|
|
JOIN vn.itemCategoryL10n l ON l.id = c.id
|
|
WHERE c.display
|
|
ORDER BY display`
|
|
);
|
|
} catch (error) {
|
|
console.error('Error getting categories:', error);
|
|
}
|
|
};
|
|
|
|
const getItemFamilies = async () => {
|
|
try {
|
|
if (!selectedCategory.value || !basketOrderId.value) return;
|
|
const res = await jApi.execQuery(
|
|
`CALL myOrder_getAvailable(#orderId);
|
|
SELECT DISTINCT t.id, l.name
|
|
FROM vn.item i
|
|
JOIN vn.itemType t ON t.id = i.typeFk
|
|
JOIN tmp.itemAvailable a ON a.id = i.id
|
|
JOIN vn.itemTypeL10n l ON l.id = t.id
|
|
WHERE t.order >= 0
|
|
AND t.categoryFk = #category
|
|
ORDER BY t.order, l.name;
|
|
DROP TEMPORARY TABLE tmp.itemAvailable`,
|
|
{
|
|
category: selectedCategory.value,
|
|
orderId: basketOrderId.value
|
|
}
|
|
);
|
|
itemFamilies.value = res.results[1].data;
|
|
} catch (error) {
|
|
console.error('Error getting available items:', error);
|
|
}
|
|
};
|
|
|
|
const getItemColors = async () => {
|
|
try {
|
|
if (!selectedCategory.value || !basketOrderId.value) return;
|
|
const res = await jApi.execQuery(
|
|
`CALL myOrder_getAvailable(#orderId);
|
|
SELECT DISTINCT l.id, l.name
|
|
FROM vn.item i
|
|
JOIN vn.itemType t ON t.id = i.typeFk
|
|
JOIN tmp.itemAvailable a ON a.id = i.id
|
|
JOIN vn.inkL10n l ON l.id = i.inkFk
|
|
WHERE (${queryFilter.value})
|
|
ORDER BY name;
|
|
DROP TEMPORARY TABLE tmp.itemAvailable;`,
|
|
{
|
|
orderId: basketOrderId.value
|
|
}
|
|
);
|
|
itemColors.value = res.results[1].data;
|
|
} catch (error) {
|
|
console.error('Error getting available items:', error);
|
|
}
|
|
};
|
|
|
|
const onItemsFetched = async () => {
|
|
items.value.forEach(item => {
|
|
const previewTags = [
|
|
{ name: item.tag5, value: item.value5 },
|
|
{ name: item.tag6, value: item.value6 },
|
|
{ name: item.tag7, value: item.value7 }
|
|
];
|
|
item.previewTags = previewTags;
|
|
});
|
|
};
|
|
|
|
const getProducers = async () => {
|
|
try {
|
|
const res = await jApi.execQuery(
|
|
`CALL myOrder_getAvailable(#orderId);
|
|
SELECT DISTINCT p.id, p.name
|
|
FROM vn.item i
|
|
JOIN vn.itemType t ON t.id = i.typeFk
|
|
JOIN tmp.itemAvailable a ON a.id = i.id
|
|
JOIN vn.producer p ON p.id = i.producerFk
|
|
WHERE (${queryFilter.value})
|
|
ORDER BY name;
|
|
DROP TEMPORARY TABLE tmp.itemAvailable;`,
|
|
{ orderId: basketOrderId.value }
|
|
);
|
|
itemProducers.value = res.results[1].data;
|
|
} catch (error) {
|
|
console.error('Error getting productors:', error);
|
|
}
|
|
};
|
|
|
|
const getOrigins = async () => {
|
|
try {
|
|
const res = await jApi.execQuery(
|
|
`CALL myOrder_getAvailable(#orderId);
|
|
SELECT DISTINCT o.id, l.name, o.code
|
|
FROM vn.item i
|
|
JOIN vn.itemType t ON t.id = i.typeFk
|
|
JOIN tmp.itemAvailable a ON a.id = i.id
|
|
JOIN vn.origin o ON o.id = i.originFk
|
|
JOIN vn.originL10n l ON l.id = o.id
|
|
WHERE (${queryFilter.value})
|
|
ORDER BY name;
|
|
DROP TEMPORARY TABLE tmp.itemAvailable;`,
|
|
{ orderId: basketOrderId.value }
|
|
);
|
|
itemOrigins.value = res.results[1].data;
|
|
} catch (error) {
|
|
console.error('Error getting productors:', error);
|
|
}
|
|
};
|
|
|
|
const getSubcategories = async () => {
|
|
try {
|
|
const res = await jApi.execQuery(
|
|
`CALL myOrder_getAvailable(#orderId);
|
|
SELECT DISTINCT i.category
|
|
FROM vn.item i
|
|
JOIN vn.itemType t ON t.id = i.typeFk
|
|
JOIN tmp.itemAvailable a ON a.id = i.id
|
|
WHERE (${queryFilter.value})
|
|
ORDER BY category;
|
|
DROP TEMPORARY TABLE tmp.itemAvailable;`,
|
|
{ orderId: basketOrderId.value }
|
|
);
|
|
itemSubcategories.value = res.results[1].data;
|
|
} catch (error) {
|
|
console.error('Error getting subcategories:', error);
|
|
}
|
|
};
|
|
|
|
const showItem = async item => {
|
|
if (isGuest.value) return;
|
|
|
|
const itemLots = await calcItem(item.id);
|
|
const tags = await getItemTags(item.id);
|
|
item.lots = itemLots;
|
|
item.tags = tags;
|
|
showItemDialog.value = true;
|
|
selectedItem.value = item;
|
|
};
|
|
|
|
const removeCategory = () => {
|
|
selectedCategory.value = null;
|
|
onCategoryChange();
|
|
};
|
|
|
|
const onCategoryChange = () => {
|
|
selectedType.value = null;
|
|
selectedColor.value = null;
|
|
selectedProducer.value = null;
|
|
selectedOrigin.value = null;
|
|
selectedSubcategory.value = null;
|
|
search.value = '';
|
|
items.value = [];
|
|
router.push({ params: { category: category.value, type: null } });
|
|
};
|
|
|
|
const getItemTags = async itemFk => {
|
|
try {
|
|
const tags = await jApi.query(
|
|
`SELECT l.name, it.value
|
|
FROM vn.itemTag it
|
|
JOIN vn.tag t ON t.id = it.tagFk
|
|
JOIN vn.tagL10n l ON l.id = t.id
|
|
WHERE it.itemFk = #itemFk
|
|
AND priority >= 0
|
|
ORDER BY it.priority`,
|
|
{
|
|
itemFk
|
|
}
|
|
);
|
|
return tags;
|
|
} catch (error) {
|
|
console.error('Error getting available items:', error);
|
|
}
|
|
};
|
|
|
|
const calcItem = async itemId => {
|
|
try {
|
|
const res = await jApi.execQuery(
|
|
`CALL myOrder_calcCatalogFromItem(#orderId, #itemId);
|
|
SELECT l.warehouseFk, w.name warehouse, p.grouping,
|
|
p.price, p.priceKg, p.rate, l.available
|
|
FROM tmp.ticketLot l
|
|
JOIN tmp.ticketComponentPrice p ON p.warehouseFk = l.warehouseFk
|
|
JOIN vn.warehouse w ON w.id = p.warehouseFk
|
|
ORDER BY warehouseFk, grouping;
|
|
DROP TEMPORARY TABLE
|
|
tmp.ticketCalculateItem,
|
|
tmp.ticketComponentPrice,
|
|
tmp.ticketComponent,
|
|
tmp.ticketLot,
|
|
tmp.zoneGetShipped;`,
|
|
{ orderId: basketOrderId.value, itemId }
|
|
);
|
|
return res.results[1].data;
|
|
} catch (error) {
|
|
console.error('Error getting items:', error);
|
|
}
|
|
};
|
|
|
|
const addedItemsAmountAcc = ref({});
|
|
const amount = ref(0);
|
|
|
|
const onAddLotClick = async lot => {
|
|
try {
|
|
const { grouping, warehouseFk, available } = lot;
|
|
|
|
let lotAmount = addedItemsAmountAcc.value[warehouseFk];
|
|
|
|
if (lotAmount === undefined) lotAmount = 0;
|
|
|
|
if (lotAmount < available) {
|
|
let newAmount = lotAmount + grouping;
|
|
|
|
if (newAmount > available) newAmount = available;
|
|
|
|
addedItemsAmountAcc.value[warehouseFk] = newAmount;
|
|
amount.value += newAmount - lotAmount;
|
|
} else {
|
|
notify(t('amountNotAvailable'), 'negative');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error adding item to basket:', error);
|
|
}
|
|
};
|
|
|
|
const resetAmounts = () => {
|
|
addedItemsAmountAcc.value = {};
|
|
amount.value = 0;
|
|
};
|
|
|
|
const addItemToOrder = async params => {
|
|
try {
|
|
await jApi.execQuery(
|
|
`CALL myOrder_addItem(#orderId, #warehouse, #item, #amount);`,
|
|
params
|
|
);
|
|
} catch (error) {
|
|
console.error('Error adding item to basket:', error);
|
|
}
|
|
};
|
|
|
|
const onConfirmClick = async params => {
|
|
try {
|
|
let amountSum = 0;
|
|
const addItemPromises = [];
|
|
|
|
for (const warehouse in addedItemsAmountAcc.value) {
|
|
const amount = addedItemsAmountAcc.value[warehouse];
|
|
amountSum += amount;
|
|
|
|
const params = {
|
|
orderId: basketOrderId.value,
|
|
warehouse,
|
|
item: selectedItem.value.id,
|
|
amount
|
|
};
|
|
addItemPromises.push(addItemToOrder(params));
|
|
}
|
|
|
|
if (amountSum > 0) {
|
|
await Promise.all(addItemPromises);
|
|
|
|
notify(
|
|
`${t('added')} ${amountSum} ${selectedItem.value.item}`,
|
|
'positive'
|
|
);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error adding item to basket:', error);
|
|
} finally {
|
|
showItemDialog.value = false;
|
|
resetAmounts();
|
|
}
|
|
};
|
|
|
|
const refreshTitle = () => {
|
|
const { meta } = route;
|
|
let title = t(meta.title);
|
|
let subtitle;
|
|
|
|
if (selectedCategory.value) {
|
|
const _category = categories.value.find(
|
|
i => i.id === selectedCategory.value
|
|
);
|
|
if (_category) {
|
|
title = _category.name;
|
|
|
|
if (selectedType.value) {
|
|
const _type = itemFamilies.value.find(
|
|
i => i.id === selectedType.value
|
|
);
|
|
if (_type) {
|
|
subtitle = title;
|
|
title = _type.name;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
appStore.$patch({ title, subtitle });
|
|
};
|
|
|
|
const onViewModeClick = () => {
|
|
viewMode.value = viewMode.value === 'list' ? 'grid' : 'list';
|
|
};
|
|
|
|
watch(
|
|
() => route.query.search,
|
|
val => {
|
|
if (val) {
|
|
search.value = val;
|
|
debouncedGetItems();
|
|
}
|
|
}
|
|
);
|
|
|
|
onBeforeMount(async () => {
|
|
if (!isGuest.value) {
|
|
await appStore.check('catalog');
|
|
} else {
|
|
// TODO: Implement this logic when isGuest is implemented
|
|
// const resultSet = await jApi.execQuery(
|
|
// 'CALL myOrder_configureForGuest(@orderId); SELECT @orderId;'
|
|
// );
|
|
// resultSet.fetchResult();
|
|
// this.orderId = resultSet.fetchValue();
|
|
}
|
|
await getOrder();
|
|
await getCategories();
|
|
|
|
if (route.params.category)
|
|
selectedCategory.value = Number(route.params.category);
|
|
if (route.params.type) selectedType.value = Number(route.params.type);
|
|
});
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.search {
|
|
max-width: 250px;
|
|
}
|
|
.basket-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
background-color: #8cc63f;
|
|
color: white;
|
|
padding: 17px 28px;
|
|
border-radius: 7px;
|
|
text-align: center;
|
|
}
|
|
.categories {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
|
|
.category {
|
|
width: 54px;
|
|
|
|
&.active {
|
|
background: rgba(0, 0, 0, 0.08);
|
|
}
|
|
& > img {
|
|
height: 40px;
|
|
width: 40px;
|
|
}
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<i18n lang="yaml">
|
|
en-US:
|
|
category: Category
|
|
deleteFilter: Delete filter
|
|
family: Family
|
|
color: Color
|
|
producer: Producer
|
|
origin: Origin
|
|
pleaseSetFilter: Choose a filter from the right menu
|
|
orderBy: Order by
|
|
relevancy: Relevancy
|
|
name: Name
|
|
lowerSize: Lower size
|
|
higherSize: Higher size
|
|
lowerPrice: Lower price
|
|
higherPrice: Higher price
|
|
add: Add
|
|
added: Added
|
|
listView: List view
|
|
gridView: Grid view
|
|
filterBy: Filter by
|
|
es-ES:
|
|
category: Categoría
|
|
deleteFilter: Quitar filtro
|
|
family: Familia
|
|
search: Buscar
|
|
color: Color
|
|
producer: Productor
|
|
origin: Origen
|
|
pleaseSetFilter: Elige un filtro en el menú de la derecha
|
|
orderBy: Ordernar por
|
|
relevancy: Relevancia
|
|
name: Nombre
|
|
lowerSize: Medida más pequeña
|
|
higherSize: Medida más grande
|
|
lowerPrice: Precio más bajo
|
|
higherPrice: Precio más alto
|
|
add: Añadir
|
|
added: Añadido
|
|
listView: Vista de lista
|
|
gridView: Vista de rejilla
|
|
filterBy: Filtrar por
|
|
ca-ES:
|
|
category: Categoría
|
|
deleteFilter: Eliminar filtro
|
|
family: Família
|
|
color: Color
|
|
producer: Productor
|
|
origin: Origen
|
|
pleaseSetFilter: Tria un filtre en el menú de la dreta
|
|
orderBy: Ordenar per
|
|
relevancy: Relevància
|
|
name: Nom
|
|
lowerSize: Mida més petita
|
|
higherSize: Mida més gran
|
|
lowerPrice: Preu més baix
|
|
higherPrice: Preu més alt
|
|
add: Afegir
|
|
listView: Vista de llista
|
|
gridView: Vista de graella
|
|
filterBy: Filtrar per
|
|
fr-FR:
|
|
category: Catégorie
|
|
deleteFilter: Supprimer le filtre
|
|
family: Famille
|
|
color: Couleur
|
|
producer: Producteur
|
|
origin: Origine
|
|
pleaseSetFilter: Choisissez un filtre dans le menu de droite
|
|
orderBy: Trier par
|
|
relevancy: Pertinence
|
|
name: Nom
|
|
lowerSize: Taille le plus bas
|
|
higherSize: Taille le plus élevé
|
|
lowerPrice: Prix le plus bas
|
|
higherPrice: Prix le plus élevé
|
|
add: Ajouter
|
|
listView: Vue en liste
|
|
gridView: Vue en grille
|
|
filterBy: Filtrer par
|
|
pt-PT:
|
|
category: Categoria
|
|
deleteFilter: Apagar filtro
|
|
family: Família
|
|
color: Cor
|
|
producer: Produtor
|
|
origin: Origem
|
|
pleaseSetFilter: Escolha um filtro no menu à direita
|
|
orderBy: Ordenar por
|
|
relevancy: Relevância
|
|
name: Nome
|
|
lowerSize: Tamanho menor
|
|
higherSize: Tamanho maior
|
|
lowerPrice: Preço mais baixo
|
|
higherPrice: Preço mais alto
|
|
add: Adicionar
|
|
listView: Vista de lista
|
|
gridView: Vista de grade
|
|
filterBy: Filtrar por
|
|
</i18n>
|