From a65db2f4cf49deb0314ee0c7f6d643a4b0b8b775 Mon Sep 17 00:00:00 2001
From: Kevin Martinez <kevin.martinez@mindshore.io>
Date: Tue, 19 Dec 2023 09:15:54 -0400
Subject: [PATCH] Create order catalog section

---
 src/i18n/en/index.js                          |   1 +
 src/pages/Order/Card/OrderCard.vue            |   6 +-
 src/pages/Order/Card/OrderCatalogItem.vue     | 182 ++++++++++++++++++
 .../Order/Card/OrderCatalogItemDialog.vue     |  83 ++++++++
 src/pages/Order/Card/OrderSummary.vue         |  20 +-
 src/pages/Order/OrderCatalog.vue              |  81 ++++++++
 src/router/modules/order.js                   |  11 +-
 7 files changed, 373 insertions(+), 11 deletions(-)
 create mode 100644 src/pages/Order/Card/OrderCatalogItem.vue
 create mode 100644 src/pages/Order/Card/OrderCatalogItemDialog.vue
 create mode 100644 src/pages/Order/OrderCatalog.vue

diff --git a/src/i18n/en/index.js b/src/i18n/en/index.js
index 21cb2ea74..752314813 100644
--- a/src/i18n/en/index.js
+++ b/src/i18n/en/index.js
@@ -555,6 +555,7 @@ export default {
             create: 'Create',
             summary: 'Summary',
             basicData: 'Basic Data',
+            catalog: 'Catalog',
         },
         field: {
             salesPersonFk: 'Sales Person',
diff --git a/src/pages/Order/Card/OrderCard.vue b/src/pages/Order/Card/OrderCard.vue
index 5a9a6452a..f6288207d 100644
--- a/src/pages/Order/Card/OrderCard.vue
+++ b/src/pages/Order/Card/OrderCard.vue
@@ -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 />
diff --git a/src/pages/Order/Card/OrderCatalogItem.vue b/src/pages/Order/Card/OrderCatalogItem.vue
new file mode 100644
index 000000000..d187df8ce
--- /dev/null
+++ b/src/pages/Order/Card/OrderCatalogItem.vue
@@ -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>
diff --git a/src/pages/Order/Card/OrderCatalogItemDialog.vue b/src/pages/Order/Card/OrderCatalogItemDialog.vue
new file mode 100644
index 000000000..8d12824e6
--- /dev/null
+++ b/src/pages/Order/Card/OrderCatalogItemDialog.vue
@@ -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>
diff --git a/src/pages/Order/Card/OrderSummary.vue b/src/pages/Order/Card/OrderSummary.vue
index 7902204ef..d3ecc7984 100644
--- a/src/pages/Order/Card/OrderSummary.vue
+++ b/src/pages/Order/Card/OrderSummary.vue
@@ -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>
diff --git a/src/pages/Order/OrderCatalog.vue b/src/pages/Order/OrderCatalog.vue
new file mode 100644
index 000000000..c4fec45a4
--- /dev/null
+++ b/src/pages/Order/OrderCatalog.vue
@@ -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>
diff --git a/src/router/modules/order.js b/src/router/modules/order.js
index ffbc1665e..e91e95493 100644
--- a/src/router/modules/order.js
+++ b/src/router/modules/order.js
@@ -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'),
+                },
             ],
         },
     ],