From cb610608dfc9e096926e2a3a5c896954e444406d Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Wed, 17 Jul 2024 08:49:38 +0200
Subject: [PATCH 1/4] feat: refs #7356 list & weekly to VnTable and style fixes

---
 src/pages/Ticket/Card/TicketCard.vue |   8 +-
 src/pages/Ticket/TicketAdvance.vue   |   4 +-
 src/pages/Ticket/TicketFilter.vue    |  12 +-
 src/pages/Ticket/TicketFuture.vue    |   4 +-
 src/pages/Ticket/TicketList.vue      | 382 +++++++++++++++++++-------
 src/pages/Ticket/TicketWeekly.vue    | 386 +++++++++++----------------
 src/pages/Ticket/locale/en.yml       |  11 +
 src/pages/Ticket/locale/es.yml       |  11 +
 8 files changed, 475 insertions(+), 343 deletions(-)

diff --git a/src/pages/Ticket/Card/TicketCard.vue b/src/pages/Ticket/Card/TicketCard.vue
index 689a717e6..8c9745c02 100644
--- a/src/pages/Ticket/Card/TicketCard.vue
+++ b/src/pages/Ticket/Card/TicketCard.vue
@@ -11,18 +11,14 @@ const { t } = useI18n();
 const route = useRoute();
 
 const routeName = computed(() => route.name);
-const searchBarDataKeys = {
-    TicketSummary: 'TicketSummary',
-    TicketSale: 'TicketSale',
-    TicketPurchaseRequest: 'TicketPurchaseRequest',
-};
 </script>
 <template>
     <VnCard
         data-key="Ticket"
+        base-url="Tickets"
         :filter-panel="TicketFilter"
         :descriptor="TicketDescriptor"
-        :search-data-key="searchBarDataKeys[routeName]"
+        search-data-key="TicketList"
         :search-custom-route-redirect="routeName"
         :searchbar-label="t('card.search')"
         :searchbar-info="t('card.searchInfo')"
diff --git a/src/pages/Ticket/TicketAdvance.vue b/src/pages/Ticket/TicketAdvance.vue
index bb9a8c8a8..bf4000fdf 100644
--- a/src/pages/Ticket/TicketAdvance.vue
+++ b/src/pages/Ticket/TicketAdvance.vue
@@ -626,7 +626,7 @@ onMounted(async () => {
             </template>
             <template #body-cell-ticketId="{ row }">
                 <QTd>
-                    <QBtn flat color="primary">
+                    <QBtn flat class="link">
                         {{ row.id }}
                         <TicketDescriptorProxy :id="row.id" />
                     </QBtn>
@@ -658,7 +658,7 @@ onMounted(async () => {
             </template>
             <template #body-cell-futureId="{ row }">
                 <QTd class="vertical-separator">
-                    <QBtn flat color="primary" dense>
+                    <QBtn flat class="link" dense>
                         {{ row.futureId }}
                         <TicketDescriptorProxy :id="row.futureId" />
                     </QBtn>
diff --git a/src/pages/Ticket/TicketFilter.vue b/src/pages/Ticket/TicketFilter.vue
index 3570cfc03..3b9833ce2 100644
--- a/src/pages/Ticket/TicketFilter.vue
+++ b/src/pages/Ticket/TicketFilter.vue
@@ -15,11 +15,11 @@ const props = defineProps({
     },
 });
 
-const workers = ref();
-const provinces = ref();
-const states = ref();
-const agencies = ref();
-const warehouses = ref();
+const workers = ref([]);
+const provinces = ref([]);
+const states = ref([]);
+const agencies = ref([]);
+const warehouses = ref([]);
 </script>
 
 <template>
@@ -33,7 +33,7 @@ const warehouses = ref();
         @on-fetch="(data) => (workers = data)"
         auto-load
     />
-    <VnFilterPanel :data-key="props.dataKey" :search-button="true">
+    <VnFilterPanel :data-key="props.dataKey" :search-button="true" search-url="table">
         <template #tags="{ tag, formatFn }">
             <div class="q-gutter-x-xs">
                 <strong>{{ t(`params.${tag.label}`) }}: </strong>
diff --git a/src/pages/Ticket/TicketFuture.vue b/src/pages/Ticket/TicketFuture.vue
index 4db32de75..2fec6dc18 100644
--- a/src/pages/Ticket/TicketFuture.vue
+++ b/src/pages/Ticket/TicketFuture.vue
@@ -442,7 +442,7 @@ onMounted(async () => {
             </template>
             <template #body-cell-ticketId="{ row }">
                 <QTd>
-                    <QBtn flat color="primary">
+                    <QBtn flat class="link">
                         {{ row.id }}
                         <TicketDescriptorProxy :id="row.id" />
                     </QBtn>
@@ -489,7 +489,7 @@ onMounted(async () => {
             </template>
             <template #body-cell-futureId="{ row }">
                 <QTd class="vertical-separator">
-                    <QBtn flat color="primary" dense>
+                    <QBtn flat class="link" dense>
                         {{ row.futureId }}
                         <TicketDescriptorProxy :id="row.futureId" />
                     </QBtn>
diff --git a/src/pages/Ticket/TicketList.vue b/src/pages/Ticket/TicketList.vue
index 6090bdbef..572fe3ec7 100644
--- a/src/pages/Ticket/TicketList.vue
+++ b/src/pages/Ticket/TicketList.vue
@@ -1,118 +1,312 @@
 <script setup>
-import { onMounted, onUnmounted } from 'vue';
+import axios from 'axios';
+import { computed, ref } from 'vue';
 import { useI18n } from 'vue-i18n';
-import { useRouter } from 'vue-router';
-import { useStateStore } from 'stores/useStateStore';
 import { toDate, toCurrency } from 'src/filters/index';
-import VnPaginate from 'src/components/ui/VnPaginate.vue';
 import TicketSummary from './Card/TicketSummary.vue';
 import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
-import TicketFilter from './TicketFilter.vue';
-import VnLv from 'src/components/ui/VnLv.vue';
-import CardList from 'src/components/ui/CardList.vue';
 import { useSummaryDialog } from 'src/composables/useSummaryDialog';
-import RightMenu from 'src/components/common/RightMenu.vue';
+import VnTable from 'src/components/VnTable/VnTable.vue';
+import VnSelect from 'src/components/common/VnSelect.vue';
+import VnInputDate from 'src/components/common/VnInputDate.vue';
+import VnRow from 'src/components/ui/VnRow.vue';
 
-const router = useRouter();
 const { t } = useI18n();
-const stateStore = useStateStore();
 const { viewSummary } = useSummaryDialog();
+const tableRef = ref();
+const clientsOptions = ref([]);
+const addressesOptions = ref([]);
+const agenciesOptions = ref([]);
+const selectedClient = ref();
 
-onMounted(() => (stateStore.rightDrawer = true));
-onUnmounted(() => (stateStore.rightDrawer = false));
+const columns = computed(() => [
+    {
+        align: 'left',
+        name: 'id',
+        label: t('ticketList.id'),
+        chip: {
+            condition: () => true,
+        },
+        isId: true,
+    },
+    {
+        align: 'left',
+        name: 'nickname',
+        label: t('ticketList.nickname'),
+        isTitle: true,
+    },
+    {
+        align: 'left',
+        name: 'state',
+        label: t('ticketList.state'),
+        cardVisible: true,
+        chip: {
+            condition: () => true,
+            color: (row) => {
+                return row?.classColor ? `bg-${row.classColor}` : 'bg-orange';
+            },
+        },
+        columnFilter: {
+            name: 'stateFk',
+            component: 'select',
+            attrs: {
+                url: 'States',
+                fields: ['id', 'name'],
+            },
+        },
+        columnField: {
+            component: null,
+        },
+    },
+    {
+        align: 'left',
+        name: 'shipped',
+        cardVisible: true,
+        label: t('ticketList.shipped'),
+        columnFilter: {
+            component: 'date',
+            alias: 't',
+            inWhere: true,
+        },
+        format: ({ shipped }) => toDate(shipped),
+    },
+    {
+        align: 'left',
+        name: 'zoneFk',
+        label: t('ticketList.zone'),
+        columnFilter: {
+            component: 'select',
+            attrs: {
+                url: 'Zones',
+                fields: ['id', 'name'],
+            },
+            alias: 't',
+            inWhere: true,
+        },
+        format: (row, dashIfEmpty) => dashIfEmpty(row.zoneName),
+    },
+    {
+        align: 'left',
+        label: t('ticketList.salesPerson'),
+        name: 'salesPersonFk',
+        component: 'select',
+        attrs: {
+            url: 'Workers/activeWithInheritedRole',
+            fields: ['id', 'name'],
+            where: { role: 'salesPerson' },
+            optionFilter: 'firstName',
+            useLike: false,
+        },
+        columnField: {
+            component: null,
+        },
+        format: (row, dashIfEmpty) => dashIfEmpty(row.salesPerson),
+    },
+    {
+        align: 'left',
+        name: 'totalWithVat',
+        label: t('ticketList.totalWithVat'),
+        cardVisible: true,
+        columnFilter: {
+            component: 'number',
+            inWhere: true,
+        },
+        format: (row) => toCurrency(row.totalWithVat),
+    },
+    {
+        align: 'right',
+        name: 'tableActions',
+        actions: [
+            {
+                title: t('ticketList.summary'),
+                icon: 'preview',
+                action: (row) => viewSummary(row.id, TicketSummary),
+            },
+        ],
+    },
+]);
 
-const from = Date.vnNew();
-const to = Date.vnNew();
-to.setDate(to.getDate() + 1);
-
-const userParams = {
-    from: from.toISOString(),
-    to: to.toISOString(),
+const onClientSelected = async (formData) => {
+    await fetchClient(formData);
+    await fetchAddresses(formData);
 };
 
-function navigate(id) {
-    router.push({ path: `/ticket/${id}` });
-}
+const fetchAvailableAgencies = async (formData) => {
+    if (!formData.warehouseId || !formData.addressId || !formData.landed) return;
+    let params = {
+        warehouseFk: formData.warehouseId,
+        addressFk: formData.addressId,
+        landed: formData.landed,
+    };
+
+    const { data } = await axios.get('Agencies/getAgenciesWithWarehouse', { params });
+
+    agenciesOptions.value = data;
+
+    const defaultAgency = agenciesOptions.value.find(
+        (agency) =>
+            agency.agencyModeFk === selectedClient.value.defaultAddress.agencyModeFk
+    );
+
+    if (defaultAgency) formData.agencyModeId = defaultAgency.agencyModeFk;
+};
+
+const fetchClient = async (formData) => {
+    try {
+        const filter = {
+            include: {
+                relation: 'defaultAddress',
+                scope: {
+                    fields: ['id', 'agencyModeFk'],
+                },
+            },
+            where: { id: formData.clientId },
+        };
+        const params = { filter: JSON.stringify(filter) };
+        const { data } = await axios.get('Clients', { params });
+        const [client] = data;
+        selectedClient.value = client;
+    } catch (err) {
+        console.error('Error fetching client');
+    }
+};
+
+const fetchAddresses = async (formData) => {
+    try {
+        if (!formData.clientId) return;
+
+        const filter = {
+            fields: ['nickname', 'street', 'city', 'id'],
+            where: { isActive: true },
+            order: 'nickname ASC',
+        };
+        const params = { filter: JSON.stringify(filter) };
+        const { data } = await axios.get(`Clients/${formData.clientId}/addresses`, {
+            params,
+        });
+        addressesOptions.value = data;
+
+        const { defaultAddress } = selectedClient.value;
+        formData.addressId = defaultAddress.id;
+    } catch (err) {
+        console.error(`Error fetching addresses`, err);
+        return err.response;
+    }
+};
 </script>
 
 <template>
     <VnSearchbar
-        data-key="TicketList"
+        data-key="Tickets"
         :label="t('Search ticket')"
         :info="t('You can search by ticket id or alias')"
     />
-    <RightMenu>
-        <template #right-panel>
-            <TicketFilter data-key="TicketList" />
+    <VnTable
+        ref="tableRef"
+        data-key="Tickets"
+        url="Tickets/filter"
+        :create="{
+            urlCreate: 'Tickets/new',
+            title: t('ticketList.createTicket'),
+            onDataSaved: ({ id }) => tableRef.redirect(id),
+            formInitialData: {},
+        }"
+        default-mode="table"
+        :columns="columns"
+        redirect="ticket"
+        auto-load
+    >
+        <template #more-create-dialog="{ data }">
+            <VnRow>
+                <VnSelect
+                    url="Clients"
+                    :label="t('ticketList.client')"
+                    v-model="data.clientId"
+                    :options="clientsOptions"
+                    option-value="id"
+                    option-label="name"
+                    hide-selected
+                    @update:model-value="(client) => onClientSelected(data)"
+                >
+                    <template #option="scope">
+                        <QItem v-bind="scope.itemProps">
+                            <QItemSection>
+                                <QItemLabel>
+                                    {{ scope.opt.name }}
+                                </QItemLabel>
+                                <QItemLabel caption>
+                                    {{ `#${scope.opt.id}` }}
+                                </QItemLabel>
+                            </QItemSection>
+                        </QItem>
+                    </template>
+                </VnSelect>
+            </VnRow>
+            <VnRow>
+                <VnSelect
+                    url="Addresses"
+                    :label="t('ticket.create.address')"
+                    v-model="data.addressId"
+                    :options="addressesOptions"
+                    option-value="id"
+                    option-label="nickname"
+                    hide-selected
+                    :disable="!data.clientId"
+                    @update:model-value="() => fetchAvailableAgencies(data)"
+                >
+                    <template #option="scope">
+                        <QItem v-bind="scope.itemProps">
+                            <QItemSection>
+                                <QItemLabel>
+                                    {{ scope.opt.nickname }}
+                                </QItemLabel>
+                                <QItemLabel caption>
+                                    {{ `${scope.opt.street}, ${scope.opt.city}` }}
+                                </QItemLabel>
+                            </QItemSection>
+                        </QItem>
+                    </template>
+                </VnSelect>
+            </VnRow>
+            <VnRow class="row q-gutter-md q-mb-md">
+                <div class="col">
+                    <VnInputDate
+                        placeholder="dd-mm-aaa"
+                        :label="t('ticket.create.landed')"
+                        v-model="data.landed"
+                        @update:model-value="() => fetchAvailableAgencies(data)"
+                    />
+                </div>
+            </VnRow>
+            <VnRow class="row q-gutter-md q-mb-md">
+                <div class="col">
+                    <VnSelect
+                        url="Warehouses"
+                        :label="t('ticket.create.warehouse')"
+                        v-model="data.warehouseId"
+                        :options="warehousesOptions"
+                        option-value="id"
+                        option-label="name"
+                        hide-selected
+                        @update:model-value="() => fetchAvailableAgencies(data)"
+                    />
+                </div>
+            </VnRow>
+            <VnRow class="row q-gutter-md q-mb-md">
+                <div class="col">
+                    <VnSelect
+                        :label="t('ticket.create.agency')"
+                        v-model="data.agencyModeId"
+                        :options="agenciesOptions"
+                        option-value="agencyModeFk"
+                        option-label="agencyMode"
+                        hide-selected
+                        :disable="!data.clientId || !data.landed || !data.warehouseId"
+                    />
+                </div>
+            </VnRow>
         </template>
-    </RightMenu>
-    <QPage class="column items-center q-pa-md">
-        <div class="vn-card-list">
-            <VnPaginate
-                data-key="TicketList"
-                url="Tickets/filter"
-                :user-params="userParams"
-                order="id DESC"
-                auto-load
-            >
-                <template #body="{ rows }">
-                    <CardList
-                        v-for="row of rows"
-                        :key="row.id"
-                        :id="row.id"
-                        :title="`${row.nickname}`"
-                        @click="navigate(row.id)"
-                    >
-                        <template #list-items>
-                            <VnLv
-                                :label="t('ticket.list.nickname')"
-                                :value="row.nickname"
-                            />
-                            <VnLv :label="t('ticket.list.state')">
-                                <template #value>
-                                    <QBadge
-                                        text-color="black"
-                                        :color="row.classColor ?? 'orange'"
-                                        class="q-ma-none"
-                                        dense
-                                    >
-                                        {{ row.state }}
-                                    </QBadge>
-                                </template>
-                            </VnLv>
-                            <VnLv
-                                :label="t('ticket.list.shipped')"
-                                :value="toDate(row.shipped)"
-                            />
-                            <VnLv :label="t('Zone')" :value="row.zoneName" />
-                            <VnLv
-                                :label="t('ticket.list.salesPerson')"
-                                :value="row.salesPerson"
-                            />
-                            <VnLv
-                                :label="t('ticket.list.total')"
-                                :value="toCurrency(row.totalWithVat)"
-                            />
-                        </template>
-                        <template #actions>
-                            <QBtn
-                                :label="t('components.smartCard.openSummary')"
-                                @click.stop="viewSummary(row.id, TicketSummary)"
-                                color="primary"
-                            />
-                        </template>
-                    </CardList>
-                </template>
-            </VnPaginate>
-        </div>
-        <QPageSticky :offset="[20, 20]">
-            <QBtn :to="{ name: 'TicketCreate' }" fab icon="add" color="primary">
-                <QTooltip>
-                    {{ t('New ticket') }}
-                </QTooltip>
-            </QBtn>
-        </QPageSticky>
-    </QPage>
+    </VnTable>
 </template>
 
 <i18n>
diff --git a/src/pages/Ticket/TicketWeekly.vue b/src/pages/Ticket/TicketWeekly.vue
index 5dbc99d22..19e9e96cc 100644
--- a/src/pages/Ticket/TicketWeekly.vue
+++ b/src/pages/Ticket/TicketWeekly.vue
@@ -1,40 +1,28 @@
 <script setup>
-import { onMounted, ref, computed, reactive, onUnmounted } from 'vue';
+import { onMounted, ref, computed, onUnmounted } from 'vue';
 import { useI18n } from 'vue-i18n';
-import { useRouter } from 'vue-router';
-
-import FetchData from 'components/FetchData.vue';
-import TableVisibleColumns from 'src/components/common/TableVisibleColumns.vue';
-import VnInput from 'src/components/common/VnInput.vue';
 import VnSelect from 'src/components/common/VnSelect.vue';
 import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
 import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
 import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
-import VnPaginate from 'components/ui/VnPaginate.vue';
-import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
 import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
-
 import { useStateStore } from 'stores/useStateStore';
-import { dashIfEmpty } from 'src/filters';
 import { useVnConfirm } from 'composables/useVnConfirm';
 import { useArrayData } from 'composables/useArrayData';
 import useNotify from 'src/composables/useNotify.js';
 import axios from 'axios';
+import VnTable from 'src/components/VnTable/VnTable.vue';
 
-const router = useRouter();
 const stateStore = useStateStore();
 const { t } = useI18n();
-const { openConfirmationModal } = useVnConfirm();
 const { notify } = useNotify();
-
-const paginateRef = ref(null);
+const { openConfirmationModal } = useVnConfirm();
 const agencyModesOptions = ref([]);
-const visibleColumns = ref([]);
 const allColumnNames = ref([]);
 
 const arrayData = useArrayData('WeeklyTickets');
 const { store } = arrayData;
-
+const tableRef = ref();
 const weekdays = [
     { id: 0, name: t('weekdays.mon') },
     { id: 1, name: t('weekdays.tue') },
@@ -45,113 +33,131 @@ const weekdays = [
     { id: 6, name: t('weekdays.sun') },
 ];
 
-const exprBuilder = (param, value) => {
-    switch (param) {
-        case 'clientName':
-            return { 'c.name': value };
-        case 'nickName':
-            return { 'u.name': value };
-    }
-};
-
-const params = reactive({});
-
-const applyColumnFilter = async (col) => {
-    try {
-        const paramKey = col.columnFilter?.filterParamKey || col.field;
-        params[paramKey] = col.columnFilter.filterValue;
-        await paginateRef.value.addFilter(null, params);
-    } catch (err) {
-        console.error('Error applying column filter', err);
-    }
-};
-
-const getInputEvents = (col) => ({ 'keyup.enter': () => applyColumnFilter(col) });
-
 const columns = computed(() => [
     {
+        align: 'left',
+        name: 'ticketFk',
         label: t('weeklyTickets.id'),
-        name: 'id',
-        field: 'ticketFk',
-        align: 'left',
-        sortable: true,
-        columnFilter: null,
+        chip: {
+            condition: () => true,
+        },
+        isId: true,
+        cardVisible: true,
     },
     {
+        align: 'left',
+        name: 'clientFk',
         label: t('weeklyTickets.client'),
-        name: 'client',
-        field: 'clientName',
-        align: 'left',
-        sortable: true,
-        columnFilter: {
-            component: VnInput,
-            type: 'text',
-            filterValue: null,
-            event: getInputEvents,
-            attrs: {
-                dense: true,
-            },
+        isTitle: true,
+        cardVisible: true,
+        component: 'select',
+        attrs: {
+            url: 'Clients',
+            optionLabel: 'name',
+            optionValue: 'id',
+            isWhere: true,
         },
-        format: (val) => dashIfEmpty(val),
+        columnField: {
+            component: null,
+        },
+        format: (row) => row.clientName,
     },
     {
+        align: 'left',
+        name: 'weekDay',
         label: t('weeklyTickets.shipment'),
-        name: 'shipment',
-        field: 'weekDay',
-        align: 'left',
-        sortable: true,
-        columnFilter: null,
-    },
-    {
-        label: t('weeklyTickets.agency'),
-        name: 'agency',
-        field: 'agencyModeFk',
-        align: 'left',
-        sortable: true,
-        columnFilter: null,
-    },
-    {
-        label: t('weeklyTickets.warehouse'),
-        name: 'warehouse',
-        field: 'warehouseName',
-        align: 'left',
-        sortable: true,
-        columnFilter: null,
-    },
-    {
-        label: t('weeklyTickets.salesperson'),
-        field: 'salesperson',
-        name: 'salesperson',
-        align: 'left',
-        sortable: true,
+        cardVisible: true,
         columnFilter: {
-            component: VnInput,
-            type: 'text',
-            filterValue: null,
-            event: getInputEvents,
-            filterParamKey: 'nickName',
-            attrs: {
-                dense: true,
-            },
+            component: 'input',
+            optionLabel: weekdays.name,
+            optionValue: weekdays.id,
+            inWhere: true,
         },
     },
     {
-        label: '',
-        name: 'actions',
         align: 'left',
-        columnFilter: null,
+        label: t('weeklyTickets.agency'),
+        name: 'agencyModeFk',
+        cardVisible: true,
+        columnFilter: {
+            component: 'select',
+            attrs: {
+                url: 'AgencyModes',
+                fields: ['id', 'name'],
+            },
+            inWhere: true,
+        },
+        columnField: {
+            component: null,
+        },
+        format: (row, dashIfEmpty) => dashIfEmpty(row?.agencyMode?.name),
+    },
+    {
+        align: 'left',
+        name: 'warehouseFk',
+        label: t('weeklyTickets.warehouse'),
+        cardVisible: true,
+        columnFilter: {
+            component: 'select',
+            attrs: {
+                url: 'Warehouses',
+                fields: ['id', 'name'],
+            },
+            inWhere: true,
+        },
+        columnField: {
+            component: null,
+        },
+        format: (row, dashIfEmpty) => dashIfEmpty(row.warehouseName),
+    },
+    {
+        align: 'left',
+        name: 'workerFk',
+        label: t('weeklyTickets.salesperson'),
+        columnFilter: {
+            component: 'select',
+            attrs: {
+                url: 'Workers/activeWithInheritedRole',
+                fields: ['id', 'name'],
+                where: { role: 'salesperson' },
+            },
+            inWhere: true,
+        },
+        columnField: {
+            component: null,
+        },
+        cardVisible: true,
+        format: (row) => row.userName,
+    },
+    {
+        align: 'right',
+        label: '',
+        name: 'tableActions',
+        actions: [
+            {
+                title: t('ticketWeekly.delete'),
+                icon: 'delete',
+                action: (row) =>
+                    openConfirmationModal(
+                        t('You are going to delete this weekly ticket'),
+                        t(
+                            'This ticket will be removed from weekly tickets! Continue anyway?'
+                        ),
+                        () => deleteWeekly(row.ticketFk)
+                    ),
+                isPrimary: true,
+            },
+        ],
     },
 ]);
 
-const redirectToTicketSummary = (ticketFk) =>
-    router.push({ name: 'TicketSummary', params: { id: ticketFk } });
-
 const deleteWeekly = async (ticketFk) => {
     try {
         await axios.delete(`TicketWeeklies/${ticketFk}`);
         notify(t('globals.dataSaved'), 'positive');
         const ticketIndex = store.data.findIndex((e) => e.ticketFk == ticketFk);
         store.data.splice(ticketIndex, 1);
+        location.reload();
     } catch (err) {
         console.error('Error deleting weekly', err);
     }
@@ -176,147 +182,61 @@ onUnmounted(() => (stateStore.rightDrawer = false));
 </script>
 
 <template>
-    <FetchData
-        url="AgencyModes/isActive"
-        :filter="{ fields: ['id', 'name'], order: 'name' }"
-        auto-load
-        @on-fetch="(data) => (agencyModesOptions = data)"
-    />
     <VnSearchbar
         data-key="WeeklyTickets"
         :label="t('weeklyTickets.search')"
         :info="t('weeklyTickets.searchInfo')"
     />
-    <VnSubToolbar>
-        <template #st-data>
-            <TableVisibleColumns
-                :all-columns="allColumnNames"
-                table-code="itemsIndex"
-                labels-traductions-path="weeklyTickets"
-                @on-config-saved="visibleColumns = [...$event, 'actions']"
+    <VnTable
+        ref="tableRef"
+        data-key="WeeklyTickets"
+        url="TicketWeeklies/filter"
+        :columns="columns"
+        default-mode="table"
+        :use-model="true"
+        :disable-option="{ card: true }"
+        auto-load
+    >
+        <template #column-ticketFk="{ row }">
+            <span class="link" @click.stop>
+                {{ row.ticketFk }}
+                <TicketDescriptorProxy :id="row.ticketFk" />
+            </span>
+        </template>
+        <template #column-weekDay="{ row }">
+            <VnSelect
+                :options="weekdays"
+                hide-selected
+                option-label="name"
+                option-value="id"
+                v-model="row.weekDay"
+                @update:model-value="onUpdate(row.ticketFk, 'weekDay', $event)"
             />
         </template>
-    </VnSubToolbar>
-    <QPage class="column items-center q-pa-md">
-        <VnPaginate
-            ref="paginateRef"
-            data-key="WeeklyTickets"
-            url="TicketWeeklies/filter"
-            :order="['weekDay', 'ticketFk']"
-            :limit="20"
-            :expr-builder="exprBuilder"
-            :user-params="params"
-            :offset="50"
-            auto-load
-        >
-            <template #body="{ rows }">
-                <QTable
-                    :rows="rows"
-                    :columns="columns"
-                    row-key="id"
-                    :pagination="{ rowsPerPage: 0 }"
-                    class="full-width q-mt-md"
-                    :visible-columns="visibleColumns"
-                    :no-data-label="t('globals.noResults')"
-                    @row-click="(_, row) => redirectToTicketSummary(row.ticketFk)"
-                >
-                    <template #top-row="{ cols }">
-                        <QTr>
-                            <QTd
-                                v-for="(col, index) in cols"
-                                :key="index"
-                                style="max-width: 100px"
-                            >
-                                <component
-                                    :is="col.columnFilter.component"
-                                    v-if="col.columnFilter"
-                                    v-model="col.columnFilter.filterValue"
-                                    v-bind="col.columnFilter.attrs"
-                                    v-on="col.columnFilter.event(col)"
-                                    dense
-                                />
-                            </QTd>
-                        </QTr>
-                    </template>
-                    <template #body-cell-id="{ row }">
-                        <QTd @click.stop>
-                            <QBtn flat color="primary">
-                                {{ row.ticketFk }}
-                                <TicketDescriptorProxy :id="row.ticketFk" />
-                            </QBtn>
-                        </QTd>
-                    </template>
-                    <template #body-cell-salesperson="{ row }">
-                        <QTd @click.stop>
-                            <QBtn flat color="primary">
-                                {{ row.userName }}
-                                <WorkerDescriptorProxy :id="row.workerFk" />
-                            </QBtn>
-                        </QTd>
-                    </template>
-                    <template #body-cell-client="{ row }">
-                        <QTd @click.stop>
-                            <QBtn flat color="primary" dense>
-                                {{ row.clientName }}
-                                <CustomerDescriptorProxy :id="row.clientFk" />
-                            </QBtn>
-                        </QTd>
-                    </template>
-                    <template #body-cell-shipment="{ row }">
-                        <QTd @click.stop>
-                            <VnSelect
-                                :options="weekdays"
-                                hide-selected
-                                option-label="name"
-                                option-value="id"
-                                v-model="row.weekDay"
-                                @update:model-value="
-                                    onUpdate(row.ticketFk, 'weekDay', $event)
-                                "
-                            />
-                        </QTd>
-                    </template>
-                    <template #body-cell-agency="{ row }">
-                        <QTd @click.stop>
-                            <VnSelect
-                                :options="agencyModesOptions"
-                                hide-selected
-                                option-label="name"
-                                option-value="id"
-                                v-model="row.agencyModeFk"
-                                @update:model-value="
-                                    onUpdate(row.ticketFk, 'agencyModeFk', $event)
-                                "
-                            />
-                        </QTd>
-                    </template>
-                    <template #body-cell-actions="{ row }">
-                        <QTd>
-                            <QIcon
-                                @click.stop="
-                                    openConfirmationModal(
-                                        t('You are going to delete this weekly ticket'),
-                                        t(
-                                            'This ticket will be removed from weekly tickets! Continue anyway?'
-                                        ),
-                                        () => deleteWeekly(row.ticketFk)
-                                    )
-                                "
-                                class="q-ml-sm cursor-pointer"
-                                color="primary"
-                                name="delete"
-                                size="sm"
-                            >
-                                <QTooltip>
-                                    {{ t('globals.delete') }}
-                                </QTooltip>
-                            </QIcon>
-                        </QTd>
-                    </template>
-                </QTable>
-            </template>
-        </VnPaginate>
-    </QPage>
+        <template #column-agencyModeFk="{ row }">
+            <VnSelect
+                url="AgencyModes/isActive"
+                :options="agencyModesOptions"
+                hide-selected
+                option-label="name"
+                option-value="id"
+                v-model="row.agencyModeFk"
+                @update:model-value="onUpdate(row.ticketFk, 'agencyModeFk', $event)"
+            />
+        </template>
+        <template #column-clientFk="{ row }">
+            <span class="link" @click.stop>
+                {{ row.clientName }}
+                <CustomerDescriptorProxy :id="row.clientFk" />
+            </span>
+        </template>
+        <template #column-workerFk="{ row }">
+            <span class="link" @click.stop>
+                {{ row.userName }}
+                <WorkerDescriptorProxy :id="row.workerFk" />
+            </span>
+        </template>
+    </VnTable>
 </template>
 
 <i18n>
diff --git a/src/pages/Ticket/locale/en.yml b/src/pages/Ticket/locale/en.yml
index d5530926f..10a8e1fa4 100644
--- a/src/pages/Ticket/locale/en.yml
+++ b/src/pages/Ticket/locale/en.yml
@@ -225,3 +225,14 @@ package:
     added: Added
     addPackage: Add package
     removePackage: Remove package
+ticketList:
+    id: Id
+    nickname: Nickname
+    state: State
+    shipped: Shipped
+    zone: Zone
+    salesPerson: Sales person
+    totalWithVat: Total with VAT
+    summary: Summary
+    client: Customer
+    createTicket: Create ticket
diff --git a/src/pages/Ticket/locale/es.yml b/src/pages/Ticket/locale/es.yml
index 132c61928..a80692bfe 100644
--- a/src/pages/Ticket/locale/es.yml
+++ b/src/pages/Ticket/locale/es.yml
@@ -228,3 +228,14 @@ ticketSaleTracking:
 Search ticket: Buscar tickets
 You can search by ticket id or alias: Puedes buscar por id o alias del ticket
 Select lines to see the options: Selecciona líneas para ver las opciones
+ticketList:
+    id: Id
+    nickname: Alias
+    state: Estado
+    shipped: F. Envío
+    zone: Zona
+    salesPerson: Comercial
+    totalWithVat: Total con IVA
+    summary: Resumen
+    client: Cliente
+    createTicket: Crear ticket

From 1e959307d885829155e6a18117a69262ce431892 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Wed, 17 Jul 2024 13:02:21 +0200
Subject: [PATCH 2/4] refactor: refs #7356 fixed VnTable filters

---
 src/pages/Ticket/TicketWeekly.vue | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/src/pages/Ticket/TicketWeekly.vue b/src/pages/Ticket/TicketWeekly.vue
index 19e9e96cc..65d79bc32 100644
--- a/src/pages/Ticket/TicketWeekly.vue
+++ b/src/pages/Ticket/TicketWeekly.vue
@@ -68,9 +68,12 @@ const columns = computed(() => [
         label: t('weeklyTickets.shipment'),
         cardVisible: true,
         columnFilter: {
-            component: 'input',
-            optionLabel: weekdays.name,
-            optionValue: weekdays.id,
+            component: 'select',
+            attrs: {
+                options: weekdays,
+                optionLabel: weekdays.name,
+                optionValue: weekdays.id,
+            },
             inWhere: true,
         },
     },
@@ -81,6 +84,7 @@ const columns = computed(() => [
         cardVisible: true,
         columnFilter: {
             component: 'select',
+            alias: 'tw',
             attrs: {
                 url: 'AgencyModes',
                 fields: ['id', 'name'],
@@ -112,10 +116,11 @@ const columns = computed(() => [
     },
     {
         align: 'left',
-        name: 'workerFk',
+        name: 'id',
         label: t('weeklyTickets.salesperson'),
         columnFilter: {
             component: 'select',
+            alias: 'u',
             attrs: {
                 url: 'Workers/activeWithInheritedRole',
                 fields: ['id', 'name'],
@@ -230,7 +235,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
                 <CustomerDescriptorProxy :id="row.clientFk" />
             </span>
         </template>
-        <template #column-workerFk="{ row }">
+        <template #column-id="{ row }">
             <span class="link" @click.stop>
                 {{ row.userName }}
                 <WorkerDescriptorProxy :id="row.workerFk" />

From 78e074d2fbe2511337739e58a414d7b9b51c2821 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Thu, 18 Jul 2024 10:53:44 +0200
Subject: [PATCH 3/4] refactor: refs #7356 requested changes

---
 src/components/common/VnSelectCache.vue | 20 +++++++++++++++++---
 src/pages/Ticket/TicketList.vue         |  4 ----
 src/pages/Ticket/TicketWeekly.vue       | 18 +++++++-----------
 3 files changed, 24 insertions(+), 18 deletions(-)

diff --git a/src/components/common/VnSelectCache.vue b/src/components/common/VnSelectCache.vue
index 51873ef6e..b56f11220 100644
--- a/src/components/common/VnSelectCache.vue
+++ b/src/components/common/VnSelectCache.vue
@@ -8,15 +8,29 @@ const $props = defineProps({
         default: null,
     },
     find: {
-        type: String,
+        type: [String, Array],
         default: null,
+        description: 'search in row to add default options',
     },
 });
 const options = ref([]);
 onBeforeMount(async () => {
-    const { url } = useAttrs();
+    const { url, optionValue, optionLabel } = useAttrs();
     const findBy = $props.find ?? url?.charAt(0)?.toLocaleLowerCase() + url?.slice(1, -1);
-    if (findBy) options.value = [$props.row[findBy]];
+    if (!findBy || !$props.row) return;
+    // is array
+    if (Array.isArray(findBy)) {
+        const [id, name] = findBy;
+        if (!$props.row[id] || !$props.row[name]) return;
+        return (options.value = [
+            {
+                [optionValue ?? 'id']: $props.row[id],
+                [optionLabel ?? 'name']: $props.row[name],
+            },
+        ]);
+    }
+    // is string
+    if ($props.row[findBy]) options.value = [$props.row[findBy]];
 });
 </script>
 
diff --git a/src/pages/Ticket/TicketList.vue b/src/pages/Ticket/TicketList.vue
index 572fe3ec7..0c32c5b87 100644
--- a/src/pages/Ticket/TicketList.vue
+++ b/src/pages/Ticket/TicketList.vue
@@ -39,7 +39,6 @@ const columns = computed(() => [
         align: 'left',
         name: 'state',
         label: t('ticketList.state'),
-        cardVisible: true,
         chip: {
             condition: () => true,
             color: (row) => {
@@ -54,9 +53,6 @@ const columns = computed(() => [
                 fields: ['id', 'name'],
             },
         },
-        columnField: {
-            component: null,
-        },
     },
     {
         align: 'left',
diff --git a/src/pages/Ticket/TicketWeekly.vue b/src/pages/Ticket/TicketWeekly.vue
index 65d79bc32..61532ee51 100644
--- a/src/pages/Ticket/TicketWeekly.vue
+++ b/src/pages/Ticket/TicketWeekly.vue
@@ -2,6 +2,7 @@
 import { onMounted, ref, computed, onUnmounted } from 'vue';
 import { useI18n } from 'vue-i18n';
 import VnSelect from 'src/components/common/VnSelect.vue';
+import VnSelectCache from 'src/components/common/VnSelectCache.vue';
 import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
 import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
 import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
@@ -17,7 +18,6 @@ const stateStore = useStateStore();
 const { t } = useI18n();
 const { notify } = useNotify();
 const { openConfirmationModal } = useVnConfirm();
-const agencyModesOptions = ref([]);
 const allColumnNames = ref([]);
 
 const arrayData = useArrayData('WeeklyTickets');
@@ -91,10 +91,6 @@ const columns = computed(() => [
             },
             inWhere: true,
         },
-        columnField: {
-            component: null,
-        },
-        format: (row, dashIfEmpty) => dashIfEmpty(row?.agencyMode?.name),
     },
     {
         align: 'left',
@@ -219,15 +215,15 @@ onUnmounted(() => (stateStore.rightDrawer = false));
             />
         </template>
         <template #column-agencyModeFk="{ row }">
-            <VnSelect
+            <VnSelectCache
                 url="AgencyModes/isActive"
-                :options="agencyModesOptions"
-                hide-selected
-                option-label="name"
-                option-value="id"
+                :row="row"
+                :find="['agencyModeFk', 'agencyModeName']"
                 v-model="row.agencyModeFk"
                 @update:model-value="onUpdate(row.ticketFk, 'agencyModeFk', $event)"
-            />
+            >
+                {{ console.log('row: ', row) }}
+            </VnSelectCache>
         </template>
         <template #column-clientFk="{ row }">
             <span class="link" @click.stop>

From 108375bb69e70836bf119c7e4cc98424c7ba0b68 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Thu, 18 Jul 2024 12:05:10 +0200
Subject: [PATCH 4/4] perf: refs #7356 TicketList state column

---
 src/pages/Ticket/TicketList.vue | 41 ++++++++++++++++++---------------
 1 file changed, 22 insertions(+), 19 deletions(-)

diff --git a/src/pages/Ticket/TicketList.vue b/src/pages/Ticket/TicketList.vue
index 0c32c5b87..c9f38790a 100644
--- a/src/pages/Ticket/TicketList.vue
+++ b/src/pages/Ticket/TicketList.vue
@@ -20,6 +20,19 @@ const agenciesOptions = ref([]);
 const selectedClient = ref();
 
 const columns = computed(() => [
+    {
+        align: 'left',
+        name: 'stateFk',
+        label: t('ticketList.state'),
+        columnFilter: {
+            name: 'stateFk',
+            component: 'select',
+            attrs: {
+                url: 'States',
+                fields: ['id', 'name'],
+            },
+        },
+    },
     {
         align: 'left',
         name: 'id',
@@ -35,25 +48,7 @@ const columns = computed(() => [
         label: t('ticketList.nickname'),
         isTitle: true,
     },
-    {
-        align: 'left',
-        name: 'state',
-        label: t('ticketList.state'),
-        chip: {
-            condition: () => true,
-            color: (row) => {
-                return row?.classColor ? `bg-${row.classColor}` : 'bg-orange';
-            },
-        },
-        columnFilter: {
-            name: 'stateFk',
-            component: 'select',
-            attrs: {
-                url: 'States',
-                fields: ['id', 'name'],
-            },
-        },
-    },
+
     {
         align: 'left',
         name: 'shipped',
@@ -189,6 +184,9 @@ const fetchAddresses = async (formData) => {
         return err.response;
     }
 };
+const getColor = (row) => {
+    return row?.classColor ? `bg-${row.classColor}` : 'bg-orange';
+};
 </script>
 
 <template>
@@ -302,6 +300,11 @@ const fetchAddresses = async (formData) => {
                 </div>
             </VnRow>
         </template>
+        <template #column-stateFk="{ row }">
+            <QChip :class="getColor(row)" dense square>
+                {{ row.state }}
+            </QChip>
+        </template>
     </VnTable>
 </template>