0
0
Fork 0

Create order catalog section

This commit is contained in:
Kevin Martinez 2023-12-19 09:15:54 -04:00
parent 42c8e8c80f
commit a65db2f4cf
7 changed files with 373 additions and 11 deletions

View File

@ -555,6 +555,7 @@ export default {
create: 'Create',
summary: 'Summary',
basicData: 'Basic Data',
catalog: 'Catalog',
},
field: {
salesPersonFk: 'Sales Person',

View File

@ -1,15 +1,11 @@
<script setup>
import LeftMenu from 'components/LeftMenu.vue';
import { useStateStore } from 'stores/useStateStore';
import OrderSearchbar from 'pages/Order/Card/OrderSearchbar.vue';
import OrderDescriptor from "pages/Order/Card/OrderDescriptor.vue";
import OrderDescriptor from 'pages/Order/Card/OrderDescriptor.vue';
const stateStore = useStateStore();
</script>
<template>
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()">
<OrderSearchbar />
</Teleport>
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
<QScrollArea class="fit">
<OrderDescriptor />

View File

@ -0,0 +1,182 @@
<script setup>
import { useSession } from 'composables/useSession';
import VnLv from 'components/ui/VnLv.vue';
import { useI18n } from 'vue-i18n';
import OrderCatalogItemDialog from 'pages/Order/Card/OrderCatalogItemDialog.vue';
import toCurrency from '../../../filters/toCurrency';
import { ref } from 'vue';
const session = useSession();
const token = session.getToken();
const { t } = useI18n();
defineProps({
item: {
type: Object,
required: true,
},
});
const dialog = ref(null);
</script>
<template>
<div class="container order-catalog-item overflow-hidden">
<div class="card shadow-6 bg-dark">
<div class="img-wrapper">
<QImg
:src="`/api/Images/catalog/200x200/${item.id}/download?access_token=${token}`"
spinner-color="primary"
:ratio="1"
height="192"
width="192"
class="image"
/>
<div v-if="item.hex" class="item-color-container">
<div
class="item-color"
:style="{ backgroundColor: `#${item.hex}` }"
></div>
</div>
</div>
<div class="content">
<span class="link">{{ item.name }}</span>
<p class="subName">{{ item.subName }}</p>
<template v-for="index in 4" :key="`tag-${index}`">
<VnLv
v-if="item?.[`tag${index + 4}`]"
:label="item?.[`tag${index + 4}`] + ':'"
:value="item?.[`value${index + 4}`]"
/>
</template>
<QRating
:model-value="item.stars"
icon="star"
icon-selected="star"
color="primary"
readonly
/>
<div class="footer">
<div class="price">
<p>{{ item.available }} to {{ item.price }}</p>
<QIcon name="add_circle" class="icon">
<QTooltip>{{ t('globals.add') }}</QTooltip>
<QPopupProxy ref="dialog">
<OrderCatalogItemDialog
:prices="item.prices"
@added="() => dialog.hide()"
/>
</QPopupProxy>
</QIcon>
</div>
<p v-if="item.priceKg" class="price-kg">
{{ t('price-kg') }} {{ toCurrency(item.priceKg) || 1123 }}
</p>
</div>
</div>
</div>
</div>
</template>
<style lang="scss">
.order-catalog-item {
.vn-label-value {
display: flex;
gap: 4px;
font-size: 11px;
.label {
color: var(--vn-label);
}
.value {
color: var(--vn-text);
}
}
}
</style>
<style lang="scss" scoped>
.container {
max-width: 448px;
width: 100%;
}
.card {
display: flex;
height: 100%;
max-height: 192px;
}
.card > * {
flex: 1;
}
.img-wrapper {
position: relative;
max-width: 192px;
}
.content {
padding: 12px;
display: flex;
flex-direction: column;
gap: 4px;
.subName {
color: var(--vn-label);
text-transform: uppercase;
}
p {
margin-bottom: 0;
}
}
.footer {
.price {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
display: flex;
align-items: center;
justify-content: space-between;
p {
font-size: 12px;
}
.icon {
color: $primary;
font-size: 24px;
cursor: pointer;
}
}
.price-kg {
font-size: 12px;
}
}
.item-color-container {
position: absolute;
bottom: 12px;
right: 12px;
background: linear-gradient($dark, $primary);
border-radius: 50%;
width: 40px;
height: 40px;
padding: 4px;
.item-color {
width: 100%;
height: 100%;
border-radius: 50%;
}
}
</style>
<i18n>
es:
price-kg: Precio por Kg
en:
price-kg: Price per Kg
</i18n>

View File

@ -0,0 +1,83 @@
<script setup>
import toCurrency from '../../../filters/toCurrency';
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import axios from 'axios';
import { useRoute } from 'vue-router';
import useNotify from 'composables/useNotify';
const { t } = useI18n();
const route = useRoute();
const { notify } = useNotify();
const props = defineProps({
prices: {
type: Array,
required: true,
},
});
const emit = defineEmits(['added']);
const fields = ref((props.prices || []).map((item) => ({ ...item, quantity: 0 })));
const addToOrder = async () => {
const items = (fields.value || []).filter((item) => Number(item.quantity) > 0);
await axios.post('/OrderRows/addToOrder', {
items,
orderFk: Number(route.params.id),
});
notify(t('globals.dataSaved'), 'positive');
emit('added');
};
</script>
<template>
<div class="container order-catalog-item q-pb-md">
<QForm @submit.prevent="addToOrder">
<QMarkupTable class="shadow-0">
<tbody>
<tr v-for="item in fields" :key="item.warehouse">
<td class="text-bold q-py-lg">
{{ item.warehouse }}
</td>
<td class="text-right">
<span
class="link"
@click="
() => {
item.quantity =
Number(item.quantity) + item.grouping;
}
"
>
{{ item.grouping }}
</span>
x {{ toCurrency(item.price) }}
</td>
<td class="text-right">
<QInput
v-model="item.quantity"
type="number"
:step="item.grouping"
min="0"
dense
/>
</td>
</tr>
</tbody>
</QMarkupTable>
<div class="flex justify-center q-mt-lg">
<QBtn color="primary" type="submit">
{{ t('globals.add') }}
</QBtn>
</div>
</QForm>
</div>
</template>
<style lang="scss" scoped>
.container {
max-width: 448px;
width: 100%;
}
</style>

View File

@ -1,14 +1,17 @@
<script setup>
import {computed, ref} from 'vue';
import { computed, ref } from 'vue';
import { useRoute } from 'vue-router';
import CardSummary from 'components/ui/CardSummary.vue';
import { useI18n } from 'vue-i18n';
import VnLv from 'components/ui/VnLv.vue';
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
import { toCurrency, toDateHour } from 'src/filters';
import OrderSearchbar from 'pages/Order/Card/OrderSearchbar.vue';
import { useStateStore } from 'stores/useStateStore';
const { t } = useI18n();
const route = useRoute();
const stateStore = useStateStore();
const $props = defineProps({
id: {
@ -44,11 +47,14 @@ const detailsColumns = ref([
name: 'amount',
label: t('claim.summary.description'),
field: (row) => toCurrency(row?.quantity * row?.price),
}
])
},
]);
</script>
<template>
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()">
<OrderSearchbar />
</Teleport>
<CardSummary ref="summary" :url="`Orders/${entityId}/summary`">
<template #header="{ entity }">
{{ t('order.summary.basket') }} #{{ entity?.id }} -
@ -147,8 +153,12 @@ const detailsColumns = ref([
<p class="header">
{{ t('order.summary.details') }}
</p>
<QTable :columns="detailsColumns" :rows="entity?.rows" flat hide-pagination>
<QTable
:columns="detailsColumns"
:rows="entity?.rows"
flat
hide-pagination
>
</QTable>
</QCard>
</template>

View File

@ -0,0 +1,81 @@
<script setup>
import { useStateStore } from 'stores/useStateStore';
import { useRoute } from 'vue-router';
import { onMounted, onUnmounted } from 'vue';
import VnPaginate from 'components/ui/VnPaginate.vue';
import OrderCatalogItem from 'pages/Order/Card/OrderCatalogItem.vue';
import OrderSearchbar from 'pages/Order/Card/OrderSearchbar.vue';
import { useI18n } from 'vue-i18n';
const route = useRoute();
const stateStore = useStateStore();
const { t } = useI18n();
onMounted(() => (stateStore.rightDrawer = true));
onUnmounted(() => (stateStore.rightDrawer = false));
const catalogParams = {
orderFk: route.params.id,
orderBy: JSON.stringify({ field: 'relevancy DESC, name', way: 'ASC', isTag: false }),
};
</script>
<template>
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()">
<OrderSearchbar
data-key="OrderCatalogList"
url="Orders/CatalogFilter"
:limit="50"
:user-params="catalogParams"
/>
</Teleport>
<Teleport v-if="stateStore.isHeaderMounted()" to="#actions-append">
<div class="row q-gutter-x-sm">
<QBtn
flat
@click.stop="stateStore.toggleRightDrawer()"
round
dense
icon="menu"
>
<QTooltip bottom anchor="bottom right">
{{ t('globals.collapseMenu') }}
</QTooltip>
</QBtn>
</div>
</Teleport>
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
<QScrollArea class="fit text-grey-8"></QScrollArea>
</QDrawer>
<QPage class="column items-center q-pa-md">
<div class="card-list">
<VnPaginate
data-key="OrderCatalogList"
url="Orders/CatalogFilter"
:limit="50"
:user-params="catalogParams"
auto-load
>
<template #body="{ rows }">
<div class="catalog-list">
<OrderCatalogItem v-for="row in rows" :key="row.id" :item="row" />
</div>
</template>
</VnPaginate>
</div>
</QPage>
</template>
<style lang="scss">
.card-list {
width: 100%;
}
.catalog-list {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
justify-content: center;
gap: 16px;
}
</style>

View File

@ -11,7 +11,7 @@ export default {
redirect: { name: 'OrderMain' },
menus: {
main: ['OrderList'],
card: ['OrderBasicData'],
card: ['OrderBasicData', 'OrderCatalog'],
},
children: [
{
@ -63,6 +63,15 @@ export default {
},
component: () => import('src/pages/Order/Card/OrderForm.vue'),
},
{
name: 'OrderCatalog',
path: 'catalog',
meta: {
title: 'catalog',
icon: 'vn:basket',
},
component: () => import('src/pages/Order/OrderCatalog.vue'),
},
],
},
],