From 907bf3cf3bccb541cd24e17d2ca7ff25a7297d88 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Mon, 2 Dec 2024 11:39:01 +0100
Subject: [PATCH 01/34] feat(Account & AccountRole): refs #8197 add VnCardMain

---
 src/components/common/VnCard.vue              |  34 ++---
 src/components/common/VnCardMain.vue          |  20 +++
 src/components/common/VnSectionMain.vue       |   3 +-
 src/composables/useArrayData.js               |   9 +-
 src/pages/Account/AccountList.vue             |  56 ++++----
 src/pages/Account/Card/AccountCard.vue        |  14 +-
 src/pages/Account/Role/AccountRoles.vue       |  63 +++++----
 src/pages/Account/Role/Card/RoleCard.vue      |  15 +--
 .../Account/Role/Card/RoleDescriptor.vue      |   2 +-
 src/pages/Account/Role/Card/RoleSummary.vue   |   4 +-
 src/router/modules/account.js                 | 120 ++++--------------
 src/router/modules/account/accountCard.js     |  71 +++++++++++
 src/router/modules/account/roleCard.js        |  54 ++++++++
 src/router/modules/index.js                   |   2 -
 src/router/modules/role.js                    |  76 -----------
 src/router/routes.js                          |   2 -
 src/utils/getSections.js                      |   8 ++
 17 files changed, 266 insertions(+), 287 deletions(-)
 create mode 100644 src/components/common/VnCardMain.vue
 create mode 100644 src/router/modules/account/accountCard.js
 create mode 100644 src/router/modules/account/roleCard.js
 delete mode 100644 src/router/modules/role.js
 create mode 100644 src/utils/getSections.js

diff --git a/src/components/common/VnCard.vue b/src/components/common/VnCard.vue
index 0d80f43ce94..88d374c74e3 100644
--- a/src/components/common/VnCard.vue
+++ b/src/components/common/VnCard.vue
@@ -59,32 +59,16 @@ if (props.baseUrl) {
 }
 </script>
 <template>
-    <QDrawer
-        v-model="stateStore.leftDrawer"
-        show-if-above
-        :width="256"
-        v-if="stateStore.isHeaderMounted()"
-    >
-        <QScrollArea class="fit">
-            <component :is="descriptor" />
-            <QSeparator />
-            <LeftMenu source="card" />
-        </QScrollArea>
-    </QDrawer>
     <slot name="searchbar" v-if="props.searchDataKey">
         <VnSearchbar :data-key="props.searchDataKey" v-bind="props.searchbarProps" />
     </slot>
-    <RightMenu>
-        <template #right-panel v-if="props.filterPanel">
-            <component :is="props.filterPanel" :data-key="searchRightDataKey" />
-        </template>
-    </RightMenu>
-    <QPageContainer>
-        <QPage>
-            <VnSubToolbar />
-            <div :class="[useCardSize(), $attrs.class]">
-                <RouterView :key="route.path" />
-            </div>
-        </QPage>
-    </QPageContainer>
+    <Teleport to="#left-panel" v-if="stateStore.isHeaderMounted()">
+        <component :is="descriptor" />
+        <QSeparator />
+        <LeftMenu source="card" />
+    </Teleport>
+    <VnSubToolbar />
+    <div :class="[useCardSize(), $attrs.class]">
+        <RouterView :key="route.path" />
+    </div>
 </template>
diff --git a/src/components/common/VnCardMain.vue b/src/components/common/VnCardMain.vue
new file mode 100644
index 00000000000..6e023153739
--- /dev/null
+++ b/src/components/common/VnCardMain.vue
@@ -0,0 +1,20 @@
+<script setup>
+import LeftMenu from '../LeftMenu.vue';
+import { useStateStore } from 'stores/useStateStore';
+const stateStore = useStateStore();
+
+defineProps({
+    section: {
+        type: String,
+        required: true,
+    },
+});
+</script>
+<template>
+    <slot name="searchbar" />
+    <Teleport to="#left-panel" v-if="stateStore.isHeaderMounted()">
+        <LeftMenu v-if="section == $route.name" />
+    </Teleport>
+    <slot name="body" v-if="section == $route.name" />
+    <RouterView v-else />
+</template>
diff --git a/src/components/common/VnSectionMain.vue b/src/components/common/VnSectionMain.vue
index 15be6ad9a43..c1b9808b5cf 100644
--- a/src/components/common/VnSectionMain.vue
+++ b/src/components/common/VnSectionMain.vue
@@ -1,6 +1,5 @@
 <script setup>
 import { useStateStore } from 'stores/useStateStore';
-import LeftMenu from 'components/LeftMenu.vue';
 import { onMounted } from 'vue';
 import { useQuasar } from 'quasar';
 
@@ -19,7 +18,7 @@ onMounted(
 <template>
     <QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
         <QScrollArea class="fit text-grey-8">
-            <LeftMenu />
+            <div id="left-panel"></div>
         </QScrollArea>
     </QDrawer>
     <QPageContainer>
diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js
index da62eee3eb9..028819a835f 100644
--- a/src/composables/useArrayData.js
+++ b/src/composables/useArrayData.js
@@ -75,18 +75,13 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
             limit: store.limit,
         };
 
-        let userParams = { ...store.userParams };
-
         Object.assign(filter, store.userFilter);
 
-        let where;
-        if (filter?.where || store.filter?.where)
-            where = Object.assign(filter?.where ?? {}, store.filter?.where ?? {});
+        delete store.filter.where;
         Object.assign(filter, store.filter);
-        filter.where = where;
         const params = { filter };
 
-        Object.assign(params, userParams);
+        Object.assign(params, store.userParams);
         if (params.filter) params.filter.skip = store.skip;
         if (store?.order && typeof store?.order == 'string') store.order = [store.order];
         if (store.order?.length) params.filter.order = [...store.order];
diff --git a/src/pages/Account/AccountList.vue b/src/pages/Account/AccountList.vue
index cbaaf8e26af..0c88e6ac88d 100644
--- a/src/pages/Account/AccountList.vue
+++ b/src/pages/Account/AccountList.vue
@@ -5,14 +5,15 @@ import VnTable from 'components/VnTable/VnTable.vue';
 import VnSearchbar from 'components/ui/VnSearchbar.vue';
 import AccountSummary from './Card/AccountSummary.vue';
 import { useSummaryDialog } from 'src/composables/useSummaryDialog';
-import AccountFilter from './AccountFilter.vue';
-import RightMenu from 'src/components/common/RightMenu.vue';
+import VnCardMain from 'src/components/common/VnCardMain.vue';
 const { t } = useI18n();
 const { viewSummary } = useSummaryDialog();
 const tableRef = ref();
 const filter = {
     include: { relation: 'role', scope: { fields: ['id', 'name'] } },
 };
+const dataKey = 'AccountList';
+const url = 'VnUsers/preview';
 const columns = computed(() => [
     {
         align: 'left',
@@ -103,31 +104,34 @@ const exprBuilder = (param, value) => {
 </script>
 
 <template>
-    <VnSearchbar
-        data-key="AccountList"
-        :expr-builder="exprBuilder"
-        :label="t('account.search')"
-        :info="t('account.searchInfo')"
-        :filter="filter"
-    />
-    <RightMenu>
-        <template #right-panel>
-            <AccountFilter data-key="AccountList" />
+    <VnCardMain :section="dataKey">
+        <template #searchbar>
+            <VnSearchbar
+                :data-key="dataKey"
+                :expr-builder="exprBuilder"
+                :label="t('account.search')"
+                :info="t('account.searchInfo')"
+                :filter="filter"
+                :url="url"
+            />
         </template>
-    </RightMenu>
-    <VnTable
-        ref="tableRef"
-        data-key="AccountList"
-        url="VnUsers/preview"
-        :filter="filter"
-        order="id DESC"
-        :columns="columns"
-        default-mode="table"
-        redirect="account"
-        :use-model="true"
-        :right-search="false"
-        auto-load
-    />
+        <template #body>
+            <VnTable
+                :style="{ display: !!$route.name.endsWith('List') ? '' : 'none' }"
+                ref="tableRef"
+                :data-key="dataKey"
+                :url="url"
+                :filter="filter"
+                order="id DESC"
+                :columns="columns"
+                default-mode="table"
+                redirect="account"
+                :use-model="true"
+                :right-search="true"
+                :expr-builder="exprBuilder"
+            />
+        </template>
+    </VnCardMain>
 </template>
 
 <i18n>
diff --git a/src/pages/Account/Card/AccountCard.vue b/src/pages/Account/Card/AccountCard.vue
index 119a7fd07ed..f69bba77842 100644
--- a/src/pages/Account/Card/AccountCard.vue
+++ b/src/pages/Account/Card/AccountCard.vue
@@ -1,20 +1,8 @@
 <script setup>
-import { useI18n } from 'vue-i18n';
 import VnCard from 'components/common/VnCard.vue';
 import AccountDescriptor from './AccountDescriptor.vue';
-
-const { t } = useI18n();
 </script>
 
 <template>
-    <VnCard
-        data-key="Account"
-        :descriptor="AccountDescriptor"
-        search-data-key="AccountList"
-        :searchbar-props="{
-            url: 'VnUsers/preview',
-            label: t('account.search'),
-            info: t('account.searchInfo'),
-        }"
-    />
+    <VnCard data-key="Account" :descriptor="AccountDescriptor" />
 </template>
diff --git a/src/pages/Account/Role/AccountRoles.vue b/src/pages/Account/Role/AccountRoles.vue
index 5a27e2ed607..683de061675 100644
--- a/src/pages/Account/Role/AccountRoles.vue
+++ b/src/pages/Account/Role/AccountRoles.vue
@@ -6,8 +6,11 @@ import { useRoute } from 'vue-router';
 import VnSearchbar from 'components/ui/VnSearchbar.vue';
 import { useSummaryDialog } from 'src/composables/useSummaryDialog';
 import RoleSummary from './Card/RoleSummary.vue';
+import VnCardMain from 'src/components/common/VnCardMain.vue';
+
 const route = useRoute();
 const { t } = useI18n();
+const { viewSummary } = useSummaryDialog();
 const $props = defineProps({
     id: {
         type: Number,
@@ -15,8 +18,10 @@ const $props = defineProps({
     },
 });
 const tableRef = ref();
+const url = 'VnRoles';
+const dataKey = 'AccountRoleList';
+
 const entityId = computed(() => $props.id || route.params.id);
-const { viewSummary } = useSummaryDialog();
 const columns = computed(() => [
     {
         align: 'left',
@@ -63,6 +68,7 @@ const columns = computed(() => [
     },
 ]);
 const exprBuilder = (param, value) => {
+    console.log('param: ', param);
     switch (param) {
         case 'search':
             return /^\d+$/.test(value)
@@ -81,30 +87,37 @@ const exprBuilder = (param, value) => {
 </script>
 
 <template>
-    <VnSearchbar
-        data-key="AccountRolesList"
-        :expr-builder="exprBuilder"
-        :label="t('role.searchRoles')"
-        :info="t('role.searchInfo')"
-    />
-    <VnTable
-        ref="tableRef"
-        data-key="AccountRolesList"
-        :url="`VnRoles`"
-        :create="{
-            urlCreate: 'VnRoles',
-            title: t('Create rol'),
-            onDataSaved: ({ id }) => tableRef.redirect(id),
-            formInitialData: {
-                editorFk: entityId,
-            },
-        }"
-        order="id ASC"
-        :disable-option="{ card: true }"
-        :columns="columns"
-        default-mode="table"
-        redirect="account/role"
-    />
+    <VnCardMain :section="dataKey">
+        <template #searchbar>
+            <VnSearchbar
+                :url="url"
+                :data-key="dataKey"
+                :expr-builder="exprBuilder"
+                :label="t('role.searchRoles')"
+                :info="t('role.searchInfo')"
+            />
+        </template>
+        <template #body>
+            <VnTable
+                ref="tableRef"
+                :data-key="dataKey"
+                :url="url"
+                :create="{
+                    urlCreate: 'VnRoles',
+                    title: t('Create rol'),
+                    onDataSaved: ({ id }) => tableRef.redirect(id),
+                    formInitialData: {
+                        editorFk: entityId,
+                    },
+                }"
+                order="id ASC"
+                :disable-option="{ card: true }"
+                :columns="columns"
+                default-mode="table"
+                redirect="account/role"
+            />
+        </template>
+    </VnCardMain>
 </template>
 
 <i18n>
diff --git a/src/pages/Account/Role/Card/RoleCard.vue b/src/pages/Account/Role/Card/RoleCard.vue
index a2d5710f47a..da6ac61d87a 100644
--- a/src/pages/Account/Role/Card/RoleCard.vue
+++ b/src/pages/Account/Role/Card/RoleCard.vue
@@ -1,20 +1,7 @@
 <script setup>
-import { useI18n } from 'vue-i18n';
 import VnCard from 'components/common/VnCard.vue';
 import RoleDescriptor from './RoleDescriptor.vue';
-
-const { t } = useI18n();
 </script>
 <template>
-    <VnCard
-        data-key="Role"
-        :descriptor="RoleDescriptor"
-        search-data-key="AccountRolesList"
-        :searchbar-props="{
-            url: 'VnRoles',
-            label: t('role.searchRoles'),
-            info: t('role.searchInfo'),
-            searchUrl: 'table',
-        }"
-    />
+    <VnCard data-key="Role" :descriptor="RoleDescriptor" />
 </template>
diff --git a/src/pages/Account/Role/Card/RoleDescriptor.vue b/src/pages/Account/Role/Card/RoleDescriptor.vue
index 693fcdf48a6..b4b4fe3168d 100644
--- a/src/pages/Account/Role/Card/RoleDescriptor.vue
+++ b/src/pages/Account/Role/Card/RoleDescriptor.vue
@@ -43,7 +43,7 @@ const removeRole = async () => {
         :filter="filter"
         module="Role"
         @on-fetch="setData"
-        data-key="accountData"
+        data-key="Role"
         :title="data.title"
         :subtitle="data.subtitle"
         :summary="$props.summary"
diff --git a/src/pages/Account/Role/Card/RoleSummary.vue b/src/pages/Account/Role/Card/RoleSummary.vue
index fef85f9193a..f0daa77fb8f 100644
--- a/src/pages/Account/Role/Card/RoleSummary.vue
+++ b/src/pages/Account/Role/Card/RoleSummary.vue
@@ -27,10 +27,10 @@ const filter = {
 <template>
     <CardSummary
         ref="summary"
-        :url="`VnRoles`"
+        :url="`VnRoles/${entityId}`"
         :filter="filter"
         @on-fetch="(data) => (role = data)"
-        data-key="RoleSummary"
+        data-key="Role"
     >
         <template #header> {{ role.id }} - {{ role.name }} </template>
         <template #body>
diff --git a/src/router/modules/account.js b/src/router/modules/account.js
index 7200131dafa..ece0ab2bb89 100644
--- a/src/router/modules/account.js
+++ b/src/router/modules/account.js
@@ -1,4 +1,7 @@
 import { RouterView } from 'vue-router';
+import accountCard from './account/accountCard';
+import roleCard from './account/roleCard';
+import getSections from 'src/utils/getSections';
 
 export default {
     path: '/account',
@@ -22,39 +25,48 @@ export default {
             'AccountAcls',
             'AccountConnections',
         ],
-        card: [
-            'AccountBasicData',
-            'AccountInheritedRoles',
-            'AccountMailForwarding',
-            'AccountMailAlias',
-            'AccountPrivileges',
-            'AccountLog',
-        ],
+        card: getSections(accountCard.children),
     },
     children: [
         {
             path: '',
             name: 'AccountMain',
             component: () => import('src/components/common/VnSectionMain.vue'),
-            redirect: { name: 'AccountList' },
+            redirect: { name: 'AccountIndexMain' },
             children: [
                 {
-                    path: 'list',
-                    name: 'AccountList',
-                    meta: {
-                        title: 'list',
-                        icon: 'view_list',
-                    },
+                    path: '',
+                    name: 'AccountIndexMain',
+                    redirect: { name: 'AccountList' },
                     component: () => import('src/pages/Account/AccountList.vue'),
+                    children: [
+                        {
+                            name: 'AccountList',
+                            path: 'list',
+                            meta: {
+                                title: 'list',
+                                icon: 'view_list',
+                            },
+                        },
+                        accountCard,
+                    ],
                 },
                 {
-                    path: 'role-list',
+                    path: 'role',
                     name: 'AccountRoles',
+                    redirect: { name: 'AccountRoleList' },
                     meta: {
                         title: 'roles',
                         icon: 'group',
                     },
                     component: () => import('src/pages/Account/Role/AccountRoles.vue'),
+                    children: [
+                        {
+                            name: 'AccountRoleList',
+                            path: 'list',
+                        },
+                        roleCard,
+                    ],
                 },
                 {
                     path: 'alias-list',
@@ -120,81 +132,5 @@ export default {
                 },
             ],
         },
-        {
-            name: 'AccountCard',
-            path: ':id',
-            component: () => import('src/pages/Account/Card/AccountCard.vue'),
-            redirect: { name: 'AccountSummary' },
-            children: [
-                {
-                    name: 'AccountSummary',
-                    path: 'summary',
-                    meta: {
-                        title: 'summary',
-                        icon: 'launch',
-                    },
-                    component: () => import('src/pages/Account/Card/AccountSummary.vue'),
-                },
-                {
-                    name: 'AccountBasicData',
-                    path: 'basic-data',
-                    meta: {
-                        title: 'basicData',
-                        icon: 'vn:settings',
-                    },
-                    component: () =>
-                        import('src/pages/Account/Card/AccountBasicData.vue'),
-                },
-                {
-                    name: 'AccountInheritedRoles',
-                    path: 'inherited-roles',
-                    meta: {
-                        title: 'inheritedRoles',
-                        icon: 'group',
-                    },
-                    component: () =>
-                        import('src/pages/Account/Card/AccountInheritedRoles.vue'),
-                },
-                {
-                    name: 'AccountMailForwarding',
-                    path: 'mail-forwarding',
-                    meta: {
-                        title: 'mailForwarding',
-                        icon: 'forward',
-                    },
-                    component: () =>
-                        import('src/pages/Account/Card/AccountMailForwarding.vue'),
-                },
-                {
-                    name: 'AccountMailAlias',
-                    path: 'mail-alias',
-                    meta: {
-                        title: 'mailAlias',
-                        icon: 'email',
-                    },
-                    component: () =>
-                        import('src/pages/Account/Card/AccountMailAlias.vue'),
-                },
-                {
-                    name: 'AccountPrivileges',
-                    path: 'privileges',
-                    meta: {
-                        title: 'privileges',
-                        icon: 'badge',
-                    },
-                    component: () =>
-                        import('src/pages/Account/Card/AccountPrivileges.vue'),
-                },
-                {
-                    name: 'AccountLog',
-                    path: 'log',
-                    meta: {
-                        title: 'log',
-                        icon: 'history',
-                    },
-                    component: () => import('src/pages/Account/Card/AccountLog.vue'),
-                },
-            ],
-        },
     ],
 };
diff --git a/src/router/modules/account/accountCard.js b/src/router/modules/account/accountCard.js
new file mode 100644
index 00000000000..0d8850f10aa
--- /dev/null
+++ b/src/router/modules/account/accountCard.js
@@ -0,0 +1,71 @@
+export default {
+    name: 'AccountCard',
+    path: ':id',
+    redirect: { name: 'AccountSummary' },
+    component: () => import('src/pages/Account/Card/AccountCard.vue'),
+    children: [
+        {
+            name: 'AccountSummary',
+            path: 'summary',
+            meta: {
+                title: 'summary',
+                icon: 'launch',
+            },
+            component: () => import('src/pages/Account/Card/AccountSummary.vue'),
+        },
+        {
+            name: 'AccountBasicData',
+            path: 'basic-data',
+            meta: {
+                title: 'basicData',
+                icon: 'vn:settings',
+            },
+            component: () => import('src/pages/Account/Card/AccountBasicData.vue'),
+        },
+        {
+            name: 'AccountInheritedRoles',
+            path: 'inherited-roles',
+            meta: {
+                title: 'inheritedRoles',
+                icon: 'group',
+            },
+            component: () => import('src/pages/Account/Card/AccountInheritedRoles.vue'),
+        },
+        {
+            name: 'AccountMailForwarding',
+            path: 'mail-forwarding',
+            meta: {
+                title: 'mailForwarding',
+                icon: 'forward',
+            },
+            component: () => import('src/pages/Account/Card/AccountMailForwarding.vue'),
+        },
+        {
+            name: 'AccountMailAlias',
+            path: 'mail-alias',
+            meta: {
+                title: 'mailAlias',
+                icon: 'email',
+            },
+            component: () => import('src/pages/Account/Card/AccountMailAlias.vue'),
+        },
+        {
+            name: 'AccountPrivileges',
+            path: 'privileges',
+            meta: {
+                title: 'privileges',
+                icon: 'badge',
+            },
+            component: () => import('src/pages/Account/Card/AccountPrivileges.vue'),
+        },
+        {
+            name: 'AccountLog',
+            path: 'log',
+            meta: {
+                title: 'log',
+                icon: 'history',
+            },
+            component: () => import('src/pages/Account/Card/AccountLog.vue'),
+        },
+    ],
+};
diff --git a/src/router/modules/account/roleCard.js b/src/router/modules/account/roleCard.js
new file mode 100644
index 00000000000..2a538756873
--- /dev/null
+++ b/src/router/modules/account/roleCard.js
@@ -0,0 +1,54 @@
+export default {
+    name: 'RoleCard',
+    path: ':id',
+    component: () => import('src/pages/Account/Role/Card/RoleCard.vue'),
+    redirect: { name: 'RoleSummary' },
+    children: [
+        {
+            name: 'RoleSummary',
+            path: 'summary',
+            meta: {
+                title: 'summary',
+                icon: 'launch',
+            },
+            component: () => import('src/pages/Account/Role/Card/RoleSummary.vue'),
+        },
+        {
+            name: 'RoleBasicData',
+            path: 'basic-data',
+            meta: {
+                title: 'basicData',
+                icon: 'vn:settings',
+            },
+            component: () => import('src/pages/Account/Role/Card/RoleBasicData.vue'),
+        },
+        {
+            name: 'SubRoles',
+            path: 'sub-roles',
+            meta: {
+                title: 'subRoles',
+                icon: 'group',
+            },
+            component: () => import('src/pages/Account/Role/Card/SubRoles.vue'),
+        },
+
+        {
+            name: 'InheritedRoles',
+            path: 'inherited-roles',
+            meta: {
+                title: 'inheritedRoles',
+                icon: 'account_tree',
+            },
+            component: () => import('src/pages/Account/Role/Card/InheritedRoles.vue'),
+        },
+        {
+            name: 'RoleLog',
+            path: 'log',
+            meta: {
+                title: 'log',
+                icon: 'history',
+            },
+            component: () => import('src/pages/Account/Role/Card/RoleLog.vue'),
+        },
+    ],
+};
diff --git a/src/router/modules/index.js b/src/router/modules/index.js
index bf7e46b000d..fb1bdc46667 100644
--- a/src/router/modules/index.js
+++ b/src/router/modules/index.js
@@ -21,7 +21,6 @@ import Zone from './zone';
 import Account from './account';
 import Monitor from './monitor';
 import MailAlias from './mailAlias';
-import Role from './role';
 
 export default [
     Item,
@@ -47,5 +46,4 @@ export default [
     Account,
     MailAlias,
     Monitor,
-    Role,
 ];
diff --git a/src/router/modules/role.js b/src/router/modules/role.js
deleted file mode 100644
index 47cd10b1889..00000000000
--- a/src/router/modules/role.js
+++ /dev/null
@@ -1,76 +0,0 @@
-import { RouterView } from 'vue-router';
-
-export default {
-    path: 'account/role',
-    name: 'Role',
-    meta: {
-        title: 'role',
-        icon: 'vn:greuge',
-        moduleName: 'Role',
-    },
-    component: RouterView,
-    redirect: { name: 'AccountRoles' },
-    menus: {
-        main: [],
-        card: ['RoleBasicData', 'SubRoles', 'InheritedRoles', 'RoleLog'],
-    },
-    children: [
-        {
-            name: 'RoleCard',
-            path: ':id',
-            component: () => import('src/pages/Account/Role/Card/RoleCard.vue'),
-            redirect: { name: 'RoleSummary' },
-            children: [
-                {
-                    name: 'RoleSummary',
-                    path: 'summary',
-                    meta: {
-                        title: 'summary',
-                        icon: 'launch',
-                    },
-                    component: () =>
-                        import('src/pages/Account/Role/Card/RoleSummary.vue'),
-                },
-                {
-                    name: 'RoleBasicData',
-                    path: 'basic-data',
-                    meta: {
-                        title: 'basicData',
-                        icon: 'vn:settings',
-                    },
-                    component: () =>
-                        import('src/pages/Account/Role/Card/RoleBasicData.vue'),
-                },
-                {
-                    name: 'SubRoles',
-                    path: 'sub-roles',
-                    meta: {
-                        title: 'subRoles',
-                        icon: 'group',
-                    },
-                    component: () => import('src/pages/Account/Role/Card/SubRoles.vue'),
-                },
-
-                {
-                    name: 'InheritedRoles',
-                    path: 'inherited-roles',
-                    meta: {
-                        title: 'inheritedRoles',
-                        icon: 'account_tree',
-                    },
-                    component: () =>
-                        import('src/pages/Account/Role/Card/InheritedRoles.vue'),
-                },
-                {
-                    name: 'RoleLog',
-                    path: 'log',
-                    meta: {
-                        title: 'log',
-                        icon: 'history',
-                    },
-                    component: () => import('src/pages/Account/Role/Card/RoleLog.vue'),
-                },
-            ],
-        },
-    ],
-};
diff --git a/src/router/routes.js b/src/router/routes.js
index cced308b5a3..d332be94194 100644
--- a/src/router/routes.js
+++ b/src/router/routes.js
@@ -10,7 +10,6 @@ import wagon from './modules/wagon';
 import supplier from './modules/Supplier';
 import travel from './modules/travel';
 import department from './modules/department';
-import role from './modules/role';
 import ItemType from './modules/itemType';
 import shelving from 'src/router/modules/shelving';
 import order from 'src/router/modules/order';
@@ -95,7 +94,6 @@ const routes = [
             ItemType,
             zone,
             account,
-            role,
             mailAlias,
             {
                 path: '/:catchAll(.*)*',
diff --git a/src/utils/getSections.js b/src/utils/getSections.js
new file mode 100644
index 00000000000..f70daf4685c
--- /dev/null
+++ b/src/utils/getSections.js
@@ -0,0 +1,8 @@
+export default (sections) => {
+    const names = [];
+    for (const section of sections) {
+        if (section.path == 'summary') continue;
+        names.push(section.name);
+    }
+    return names;
+};

From 68fc5653241e7cb67cb580599dcc67bc9fd28394 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Wed, 4 Dec 2024 09:44:09 +0100
Subject: [PATCH 02/34] feat(VnPaginate): refs #8197  hold data when change to
 Card

---
 src/components/common/VnSelect.vue           |  2 +-
 src/components/ui/VnFilterPanel.vue          |  2 +-
 src/components/ui/VnPaginate.vue             |  6 ++++--
 src/components/ui/VnSearchbar.vue            |  2 +-
 src/composables/useArrayData.js              | 10 +++++++---
 src/pages/Account/AccountList.vue            |  2 +-
 src/pages/Account/Card/AccountCard.vue       |  2 +-
 src/pages/Account/Card/AccountDescriptor.vue |  2 +-
 src/stores/useArrayDataStore.js              |  6 ++++++
 9 files changed, 23 insertions(+), 11 deletions(-)

diff --git a/src/components/common/VnSelect.vue b/src/components/common/VnSelect.vue
index f24f054a5fb..db47231f48a 100644
--- a/src/components/common/VnSelect.vue
+++ b/src/components/common/VnSelect.vue
@@ -201,7 +201,7 @@ async function fetchFilter(val) {
     const fetchOptions = { where, include, limit };
     if (fields) fetchOptions.fields = fields;
     if (sortBy) fetchOptions.order = sortBy;
-    arrayData.reset(['skip', 'filter.skip', 'page']);
+    arrayData.resetPagination();
 
     const { data } = await arrayData.applyFilter({ filter: fetchOptions });
     setOptions(data);
diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue
index b188bde4820..716d8331fcf 100644
--- a/src/components/ui/VnFilterPanel.vue
+++ b/src/components/ui/VnFilterPanel.vue
@@ -138,7 +138,7 @@ async function clearFilters() {
     try {
         isLoading.value = true;
         store.userParamsChanged = true;
-        arrayData.reset(['skip', 'filter.skip', 'page']);
+        arrayData.resetPagination();
         // Filtrar los params no removibles
         const removableFilters = Object.keys(userParams.value).filter((param) =>
             $props.unremovableParams.includes(param)
diff --git a/src/components/ui/VnPaginate.vue b/src/components/ui/VnPaginate.vue
index 3649ba8f551..c5fbbb7314b 100644
--- a/src/components/ui/VnPaginate.vue
+++ b/src/components/ui/VnPaginate.vue
@@ -104,7 +104,9 @@ onMounted(async () => {
     mounted.value = true;
 });
 
-onBeforeUnmount(() => arrayData.reset());
+onBeforeUnmount(() => {
+    arrayData.resetPagination();
+});
 
 watch(
     () => props.data,
@@ -132,7 +134,7 @@ const addFilter = async (filter, params) => {
 
 async function fetch(params) {
     useArrayData(props.dataKey, params);
-    arrayData.reset(['filter.skip', 'skip', 'page']);
+    arrayData.resetPagination();
     await arrayData.fetch({ append: false, updateRouter: mounted.value });
     return emitStoreData();
 }
diff --git a/src/components/ui/VnSearchbar.vue b/src/components/ui/VnSearchbar.vue
index da2d370fe09..a5690f35a9c 100644
--- a/src/components/ui/VnSearchbar.vue
+++ b/src/components/ui/VnSearchbar.vue
@@ -101,7 +101,7 @@ onMounted(() => {
 
 async function search() {
     const staticParams = Object.entries(store.userParams);
-    arrayData.reset(['skip', 'page']);
+    arrayData.resetPagination();
 
     const filter = {
         params: {
diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js
index 028819a835f..ee66f6be7d8 100644
--- a/src/composables/useArrayData.js
+++ b/src/composables/useArrayData.js
@@ -142,6 +142,10 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
         if (arrayDataStore.get(key)) arrayDataStore.reset(key, opts);
     }
 
+    function resetPagination() {
+        if (arrayDataStore.get(key)) arrayDataStore.resetPagination(key);
+    }
+
     function cancelRequest() {
         if (canceller) {
             canceller.abort();
@@ -165,7 +169,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
         userParams = sanitizerParams(userParams, store?.exprBuilder);
 
         store.userParams = userParams;
-        reset(['skip', 'filter.skip', 'page']);
+        resetPagination();
 
         await fetch({});
         return { filter, params };
@@ -192,7 +196,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
         }
 
         store.order = order;
-        reset(['skip', 'filter.skip', 'page']);
+        resetPagination();
         fetch({});
         index++;
 
@@ -275,7 +279,6 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
                 const pushUrl = { path: to };
                 if (to.endsWith('/list') || to.endsWith('/'))
                     pushUrl.query = newUrl.query;
-                else destroy();
                 return router.push(pushUrl);
             }
         }
@@ -302,5 +305,6 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
         isLoading,
         deleteOption,
         reset,
+        resetPagination,
     };
 }
diff --git a/src/pages/Account/AccountList.vue b/src/pages/Account/AccountList.vue
index 0c88e6ac88d..4b8e8fb2836 100644
--- a/src/pages/Account/AccountList.vue
+++ b/src/pages/Account/AccountList.vue
@@ -6,6 +6,7 @@ import VnSearchbar from 'components/ui/VnSearchbar.vue';
 import AccountSummary from './Card/AccountSummary.vue';
 import { useSummaryDialog } from 'src/composables/useSummaryDialog';
 import VnCardMain from 'src/components/common/VnCardMain.vue';
+
 const { t } = useI18n();
 const { viewSummary } = useSummaryDialog();
 const tableRef = ref();
@@ -117,7 +118,6 @@ const exprBuilder = (param, value) => {
         </template>
         <template #body>
             <VnTable
-                :style="{ display: !!$route.name.endsWith('List') ? '' : 'none' }"
                 ref="tableRef"
                 :data-key="dataKey"
                 :url="url"
diff --git a/src/pages/Account/Card/AccountCard.vue b/src/pages/Account/Card/AccountCard.vue
index f69bba77842..ba9040852cf 100644
--- a/src/pages/Account/Card/AccountCard.vue
+++ b/src/pages/Account/Card/AccountCard.vue
@@ -4,5 +4,5 @@ import AccountDescriptor from './AccountDescriptor.vue';
 </script>
 
 <template>
-    <VnCard data-key="Account" :descriptor="AccountDescriptor" />
+    <VnCard data-key="AccountId" :descriptor="AccountDescriptor" />
 </template>
diff --git a/src/pages/Account/Card/AccountDescriptor.vue b/src/pages/Account/Card/AccountDescriptor.vue
index 3156f8e1ec3..4e10e1366d6 100644
--- a/src/pages/Account/Card/AccountDescriptor.vue
+++ b/src/pages/Account/Card/AccountDescriptor.vue
@@ -41,7 +41,7 @@ const hasAccount = ref(false);
     />
     <CardDescriptor
         ref="descriptor"
-        :url="`VnUsers/preview`"
+        url="VnUsers/preview"
         :filter="filter"
         module="Account"
         @on-fetch="setData"
diff --git a/src/stores/useArrayDataStore.js b/src/stores/useArrayDataStore.js
index 6a0e7dfa8d1..be65de19a65 100644
--- a/src/stores/useArrayDataStore.js
+++ b/src/stores/useArrayDataStore.js
@@ -49,10 +49,16 @@ export const useArrayDataStore = defineStore('arrayDataStore', () => {
         });
     }
 
+    function resetPagination(key) {
+        reset(key, ['skip', 'filter.skip', 'page']);
+    }
+
     return {
+        state,
         get,
         set,
         clear,
         reset,
+        resetPagination,
     };
 });

From 1b2af7cb84e0b033ba3425a552f627e92feaaf09 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Wed, 4 Dec 2024 09:44:21 +0100
Subject: [PATCH 03/34] chore: refs #8197 remove console log

---
 src/pages/Account/Role/AccountRoles.vue | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/pages/Account/Role/AccountRoles.vue b/src/pages/Account/Role/AccountRoles.vue
index 683de061675..74c4ab8a558 100644
--- a/src/pages/Account/Role/AccountRoles.vue
+++ b/src/pages/Account/Role/AccountRoles.vue
@@ -68,7 +68,6 @@ const columns = computed(() => [
     },
 ]);
 const exprBuilder = (param, value) => {
-    console.log('param: ', param);
     switch (param) {
         case 'search':
             return /^\d+$/.test(value)

From 2d2501838b63303ae4bbbbfe5d2688f17b8c15cc Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Wed, 4 Dec 2024 09:49:48 +0100
Subject: [PATCH 04/34] revert: refs #8197 arrayData changes

---
 src/composables/useArrayData.js           | 9 +++++++--
 src/pages/Account/Card/AccountSummary.vue | 2 +-
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js
index ee66f6be7d8..c36eb99900c 100644
--- a/src/composables/useArrayData.js
+++ b/src/composables/useArrayData.js
@@ -75,13 +75,18 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
             limit: store.limit,
         };
 
+        let userParams = { ...store.userParams };
+
         Object.assign(filter, store.userFilter);
 
-        delete store.filter.where;
+        let where;
+        if (filter?.where || store.filter?.where)
+            where = Object.assign(filter?.where ?? {}, store.filter?.where ?? {});
         Object.assign(filter, store.filter);
+        filter.where = where;
         const params = { filter };
 
-        Object.assign(params, store.userParams);
+        Object.assign(params, userParams);
         if (params.filter) params.filter.skip = store.skip;
         if (store?.order && typeof store?.order == 'string') store.order = [store.order];
         if (store.order?.length) params.filter.order = [...store.order];
diff --git a/src/pages/Account/Card/AccountSummary.vue b/src/pages/Account/Card/AccountSummary.vue
index 5a21e18a5c9..e6c21ed34af 100644
--- a/src/pages/Account/Card/AccountSummary.vue
+++ b/src/pages/Account/Card/AccountSummary.vue
@@ -30,7 +30,7 @@ const filter = {
 
 <template>
     <CardSummary
-        data-key="AccountSummary"
+        data-key="AccountId"
         ref="AccountSummary"
         url="VnUsers/preview"
         :filter="filter"

From 1d86b2912944b9c1abc5d7b9e040a93966c1077d Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Mon, 9 Dec 2024 11:21:18 +0100
Subject: [PATCH 05/34] feat: refs #8197 vnTableFilter

---
 src/components/VnTable/VnFilter.vue      |   5 +-
 src/components/VnTable/VnTable.vue       | 138 +++--------------------
 src/components/VnTable/VnTableFilter.vue |  85 ++++++++++++++
 src/components/common/VnCard.vue         |  11 +-
 src/components/common/VnCardMain.vue     |   2 +
 src/components/ui/VnFilterPanel.vue      |  93 +++++----------
 src/components/ui/VnPaginate.vue         |  10 +-
 src/composables/useArrayData.js          |   8 +-
 src/composables/useFilterParams.js       |  65 +++++++++++
 src/pages/Account/AccountList.vue        |  30 +++--
 src/utils/getUserParams.js               |   0
 11 files changed, 233 insertions(+), 214 deletions(-)
 create mode 100644 src/components/VnTable/VnTableFilter.vue
 create mode 100644 src/composables/useFilterParams.js
 create mode 100644 src/utils/getUserParams.js

diff --git a/src/components/VnTable/VnFilter.vue b/src/components/VnTable/VnFilter.vue
index 86802ee92ae..d859d12aa28 100644
--- a/src/components/VnTable/VnFilter.vue
+++ b/src/components/VnTable/VnFilter.vue
@@ -32,7 +32,10 @@ const $props = defineProps({
 defineExpose({ addFilter, props: $props });
 
 const model = defineModel(undefined, { required: true });
-const arrayData = useArrayData($props.dataKey, { searchUrl: $props.searchUrl });
+const arrayData = useArrayData(
+    $props.dataKey,
+    $props.searchUrl ? { searchUrl: $props.searchUrl } : null
+);
 const columnFilter = computed(() => $props.column?.columnFilter);
 
 const updateEvent = { 'update:modelValue': addFilter };
diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index 94147708408..324c49cde98 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -1,20 +1,21 @@
 <script setup>
-import { ref, onBeforeMount, onMounted, computed, watch } from 'vue';
+import { ref, onBeforeMount, onMounted, computed, watch, useAttrs } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useRoute, useRouter } from 'vue-router';
 import { useQuasar } from 'quasar';
 import { useStateStore } from 'stores/useStateStore';
+import { useFilterParams } from 'src/composables/useFilterParams';
 
 import CrudModel from 'src/components/CrudModel.vue';
 import FormModelPopup from 'components/FormModelPopup.vue';
 
-import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
 import VnTableColumn from 'components/VnTable/VnColumn.vue';
 import VnFilter from 'components/VnTable/VnFilter.vue';
 import VnTableChip from 'components/VnTable/VnChip.vue';
 import VnVisibleColumn from 'src/components/VnTable/VnVisibleColumn.vue';
 import VnLv from 'components/ui/VnLv.vue';
 import VnTableOrder from 'src/components/VnTable/VnOrder.vue';
+import VnTableFilter from './VnTableFilter.vue';
 
 const $props = defineProps({
     columns: {
@@ -33,6 +34,10 @@ const $props = defineProps({
         type: Boolean,
         default: true,
     },
+    rightSearchIcon: {
+        type: Boolean,
+        default: true,
+    },
     rowClick: {
         type: [Function, Boolean],
         default: null,
@@ -101,10 +106,6 @@ const $props = defineProps({
         type: String,
         default: '90vh',
     },
-    chipLocale: {
-        type: String,
-        default: null,
-    },
     footer: {
         type: Boolean,
         default: false,
@@ -119,22 +120,21 @@ const stateStore = useStateStore();
 const route = useRoute();
 const router = useRouter();
 const quasar = useQuasar();
+const $attrs = useAttrs();
 
 const CARD_MODE = 'card';
 const TABLE_MODE = 'table';
 const mode = ref(CARD_MODE);
 const selected = ref([]);
 const hasParams = ref(false);
-const routeQuery = JSON.parse(route?.query[$props.searchUrl] ?? '{}');
-const params = ref({ ...routeQuery, ...routeQuery.filter?.where });
-const orders = ref(parseOrder(routeQuery.filter?.order));
 const CrudModelRef = ref({});
 const showForm = ref(false);
 const splittedColumns = ref({ columns: [] });
 const columnsVisibilitySkipped = ref();
 const createForm = ref();
-const tableFilterRef = ref([]);
 const tableRef = ref();
+const params = ref(useFilterParams($attrs['data-key']).params);
+const orders = ref(useFilterParams($attrs['data-key']).orders);
 
 const tableModes = [
     {
@@ -163,7 +163,7 @@ onMounted(() => {
     stateStore.rightDrawer = quasar.screen.gt.xs;
     columnsVisibilitySkipped.value = [
         ...splittedColumns.value.columns
-            .filter((c) => c.visible == false)
+            .filter((c) => c.visible === false)
             .map((c) => c.name),
         ...['tableActions'],
     ];
@@ -183,41 +183,8 @@ watch(
     { immediate: true }
 );
 
-watch(
-    () => route.query[$props.searchUrl],
-    (val) => setUserParams(val),
-    { immediate: true, deep: true }
-);
-
 const isTableMode = computed(() => mode.value == TABLE_MODE);
-
-function setUserParams(watchedParams, watchedOrder) {
-    if (!watchedParams) return;
-
-    if (typeof watchedParams == 'string') watchedParams = JSON.parse(watchedParams);
-    const filter =
-        typeof watchedParams?.filter == 'string'
-            ? JSON.parse(watchedParams?.filter ?? '{}')
-            : watchedParams?.filter;
-    const where = filter?.where;
-    const order = watchedOrder ?? filter?.order;
-
-    watchedParams = { ...watchedParams, ...where };
-    delete watchedParams.filter;
-    delete params.value?.filter;
-    params.value = { ...params.value, ...sanitizer(watchedParams) };
-    orders.value = parseOrder(order);
-}
-
-function sanitizer(params) {
-    for (const [key, value] of Object.entries(params)) {
-        if (value && typeof value == 'object') {
-            const param = Object.values(value)[0];
-            if (typeof param == 'string') params[key] = param.replaceAll('%', '');
-        }
-    }
-    return params;
-}
+const showRightIcon = computed(() => $props.rightSearch || $props.rightSearchIcon);
 
 function splitColumns(columns) {
     splittedColumns.value = {
@@ -298,17 +265,6 @@ function getColAlign(col) {
     return 'text-' + (col.align ?? 'left');
 }
 
-function parseOrder(urlOrders) {
-    const orderObject = {};
-    if (!urlOrders) return orderObject;
-    if (typeof urlOrders == 'string') urlOrders = [urlOrders];
-    for (const [index, orders] of urlOrders.entries()) {
-        const [name, direction] = orders.split(' ');
-        orderObject[name] = { direction, index: index + 1 };
-    }
-    return orderObject;
-}
-
 const emit = defineEmits(['onFetch', 'update:selected', 'saveChanges']);
 defineExpose({
     create: createForm,
@@ -349,71 +305,11 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
 }
 </script>
 <template>
-    <QDrawer
+    <VnTableFilter
         v-if="$props.rightSearch"
-        v-model="stateStore.rightDrawer"
-        side="right"
-        :width="256"
-        show-if-above
-    >
-        <QScrollArea class="fit">
-            <VnFilterPanel
-                :data-key="$attrs['data-key']"
-                :search-button="true"
-                v-model="params"
-                :search-url="searchUrl"
-                :redirect="!!redirect"
-                @set-user-params="setUserParams"
-                :disable-submit-event="true"
-                @remove="
-                    (key) =>
-                        tableFilterRef
-                            .find((f) => f.props?.column.name == key)
-                            ?.addFilter()
-                "
-            >
-                <template #body>
-                    <div
-                        class="row no-wrap flex-center"
-                        v-for="col of splittedColumns.columns.filter(
-                            (c) => c.columnFilter ?? true
-                        )"
-                        :key="col.id"
-                    >
-                        <VnFilter
-                            ref="tableFilterRef"
-                            :column="col"
-                            :data-key="$attrs['data-key']"
-                            v-model="params[columnName(col)]"
-                            :search-url="searchUrl"
-                        />
-                        <VnTableOrder
-                            v-if="
-                                col?.columnFilter !== false &&
-                                col?.name !== 'tableActions'
-                            "
-                            v-model="orders[col.orderBy ?? col.name]"
-                            :name="col.orderBy ?? col.name"
-                            :data-key="$attrs['data-key']"
-                            :search-url="searchUrl"
-                            :vertical="true"
-                        />
-                    </div>
-                    <slot
-                        name="moreFilterPanel"
-                        :params="params"
-                        :columns="splittedColumns.columns"
-                    />
-                </template>
-                <template #tags="{ tag, formatFn }" v-if="chipLocale">
-                    <div class="q-gutter-x-xs">
-                        <strong>{{ t(`${chipLocale}.${tag.label}`) }}: </strong>
-                        <span>{{ formatFn(tag.value) }}</span>
-                    </div>
-                </template>
-            </VnFilterPanel>
-        </QScrollArea>
-    </QDrawer>
+        :data-key="$attrs['data-key']"
+        :columns="columns"
+    />
     <CrudModel
         v-bind="$attrs"
         :class="$attrs['class'] ?? 'q-px-md'"
@@ -467,7 +363,7 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
                         :options="tableModes.filter((mode) => !mode.disable)"
                     />
                     <QBtn
-                        v-if="$props.rightSearch"
+                        v-if="showRightIcon"
                         icon="filter_alt"
                         class="bg-vn-section-color q-ml-sm"
                         dense
diff --git a/src/components/VnTable/VnTableFilter.vue b/src/components/VnTable/VnTableFilter.vue
new file mode 100644
index 00000000000..2d1758786e7
--- /dev/null
+++ b/src/components/VnTable/VnTableFilter.vue
@@ -0,0 +1,85 @@
+<script setup>
+import { ref } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { useStateStore } from 'stores/useStateStore';
+
+import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
+import VnFilter from 'components/VnTable/VnFilter.vue';
+import VnTableOrder from 'src/components/VnTable/VnOrder.vue';
+
+defineProps({
+    columns: {
+        type: Array,
+        required: true,
+    },
+    chipLocale: {
+        type: String,
+        default: null,
+    },
+    searchUrl: {
+        type: [String, Boolean],
+        default: 'table',
+    },
+});
+const { t } = useI18n();
+const stateStore = useStateStore();
+
+const tableFilterRef = ref([]);
+
+function columnName(col) {
+    const column = { ...col, ...col.columnFilter };
+    let name = column.name;
+    if (column.alias) name = column.alias + '.' + name;
+    return name;
+}
+</script>
+<template>
+    <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
+        <QScrollArea class="fit">
+            <VnFilterPanel
+                v-bind="$attrs"
+                :search-button="true"
+                :disable-submit-event="true"
+            >
+                <template #body="{ params, orders }">
+                    <div
+                        class="row no-wrap flex-center"
+                        v-for="col of columns.filter((c) => c.columnFilter ?? true)"
+                        :key="col.id"
+                    >
+                        <VnFilter
+                            ref="tableFilterRef"
+                            :column="col"
+                            :data-key="$attrs['data-key']"
+                            v-model="params[columnName(col)]"
+                            :search-url="searchUrl"
+                        />
+                        <VnTableOrder
+                            v-if="
+                                col?.columnFilter !== false &&
+                                col?.name !== 'tableActions'
+                            "
+                            v-model="orders[col.orderBy ?? col.name]"
+                            :name="col.orderBy ?? col.name"
+                            :data-key="$attrs['data-key']"
+                            :search-url="searchUrl"
+                            :vertical="true"
+                        />
+                    </div>
+                    <slot
+                        name="moreFilterPanel"
+                        :params="params"
+                        :orders="orders"
+                        :columns="columns"
+                    />
+                </template>
+                <template #tags="{ tag, formatFn }" v-if="chipLocale">
+                    <div class="q-gutter-x-xs">
+                        <strong>{{ t(`${chipLocale}.${tag.label}`) }}: </strong>
+                        <span>{{ formatFn(tag.value) }}</span>
+                    </div>
+                </template>
+            </VnFilterPanel>
+        </QScrollArea>
+    </QDrawer>
+</template>
diff --git a/src/components/common/VnCard.vue b/src/components/common/VnCard.vue
index 88d374c74e3..16a077a79f1 100644
--- a/src/components/common/VnCard.vue
+++ b/src/components/common/VnCard.vue
@@ -4,10 +4,7 @@ import { useRoute, useRouter, onBeforeRouteUpdate } from 'vue-router';
 import { useArrayData } from 'src/composables/useArrayData';
 import { useStateStore } from 'stores/useStateStore';
 import useCardSize from 'src/composables/useCardSize';
-import VnSubToolbar from '../ui/VnSubToolbar.vue';
-import VnSearchbar from 'components/ui/VnSearchbar.vue';
 import LeftMenu from 'components/LeftMenu.vue';
-import RightMenu from 'components/common/RightMenu.vue';
 const props = defineProps({
     dataKey: { type: String, required: true },
     baseUrl: { type: String, default: undefined },
@@ -29,10 +26,7 @@ const url = computed(() => {
     }
     return props.customUrl;
 });
-const searchRightDataKey = computed(() => {
-    if (!props.searchDataKey) return route.name;
-    return props.searchDataKey;
-});
+
 const arrayData = useArrayData(props.dataKey, {
     url: url.value,
     filter: props.filter,
@@ -59,9 +53,6 @@ if (props.baseUrl) {
 }
 </script>
 <template>
-    <slot name="searchbar" v-if="props.searchDataKey">
-        <VnSearchbar :data-key="props.searchDataKey" v-bind="props.searchbarProps" />
-    </slot>
     <Teleport to="#left-panel" v-if="stateStore.isHeaderMounted()">
         <component :is="descriptor" />
         <QSeparator />
diff --git a/src/components/common/VnCardMain.vue b/src/components/common/VnCardMain.vue
index 6e023153739..3ebfcfb8bdc 100644
--- a/src/components/common/VnCardMain.vue
+++ b/src/components/common/VnCardMain.vue
@@ -12,9 +12,11 @@ defineProps({
 </script>
 <template>
     <slot name="searchbar" />
+    {{ stateStore.isHeaderMounted() }}
     <Teleport to="#left-panel" v-if="stateStore.isHeaderMounted()">
         <LeftMenu v-if="section == $route.name" />
     </Teleport>
     <slot name="body" v-if="section == $route.name" />
     <RouterView v-else />
+    <slot name="rightPanel" />
 </template>
diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue
index 7319dc866cd..b59df89904d 100644
--- a/src/components/ui/VnFilterPanel.vue
+++ b/src/components/ui/VnFilterPanel.vue
@@ -1,10 +1,10 @@
 <script setup>
-import { onMounted, ref, computed, watch } from 'vue';
+import { ref, computed } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useArrayData } from 'composables/useArrayData';
-import { useRoute } from 'vue-router';
 import toDate from 'filters/toDate';
 import VnFilterPanelChip from 'components/ui/VnFilterPanelChip.vue';
+import { useFilterParams } from 'src/composables/useFilterParams';
 
 const { t } = useI18n();
 const $props = defineProps({
@@ -55,6 +55,10 @@ const $props = defineProps({
         type: Boolean,
         default: true,
     },
+    arrayData: {
+        type: Object,
+        default: null,
+    },
 });
 
 const emit = defineEmits([
@@ -67,52 +71,19 @@ const emit = defineEmits([
     'setUserParams',
 ]);
 
-const arrayData = useArrayData($props.dataKey, {
-    exprBuilder: $props.exprBuilder,
-    searchUrl: $props.searchUrl,
-    navigate: $props.redirect ? {} : null,
-});
-const route = useRoute();
+const arrayData =
+    $props.arrayData ??
+    useArrayData($props.dataKey, {
+        exprBuilder: $props.exprBuilder,
+        searchUrl: $props.searchUrl,
+        navigate: $props.redirect ? {} : null,
+    });
+
 const store = arrayData.store;
-const userParams = ref({});
+const userParams = ref(useFilterParams($props.dataKey).params);
+const userOrders = ref(useFilterParams($props.dataKey).orders);
 
-defineExpose({ search, sanitizer, params: userParams });
-
-onMounted(() => {
-    if (!userParams.value) userParams.value = $props.modelValue ?? {};
-    emit('init', { params: userParams.value });
-});
-
-function setUserParams(watchedParams) {
-    if (!watchedParams || Object.keys(watchedParams).length == 0) return;
-
-    if (typeof watchedParams == 'string') watchedParams = JSON.parse(watchedParams);
-    if (typeof watchedParams?.filter == 'string')
-        watchedParams.filter = JSON.parse(watchedParams.filter);
-
-    watchedParams = { ...watchedParams, ...watchedParams.filter?.where };
-    const order = watchedParams.filter?.order;
-
-    delete watchedParams.filter;
-    userParams.value = sanitizer(watchedParams);
-    emit('setUserParams', userParams.value, order);
-}
-
-watch(
-    () => route.query[$props.searchUrl],
-    (val, oldValue) => (val || oldValue) && setUserParams(val)
-);
-
-watch(
-    () => arrayData.store.userParams,
-    (val, oldValue) => (val || oldValue) && setUserParams(val),
-    { immediate: true }
-);
-
-watch(
-    () => $props.modelValue,
-    (val) => (userParams.value = val ?? {})
-);
+defineExpose({ search, params: userParams, remove });
 
 const isLoading = ref(false);
 async function search(evt) {
@@ -123,10 +94,9 @@ async function search(evt) {
         isLoading.value = true;
         const filter = { ...userParams.value, ...$props.modelValue };
         store.userParamsChanged = true;
-        const { params: newParams } = await arrayData.addFilter({
+        await arrayData.addFilter({
             params: filter,
         });
-        userParams.value = newParams;
 
         if (!$props.showAll && !Object.values(filter).length) store.data = [];
         emit('search');
@@ -149,9 +119,8 @@ async function clearFilters() {
         for (const key of removableFilters) {
             newParams[key] = userParams.value[key];
         }
-        userParams.value = {};
-        userParams.value = { ...newParams }; // Actualizar los params con los removibles
-        await arrayData.applyFilter({ params: userParams.value });
+
+        await arrayData.applyFilter({ params: { ...newParams } });
 
         if (!$props.showAll) {
             store.data = [];
@@ -213,21 +182,6 @@ function formatValue(value) {
 
     return `"${value}"`;
 }
-
-function sanitizer(params) {
-    for (const [key, value] of Object.entries(params)) {
-        if (key === 'and' && Array.isArray(value)) {
-            value.forEach((item) => {
-                Object.assign(params, item);
-            });
-            delete params[key];
-        } else if (value && typeof value === 'object') {
-            const param = Object.values(value)[0];
-            if (typeof param == 'string') params[key] = param.replaceAll('%', '');
-        }
-    }
-    return params;
-}
 </script>
 
 <template>
@@ -296,7 +250,12 @@ function sanitizer(params) {
             <QSeparator />
         </QList>
         <QList dense class="list q-gutter-y-sm q-mt-sm">
-            <slot name="body" :params="sanitizer(userParams)" :search-fn="search"></slot>
+            <slot
+                name="body"
+                :params="userParams"
+                :orders="userOrders"
+                :search-fn="search"
+            ></slot>
         </QList>
     </QForm>
     <QInnerLoading
diff --git a/src/components/ui/VnPaginate.vue b/src/components/ui/VnPaginate.vue
index c5fbbb7314b..13361bd0639 100644
--- a/src/components/ui/VnPaginate.vue
+++ b/src/components/ui/VnPaginate.vue
@@ -106,6 +106,7 @@ onMounted(async () => {
 
 onBeforeUnmount(() => {
     arrayData.resetPagination();
+    arrayData.reset(['currentFilter', 'userParams', 'userFilter']);
 });
 
 watch(
@@ -197,7 +198,14 @@ async function onLoad(index, done) {
     done(isDone);
 }
 
-defineExpose({ fetch, update, addFilter, paginate });
+defineExpose({
+    fetch,
+    update,
+    addFilter,
+    paginate,
+    userParams: arrayData.store.userParams,
+    currentFilter: arrayData.store.currentFilter,
+});
 </script>
 
 <template>
diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js
index c36eb99900c..c0c744852ee 100644
--- a/src/composables/useArrayData.js
+++ b/src/composables/useArrayData.js
@@ -25,11 +25,14 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
         const searchUrl = store.searchUrl;
         if (query[searchUrl]) {
             const params = JSON.parse(query[searchUrl]);
-            const filter = params?.filter && JSON.parse(params?.filter ?? '{}');
+            const filter =
+                params?.filter && typeof params?.filter == 'object'
+                    ? params?.filter
+                    : JSON.parse(params?.filter ?? '{}');
             delete params.filter;
 
             store.userParams = { ...store.userParams, ...params };
-            store.userFilter = { ...filter, ...store.userFilter };
+            store.filter = { ...filter, ...store.userFilter };
             if (filter?.order) store.order = filter.order;
         }
     });
@@ -74,7 +77,6 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
         const filter = {
             limit: store.limit,
         };
-
         let userParams = { ...store.userParams };
 
         Object.assign(filter, store.userFilter);
diff --git a/src/composables/useFilterParams.js b/src/composables/useFilterParams.js
new file mode 100644
index 00000000000..2878e4b76ab
--- /dev/null
+++ b/src/composables/useFilterParams.js
@@ -0,0 +1,65 @@
+import { useArrayData } from 'src/composables/useArrayData';
+import { onBeforeMount, ref, watch } from 'vue';
+
+export function useFilterParams(key) {
+    if (!key) throw new Error('ArrayData: A key is required to use this composable');
+    const params = ref({});
+    const orders = ref({});
+    const arrayData = ref({});
+
+    onBeforeMount(() => {
+        arrayData.value = useArrayData(key);
+    });
+
+    watch(
+        () => arrayData.value.store?.currentFilter,
+        (val, oldValue) => (val || oldValue) && setUserParams(val),
+        { immediate: true, deep: true }
+    );
+
+    function parseOrder(urlOrders) {
+        const orderObject = {};
+        if (urlOrders) {
+            if (typeof urlOrders == 'string') urlOrders = [urlOrders];
+            for (const [index, orders] of urlOrders.entries()) {
+                const [name, direction] = orders.split(' ');
+                orderObject[name] = { direction, index: index + 1 };
+            }
+        }
+        orders.value = orderObject;
+    }
+
+    function setUserParams(watchedParams) {
+        if (!watchedParams || Object.keys(watchedParams).length == 0) return;
+
+        if (typeof watchedParams == 'string') watchedParams = JSON.parse(watchedParams);
+        if (typeof watchedParams?.filter == 'string')
+            watchedParams.filter = JSON.parse(watchedParams.filter);
+
+        watchedParams = { ...watchedParams, ...watchedParams.filter?.where };
+        parseOrder(watchedParams.filter?.order);
+
+        delete watchedParams.filter;
+        params.value = sanitizer(watchedParams);
+    }
+
+    function sanitizer(params) {
+        for (const [key, value] of Object.entries(params)) {
+            if (key === 'and' && Array.isArray(value)) {
+                value.forEach((item) => {
+                    Object.assign(params, item);
+                });
+                delete params[key];
+            } else if (value && typeof value === 'object') {
+                const param = Object.values(value)[0];
+                if (typeof param == 'string') params[key] = param.replaceAll('%', '');
+            }
+        }
+        return params;
+    }
+
+    return {
+        params,
+        orders,
+    };
+}
diff --git a/src/pages/Account/AccountList.vue b/src/pages/Account/AccountList.vue
index 4b8e8fb2836..a0e2a3842ab 100644
--- a/src/pages/Account/AccountList.vue
+++ b/src/pages/Account/AccountList.vue
@@ -1,11 +1,13 @@
 <script setup>
 import { useI18n } from 'vue-i18n';
-import { ref, computed } from 'vue';
+import { ref, computed, onBeforeMount } from 'vue';
 import VnTable from 'components/VnTable/VnTable.vue';
 import VnSearchbar from 'components/ui/VnSearchbar.vue';
 import AccountSummary from './Card/AccountSummary.vue';
 import { useSummaryDialog } from 'src/composables/useSummaryDialog';
 import VnCardMain from 'src/components/common/VnCardMain.vue';
+import VnTableFilter from 'src/components/VnTable/VnTableFilter.vue';
+import { useArrayData } from 'src/composables/useArrayData';
 
 const { t } = useI18n();
 const { viewSummary } = useSummaryDialog();
@@ -84,7 +86,17 @@ const columns = computed(() => [
         ],
     },
 ]);
-const exprBuilder = (param, value) => {
+
+onBeforeMount(() => {
+    useArrayData(dataKey, {
+        url,
+        userFilter: filter,
+        order: 'id DESC',
+        exprBuilder,
+        searchUrl: 'table',
+    });
+});
+function exprBuilder(param, value) {
     switch (param) {
         case 'search':
             return /^\d+$/.test(value)
@@ -101,7 +113,7 @@ const exprBuilder = (param, value) => {
         case 'roleFk':
             return { [param]: value };
     }
-};
+}
 </script>
 
 <template>
@@ -109,28 +121,24 @@ const exprBuilder = (param, value) => {
         <template #searchbar>
             <VnSearchbar
                 :data-key="dataKey"
-                :expr-builder="exprBuilder"
                 :label="t('account.search')"
                 :info="t('account.searchInfo')"
-                :filter="filter"
-                :url="url"
             />
         </template>
         <template #body>
             <VnTable
                 ref="tableRef"
                 :data-key="dataKey"
-                :url="url"
-                :filter="filter"
-                order="id DESC"
                 :columns="columns"
                 default-mode="table"
                 redirect="account"
                 :use-model="true"
-                :right-search="true"
-                :expr-builder="exprBuilder"
+                :right-search="false"
             />
         </template>
+        <template #rightPanel>
+            <VnTableFilter :data-key="dataKey" :columns="columns" />
+        </template>
     </VnCardMain>
 </template>
 
diff --git a/src/utils/getUserParams.js b/src/utils/getUserParams.js
new file mode 100644
index 00000000000..e69de29bb2d

From 2ae0d90e32738bdfc816b3a6a5e64ca94a45e658 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Mon, 9 Dec 2024 14:15:33 +0100
Subject: [PATCH 06/34] chore: refs #8197 replace name

---
 src/components/common/VnCardMain.vue            | 1 -
 src/router/modules/index.js                     | 2 +-
 src/router/modules/{Supplier.js => supplier.js} | 0
 src/router/routes.js                            | 2 +-
 4 files changed, 2 insertions(+), 3 deletions(-)
 rename src/router/modules/{Supplier.js => supplier.js} (100%)

diff --git a/src/components/common/VnCardMain.vue b/src/components/common/VnCardMain.vue
index 3ebfcfb8bdc..7a56aa5cf16 100644
--- a/src/components/common/VnCardMain.vue
+++ b/src/components/common/VnCardMain.vue
@@ -12,7 +12,6 @@ defineProps({
 </script>
 <template>
     <slot name="searchbar" />
-    {{ stateStore.isHeaderMounted() }}
     <Teleport to="#left-panel" v-if="stateStore.isHeaderMounted()">
         <LeftMenu v-if="section == $route.name" />
     </Teleport>
diff --git a/src/router/modules/index.js b/src/router/modules/index.js
index fb1bdc46667..77076d04a20 100644
--- a/src/router/modules/index.js
+++ b/src/router/modules/index.js
@@ -8,7 +8,7 @@ import Worker from './worker';
 import Shelving from './shelving';
 import Wagon from './wagon';
 import Route from './route';
-import Supplier from './Supplier';
+import Supplier from './supplier';
 import Travel from './travel';
 import Order from './order';
 import Department from './department';
diff --git a/src/router/modules/Supplier.js b/src/router/modules/supplier.js
similarity index 100%
rename from src/router/modules/Supplier.js
rename to src/router/modules/supplier.js
diff --git a/src/router/routes.js b/src/router/routes.js
index d332be94194..131021c9a23 100644
--- a/src/router/routes.js
+++ b/src/router/routes.js
@@ -7,7 +7,7 @@ import worker from './modules/worker';
 import invoiceOut from './modules/invoiceOut';
 import invoiceIn from './modules/invoiceIn';
 import wagon from './modules/wagon';
-import supplier from './modules/Supplier';
+import supplier from './modules/supplier';
 import travel from './modules/travel';
 import department from './modules/department';
 import ItemType from './modules/itemType';

From 3984327b51272ef78f9f64282aebdd8fec250286 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Thu, 12 Dec 2024 11:03:28 +0100
Subject: [PATCH 07/34] feat: refs #8197 working rightMenu

---
 src/components/VnTable/VnTableFilter.vue | 85 ++++++++++--------------
 src/components/common/VnCardMain.vue     |  7 +-
 src/pages/Account/AccountList.vue        |  2 +-
 src/pages/Account/Role/AccountRoles.vue  |  1 +
 4 files changed, 44 insertions(+), 51 deletions(-)

diff --git a/src/components/VnTable/VnTableFilter.vue b/src/components/VnTable/VnTableFilter.vue
index 2d1758786e7..f23c657cf12 100644
--- a/src/components/VnTable/VnTableFilter.vue
+++ b/src/components/VnTable/VnTableFilter.vue
@@ -1,7 +1,6 @@
 <script setup>
 import { ref } from 'vue';
 import { useI18n } from 'vue-i18n';
-import { useStateStore } from 'stores/useStateStore';
 
 import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
 import VnFilter from 'components/VnTable/VnFilter.vue';
@@ -22,7 +21,6 @@ defineProps({
     },
 });
 const { t } = useI18n();
-const stateStore = useStateStore();
 
 const tableFilterRef = ref([]);
 
@@ -34,52 +32,41 @@ function columnName(col) {
 }
 </script>
 <template>
-    <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
-        <QScrollArea class="fit">
-            <VnFilterPanel
-                v-bind="$attrs"
-                :search-button="true"
-                :disable-submit-event="true"
+    <VnFilterPanel v-bind="$attrs" :search-button="true" :disable-submit-event="true">
+        <template #body="{ params, orders }">
+            <div
+                class="row no-wrap flex-center"
+                v-for="col of columns.filter((c) => c.columnFilter ?? true)"
+                :key="col.id"
             >
-                <template #body="{ params, orders }">
-                    <div
-                        class="row no-wrap flex-center"
-                        v-for="col of columns.filter((c) => c.columnFilter ?? true)"
-                        :key="col.id"
-                    >
-                        <VnFilter
-                            ref="tableFilterRef"
-                            :column="col"
-                            :data-key="$attrs['data-key']"
-                            v-model="params[columnName(col)]"
-                            :search-url="searchUrl"
-                        />
-                        <VnTableOrder
-                            v-if="
-                                col?.columnFilter !== false &&
-                                col?.name !== 'tableActions'
-                            "
-                            v-model="orders[col.orderBy ?? col.name]"
-                            :name="col.orderBy ?? col.name"
-                            :data-key="$attrs['data-key']"
-                            :search-url="searchUrl"
-                            :vertical="true"
-                        />
-                    </div>
-                    <slot
-                        name="moreFilterPanel"
-                        :params="params"
-                        :orders="orders"
-                        :columns="columns"
-                    />
-                </template>
-                <template #tags="{ tag, formatFn }" v-if="chipLocale">
-                    <div class="q-gutter-x-xs">
-                        <strong>{{ t(`${chipLocale}.${tag.label}`) }}: </strong>
-                        <span>{{ formatFn(tag.value) }}</span>
-                    </div>
-                </template>
-            </VnFilterPanel>
-        </QScrollArea>
-    </QDrawer>
+                <VnFilter
+                    ref="tableFilterRef"
+                    :column="col"
+                    :data-key="$attrs['data-key']"
+                    v-model="params[columnName(col)]"
+                    :search-url="searchUrl"
+                />
+                <VnTableOrder
+                    v-if="col?.columnFilter !== false && col?.name !== 'tableActions'"
+                    v-model="orders[col.orderBy ?? col.name]"
+                    :name="col.orderBy ?? col.name"
+                    :data-key="$attrs['data-key']"
+                    :search-url="searchUrl"
+                    :vertical="true"
+                />
+            </div>
+            <slot
+                name="moreFilterPanel"
+                :params="params"
+                :orders="orders"
+                :columns="columns"
+            />
+        </template>
+        <template #tags="{ tag, formatFn }" v-if="chipLocale">
+            <div class="q-gutter-x-xs">
+                <strong>{{ t(`${chipLocale}.${tag.label}`) }}: </strong>
+                <span>{{ formatFn(tag.value) }}</span>
+            </div>
+        </template>
+    </VnFilterPanel>
 </template>
diff --git a/src/components/common/VnCardMain.vue b/src/components/common/VnCardMain.vue
index 7a56aa5cf16..ab664917ae7 100644
--- a/src/components/common/VnCardMain.vue
+++ b/src/components/common/VnCardMain.vue
@@ -1,6 +1,7 @@
 <script setup>
 import LeftMenu from '../LeftMenu.vue';
 import { useStateStore } from 'stores/useStateStore';
+import RightMenu from './RightMenu.vue';
 const stateStore = useStateStore();
 
 defineProps({
@@ -17,5 +18,9 @@ defineProps({
     </Teleport>
     <slot name="body" v-if="section == $route.name" />
     <RouterView v-else />
-    <slot name="rightPanel" />
+    <RightMenu>
+        <template #right-panel v-if="$slots['rightMenu']">
+            <slot name="rightMenu" />
+        </template>
+    </RightMenu>
 </template>
diff --git a/src/pages/Account/AccountList.vue b/src/pages/Account/AccountList.vue
index a0e2a3842ab..ed2030d2964 100644
--- a/src/pages/Account/AccountList.vue
+++ b/src/pages/Account/AccountList.vue
@@ -136,7 +136,7 @@ function exprBuilder(param, value) {
                 :right-search="false"
             />
         </template>
-        <template #rightPanel>
+        <template #rightMenu>
             <VnTableFilter :data-key="dataKey" :columns="columns" />
         </template>
     </VnCardMain>
diff --git a/src/pages/Account/Role/AccountRoles.vue b/src/pages/Account/Role/AccountRoles.vue
index 74c4ab8a558..9aebef64c5c 100644
--- a/src/pages/Account/Role/AccountRoles.vue
+++ b/src/pages/Account/Role/AccountRoles.vue
@@ -114,6 +114,7 @@ const exprBuilder = (param, value) => {
                 :columns="columns"
                 default-mode="table"
                 redirect="account/role"
+                :right-search="false"
             />
         </template>
     </VnCardMain>

From 5d744ca456e39708124e78c866d33efe14d71f06 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Mon, 16 Dec 2024 09:41:50 +0100
Subject: [PATCH 08/34] feat: refs #8197 better leftMenu and VnCardMain
 improvements

---
 src/components/LeftMenu.vue               | 25 ++++-----
 src/components/common/VnBreadcrumbs.vue   |  2 +-
 src/components/common/VnCardMain.vue      | 65 ++++++++++++++++++++---
 src/pages/Account/AccountList.vue         | 40 +++++---------
 src/pages/Account/Role/AccountRoles.vue   | 20 +++----
 src/pages/Account/locale/en.yml           |  2 +-
 src/router/modules/account.js             | 10 ++--
 src/router/modules/account/accountCard.js | 10 ++++
 src/router/modules/account/roleCard.js    |  3 ++
 src/utils/getSections.js                  |  8 ---
 10 files changed, 108 insertions(+), 77 deletions(-)
 delete mode 100644 src/utils/getSections.js

diff --git a/src/components/LeftMenu.vue b/src/components/LeftMenu.vue
index ab2931dfd29..eed2e192bed 100644
--- a/src/components/LeftMenu.vue
+++ b/src/components/LeftMenu.vue
@@ -92,13 +92,11 @@ function findMatches(search, item) {
 }
 
 function addChildren(module, route, parent) {
-    if (route.menus) {
-        const mainMenus = route.menus[props.source];
-        const matches = findMatches(mainMenus, route);
+    if (!route?.meta?.menu) return;
+    const matches = findMatches(route.meta.menu, route);
 
-        for (const child of matches) {
-            navigation.addMenuItem(module, child, parent);
-        }
+    for (const child of matches) {
+        navigation.addMenuItem(module, child, parent);
     }
 }
 
@@ -120,15 +118,14 @@ function getRoutes() {
     }
 
     if (props.source === 'card') {
-        const currentRoute = route.matched[1];
-        const currentModule = toLowerCamel(currentRoute.name);
-        const moduleDef = routes.find(
-            (route) => toLowerCamel(route.name) === currentModule
-        );
+        let menuRoute;
+        let index = route.matched.length - 1;
 
-        if (!moduleDef) return;
-
-        addChildren(currentModule, moduleDef, items.value);
+        while (!menuRoute && index > 0) {
+            if (route.matched[index]?.meta?.menu) menuRoute = route.matched[index];
+            index--;
+        }
+        addChildren('', menuRoute, items.value);
     }
 }
 
diff --git a/src/components/common/VnBreadcrumbs.vue b/src/components/common/VnBreadcrumbs.vue
index 02226e4975a..334ab4d2115 100644
--- a/src/components/common/VnBreadcrumbs.vue
+++ b/src/components/common/VnBreadcrumbs.vue
@@ -15,7 +15,7 @@ let root = ref(null);
 
 watchEffect(() => {
     matched.value = currentRoute.value.matched.filter(
-        (matched) => Object.keys(matched.meta).length
+        (matched) => !!matched?.meta?.title || !!matched?.meta?.icon
     );
     breadcrumbs.value.length = 0;
     if (!matched.value[0]) return;
diff --git a/src/components/common/VnCardMain.vue b/src/components/common/VnCardMain.vue
index ab664917ae7..b222748b9f7 100644
--- a/src/components/common/VnCardMain.vue
+++ b/src/components/common/VnCardMain.vue
@@ -2,25 +2,78 @@
 import LeftMenu from '../LeftMenu.vue';
 import { useStateStore } from 'stores/useStateStore';
 import RightMenu from './RightMenu.vue';
+import VnSearchbar from 'components/ui/VnSearchbar.vue';
+import VnTableFilter from '../VnTable/VnTableFilter.vue';
+import { onBeforeMount } from 'vue';
+import { useArrayData } from 'src/composables/useArrayData';
 const stateStore = useStateStore();
 
-defineProps({
+const $props = defineProps({
     section: {
         type: String,
         required: true,
     },
+    dataKey: {
+        type: String,
+        default: null,
+    },
+    searchBar: {
+        type: Boolean,
+        default: true,
+    },
+    prefix: {
+        type: String,
+        default: null,
+    },
+    rightFilter: {
+        type: Boolean,
+        default: true,
+    },
+    columns: {
+        type: Array,
+        default: null,
+    },
+    arrayDataProps: {
+        type: Object,
+        default: null,
+    },
+});
+
+onBeforeMount(() => {
+    if ($props.dataKey)
+        useArrayData($props.dataKey, {
+            searchUrl: 'table',
+            ...$props.arrayDataProps,
+        });
 });
 </script>
 <template>
-    <slot name="searchbar" />
+    <slot name="searchbar">
+        <VnSearchbar
+            v-if="searchBar"
+            v-bind="arrayDataProps"
+            :data-key="dataKey"
+            :label="$t(`${prefix}.search`)"
+            :info="$t(`${prefix}.searchInfo`)"
+        />
+    </slot>
+
     <Teleport to="#left-panel" v-if="stateStore.isHeaderMounted()">
         <LeftMenu v-if="section == $route.name" />
     </Teleport>
-    <slot name="body" v-if="section == $route.name" />
-    <RouterView v-else />
+
     <RightMenu>
-        <template #right-panel v-if="$slots['rightMenu']">
-            <slot name="rightMenu" />
+        <template #right-panel v-if="$slots['rightMenu'] || rightFilter">
+            <slot name="rightMenu">
+                <VnTableFilter
+                    v-if="rightFilter && columns"
+                    :data-key="dataKey"
+                    :columns="columns"
+                />
+            </slot>
         </template>
     </RightMenu>
+
+    <slot name="body" v-if="section == $route.name" />
+    <RouterView v-else />
 </template>
diff --git a/src/pages/Account/AccountList.vue b/src/pages/Account/AccountList.vue
index ed2030d2964..34a653e61db 100644
--- a/src/pages/Account/AccountList.vue
+++ b/src/pages/Account/AccountList.vue
@@ -1,22 +1,17 @@
 <script setup>
 import { useI18n } from 'vue-i18n';
-import { ref, computed, onBeforeMount } from 'vue';
+import { computed } from 'vue';
 import VnTable from 'components/VnTable/VnTable.vue';
-import VnSearchbar from 'components/ui/VnSearchbar.vue';
 import AccountSummary from './Card/AccountSummary.vue';
 import { useSummaryDialog } from 'src/composables/useSummaryDialog';
 import VnCardMain from 'src/components/common/VnCardMain.vue';
-import VnTableFilter from 'src/components/VnTable/VnTableFilter.vue';
-import { useArrayData } from 'src/composables/useArrayData';
 
 const { t } = useI18n();
 const { viewSummary } = useSummaryDialog();
-const tableRef = ref();
 const filter = {
     include: { relation: 'role', scope: { fields: ['id', 'name'] } },
 };
 const dataKey = 'AccountList';
-const url = 'VnUsers/preview';
 const columns = computed(() => [
     {
         align: 'left',
@@ -87,15 +82,6 @@ const columns = computed(() => [
     },
 ]);
 
-onBeforeMount(() => {
-    useArrayData(dataKey, {
-        url,
-        userFilter: filter,
-        order: 'id DESC',
-        exprBuilder,
-        searchUrl: 'table',
-    });
-});
 function exprBuilder(param, value) {
     switch (param) {
         case 'search':
@@ -117,17 +103,20 @@ function exprBuilder(param, value) {
 </script>
 
 <template>
-    <VnCardMain :section="dataKey">
-        <template #searchbar>
-            <VnSearchbar
-                :data-key="dataKey"
-                :label="t('account.search')"
-                :info="t('account.searchInfo')"
-            />
-        </template>
+    <VnCardMain
+        :section="dataKey"
+        :data-key="dataKey"
+        :columns="columns"
+        prefix="account"
+        :array-data-props="{
+            url: 'VnUsers/preview',
+            userFilter: filter,
+            order: 'id DESC',
+            exprBuilder,
+        }"
+    >
         <template #body>
             <VnTable
-                ref="tableRef"
                 :data-key="dataKey"
                 :columns="columns"
                 default-mode="table"
@@ -136,9 +125,6 @@ function exprBuilder(param, value) {
                 :right-search="false"
             />
         </template>
-        <template #rightMenu>
-            <VnTableFilter :data-key="dataKey" :columns="columns" />
-        </template>
     </VnCardMain>
 </template>
 
diff --git a/src/pages/Account/Role/AccountRoles.vue b/src/pages/Account/Role/AccountRoles.vue
index 9aebef64c5c..8cc392f1bb8 100644
--- a/src/pages/Account/Role/AccountRoles.vue
+++ b/src/pages/Account/Role/AccountRoles.vue
@@ -3,7 +3,6 @@ import { useI18n } from 'vue-i18n';
 import { computed, ref } from 'vue';
 import VnTable from 'components/VnTable/VnTable.vue';
 import { useRoute } from 'vue-router';
-import VnSearchbar from 'components/ui/VnSearchbar.vue';
 import { useSummaryDialog } from 'src/composables/useSummaryDialog';
 import RoleSummary from './Card/RoleSummary.vue';
 import VnCardMain from 'src/components/common/VnCardMain.vue';
@@ -86,21 +85,17 @@ const exprBuilder = (param, value) => {
 </script>
 
 <template>
-    <VnCardMain :section="dataKey">
-        <template #searchbar>
-            <VnSearchbar
-                :url="url"
-                :data-key="dataKey"
-                :expr-builder="exprBuilder"
-                :label="t('role.searchRoles')"
-                :info="t('role.searchInfo')"
-            />
-        </template>
+    <VnCardMain
+        :section="dataKey"
+        :data-key="dataKey"
+        :columns="columns"
+        prefix="role"
+        :array-data-props="{ url, exprBuilder, order: 'id ASC' }"
+    >
         <template #body>
             <VnTable
                 ref="tableRef"
                 :data-key="dataKey"
-                :url="url"
                 :create="{
                     urlCreate: 'VnRoles',
                     title: t('Create rol'),
@@ -109,7 +104,6 @@ const exprBuilder = (param, value) => {
                         editorFk: entityId,
                     },
                 }"
-                order="id ASC"
                 :disable-option="{ card: true }"
                 :columns="columns"
                 default-mode="table"
diff --git a/src/pages/Account/locale/en.yml b/src/pages/Account/locale/en.yml
index f2f563923c3..88a6b11e990 100644
--- a/src/pages/Account/locale/en.yml
+++ b/src/pages/Account/locale/en.yml
@@ -66,7 +66,7 @@ account:
         mailInputInfo: All emails will be forwarded to the specified address.
 role:
     newRole: New role
-    searchRoles: Search role
+    search: Search role
     searchInfo: Search role by id or name
     description: Description
     id: Id
diff --git a/src/router/modules/account.js b/src/router/modules/account.js
index ece0ab2bb89..2ee7c915d9d 100644
--- a/src/router/modules/account.js
+++ b/src/router/modules/account.js
@@ -1,7 +1,6 @@
 import { RouterView } from 'vue-router';
 import accountCard from './account/accountCard';
 import roleCard from './account/roleCard';
-import getSections from 'src/utils/getSections';
 
 export default {
     path: '/account',
@@ -11,11 +10,7 @@ export default {
         icon: 'face',
         moduleName: 'Account',
         keyBinding: 'u',
-    },
-    component: RouterView,
-    redirect: { name: 'AccountMain' },
-    menus: {
-        main: [
+        menu: [
             'AccountList',
             'AccountAliasList',
             'AccountRoles',
@@ -25,8 +20,9 @@ export default {
             'AccountAcls',
             'AccountConnections',
         ],
-        card: getSections(accountCard.children),
     },
+    component: RouterView,
+    redirect: { name: 'AccountMain' },
     children: [
         {
             path: '',
diff --git a/src/router/modules/account/accountCard.js b/src/router/modules/account/accountCard.js
index 0d8850f10aa..3ba687adfda 100644
--- a/src/router/modules/account/accountCard.js
+++ b/src/router/modules/account/accountCard.js
@@ -3,6 +3,16 @@ export default {
     path: ':id',
     redirect: { name: 'AccountSummary' },
     component: () => import('src/pages/Account/Card/AccountCard.vue'),
+    meta: {
+        menu: [
+            'AccountBasicData',
+            'AccountInheritedRoles',
+            'AccountMailForwarding',
+            'AccountMailAlias',
+            'AccountPrivileges',
+            'AccountLog',
+        ],
+    },
     children: [
         {
             name: 'AccountSummary',
diff --git a/src/router/modules/account/roleCard.js b/src/router/modules/account/roleCard.js
index 2a538756873..c36ce71b9da 100644
--- a/src/router/modules/account/roleCard.js
+++ b/src/router/modules/account/roleCard.js
@@ -3,6 +3,9 @@ export default {
     path: ':id',
     component: () => import('src/pages/Account/Role/Card/RoleCard.vue'),
     redirect: { name: 'RoleSummary' },
+    meta: {
+        menu: ['RoleBasicData', 'SubRoles', 'InheritedRoles', 'RoleLog'],
+    },
     children: [
         {
             name: 'RoleSummary',
diff --git a/src/utils/getSections.js b/src/utils/getSections.js
deleted file mode 100644
index f70daf4685c..00000000000
--- a/src/utils/getSections.js
+++ /dev/null
@@ -1,8 +0,0 @@
-export default (sections) => {
-    const names = [];
-    for (const section of sections) {
-        if (section.path == 'summary') continue;
-        names.push(section.name);
-    }
-    return names;
-};

From 18fd41b82bf8ddf8317aad06f912115985ed9531 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Mon, 16 Dec 2024 09:47:46 +0100
Subject: [PATCH 09/34] refactor: refs #8197 adapt AccountAlias

---
 src/pages/Account/AccountAliasList.vue  | 77 ++++++++++++-------------
 src/router/modules/account.js           | 15 ++++-
 src/router/modules/account/aliasCard.js | 36 ++++++++++++
 src/router/modules/index.js             |  2 -
 src/router/modules/mailAlias.js         | 57 ------------------
 src/router/routes.js                    |  2 -
 6 files changed, 85 insertions(+), 104 deletions(-)
 create mode 100644 src/router/modules/account/aliasCard.js
 delete mode 100644 src/router/modules/mailAlias.js

diff --git a/src/pages/Account/AccountAliasList.vue b/src/pages/Account/AccountAliasList.vue
index c6728329747..721a009e512 100644
--- a/src/pages/Account/AccountAliasList.vue
+++ b/src/pages/Account/AccountAliasList.vue
@@ -2,21 +2,12 @@
 import { useI18n } from 'vue-i18n';
 import { ref, computed } from 'vue';
 import VnTable from 'components/VnTable/VnTable.vue';
-import VnSearchbar from 'components/ui/VnSearchbar.vue';
-import { useStateStore } from 'stores/useStateStore';
+import VnCardMain from 'src/components/common/VnCardMain.vue';
 
 const tableRef = ref();
 const { t } = useI18n();
-const stateStore = useStateStore();
+const dataKey = 'AccountAliasList';
 
-const exprBuilder = (param, value) => {
-    switch (param) {
-        case 'search':
-            return /^\d+$/.test(value)
-                ? { id: value }
-                : { alias: { like: `%${value}%` } };
-    }
-};
 const columns = computed(() => [
     {
         align: 'left',
@@ -40,40 +31,46 @@ const columns = computed(() => [
         create: true,
     },
 ]);
+
+const exprBuilder = (param, value) => {
+    switch (param) {
+        case 'search':
+            return /^\d+$/.test(value)
+                ? { id: value }
+                : { alias: { like: `%${value}%` } };
+    }
+};
 </script>
 
 <template>
-    <template v-if="stateStore.isHeaderMounted()">
-        <Teleport to="#searchbar">
-            <VnSearchbar
-                data-key="AccountAliasList"
-                url="MailAliases"
-                :expr-builder="exprBuilder"
-                :label="t('mailAlias.search')"
-                :info="t('mailAlias.searchInfo')"
-            />
-        </Teleport>
-    </template>
-    <VnTable
-        ref="tableRef"
-        data-key="AccountAliasList"
-        url="MailAliases"
-        :create="{
-            urlCreate: 'MailAliases',
-            title: 'Create MailAlias',
-            onDataSaved: ({ id }) => tableRef.redirect(id),
-            formInitialData: {},
-        }"
-        order="id DESC"
+    <VnCardMain
+        :section="dataKey"
+        :data-key="dataKey"
         :columns="columns"
-        :disable-option="{ card: true }"
-        default-mode="table"
-        redirect="account/alias"
-        :is-editable="true"
-        :use-model="true"
-    />
+        prefix="mailAlias"
+        :array-data-props="{ url: 'MailAliases', order: 'id DESC', exprBuilder }"
+    >
+        <template #body>
+            <VnTable
+                :data-key="dataKey"
+                ref="tableRef"
+                :create="{
+                    urlCreate: 'MailAliases',
+                    title: 'Create MailAlias',
+                    onDataSaved: ({ id }) => tableRef.redirect(id),
+                    formInitialData: {},
+                }"
+                :columns="columns"
+                :disable-option="{ card: true }"
+                default-mode="table"
+                redirect="account/alias"
+                :is-editable="true"
+                :use-model="true"
+                :right-search="false"
+            />
+        </template>
+    </VnCardMain>
 </template>
-
 <i18n>
     es:
         Id: Id
diff --git a/src/router/modules/account.js b/src/router/modules/account.js
index 2ee7c915d9d..6f5ca90f3cf 100644
--- a/src/router/modules/account.js
+++ b/src/router/modules/account.js
@@ -1,6 +1,7 @@
 import { RouterView } from 'vue-router';
 import accountCard from './account/accountCard';
 import roleCard from './account/roleCard';
+import aliasCard from './account/aliasCard';
 
 export default {
     path: '/account',
@@ -12,8 +13,8 @@ export default {
         keyBinding: 'u',
         menu: [
             'AccountList',
-            'AccountAliasList',
             'AccountRoles',
+            'AccountAlias',
             'AccountAccounts',
             'AccountLdap',
             'AccountSamba',
@@ -65,13 +66,21 @@ export default {
                     ],
                 },
                 {
-                    path: 'alias-list',
-                    name: 'AccountAliasList',
+                    path: 'alias',
+                    name: 'AccountAlias',
+                    redirect: { name: 'AccountAliasList' },
                     meta: {
                         title: 'alias',
                         icon: 'email',
                     },
                     component: () => import('src/pages/Account/AccountAliasList.vue'),
+                    children: [
+                        {
+                            name: 'AccountAliasList',
+                            path: 'list',
+                        },
+                        aliasCard,
+                    ],
                 },
                 {
                     path: 'acls',
diff --git a/src/router/modules/account/aliasCard.js b/src/router/modules/account/aliasCard.js
new file mode 100644
index 00000000000..cbbd31e5103
--- /dev/null
+++ b/src/router/modules/account/aliasCard.js
@@ -0,0 +1,36 @@
+export default {
+    name: 'AliasCard',
+    path: ':id',
+    component: () => import('src/pages/Account/Alias/Card/AliasCard.vue'),
+    redirect: { name: 'AliasSummary' },
+    meta: { menu: ['AliasBasicData', 'AliasUsers'] },
+    children: [
+        {
+            name: 'AliasSummary',
+            path: 'summary',
+            meta: {
+                title: 'summary',
+                icon: 'launch',
+            },
+            component: () => import('src/pages/Account/Alias/Card/AliasSummary.vue'),
+        },
+        {
+            name: 'AliasBasicData',
+            path: 'basic-data',
+            meta: {
+                title: 'basicData',
+                icon: 'vn:settings',
+            },
+            component: () => import('src/pages/Account/Alias/Card/AliasBasicData.vue'),
+        },
+        {
+            name: 'AliasUsers',
+            path: 'users',
+            meta: {
+                title: 'aliasUsers',
+                icon: 'group',
+            },
+            component: () => import('src/pages/Account/Alias/Card/AliasUsers.vue'),
+        },
+    ],
+};
diff --git a/src/router/modules/index.js b/src/router/modules/index.js
index 77076d04a20..f28fed1c2c3 100644
--- a/src/router/modules/index.js
+++ b/src/router/modules/index.js
@@ -20,7 +20,6 @@ import ItemType from './itemType';
 import Zone from './zone';
 import Account from './account';
 import Monitor from './monitor';
-import MailAlias from './mailAlias';
 
 export default [
     Item,
@@ -44,6 +43,5 @@ export default [
     ItemType,
     Zone,
     Account,
-    MailAlias,
     Monitor,
 ];
diff --git a/src/router/modules/mailAlias.js b/src/router/modules/mailAlias.js
deleted file mode 100644
index 8e0f8abdcb9..00000000000
--- a/src/router/modules/mailAlias.js
+++ /dev/null
@@ -1,57 +0,0 @@
-import { RouterView } from 'vue-router';
-
-export default {
-    path: 'account/alias',
-    name: 'Alias',
-    meta: {
-        title: 'alias',
-        icon: 'email',
-        moduleName: 'Alias',
-    },
-    component: RouterView,
-    redirect: { name: 'AccountAliasList' },
-    menus: {
-        main: [],
-        card: ['AliasBasicData', 'AliasUsers'],
-    },
-    children: [
-        {
-            name: 'AliasCard',
-            path: ':id',
-            component: () => import('src/pages/Account/Alias/Card/AliasCard.vue'),
-            redirect: { name: 'AliasSummary' },
-            children: [
-                {
-                    name: 'AliasSummary',
-                    path: 'summary',
-                    meta: {
-                        title: 'summary',
-                        icon: 'launch',
-                    },
-                    component: () =>
-                        import('src/pages/Account/Alias/Card/AliasSummary.vue'),
-                },
-                {
-                    name: 'AliasBasicData',
-                    path: 'basic-data',
-                    meta: {
-                        title: 'basicData',
-                        icon: 'vn:settings',
-                    },
-                    component: () =>
-                        import('src/pages/Account/Alias/Card/AliasBasicData.vue'),
-                },
-                {
-                    name: 'AliasUsers',
-                    path: 'users',
-                    meta: {
-                        title: 'aliasUsers',
-                        icon: 'group',
-                    },
-                    component: () =>
-                        import('src/pages/Account/Alias/Card/AliasUsers.vue'),
-                },
-            ],
-        },
-    ],
-};
diff --git a/src/router/routes.js b/src/router/routes.js
index 131021c9a23..b9120f8c414 100644
--- a/src/router/routes.js
+++ b/src/router/routes.js
@@ -20,7 +20,6 @@ import agency from 'src/router/modules/agency';
 import zone from 'src/router/modules/zone';
 import account from './modules/account';
 import monitor from 'src/router/modules/monitor';
-import mailAlias from './modules/mailAlias';
 
 const routes = [
     {
@@ -94,7 +93,6 @@ const routes = [
             ItemType,
             zone,
             account,
-            mailAlias,
             {
                 path: '/:catchAll(.*)*',
                 name: 'NotFound',

From 1e76d5fd3fb45bba11060ea9ab0898e2fc4fa79d Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Mon, 16 Dec 2024 12:03:57 +0100
Subject: [PATCH 10/34] refactor: ignore params when searching by id on
 searchbar

---
 src/components/ui/VnSearchbar.vue         | 19 +++++++++++++------
 src/pages/Order/Card/OrderCatalog.vue     |  3 ++-
 src/pages/Zone/Card/ZoneLocationsTree.vue |  8 +++++++-
 3 files changed, 22 insertions(+), 8 deletions(-)

diff --git a/src/components/ui/VnSearchbar.vue b/src/components/ui/VnSearchbar.vue
index da2d370fe09..92babfcc6fc 100644
--- a/src/components/ui/VnSearchbar.vue
+++ b/src/components/ui/VnSearchbar.vue
@@ -51,10 +51,6 @@ const props = defineProps({
         type: Object,
         default: null,
     },
-    staticParams: {
-        type: Array,
-        default: () => [],
-    },
     exprBuilder: {
         type: Function,
         default: null,
@@ -67,6 +63,10 @@ const props = defineProps({
         type: Function,
         default: undefined,
     },
+    searchRemoveParams: {
+        type: Boolean,
+        default: true,
+    },
 });
 
 const searchText = ref();
@@ -105,12 +105,18 @@ async function search() {
 
     const filter = {
         params: {
-            ...Object.fromEntries(staticParams),
             search: searchText.value,
         },
-        ...{ filter: props.filter },
+        filter: props.filter,
     };
 
+    if (!props.searchRemoveParams || !searchText.value) {
+        filter.params = {
+            ...Object.fromEntries(staticParams),
+            search: searchText.value,
+        };
+    }
+
     if (props.whereFilter) {
         filter.filter = {
             where: props.whereFilter(searchText.value),
@@ -130,6 +136,7 @@ async function search() {
                 dense
                 standout
                 autofocus
+                data-cy="vnSearchBar"
             >
                 <template #prepend>
                     <QIcon
diff --git a/src/pages/Order/Card/OrderCatalog.vue b/src/pages/Order/Card/OrderCatalog.vue
index 948970cc3c3..744f87297ac 100644
--- a/src/pages/Order/Card/OrderCatalog.vue
+++ b/src/pages/Order/Card/OrderCatalog.vue
@@ -1,7 +1,7 @@
 <script setup>
 import { useStateStore } from 'stores/useStateStore';
 import { useRoute, useRouter } from 'vue-router';
-import { onMounted, onUnmounted, ref, computed, watch, provide, nextTick } from 'vue';
+import { onMounted, onUnmounted, ref, computed, watch, provide } from 'vue';
 import axios from 'axios';
 import { useI18n } from 'vue-i18n';
 import VnPaginate from 'src/components/ui/VnPaginate.vue';
@@ -101,6 +101,7 @@ provide('onItemSaved', onItemSaved);
         url="Orders/CatalogFilter"
         :label="t('Search items')"
         :info="t('You can search items by name or id')"
+        :search-remove-params="false"
     />
     <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
         <QScrollArea class="fit text-grey-8">
diff --git a/src/pages/Zone/Card/ZoneLocationsTree.vue b/src/pages/Zone/Card/ZoneLocationsTree.vue
index 650047e40c7..5c87faf999e 100644
--- a/src/pages/Zone/Card/ZoneLocationsTree.vue
+++ b/src/pages/Zone/Card/ZoneLocationsTree.vue
@@ -163,7 +163,13 @@ onUnmounted(() => {
             <QBtn color="primary" icon="search" dense flat @click="reFetch()" />
         </template>
     </VnInput>
-    <VnSearchbar v-if="!showSearchBar" :data-key="datakey" :url="url" :redirect="false" />
+    <VnSearchbar
+        v-if="!showSearchBar"
+        :data-key="datakey"
+        :url="url"
+        :redirect="false"
+        :search-remove-params="false"
+    />
     <QTree
         ref="treeRef"
         :nodes="nodes"

From 0ab12d7b5d9b964fae83e64ab84367eebd473bbf Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Mon, 16 Dec 2024 12:26:41 +0100
Subject: [PATCH 11/34] fix: refs #8197 vnTableFilter in vnTable

---
 src/components/VnTable/VnTable.vue | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index 521c19d3243..5f073fb65ec 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -305,11 +305,17 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
 }
 </script>
 <template>
-    <VnTableFilter
+    <QDrawer
         v-if="$props.rightSearch"
-        :data-key="$attrs['data-key']"
-        :columns="columns"
-    />
+        v-model="stateStore.rightDrawer"
+        side="right"
+        :width="256"
+        show-if-above
+    >
+        <QScrollArea class="fit">
+            <VnTableFilter :data-key="$attrs['data-key']" :columns="columns" />
+        </QScrollArea>
+    </QDrawer>
     <CrudModel
         v-bind="$attrs"
         :class="$attrs['class'] ?? 'q-px-md'"

From 95420e96d1681f4838f584a9c60a1112cd29744f Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Mon, 16 Dec 2024 12:27:13 +0100
Subject: [PATCH 12/34] refactor: refs #8197 adapt AccountAcls to VnCardMain

---
 src/pages/Account/AccountAcls.vue | 60 ++++++++++++++++---------------
 1 file changed, 31 insertions(+), 29 deletions(-)

diff --git a/src/pages/Account/AccountAcls.vue b/src/pages/Account/AccountAcls.vue
index d80f835ecc2..73771a34107 100644
--- a/src/pages/Account/AccountAcls.vue
+++ b/src/pages/Account/AccountAcls.vue
@@ -1,16 +1,15 @@
 <script setup>
 import { useI18n } from 'vue-i18n';
 import { ref, computed } from 'vue';
-import { useStateStore } from 'stores/useStateStore';
 import axios from 'axios';
 import useNotify from 'src/composables/useNotify.js';
 import { useQuasar } from 'quasar';
 
 import VnTable from 'components/VnTable/VnTable.vue';
-import VnSearchbar from 'components/ui/VnSearchbar.vue';
 import VnConfirm from 'components/ui/VnConfirm.vue';
 import FetchData from 'src/components/FetchData.vue';
 import { useValidator } from 'src/composables/useValidator';
+import VnCardMain from 'src/components/common/VnCardMain.vue';
 
 defineProps({
     id: {
@@ -21,13 +20,13 @@ defineProps({
 
 const { notify } = useNotify();
 const { t } = useI18n();
-const stateStore = useStateStore();
 const quasar = useQuasar();
 
 const tableRef = ref();
 const roles = ref();
 const validationsStore = useValidator();
 const { models } = validationsStore;
+const dataKey = 'AccountAcls';
 const exprBuilder = (param, value) => {
     switch (param) {
         case 'search':
@@ -134,38 +133,41 @@ const deleteAcl = async ({ id }) => {
 </script>
 
 <template>
-    <VnSearchbar
-        data-key="AccountAcls"
-        url="ACLs"
-        :expr-builder="exprBuilder"
-        :label="t('acls.search')"
-        :info="t('acls.searchInfo')"
-    />
-    <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
-    </QDrawer>
     <FetchData
         url="VnRoles?fields=['name']"
         auto-load
         @on-fetch="(data) => (roles = data)"
     />
-    <VnTable
-        ref="tableRef"
-        data-key="AccountAcls"
-        :url="`ACLs`"
-        :create="{
-            urlCreate: 'ACLs',
-            title: 'Create ACL',
-            onDataSaved: () => tableRef.reload(),
-            formInitialData: {},
-        }"
-        order="id DESC"
-        :disable-option="{ card: true }"
+    <VnCardMain
+        :section="dataKey"
+        :data-key="dataKey"
         :columns="columns"
-        default-mode="table"
-        :right-search="true"
-        :is-editable="true"
-        :use-model="true"
-    />
+        prefix="acls"
+        :array-data-props="{
+            url: 'ACLs',
+            order: 'id DESC',
+            exprBuilder,
+        }"
+    >
+        <template #body>
+            <VnTable
+                ref="tableRef"
+                data-key="AccountAcls"
+                :create="{
+                    urlCreate: 'ACLs',
+                    title: 'Create ACL',
+                    onDataSaved: () => tableRef.reload(),
+                    formInitialData: {},
+                }"
+                :disable-option="{ card: true }"
+                :columns="columns"
+                default-mode="table"
+                :right-search="false"
+                :is-editable="true"
+                :use-model="true"
+            />
+        </template>
+    </VnCardMain>
 </template>
 
 <i18n>

From dc665d43fe2da557a056e8e1feb5e21e9fcd481c Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Mon, 16 Dec 2024 13:33:59 +0100
Subject: [PATCH 13/34] feat: refs #8197 default leftMenu

---
 src/components/common/VnCardMain.vue    |  7 -------
 src/components/common/VnSectionMain.vue | 24 ++++++++++++++++++++++--
 2 files changed, 22 insertions(+), 9 deletions(-)

diff --git a/src/components/common/VnCardMain.vue b/src/components/common/VnCardMain.vue
index b222748b9f7..5b8e6b5e87a 100644
--- a/src/components/common/VnCardMain.vue
+++ b/src/components/common/VnCardMain.vue
@@ -1,12 +1,9 @@
 <script setup>
-import LeftMenu from '../LeftMenu.vue';
-import { useStateStore } from 'stores/useStateStore';
 import RightMenu from './RightMenu.vue';
 import VnSearchbar from 'components/ui/VnSearchbar.vue';
 import VnTableFilter from '../VnTable/VnTableFilter.vue';
 import { onBeforeMount } from 'vue';
 import { useArrayData } from 'src/composables/useArrayData';
-const stateStore = useStateStore();
 
 const $props = defineProps({
     section: {
@@ -58,10 +55,6 @@ onBeforeMount(() => {
         />
     </slot>
 
-    <Teleport to="#left-panel" v-if="stateStore.isHeaderMounted()">
-        <LeftMenu v-if="section == $route.name" />
-    </Teleport>
-
     <RightMenu>
         <template #right-panel v-if="$slots['rightMenu'] || rightFilter">
             <slot name="rightMenu">
diff --git a/src/components/common/VnSectionMain.vue b/src/components/common/VnSectionMain.vue
index c1b9808b5cf..4e800fa8aaa 100644
--- a/src/components/common/VnSectionMain.vue
+++ b/src/components/common/VnSectionMain.vue
@@ -1,7 +1,8 @@
 <script setup>
 import { useStateStore } from 'stores/useStateStore';
-import { onMounted } from 'vue';
+import { onMounted, ref } from 'vue';
 import { useQuasar } from 'quasar';
+import LeftMenu from '../LeftMenu.vue';
 
 const stateStore = useStateStore();
 const $props = defineProps({
@@ -13,12 +14,31 @@ const $props = defineProps({
 onMounted(
     () => (stateStore.leftDrawer = useQuasar().screen.gt.xs ? $props.leftDrawer : false)
 );
+
+const targetId = 'left-panel';
+const teleportRef = ref({});
+const hasContent = ref();
+let observer;
+
+onMounted(() => {
+    if (teleportRef.value) {
+        const checkContent = () => {
+            hasContent.value = teleportRef.value.innerHTML.trim() !== '';
+        };
+
+        observer = new MutationObserver(checkContent);
+        observer.observe(teleportRef.value, { childList: true, subtree: true });
+
+        checkContent();
+    }
+});
 </script>
 
 <template>
     <QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
         <QScrollArea class="fit text-grey-8">
-            <div id="left-panel"></div>
+            <div :id="targetId" ref="teleportRef"></div>
+            <LeftMenu v-if="!hasContent" />
         </QScrollArea>
     </QDrawer>
     <QPageContainer>

From dc83d50e96e1882b98499eb5addc1acbf55ad998 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Mon, 16 Dec 2024 14:30:23 +0100
Subject: [PATCH 14/34] refactor: refs #8197 backward compatible

---
 src/components/LeftMenu.vue                | 31 ++++++++---
 src/components/common/VnCard.vue           | 45 +++++++++++----
 src/components/common/VnCardBeta.vue       | 65 ++++++++++++++++++++++
 src/pages/Account/Alias/Card/AliasCard.vue |  4 +-
 src/pages/Account/Card/AccountCard.vue     |  4 +-
 src/pages/Account/Role/Card/RoleCard.vue   |  4 +-
 6 files changed, 128 insertions(+), 25 deletions(-)
 create mode 100644 src/components/common/VnCardBeta.vue

diff --git a/src/components/LeftMenu.vue b/src/components/LeftMenu.vue
index eed2e192bed..09e126213fd 100644
--- a/src/components/LeftMenu.vue
+++ b/src/components/LeftMenu.vue
@@ -92,8 +92,10 @@ function findMatches(search, item) {
 }
 
 function addChildren(module, route, parent) {
-    if (!route?.meta?.menu) return;
-    const matches = findMatches(route.meta.menu, route);
+    const menus = route?.meta?.menu ?? route?.menus?.[props.source]; //backwards compatible
+    if (!menus) return;
+
+    const matches = findMatches(menus, route);
 
     for (const child of matches) {
         navigation.addMenuItem(module, child, parent);
@@ -118,17 +120,28 @@ function getRoutes() {
     }
 
     if (props.source === 'card') {
-        let menuRoute;
-        let index = route.matched.length - 1;
+        const currentRoute = route.matched[1];
+        const currentModule = toLowerCamel(currentRoute.name);
+        let moduleDef = routes.find(
+            (route) => toLowerCamel(route.name) === currentModule
+        );
 
-        while (!menuRoute && index > 0) {
-            if (route.matched[index]?.meta?.menu) menuRoute = route.matched[index];
-            index--;
-        }
-        addChildren('', menuRoute, items.value);
+        if (!moduleDef) return;
+        if (!moduleDef?.menus) moduleDef = betaGetRoutes();
+        addChildren(currentModule, moduleDef, items.value);
     }
 }
 
+function betaGetRoutes() {
+    let menuRoute;
+    let index = route.matched.length - 1;
+    while (!menuRoute && index > 0) {
+        if (route.matched[index]?.meta?.menu) menuRoute = route.matched[index];
+        index--;
+    }
+    return menuRoute;
+}
+
 async function togglePinned(item, event) {
     if (event.defaultPrevented) return;
     event.preventDefault();
diff --git a/src/components/common/VnCard.vue b/src/components/common/VnCard.vue
index 16a077a79f1..0d80f43ce94 100644
--- a/src/components/common/VnCard.vue
+++ b/src/components/common/VnCard.vue
@@ -4,7 +4,10 @@ import { useRoute, useRouter, onBeforeRouteUpdate } from 'vue-router';
 import { useArrayData } from 'src/composables/useArrayData';
 import { useStateStore } from 'stores/useStateStore';
 import useCardSize from 'src/composables/useCardSize';
+import VnSubToolbar from '../ui/VnSubToolbar.vue';
+import VnSearchbar from 'components/ui/VnSearchbar.vue';
 import LeftMenu from 'components/LeftMenu.vue';
+import RightMenu from 'components/common/RightMenu.vue';
 const props = defineProps({
     dataKey: { type: String, required: true },
     baseUrl: { type: String, default: undefined },
@@ -26,7 +29,10 @@ const url = computed(() => {
     }
     return props.customUrl;
 });
-
+const searchRightDataKey = computed(() => {
+    if (!props.searchDataKey) return route.name;
+    return props.searchDataKey;
+});
 const arrayData = useArrayData(props.dataKey, {
     url: url.value,
     filter: props.filter,
@@ -53,13 +59,32 @@ if (props.baseUrl) {
 }
 </script>
 <template>
-    <Teleport to="#left-panel" v-if="stateStore.isHeaderMounted()">
-        <component :is="descriptor" />
-        <QSeparator />
-        <LeftMenu source="card" />
-    </Teleport>
-    <VnSubToolbar />
-    <div :class="[useCardSize(), $attrs.class]">
-        <RouterView :key="route.path" />
-    </div>
+    <QDrawer
+        v-model="stateStore.leftDrawer"
+        show-if-above
+        :width="256"
+        v-if="stateStore.isHeaderMounted()"
+    >
+        <QScrollArea class="fit">
+            <component :is="descriptor" />
+            <QSeparator />
+            <LeftMenu source="card" />
+        </QScrollArea>
+    </QDrawer>
+    <slot name="searchbar" v-if="props.searchDataKey">
+        <VnSearchbar :data-key="props.searchDataKey" v-bind="props.searchbarProps" />
+    </slot>
+    <RightMenu>
+        <template #right-panel v-if="props.filterPanel">
+            <component :is="props.filterPanel" :data-key="searchRightDataKey" />
+        </template>
+    </RightMenu>
+    <QPageContainer>
+        <QPage>
+            <VnSubToolbar />
+            <div :class="[useCardSize(), $attrs.class]">
+                <RouterView :key="route.path" />
+            </div>
+        </QPage>
+    </QPageContainer>
 </template>
diff --git a/src/components/common/VnCardBeta.vue b/src/components/common/VnCardBeta.vue
new file mode 100644
index 00000000000..16a077a79f1
--- /dev/null
+++ b/src/components/common/VnCardBeta.vue
@@ -0,0 +1,65 @@
+<script setup>
+import { onBeforeMount, computed } from 'vue';
+import { useRoute, useRouter, onBeforeRouteUpdate } from 'vue-router';
+import { useArrayData } from 'src/composables/useArrayData';
+import { useStateStore } from 'stores/useStateStore';
+import useCardSize from 'src/composables/useCardSize';
+import LeftMenu from 'components/LeftMenu.vue';
+const props = defineProps({
+    dataKey: { type: String, required: true },
+    baseUrl: { type: String, default: undefined },
+    customUrl: { type: String, default: undefined },
+    filter: { type: Object, default: () => {} },
+    descriptor: { type: Object, required: true },
+    filterPanel: { type: Object, default: undefined },
+    searchDataKey: { type: String, default: undefined },
+    searchbarProps: { type: Object, default: undefined },
+    redirectOnError: { type: Boolean, default: false },
+});
+
+const stateStore = useStateStore();
+const route = useRoute();
+const router = useRouter();
+const url = computed(() => {
+    if (props.baseUrl) {
+        return `${props.baseUrl}/${route.params.id}`;
+    }
+    return props.customUrl;
+});
+
+const arrayData = useArrayData(props.dataKey, {
+    url: url.value,
+    filter: props.filter,
+});
+
+onBeforeMount(async () => {
+    try {
+        if (!props.baseUrl) arrayData.store.filter.where = { id: route.params.id };
+        await arrayData.fetch({ append: false, updateRouter: false });
+    } catch {
+        const { matched: matches } = router.currentRoute.value;
+        const { path } = matches.at(-1);
+        router.push({ path: path.replace(/:id.*/, '') });
+    }
+});
+
+if (props.baseUrl) {
+    onBeforeRouteUpdate(async (to, from) => {
+        if (to.params.id !== from.params.id) {
+            arrayData.store.url = `${props.baseUrl}/${to.params.id}`;
+            await arrayData.fetch({ append: false, updateRouter: false });
+        }
+    });
+}
+</script>
+<template>
+    <Teleport to="#left-panel" v-if="stateStore.isHeaderMounted()">
+        <component :is="descriptor" />
+        <QSeparator />
+        <LeftMenu source="card" />
+    </Teleport>
+    <VnSubToolbar />
+    <div :class="[useCardSize(), $attrs.class]">
+        <RouterView :key="route.path" />
+    </div>
+</template>
diff --git a/src/pages/Account/Alias/Card/AliasCard.vue b/src/pages/Account/Alias/Card/AliasCard.vue
index 65951b3bf61..3a814edc051 100644
--- a/src/pages/Account/Alias/Card/AliasCard.vue
+++ b/src/pages/Account/Alias/Card/AliasCard.vue
@@ -1,12 +1,12 @@
 <script setup>
 import { useI18n } from 'vue-i18n';
-import VnCard from 'components/common/VnCard.vue';
+import VnCardBeta from 'components/common/VnCardBeta.vue';
 import AliasDescriptor from './AliasDescriptor.vue';
 const { t } = useI18n();
 </script>
 
 <template>
-    <VnCard
+    <VnCardBeta
         data-key="Alias"
         base-url="MailAliases"
         :descriptor="AliasDescriptor"
diff --git a/src/pages/Account/Card/AccountCard.vue b/src/pages/Account/Card/AccountCard.vue
index ba9040852cf..35ff7e73229 100644
--- a/src/pages/Account/Card/AccountCard.vue
+++ b/src/pages/Account/Card/AccountCard.vue
@@ -1,8 +1,8 @@
 <script setup>
-import VnCard from 'components/common/VnCard.vue';
+import VnCardBeta from 'components/common/VnCardBeta.vue';
 import AccountDescriptor from './AccountDescriptor.vue';
 </script>
 
 <template>
-    <VnCard data-key="AccountId" :descriptor="AccountDescriptor" />
+    <VnCardBeta data-key="AccountId" :descriptor="AccountDescriptor" />
 </template>
diff --git a/src/pages/Account/Role/Card/RoleCard.vue b/src/pages/Account/Role/Card/RoleCard.vue
index da6ac61d87a..7664deca8da 100644
--- a/src/pages/Account/Role/Card/RoleCard.vue
+++ b/src/pages/Account/Role/Card/RoleCard.vue
@@ -1,7 +1,7 @@
 <script setup>
-import VnCard from 'components/common/VnCard.vue';
+import VnCardBeta from 'components/common/VnCardBeta.vue';
 import RoleDescriptor from './RoleDescriptor.vue';
 </script>
 <template>
-    <VnCard data-key="Role" :descriptor="RoleDescriptor" />
+    <VnCardBeta data-key="Role" :descriptor="RoleDescriptor" />
 </template>

From 14ca6d73f1a992bdbfe7134e6fac5f73cdfa9fe2 Mon Sep 17 00:00:00 2001
From: Jtubau <jtubau@verdnatura.es>
Date: Tue, 17 Dec 2024 07:36:16 +0100
Subject: [PATCH 15/34] feat: refs #7074 tests for fns setData(), parseDms()
 and showFormDialog()

---
 .../components/common/VnDmsList.spec.js       | 93 +++++++++++++++++++
 1 file changed, 93 insertions(+)
 create mode 100644 test/vitest/__tests__/components/common/VnDmsList.spec.js

diff --git a/test/vitest/__tests__/components/common/VnDmsList.spec.js b/test/vitest/__tests__/components/common/VnDmsList.spec.js
new file mode 100644
index 00000000000..49228ddf8fc
--- /dev/null
+++ b/test/vitest/__tests__/components/common/VnDmsList.spec.js
@@ -0,0 +1,93 @@
+import { createWrapper, axios } from 'app/test/vitest/helper';
+import VnDmsList from 'src/components/common/VnDmsList.vue';
+import { vi, afterEach, beforeAll, describe, expect, it } from 'vitest';
+
+describe('VnDmsList', () => {
+    let vm;
+    
+    beforeAll(() => {
+        vi.spyOn(axios, 'get').mockResolvedValue({ data: [] });
+        vm = createWrapper(VnDmsList, {
+            props: {
+                model: 'WorkerDms/1110/filter',
+                defaultDmsCode: 'hhrrData',
+                filter: 'wd.workerFk',
+                updateModel: 'Workers',
+                deleteModel: 'WorkerDms',
+                downloadModel: 'WorkerDms' 
+            }
+        }).vm;
+    });
+
+    afterEach(() => {
+        vi.clearAllMocks();
+    });
+
+    describe('setData()', () => {
+        const data = [
+            { 
+                userFk: 1, 
+                name: 'Jessica',
+                lastName: 'Jones',
+                file: '4.jpg',
+                created: '2021-07-28 21:00:00'
+            },
+            { 
+                userFk: 2, 
+                name: 'Bruce',
+                lastName: 'Banner',
+                created: '2022-07-28 21:00:00',
+                dms: {
+                    userFk: 2, 
+                    name: 'Bruce',
+                    lastName: 'BannerDMS',
+                    created: '2022-07-28 21:00:00',
+                    file: '4.jpg',
+                } 
+            },
+            {
+                userFk: 3,
+                name: 'Natasha',
+                lastName: 'Romanoff',
+                file: '4.jpg',
+                created: '2021-10-28 21:00:00'
+            }  
+        ]
+
+        it('Should replace objects that contain the "dms" property with the value of the same and sort by creation date', () => {
+            vm.setData(data);
+            expect([vm.rows][0][0].lastName).toEqual('BannerDMS');
+            expect([vm.rows][0][1].lastName).toEqual('Romanoff');
+
+        });
+    });
+
+    describe('parseDms()', () => {
+        const dms = { 
+            userFk: 1, 
+            name: 'DMS 1' 
+        };
+
+        const resultDms = { ...dms, userId:1};
+        
+        it('Should add properties that end with "Fk" by changing the suffix to "Id"', () => {
+            const parsedDms = vm.parseDms(dms);
+            expect(parsedDms).toEqual(resultDms);
+        });
+    });
+
+    describe('showFormDialog()', () => {
+        const dms = { 
+            userFk: 1, 
+            name: 'DMS 1' 
+        };
+
+        const resultDms = { ...dms, userId:1};
+        
+        it('should call fn parseDms() and set show true if dms is defined', () => {
+            vm.showFormDialog(dms);
+            expect(vm.formDialog.show).toEqual(true);
+            expect(vm.formDialog.dms).toEqual(resultDms);
+        });
+    });
+});	
\ No newline at end of file

From 586d5eff3e5a6e34aa9fac9de09958ebe6888bed Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Tue, 17 Dec 2024 09:05:22 +0100
Subject: [PATCH 16/34] chore: refs #8197 unnecessary file

---
 src/utils/getUserParams.js | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 src/utils/getUserParams.js

diff --git a/src/utils/getUserParams.js b/src/utils/getUserParams.js
deleted file mode 100644
index e69de29bb2d..00000000000

From 8ab10dda1b8e6461ed5392baa4d6ceb9bf28f286 Mon Sep 17 00:00:00 2001
From: Jtubau <jtubau@verdnatura.es>
Date: Wed, 18 Dec 2024 08:07:44 +0100
Subject: [PATCH 17/34] refactor: refs #7074 move dms constant to global scope

---
 .../__tests__/components/common/VnDmsList.spec.js  | 14 ++++----------
 1 file changed, 4 insertions(+), 10 deletions(-)

diff --git a/test/vitest/__tests__/components/common/VnDmsList.spec.js b/test/vitest/__tests__/components/common/VnDmsList.spec.js
index 49228ddf8fc..9649943a237 100644
--- a/test/vitest/__tests__/components/common/VnDmsList.spec.js
+++ b/test/vitest/__tests__/components/common/VnDmsList.spec.js
@@ -4,6 +4,10 @@ import { vi, afterEach, beforeAll, describe, expect, it } from 'vitest';
 
 describe('VnDmsList', () => {
     let vm;
+    const dms = { 
+        userFk: 1, 
+        name: 'DMS 1' 
+    };
     
     beforeAll(() => {
         vi.spyOn(axios, 'get').mockResolvedValue({ data: [] });
@@ -63,11 +67,6 @@ describe('VnDmsList', () => {
     });
 
     describe('parseDms()', () => {
-        const dms = { 
-            userFk: 1, 
-            name: 'DMS 1' 
-        };
-
         const resultDms = { ...dms, userId:1};
         
         it('Should add properties that end with "Fk" by changing the suffix to "Id"', () => {
@@ -77,11 +76,6 @@ describe('VnDmsList', () => {
     });
 
     describe('showFormDialog()', () => {
-        const dms = { 
-            userFk: 1, 
-            name: 'DMS 1' 
-        };
-
         const resultDms = { ...dms, userId:1};
         
         it('should call fn parseDms() and set show true if dms is defined', () => {

From 9783be1ff00dd2709f9e3633956e85797be49616 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Wed, 18 Dec 2024 10:45:02 +0100
Subject: [PATCH 18/34] fix: fixed recipient param

---
 src/pages/Worker/Card/WorkerTimeControl.vue | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/src/pages/Worker/Card/WorkerTimeControl.vue b/src/pages/Worker/Card/WorkerTimeControl.vue
index 491e5e1802b..96e7cd44146 100644
--- a/src/pages/Worker/Card/WorkerTimeControl.vue
+++ b/src/pages/Worker/Card/WorkerTimeControl.vue
@@ -100,15 +100,23 @@ const formattedWeekTotalHours = computed(() =>
     secondsToHoursMinutes(weekTotalHours.value)
 );
 
+// const onInputChange = async (date) => {
+//     if (!date) return;
+
+//     const { timestamp, outside } = date.scope;
+//     const { year, month, day } = timestamp;
+//     const _date = new Date(year, month - 1, day);
+//     setDate(_date);
+
+//     if (outside) getMailStates(_date);
+// };
+
 const onInputChange = async (date) => {
     if (!date) return;
 
-    const { timestamp, outside } = date.scope;
-    const { year, month, day } = timestamp;
+    const { year, month, day } = date.scope.timestamp;
     const _date = new Date(year, month - 1, day);
     setDate(_date);
-
-    if (outside) getMailStates(_date);
 };
 
 const setDate = async (date) => {
@@ -381,12 +389,13 @@ const isUnsatisfied = async (reason) => {
 
 const resendEmail = async () => {
     const params = {
-        recipient: worker.value?.user?.email,
+        recipient: worker.value[0]?.user?.emailUser?.email,
         week: selectedWeekNumber.value,
         year: selectedDate.value.getFullYear(),
         workerId: Number(route.params.id),
         state: 'SENDED',
     };
+    console.log('params: ', params);
     await axios.post('WorkerTimeControls/weekly-hour-record-email', params);
     await getMailStates(selectedDate.value);
     notify(t('Email sended'), 'positive');

From 06d3a025fcd355981a41887a4a4382ca584941af Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Wed, 18 Dec 2024 10:45:42 +0100
Subject: [PATCH 19/34] fix: deleted code

---
 src/pages/Worker/Card/WorkerTimeControl.vue | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/pages/Worker/Card/WorkerTimeControl.vue b/src/pages/Worker/Card/WorkerTimeControl.vue
index 96e7cd44146..f4c81102f7e 100644
--- a/src/pages/Worker/Card/WorkerTimeControl.vue
+++ b/src/pages/Worker/Card/WorkerTimeControl.vue
@@ -395,7 +395,6 @@ const resendEmail = async () => {
         workerId: Number(route.params.id),
         state: 'SENDED',
     };
-    console.log('params: ', params);
     await axios.post('WorkerTimeControls/weekly-hour-record-email', params);
     await getMailStates(selectedDate.value);
     notify(t('Email sended'), 'positive');

From 76788fe8892e2744698283ce749787f4549d46a0 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Wed, 18 Dec 2024 10:46:50 +0100
Subject: [PATCH 20/34] fix: changes

---
 src/pages/Worker/Card/WorkerTimeControl.vue | 16 ++++------------
 1 file changed, 4 insertions(+), 12 deletions(-)

diff --git a/src/pages/Worker/Card/WorkerTimeControl.vue b/src/pages/Worker/Card/WorkerTimeControl.vue
index f4c81102f7e..c480d5bd89c 100644
--- a/src/pages/Worker/Card/WorkerTimeControl.vue
+++ b/src/pages/Worker/Card/WorkerTimeControl.vue
@@ -100,23 +100,15 @@ const formattedWeekTotalHours = computed(() =>
     secondsToHoursMinutes(weekTotalHours.value)
 );
 
-// const onInputChange = async (date) => {
-//     if (!date) return;
-
-//     const { timestamp, outside } = date.scope;
-//     const { year, month, day } = timestamp;
-//     const _date = new Date(year, month - 1, day);
-//     setDate(_date);
-
-//     if (outside) getMailStates(_date);
-// };
-
 const onInputChange = async (date) => {
     if (!date) return;
 
-    const { year, month, day } = date.scope.timestamp;
+    const { timestamp, outside } = date.scope;
+    const { year, month, day } = timestamp;
     const _date = new Date(year, month - 1, day);
     setDate(_date);
+
+    if (outside) getMailStates(_date);
 };
 
 const setDate = async (date) => {

From b54f39f1a066dde5a59a8b3e4911e1636c2daebe Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Wed, 18 Dec 2024 13:04:00 +0100
Subject: [PATCH 21/34] feat: refs #8197 default sectionName

---
 src/components/common/VnCardMain.vue | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/components/common/VnCardMain.vue b/src/components/common/VnCardMain.vue
index 5b8e6b5e87a..e6afea4b6a3 100644
--- a/src/components/common/VnCardMain.vue
+++ b/src/components/common/VnCardMain.vue
@@ -2,7 +2,7 @@
 import RightMenu from './RightMenu.vue';
 import VnSearchbar from 'components/ui/VnSearchbar.vue';
 import VnTableFilter from '../VnTable/VnTableFilter.vue';
-import { onBeforeMount } from 'vue';
+import { onBeforeMount, computed } from 'vue';
 import { useArrayData } from 'src/composables/useArrayData';
 
 const $props = defineProps({
@@ -36,6 +36,8 @@ const $props = defineProps({
     },
 });
 
+const sectionValue = computed(() => $props.section ?? $props.dataKey);
+
 onBeforeMount(() => {
     if ($props.dataKey)
         useArrayData($props.dataKey, {
@@ -67,6 +69,6 @@ onBeforeMount(() => {
         </template>
     </RightMenu>
 
-    <slot name="body" v-if="section == $route.name" />
+    <slot name="body" v-if="sectionValue == $route.name" />
     <RouterView v-else />
 </template>

From 76b73cc6167e27f88d004625bcccb3f0c159dfd1 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Wed, 18 Dec 2024 13:04:10 +0100
Subject: [PATCH 22/34] perf: refs #8197 perf

---
 src/components/common/VnSectionMain.vue | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/components/common/VnSectionMain.vue b/src/components/common/VnSectionMain.vue
index 4e800fa8aaa..505b3a8b56e 100644
--- a/src/components/common/VnSectionMain.vue
+++ b/src/components/common/VnSectionMain.vue
@@ -15,7 +15,6 @@ onMounted(
     () => (stateStore.leftDrawer = useQuasar().screen.gt.xs ? $props.leftDrawer : false)
 );
 
-const targetId = 'left-panel';
 const teleportRef = ref({});
 const hasContent = ref();
 let observer;
@@ -37,7 +36,7 @@ onMounted(() => {
 <template>
     <QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
         <QScrollArea class="fit text-grey-8">
-            <div :id="targetId" ref="teleportRef"></div>
+            <div id="left-panel" ref="teleportRef"></div>
             <LeftMenu v-if="!hasContent" />
         </QScrollArea>
     </QDrawer>

From 32fd07dd14e401d6fed3608cc274dcf92094c342 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Wed, 18 Dec 2024 13:04:31 +0100
Subject: [PATCH 23/34] feat: refs #8197 default sectionName

---
 src/pages/Account/AccountAcls.vue       | 1 -
 src/pages/Account/AccountAliasList.vue  | 1 -
 src/pages/Account/AccountList.vue       | 1 -
 src/pages/Account/Role/AccountRoles.vue | 1 -
 4 files changed, 4 deletions(-)

diff --git a/src/pages/Account/AccountAcls.vue b/src/pages/Account/AccountAcls.vue
index 73771a34107..b457bb7f034 100644
--- a/src/pages/Account/AccountAcls.vue
+++ b/src/pages/Account/AccountAcls.vue
@@ -139,7 +139,6 @@ const deleteAcl = async ({ id }) => {
         @on-fetch="(data) => (roles = data)"
     />
     <VnCardMain
-        :section="dataKey"
         :data-key="dataKey"
         :columns="columns"
         prefix="acls"
diff --git a/src/pages/Account/AccountAliasList.vue b/src/pages/Account/AccountAliasList.vue
index 721a009e512..9631c96399d 100644
--- a/src/pages/Account/AccountAliasList.vue
+++ b/src/pages/Account/AccountAliasList.vue
@@ -44,7 +44,6 @@ const exprBuilder = (param, value) => {
 
 <template>
     <VnCardMain
-        :section="dataKey"
         :data-key="dataKey"
         :columns="columns"
         prefix="mailAlias"
diff --git a/src/pages/Account/AccountList.vue b/src/pages/Account/AccountList.vue
index 34a653e61db..5296cc1d668 100644
--- a/src/pages/Account/AccountList.vue
+++ b/src/pages/Account/AccountList.vue
@@ -104,7 +104,6 @@ function exprBuilder(param, value) {
 
 <template>
     <VnCardMain
-        :section="dataKey"
         :data-key="dataKey"
         :columns="columns"
         prefix="account"
diff --git a/src/pages/Account/Role/AccountRoles.vue b/src/pages/Account/Role/AccountRoles.vue
index 8cc392f1bb8..4e67a691bef 100644
--- a/src/pages/Account/Role/AccountRoles.vue
+++ b/src/pages/Account/Role/AccountRoles.vue
@@ -86,7 +86,6 @@ const exprBuilder = (param, value) => {
 
 <template>
     <VnCardMain
-        :section="dataKey"
         :data-key="dataKey"
         :columns="columns"
         prefix="role"

From 8c3c318099bc7d76696d5e3d43c758b65011b396 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Wed, 18 Dec 2024 13:16:45 +0100
Subject: [PATCH 24/34] test(VnTable): refs #8197 mock  useFilterParams

---
 test/vitest/__tests__/components/VnTable.spec.js | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/test/vitest/__tests__/components/VnTable.spec.js b/test/vitest/__tests__/components/VnTable.spec.js
index 162df727dd6..74ba0698765 100644
--- a/test/vitest/__tests__/components/VnTable.spec.js
+++ b/test/vitest/__tests__/components/VnTable.spec.js
@@ -1,4 +1,4 @@
-import { describe, expect, it, beforeAll, beforeEach } from 'vitest';
+import { describe, expect, it, beforeAll, beforeEach, vi } from 'vitest';
 import { createWrapper } from 'app/test/vitest/helper';
 import VnTable from 'src/components/VnTable/VnTable.vue';
 
@@ -13,6 +13,15 @@ describe('VnTable', () => {
             },
         });
         vm = wrapper.vm;
+
+        vi.mock('src/composables/useFilterParams', () => {
+            return {
+                useFilterParams: vi.fn(() => ({
+                    params: {},
+                    orders: {},
+                })),
+            };
+        });
     });
 
     beforeEach(() => (vm.selected = []));

From fccca9ea477c74237adfffcc5566a0c592560e05 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Wed, 18 Dec 2024 13:42:57 +0100
Subject: [PATCH 25/34] refactor: refs #8197 rename VnSectionMain to VnModule
 and VnCardMain to VnSection

---
 src/components/common/VnCardBeta.vue          |   2 +
 .../{VnSectionMain.vue => VnModule.vue}       |   0
 .../common/{VnCardMain.vue => VnSection.vue}  |   0
 src/pages/Account/AccountAcls.vue             |   6 +-
 src/pages/Account/AccountAliasList.vue        |   6 +-
 src/pages/Account/AccountList.vue             |   6 +-
 src/pages/Account/Role/AccountRoles.vue       |   6 +-
 src/pages/Ticket/Card/TicketBoxing.vue        | 136 +++++++++---------
 src/router/modules/account.js                 |   2 +-
 src/router/modules/claim.js                   |   2 +-
 src/router/modules/customer.js                |   2 +-
 src/router/modules/entry.js                   |   2 +-
 src/router/modules/invoiceIn.js               |   2 +-
 src/router/modules/invoiceOut.js              |   2 +-
 src/router/modules/item.js                    |   2 +-
 src/router/modules/monitor.js                 |   2 +-
 src/router/modules/order.js                   |   2 +-
 src/router/modules/route.js                   |   2 +-
 src/router/modules/shelving.js                |   2 +-
 src/router/modules/supplier.js                |   2 +-
 src/router/modules/travel.js                  |   2 +-
 src/router/modules/wagon.js                   |   4 +-
 src/router/modules/worker.js                  |   2 +-
 src/router/modules/zone.js                    |   2 +-
 24 files changed, 98 insertions(+), 98 deletions(-)
 rename src/components/common/{VnSectionMain.vue => VnModule.vue} (100%)
 rename src/components/common/{VnCardMain.vue => VnSection.vue} (100%)

diff --git a/src/components/common/VnCardBeta.vue b/src/components/common/VnCardBeta.vue
index 16a077a79f1..349956be9f7 100644
--- a/src/components/common/VnCardBeta.vue
+++ b/src/components/common/VnCardBeta.vue
@@ -5,6 +5,8 @@ import { useArrayData } from 'src/composables/useArrayData';
 import { useStateStore } from 'stores/useStateStore';
 import useCardSize from 'src/composables/useCardSize';
 import LeftMenu from 'components/LeftMenu.vue';
+import VnSubToolbar from '../ui/VnSubToolbar.vue';
+
 const props = defineProps({
     dataKey: { type: String, required: true },
     baseUrl: { type: String, default: undefined },
diff --git a/src/components/common/VnSectionMain.vue b/src/components/common/VnModule.vue
similarity index 100%
rename from src/components/common/VnSectionMain.vue
rename to src/components/common/VnModule.vue
diff --git a/src/components/common/VnCardMain.vue b/src/components/common/VnSection.vue
similarity index 100%
rename from src/components/common/VnCardMain.vue
rename to src/components/common/VnSection.vue
diff --git a/src/pages/Account/AccountAcls.vue b/src/pages/Account/AccountAcls.vue
index b457bb7f034..b4eeb0648d4 100644
--- a/src/pages/Account/AccountAcls.vue
+++ b/src/pages/Account/AccountAcls.vue
@@ -9,7 +9,7 @@ import VnTable from 'components/VnTable/VnTable.vue';
 import VnConfirm from 'components/ui/VnConfirm.vue';
 import FetchData from 'src/components/FetchData.vue';
 import { useValidator } from 'src/composables/useValidator';
-import VnCardMain from 'src/components/common/VnCardMain.vue';
+import VnSection from 'src/components/common/VnSection.vue';
 
 defineProps({
     id: {
@@ -138,7 +138,7 @@ const deleteAcl = async ({ id }) => {
         auto-load
         @on-fetch="(data) => (roles = data)"
     />
-    <VnCardMain
+    <VnSection
         :data-key="dataKey"
         :columns="columns"
         prefix="acls"
@@ -166,7 +166,7 @@ const deleteAcl = async ({ id }) => {
                 :use-model="true"
             />
         </template>
-    </VnCardMain>
+    </VnSection>
 </template>
 
 <i18n>
diff --git a/src/pages/Account/AccountAliasList.vue b/src/pages/Account/AccountAliasList.vue
index 9631c96399d..f6016fb6c18 100644
--- a/src/pages/Account/AccountAliasList.vue
+++ b/src/pages/Account/AccountAliasList.vue
@@ -2,7 +2,7 @@
 import { useI18n } from 'vue-i18n';
 import { ref, computed } from 'vue';
 import VnTable from 'components/VnTable/VnTable.vue';
-import VnCardMain from 'src/components/common/VnCardMain.vue';
+import VnSection from 'src/components/common/VnSection.vue';
 
 const tableRef = ref();
 const { t } = useI18n();
@@ -43,7 +43,7 @@ const exprBuilder = (param, value) => {
 </script>
 
 <template>
-    <VnCardMain
+    <VnSection
         :data-key="dataKey"
         :columns="columns"
         prefix="mailAlias"
@@ -68,7 +68,7 @@ const exprBuilder = (param, value) => {
                 :right-search="false"
             />
         </template>
-    </VnCardMain>
+    </VnSection>
 </template>
 <i18n>
     es:
diff --git a/src/pages/Account/AccountList.vue b/src/pages/Account/AccountList.vue
index 5296cc1d668..997e3104142 100644
--- a/src/pages/Account/AccountList.vue
+++ b/src/pages/Account/AccountList.vue
@@ -4,7 +4,7 @@ import { computed } from 'vue';
 import VnTable from 'components/VnTable/VnTable.vue';
 import AccountSummary from './Card/AccountSummary.vue';
 import { useSummaryDialog } from 'src/composables/useSummaryDialog';
-import VnCardMain from 'src/components/common/VnCardMain.vue';
+import VnSection from 'src/components/common/VnSection.vue';
 
 const { t } = useI18n();
 const { viewSummary } = useSummaryDialog();
@@ -103,7 +103,7 @@ function exprBuilder(param, value) {
 </script>
 
 <template>
-    <VnCardMain
+    <VnSection
         :data-key="dataKey"
         :columns="columns"
         prefix="account"
@@ -124,7 +124,7 @@ function exprBuilder(param, value) {
                 :right-search="false"
             />
         </template>
-    </VnCardMain>
+    </VnSection>
 </template>
 
 <i18n>
diff --git a/src/pages/Account/Role/AccountRoles.vue b/src/pages/Account/Role/AccountRoles.vue
index 4e67a691bef..3c3d6b243fc 100644
--- a/src/pages/Account/Role/AccountRoles.vue
+++ b/src/pages/Account/Role/AccountRoles.vue
@@ -5,7 +5,7 @@ import VnTable from 'components/VnTable/VnTable.vue';
 import { useRoute } from 'vue-router';
 import { useSummaryDialog } from 'src/composables/useSummaryDialog';
 import RoleSummary from './Card/RoleSummary.vue';
-import VnCardMain from 'src/components/common/VnCardMain.vue';
+import VnSection from 'src/components/common/VnSection.vue';
 
 const route = useRoute();
 const { t } = useI18n();
@@ -85,7 +85,7 @@ const exprBuilder = (param, value) => {
 </script>
 
 <template>
-    <VnCardMain
+    <VnSection
         :data-key="dataKey"
         :columns="columns"
         prefix="role"
@@ -110,7 +110,7 @@ const exprBuilder = (param, value) => {
                 :right-search="false"
             />
         </template>
-    </VnCardMain>
+    </VnSection>
 </template>
 
 <i18n>
diff --git a/src/pages/Ticket/Card/TicketBoxing.vue b/src/pages/Ticket/Card/TicketBoxing.vue
index 1a728739645..5675fe1b30f 100644
--- a/src/pages/Ticket/Card/TicketBoxing.vue
+++ b/src/pages/Ticket/Card/TicketBoxing.vue
@@ -1,15 +1,18 @@
 <script setup>
 import axios from 'axios';
 import { date, useQuasar } from 'quasar';
-import { computed, onMounted, reactive, ref } from 'vue';
+import { computed, onMounted, onUnmounted, reactive, ref } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useRouter } from 'vue-router';
+import { useStateStore } from 'stores/useStateStore';
 
 const router = useRouter();
+const stateStore = useStateStore();
 const { t } = useI18n();
 const quasar = useQuasar();
 
 onMounted(async () => {
+    stateStore.rightDrawer = true;
     await fetch();
 });
 
@@ -84,74 +87,69 @@ async function getVideoList(expeditionId, timed) {
 </script>
 
 <template>
-    <QDrawer show-if-above side="right">
-        <QScrollArea class="fit">
-            <QList bordered separator style="max-width: 318px">
-                <QItem v-if="lastExpedition && videoList.length">
-                    <QItemSection>
-                        <QItemLabel class="text-h6">
-                            {{ t('ticket.boxing.selectTime') }} ({{ time.min }}-{{
-                                time.max
-                            }})
-                        </QItemLabel>
-                        <QRange
-                            v-model="time"
-                            @change="getVideoList(lastExpedition, time)"
-                            :min="0"
-                            :max="24"
-                            :step="1"
-                            :left-label-value="time.min + ':00'"
-                            :right-label-value="time.max + ':00'"
-                            label
-                            markers
-                            snap
-                            color="primary"
-                        />
-                    </QItemSection>
-                </QItem>
-                <QItem v-if="lastExpedition && videoList.length">
-                    <QItemSection>
-                        <QSelect
-                            color="primary"
-                            v-model="slide"
-                            :options="videoList"
-                            :label="t('ticket.boxing.selectVideo')"
-                            emit-value
-                            map-options
-                        >
-                            <template #prepend>
-                                <QIcon name="schedule" />
-                            </template>
-                        </QSelect>
-                    </QItemSection>
-                </QItem>
-                <QItem
-                    v-for="expedition in expeditions"
-                    :key="expedition.id"
-                    @click="getVideoList(expedition.id)"
-                    clickable
-                    v-ripple
-                >
-                    <QItemSection>
-                        <QItemLabel class="text-h6">#{{ expedition.id }}</QItemLabel>
-                    </QItemSection>
-                    <QItemSection>
-                        <QItemLabel caption>{{ t('globals.created') }}</QItemLabel>
-                        <QItemLabel>
-                            {{
-                                date.formatDate(expedition.created, 'YYYY-MM-DD HH:mm:ss')
-                            }}
-                        </QItemLabel>
-                        <QItemLabel caption>{{ t('globals.item') }}</QItemLabel>
-                        <QItemLabel>{{ expedition.packagingItemFk }}</QItemLabel>
-                        <QItemLabel caption>{{ t('ticket.boxing.worker') }}</QItemLabel>
-                        <QItemLabel>{{ expedition.userName }}</QItemLabel>
-                    </QItemSection>
-                </QItem>
-            </QList>
-        </QScrollArea>
-    </QDrawer>
-
+    <Teleport to="#right-panel" v-if="stateStore.isHeaderMounted()">
+        <QList bordered separator style="max-width: 318px">
+            <QItem v-if="lastExpedition && videoList.length">
+                <QItemSection>
+                    <QItemLabel class="text-h6">
+                        {{ t('ticket.boxing.selectTime') }} ({{ time.min }}-{{
+                            time.max
+                        }})
+                    </QItemLabel>
+                    <QRange
+                        v-model="time"
+                        @change="getVideoList(lastExpedition, time)"
+                        :min="0"
+                        :max="24"
+                        :step="1"
+                        :left-label-value="time.min + ':00'"
+                        :right-label-value="time.max + ':00'"
+                        label
+                        markers
+                        snap
+                        color="primary"
+                    />
+                </QItemSection>
+            </QItem>
+            <QItem v-if="lastExpedition && videoList.length">
+                <QItemSection>
+                    <QSelect
+                        color="primary"
+                        v-model="slide"
+                        :options="videoList"
+                        :label="t('ticket.boxing.selectVideo')"
+                        emit-value
+                        map-options
+                    >
+                        <template #prepend>
+                            <QIcon name="schedule" />
+                        </template>
+                    </QSelect>
+                </QItemSection>
+            </QItem>
+            <QItem
+                v-for="expedition in expeditions"
+                :key="expedition.id"
+                @click="getVideoList(expedition.id)"
+                clickable
+                v-ripple
+            >
+                <QItemSection>
+                    <QItemLabel class="text-h6">#{{ expedition.id }}</QItemLabel>
+                </QItemSection>
+                <QItemSection>
+                    <QItemLabel caption>{{ t('globals.created') }}</QItemLabel>
+                    <QItemLabel>
+                        {{ date.formatDate(expedition.created, 'YYYY-MM-DD HH:mm:ss') }}
+                    </QItemLabel>
+                    <QItemLabel caption>{{ t('globals.item') }}</QItemLabel>
+                    <QItemLabel>{{ expedition.packagingItemFk }}</QItemLabel>
+                    <QItemLabel caption>{{ t('ticket.boxing.worker') }}</QItemLabel>
+                    <QItemLabel>{{ expedition.userName }}</QItemLabel>
+                </QItemSection>
+            </QItem>
+        </QList>
+    </Teleport>
     <QCard>
         <QCarousel animated v-model="slide" height="max-content">
             <QCarouselSlide
diff --git a/src/router/modules/account.js b/src/router/modules/account.js
index 6f5ca90f3cf..466db953945 100644
--- a/src/router/modules/account.js
+++ b/src/router/modules/account.js
@@ -28,7 +28,7 @@ export default {
         {
             path: '',
             name: 'AccountMain',
-            component: () => import('src/components/common/VnSectionMain.vue'),
+            component: () => import('src/components/common/VnModule.vue'),
             redirect: { name: 'AccountIndexMain' },
             children: [
                 {
diff --git a/src/router/modules/claim.js b/src/router/modules/claim.js
index b58a58e8dad..8b0a7089667 100644
--- a/src/router/modules/claim.js
+++ b/src/router/modules/claim.js
@@ -27,7 +27,7 @@ export default {
         {
             name: 'ClaimMain',
             path: '',
-            component: () => import('src/components/common/VnSectionMain.vue'),
+            component: () => import('src/components/common/VnModule.vue'),
             redirect: { name: 'ClaimList' },
             children: [
                 {
diff --git a/src/router/modules/customer.js b/src/router/modules/customer.js
index 1b707f1a238..9e7f6fe703b 100644
--- a/src/router/modules/customer.js
+++ b/src/router/modules/customer.js
@@ -39,7 +39,7 @@ export default {
         {
             path: '',
             name: 'CustomerMain',
-            component: () => import('src/components/common/VnSectionMain.vue'),
+            component: () => import('src/components/common/VnModule.vue'),
             redirect: { name: 'CustomerList' },
             children: [
                 {
diff --git a/src/router/modules/entry.js b/src/router/modules/entry.js
index 3add239df63..26ce773c5d1 100644
--- a/src/router/modules/entry.js
+++ b/src/router/modules/entry.js
@@ -25,7 +25,7 @@ export default {
         {
             path: '',
             name: 'EntryMain',
-            component: () => import('src/components/common/VnSectionMain.vue'),
+            component: () => import('src/components/common/VnModule.vue'),
             redirect: { name: 'EntryList' },
             children: [
                 {
diff --git a/src/router/modules/invoiceIn.js b/src/router/modules/invoiceIn.js
index 168d64f373a..788b27d37d6 100644
--- a/src/router/modules/invoiceIn.js
+++ b/src/router/modules/invoiceIn.js
@@ -25,7 +25,7 @@ export default {
         {
             path: '',
             name: 'InvoiceInMain',
-            component: () => import('src/components/common/VnSectionMain.vue'),
+            component: () => import('src/components/common/VnModule.vue'),
             redirect: { name: 'InvoiceInList' },
             children: [
                 {
diff --git a/src/router/modules/invoiceOut.js b/src/router/modules/invoiceOut.js
index 5e83b0859a3..53d27d0e8ee 100644
--- a/src/router/modules/invoiceOut.js
+++ b/src/router/modules/invoiceOut.js
@@ -18,7 +18,7 @@ export default {
         {
             path: '',
             name: 'InvoiceOutMain',
-            component: () => import('src/components/common/VnSectionMain.vue'),
+            component: () => import('src/components/common/VnModule.vue'),
             redirect: { name: 'InvoiceOutList' },
             children: [
                 {
diff --git a/src/router/modules/item.js b/src/router/modules/item.js
index 0f810434c09..e2afd6c7bb7 100644
--- a/src/router/modules/item.js
+++ b/src/router/modules/item.js
@@ -36,7 +36,7 @@ export default {
         {
             path: '',
             name: 'ItemMain',
-            component: () => import('src/components/common/VnSectionMain.vue'),
+            component: () => import('src/components/common/VnModule.vue'),
             redirect: { name: 'ItemList' },
             children: [
                 {
diff --git a/src/router/modules/monitor.js b/src/router/modules/monitor.js
index 2af60c09cd0..89ba4078f99 100644
--- a/src/router/modules/monitor.js
+++ b/src/router/modules/monitor.js
@@ -19,7 +19,7 @@ export default {
         {
             path: '',
             name: 'MonitorMain',
-            component: () => import('src/components/common/VnSectionMain.vue'),
+            component: () => import('src/components/common/VnModule.vue'),
             props: (route) => ({ leftDrawer: route.name === 'MonitorClientsActions' }),
             redirect: { name: 'MonitorTickets' },
             children: [
diff --git a/src/router/modules/order.js b/src/router/modules/order.js
index bfa37fce50c..77af812cf79 100644
--- a/src/router/modules/order.js
+++ b/src/router/modules/order.js
@@ -19,7 +19,7 @@ export default {
         {
             path: '',
             name: 'OrderMain',
-            component: () => import('src/components/common/VnSectionMain.vue'),
+            component: () => import('src/components/common/VnModule.vue'),
             redirect: { name: 'OrderList' },
             children: [
                 {
diff --git a/src/router/modules/route.js b/src/router/modules/route.js
index 9a7b16df3a2..a6c4f30a223 100644
--- a/src/router/modules/route.js
+++ b/src/router/modules/route.js
@@ -25,7 +25,7 @@ export default {
         {
             path: '/route',
             name: 'RouteMain',
-            component: () => import('src/components/common/VnSectionMain.vue'),
+            component: () => import('src/components/common/VnModule.vue'),
             redirect: { name: 'RouteList' },
             children: [
                 {
diff --git a/src/router/modules/shelving.js b/src/router/modules/shelving.js
index b7f50a3b606..dd254db6946 100644
--- a/src/router/modules/shelving.js
+++ b/src/router/modules/shelving.js
@@ -18,7 +18,7 @@ export default {
         {
             path: '',
             name: 'ShelvingMain',
-            component: () => import('src/components/common/VnSectionMain.vue'),
+            component: () => import('src/components/common/VnModule.vue'),
             redirect: { name: 'ShelvingList' },
             children: [
                 {
diff --git a/src/router/modules/supplier.js b/src/router/modules/supplier.js
index c08fb596114..647f4bdd33e 100644
--- a/src/router/modules/supplier.js
+++ b/src/router/modules/supplier.js
@@ -30,7 +30,7 @@ export default {
         {
             path: '',
             name: 'SupplierMain',
-            component: () => import('src/components/common/VnSectionMain.vue'),
+            component: () => import('src/components/common/VnModule.vue'),
             redirect: { name: 'SupplierList' },
             children: [
                 {
diff --git a/src/router/modules/travel.js b/src/router/modules/travel.js
index dff693d2fae..49272be1e0a 100644
--- a/src/router/modules/travel.js
+++ b/src/router/modules/travel.js
@@ -18,7 +18,7 @@ export default {
         {
             path: '',
             name: 'TravelMain',
-            component: () => import('src/components/common/VnSectionMain.vue'),
+            component: () => import('src/components/common/VnModule.vue'),
             redirect: { name: 'TravelList' },
             children: [
                 {
diff --git a/src/router/modules/wagon.js b/src/router/modules/wagon.js
index e25e585eb57..5c7e881c2cb 100644
--- a/src/router/modules/wagon.js
+++ b/src/router/modules/wagon.js
@@ -18,7 +18,7 @@ export default {
         {
             path: '/wagon',
             name: 'WagonMain',
-            component: () => import('src/components/common/VnSectionMain.vue'),
+            component: () => import('src/components/common/VnModule.vue'),
             redirect: { name: 'WagonList' },
             children: [
                 {
@@ -62,7 +62,7 @@ export default {
         {
             path: '/wagon/type',
             name: 'WagonTypeMain',
-            component: () => import('src/components/common/VnSectionMain.vue'),
+            component: () => import('src/components/common/VnModule.vue'),
             redirect: { name: 'WagonTypeList' },
             children: [
                 {
diff --git a/src/router/modules/worker.js b/src/router/modules/worker.js
index 9250197342d..c732664ecbe 100644
--- a/src/router/modules/worker.js
+++ b/src/router/modules/worker.js
@@ -35,7 +35,7 @@ export default {
         {
             path: '',
             name: 'WorkerMain',
-            component: () => import('src/components/common/VnSectionMain.vue'),
+            component: () => import('src/components/common/VnModule.vue'),
             redirect: { name: 'WorkerList' },
             children: [
                 {
diff --git a/src/router/modules/zone.js b/src/router/modules/zone.js
index c5ebe762ee9..334ba2b51ad 100644
--- a/src/router/modules/zone.js
+++ b/src/router/modules/zone.js
@@ -30,7 +30,7 @@ export default {
         {
             path: '/zone',
             name: 'ZoneMain',
-            component: () => import('src/components/common/VnSectionMain.vue'),
+            component: () => import('src/components/common/VnModule.vue'),
             redirect: { name: 'ZoneList' },
             children: [
                 {

From 84ac4dd2102a825e0732128d22c1a7b3906744d1 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Wed, 18 Dec 2024 14:21:59 +0100
Subject: [PATCH 26/34] fix: refs #8197 vnPaginate onFetch emit

---
 src/components/ui/VnPaginate.vue | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/components/ui/VnPaginate.vue b/src/components/ui/VnPaginate.vue
index 90089593e05..42f558f89b8 100644
--- a/src/components/ui/VnPaginate.vue
+++ b/src/components/ui/VnPaginate.vue
@@ -106,6 +106,7 @@ const store = arrayData.store;
 
 onMounted(async () => {
     if (props.autoLoad && !store.data?.length) await fetch();
+    else emit('onFetch', store.data);
     mounted.value = true;
 });
 

From a940eb9861b9193d659cf0cc69e43f3b81fba3c0 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Wed, 18 Dec 2024 14:22:19 +0100
Subject: [PATCH 27/34] refactor: refs #8197 adapt Ticket to VnCardMain

---
 src/pages/Ticket/Card/TicketCard.vue |  20 +-
 src/pages/Ticket/TicketList.vue      | 431 ++++++++++++++-------------
 src/router/modules/ticket.js         | 379 +++++++++++------------
 3 files changed, 413 insertions(+), 417 deletions(-)

diff --git a/src/pages/Ticket/Card/TicketCard.vue b/src/pages/Ticket/Card/TicketCard.vue
index 73b6f5543c2..6886a8e577a 100644
--- a/src/pages/Ticket/Card/TicketCard.vue
+++ b/src/pages/Ticket/Card/TicketCard.vue
@@ -1,23 +1,7 @@
 <script setup>
-import { useI18n } from 'vue-i18n';
-
-import VnCard from 'components/common/VnCard.vue';
+import VnCardBeta from 'components/common/VnCardBeta.vue';
 import TicketDescriptor from './TicketDescriptor.vue';
-import TicketFilter from '../TicketFilter.vue';
-
-const { t } = useI18n();
 </script>
 <template>
-    <VnCard
-        data-key="Ticket"
-        base-url="Tickets"
-        :filter-panel="TicketFilter"
-        :descriptor="TicketDescriptor"
-        search-data-key="TicketList"
-        :searchbar-props="{
-            url: 'Tickets/filter',
-            label: t('card.search'),
-            info: t('card.searchInfo'),
-        }"
-    />
+    <VnCardBeta data-key="Ticket" base-url="Tickets" :descriptor="TicketDescriptor" />
 </template>
diff --git a/src/pages/Ticket/TicketList.vue b/src/pages/Ticket/TicketList.vue
index eb03a492721..823f74fc51a 100644
--- a/src/pages/Ticket/TicketList.vue
+++ b/src/pages/Ticket/TicketList.vue
@@ -8,13 +8,11 @@ import { useQuasar } from 'quasar';
 import { toDate, toCurrency, dashIfEmpty } from 'src/filters/index';
 import useNotify from 'src/composables/useNotify';
 import TicketSummary from './Card/TicketSummary.vue';
-import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
 import { useSummaryDialog } from 'src/composables/useSummaryDialog';
 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';
-import RightMenu from 'src/components/common/RightMenu.vue';
 import TicketFilter from './TicketFilter.vue';
 import VnInput from 'src/components/common/VnInput.vue';
 import FetchData from 'src/components/FetchData.vue';
@@ -23,6 +21,7 @@ import ZoneDescriptorProxy from 'src/pages/Zone/Card/ZoneDescriptorProxy.vue';
 import { toTimeFormat } from 'src/filters/date';
 import InvoiceOutDescriptorProxy from 'src/pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue';
 import TicketProblems from 'src/components/TicketProblems.vue';
+import VnSection from 'src/components/common/VnSection.vue';
 
 const route = useRoute();
 const router = useRouter();
@@ -66,6 +65,7 @@ const dialogData = ref();
 const companiesOptions = ref([]);
 const accountingOptions = ref([]);
 const amountToReturn = ref();
+const dataKey = 'TicketList';
 
 const columns = computed(() => [
     {
@@ -452,223 +452,228 @@ function setReference(data) {
         @on-fetch="(data) => (accountingOptions = data)"
         auto-load
     />
-    <VnSearchbar
-        data-key="TicketList"
-        :label="t('Search ticket')"
-        :info="t('You can search by ticket id or alias')"
-        data-cy="ticketListSearchBar"
-    />
-    <RightMenu>
-        <template #right-panel>
+    <VnSection
+        :data-key="dataKey"
+        :columns="columns"
+        prefix="card"
+        :array-data-props="{
+            url: 'Tickets/filter',
+            order: ['shippedDate DESC', 'shippedHour ASC', 'zoneLanding ASC', 'id'],
+            exprBuilder,
+        }"
+    >
+        <template #rightMenu>
             <TicketFilter data-key="TicketList" />
         </template>
-    </RightMenu>
-    <VnTable
-        ref="tableRef"
-        data-key="TicketList"
-        url="Tickets/filter"
-        :create="{
-            urlCreate: 'Tickets/new',
-            title: t('ticketList.createTicket'),
-            onDataSaved: ({ id }) => tableRef.redirect(id),
-            formInitialData: { clientId: null },
-        }"
-        default-mode="table"
-        :order="['shippedDate DESC', 'shippedHour ASC', 'zoneLanding ASC', 'id']"
-        :columns="columns"
-        :user-params="userParams"
-        :right-search="false"
-        redirect="ticket"
-        v-model:selected="selectedRows"
-        :table="{
-            'row-key': 'id',
-            selection: 'multiple',
-        }"
-        data-cy="ticketListTable"
-    >
-        <template #column-statusIcons="{ row }">
-            <TicketProblems :row="row" />
-        </template>
-        <template #column-salesPersonFk="{ row }">
-            <span class="link" @click.stop>
-                {{ dashIfEmpty(row.userName) }}
-                <CustomerDescriptorProxy :id="row.salesPersonFk" />
-            </span>
-        </template>
-        <template #column-shippedDate="{ row }">
-            <span v-if="getDateColor(row.shipped)">
-                <QChip :class="getDateColor(row.shipped)" dense square>
-                    {{ toDate(row.shippedDate) }}
-                </QChip>
-            </span>
-        </template>
-        <template #column-nickname="{ row }">
-            <span class="link" @click.stop>
-                {{ row.nickname }}
-                <CustomerDescriptorProxy :id="row.clientFk" />
-            </span>
-        </template>
-        <template #column-addressNickname="{ row }">
-            <span class="link" @click.stop>
-                {{ row.addressNickname }}
-                <CustomerDescriptorProxy :id="row.clientFk" />
-            </span>
-        </template>
-        <template #column-stateFk="{ row }">
-            <span v-if="row.refFk">
-                <span class="link" @click.stop>
-                    {{ row.refFk }}
-                    <InvoiceOutDescriptorProxy :id="row.invoiceOutId" />
-                </span>
-            </span>
-            <span v-else-if="getColor(row)">
-                <QChip :class="getColor(row)" dense square>
-                    {{ row.state }}
-                </QChip>
-            </span>
-            <span v-else>
-                {{ row.state }}
-            </span>
-        </template>
-        <template #column-zoneFk="{ row }">
-            <span class="link" @click.stop>
-                {{ dashIfEmpty(row.zoneName) }}
-                <ZoneDescriptorProxy :id="row.zoneFk" />
-            </span>
-        </template>
-        <template #column-totalWithVat="{ row }">
-            <QChip
-                v-if="row.totalWithVat > 0 && row.totalWithVat < 50"
-                class="bg-warning"
-                dense
-                square
+        <template #body>
+            <VnTable
+                ref="tableRef"
+                :data-key="dataKey"
+                :create="{
+                    urlCreate: 'Tickets/new',
+                    title: t('ticketList.createTicket'),
+                    onDataSaved: ({ id }) => tableRef.redirect(id),
+                    formInitialData: { clientId: null },
+                }"
+                default-mode="table"
+                :columns="columns"
+                :user-params="userParams"
+                :right-search="false"
+                redirect="ticket"
+                v-model:selected="selectedRows"
+                :table="{
+                    'row-key': 'id',
+                    selection: 'multiple',
+                }"
+                data-cy="ticketListTable"
             >
-                {{ row.totalWithVat }}
-            </QChip>
-        </template>
-        <template #more-create-dialog="{ data }">
-            <VnRow>
-                <VnSelect
-                    url="Clients"
-                    :fields="['id', 'name']"
-                    :label="t('ticketList.client')"
-                    v-model="data.clientId"
-                    :options="clientsOptions"
-                    option-value="id"
-                    option-label="name"
-                    hide-selected
-                    required
-                    @update:model-value="(client) => onClientSelected(data)"
-                    :sort-by="'id ASC'"
-                >
-                    <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
-                    :label="t('basicData.address')"
-                    v-model="data.addressId"
-                    :options="addressesOptions"
-                    option-value="id"
-                    option-label="nickname"
-                    hide-selected
-                    map-options
-                    required
-                    :disable="!data.clientId"
-                    :sort-by="'isActive DESC'"
-                    @update:model-value="() => fetchAvailableAgencies(data)"
-                >
-                    <template #option="scope">
-                        <QItem
-                            v-bind="scope.itemProps"
-                            :class="{ disabled: !scope.opt.isActive }"
+                <template #column-statusIcons="{ row }">
+                    <TicketProblems :row="row" />
+                </template>
+                <template #column-salesPersonFk="{ row }">
+                    <span class="link" @click.stop>
+                        {{ dashIfEmpty(row.userName) }}
+                        <CustomerDescriptorProxy :id="row.salesPersonFk" />
+                    </span>
+                </template>
+                <template #column-shippedDate="{ row }">
+                    <span v-if="getDateColor(row.shipped)">
+                        <QChip :class="getDateColor(row.shipped)" dense square>
+                            {{ toDate(row.shippedDate) }}
+                        </QChip>
+                    </span>
+                </template>
+                <template #column-nickname="{ row }">
+                    <span class="link" @click.stop>
+                        {{ row.nickname }}
+                        <CustomerDescriptorProxy :id="row.clientFk" />
+                    </span>
+                </template>
+                <template #column-addressNickname="{ row }">
+                    <span class="link" @click.stop>
+                        {{ row.addressNickname }}
+                        <CustomerDescriptorProxy :id="row.clientFk" />
+                    </span>
+                </template>
+                <template #column-stateFk="{ row }">
+                    <span v-if="row.refFk">
+                        <span class="link" @click.stop>
+                            {{ row.refFk }}
+                            <InvoiceOutDescriptorProxy :id="row.invoiceOutId" />
+                        </span>
+                    </span>
+                    <span v-else-if="getColor(row)">
+                        <QChip :class="getColor(row)" dense square>
+                            {{ row.state }}
+                        </QChip>
+                    </span>
+                    <span v-else>
+                        {{ row.state }}
+                    </span>
+                </template>
+                <template #column-zoneFk="{ row }">
+                    <span class="link" @click.stop>
+                        {{ dashIfEmpty(row.zoneName) }}
+                        <ZoneDescriptorProxy :id="row.zoneFk" />
+                    </span>
+                </template>
+                <template #column-totalWithVat="{ row }">
+                    <QChip
+                        v-if="row.totalWithVat > 0 && row.totalWithVat < 50"
+                        class="bg-warning"
+                        dense
+                        square
+                    >
+                        {{ row.totalWithVat }}
+                    </QChip>
+                </template>
+                <template #more-create-dialog="{ data }">
+                    <VnRow>
+                        <VnSelect
+                            url="Clients"
+                            :fields="['id', 'name']"
+                            :label="t('ticketList.client')"
+                            v-model="data.clientId"
+                            :options="clientsOptions"
+                            option-value="id"
+                            option-label="name"
+                            hide-selected
+                            required
+                            @update:model-value="(client) => onClientSelected(data)"
+                            :sort-by="'id ASC'"
                         >
-                            <QItemSection style="min-width: min-content" avatar>
-                                <QIcon
-                                    v-if="
-                                        scope.opt.isActive &&
-                                        selectedClient?.defaultAddressFk === scope.opt.id
-                                    "
-                                    size="sm"
-                                    color="grey"
-                                    name="star"
-                                    class="fill-icon"
-                                />
-                            </QItemSection>
-                            <QItemSection>
-                                <QItemLabel
-                                    :class="{
-                                        'color-vn-label': !scope.opt?.isActive,
-                                    }"
+                            <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
+                            :label="t('basicData.address')"
+                            v-model="data.addressId"
+                            :options="addressesOptions"
+                            option-value="id"
+                            option-label="nickname"
+                            hide-selected
+                            map-options
+                            required
+                            :disable="!data.clientId"
+                            :sort-by="'isActive DESC'"
+                            @update:model-value="() => fetchAvailableAgencies(data)"
+                        >
+                            <template #option="scope">
+                                <QItem
+                                    v-bind="scope.itemProps"
+                                    :class="{ disabled: !scope.opt.isActive }"
                                 >
-                                    {{
-                                        `${
-                                            !scope.opt?.isActive
-                                                ? t('basicData.inactive')
-                                                : ''
-                                        } `
-                                    }}
-                                    <span>
-                                        {{ scope.opt?.nickname }}:
-                                        {{ scope.opt?.street }}, {{ scope.opt?.city }}
-                                    </span>
-                                </QItemLabel>
-                            </QItemSection>
-                        </QItem>
-                    </template>
-                </VnSelect>
-            </VnRow>
-            <VnRow>
-                <div class="col">
-                    <VnInputDate
-                        placeholder="dd-mm-aaa"
-                        :label="t('globals.landed')"
-                        v-model="data.landed"
-                        @update:model-value="() => fetchAvailableAgencies(data)"
-                    />
-                </div>
-            </VnRow>
-            <VnRow>
-                <div class="col">
-                    <VnSelect
-                        url="Warehouses"
-                        :sort-by="['name']"
-                        :label="t('globals.warehouse')"
-                        v-model="data.warehouseId"
-                        :options="warehousesOptions"
-                        option-value="id"
-                        option-label="name"
-                        hide-selected
-                        required
-                        @update:model-value="() => fetchAvailableAgencies(data)"
-                    />
-                </div>
-            </VnRow>
-            <VnRow>
-                <div class="col">
-                    <VnSelect
-                        :label="t('globals.agency')"
-                        v-model="data.agencyModeId"
-                        :options="agenciesOptions"
-                        option-value="agencyModeFk"
-                        option-label="agencyMode"
-                        hide-selected
-                    />
-                </div>
-            </VnRow>
+                                    <QItemSection style="min-width: min-content" avatar>
+                                        <QIcon
+                                            v-if="
+                                                scope.opt.isActive &&
+                                                selectedClient?.defaultAddressFk ===
+                                                    scope.opt.id
+                                            "
+                                            size="sm"
+                                            color="grey"
+                                            name="star"
+                                            class="fill-icon"
+                                        />
+                                    </QItemSection>
+                                    <QItemSection>
+                                        <QItemLabel
+                                            :class="{
+                                                'color-vn-label': !scope.opt?.isActive,
+                                            }"
+                                        >
+                                            {{
+                                                `${
+                                                    !scope.opt?.isActive
+                                                        ? t('basicData.inactive')
+                                                        : ''
+                                                } `
+                                            }}
+                                            <span>
+                                                {{ scope.opt?.nickname }}:
+                                                {{ scope.opt?.street }},
+                                                {{ scope.opt?.city }}
+                                            </span>
+                                        </QItemLabel>
+                                    </QItemSection>
+                                </QItem>
+                            </template>
+                        </VnSelect>
+                    </VnRow>
+                    <VnRow>
+                        <div class="col">
+                            <VnInputDate
+                                placeholder="dd-mm-aaa"
+                                :label="t('globals.landed')"
+                                v-model="data.landed"
+                                @update:model-value="() => fetchAvailableAgencies(data)"
+                            />
+                        </div>
+                    </VnRow>
+                    <VnRow>
+                        <div class="col">
+                            <VnSelect
+                                url="Warehouses"
+                                :sort-by="['name']"
+                                :label="t('globals.warehouse')"
+                                v-model="data.warehouseId"
+                                :options="warehousesOptions"
+                                option-value="id"
+                                option-label="name"
+                                hide-selected
+                                required
+                                @update:model-value="() => fetchAvailableAgencies(data)"
+                            />
+                        </div>
+                    </VnRow>
+                    <VnRow>
+                        <div class="col">
+                            <VnSelect
+                                :label="t('globals.agency')"
+                                v-model="data.agencyModeId"
+                                :options="agenciesOptions"
+                                option-value="agencyModeFk"
+                                option-label="agencyMode"
+                                hide-selected
+                            />
+                        </div>
+                    </VnRow>
+                </template>
+            </VnTable>
         </template>
-    </VnTable>
+    </VnSection>
     <QPageSticky :offset="[20, 80]" style="z-index: 2">
         <QBtn
             v-if="hasSelectedRows"
diff --git a/src/router/modules/ticket.js b/src/router/modules/ticket.js
index 6e407b88b57..600b64c1454 100644
--- a/src/router/modules/ticket.js
+++ b/src/router/modules/ticket.js
@@ -1,19 +1,12 @@
 import { RouterView } from 'vue-router';
 
-export default {
-    name: 'Ticket',
-    path: '/ticket',
+const ticketCard = {
+    name: 'TicketCard',
+    path: ':id',
+    component: () => import('src/pages/Ticket/Card/TicketCard.vue'),
+    redirect: { name: 'TicketSummary' },
     meta: {
-        title: 'tickets',
-        icon: 'vn:ticket',
-        moduleName: 'Ticket',
-        keyBinding: 't',
-    },
-    component: RouterView,
-    redirect: { name: 'TicketMain' },
-    menus: {
-        main: ['TicketList', 'TicketAdvance', 'TicketWeekly', 'TicketFuture'],
-        card: [
+        menu: [
             'TicketBasicData',
             'TicketSale',
             'TicketLog',
@@ -32,21 +25,200 @@ export default {
             'TicketSms',
         ],
     },
+    children: [
+        {
+            path: 'summary',
+            name: 'TicketSummary',
+            meta: {
+                title: 'summary',
+                icon: 'launch',
+            },
+            component: () => import('src/pages/Ticket/Card/TicketSummary.vue'),
+        },
+        {
+            path: 'basic-data',
+            name: 'TicketBasicData',
+            meta: {
+                title: 'basicData',
+                icon: 'vn:settings',
+            },
+            component: () =>
+                import('src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue'),
+        },
+        {
+            path: 'sale',
+            name: 'TicketSale',
+            meta: {
+                title: 'sale',
+                icon: 'vn:lines',
+            },
+            component: () => import('src/pages/Ticket/Card/TicketSale.vue'),
+        },
+        {
+            path: 'request',
+            name: 'TicketPurchaseRequest',
+            meta: {
+                title: 'purchaseRequest',
+                icon: 'vn:buyrequest',
+            },
+            component: () => import('src/pages/Ticket/Card/TicketPurchaseRequest.vue'),
+        },
+        {
+            path: 'tracking',
+            name: 'TicketTracking',
+            meta: {
+                title: 'tracking',
+                icon: 'vn:eye',
+            },
+            component: () => import('src/pages/Ticket/Card/TicketTracking.vue'),
+        },
+        {
+            path: 'log',
+            name: 'TicketLog',
+            meta: {
+                title: 'log',
+                icon: 'history',
+            },
+            component: () => import('src/pages/Ticket/Card/TicketLog.vue'),
+        },
+        {
+            path: 'observation',
+            name: 'TicketNotes',
+            meta: {
+                title: 'notes',
+                icon: 'vn:notes',
+            },
+            component: () => import('src/pages/Ticket/Card/TicketNotes.vue'),
+        },
+        {
+            path: 'picture',
+            name: 'TicketPicture',
+            meta: {
+                title: 'pictures',
+                icon: 'vn:photo',
+            },
+            component: () => import('src/pages/Ticket/Card/TicketPicture.vue'),
+        },
+        {
+            path: 'volume',
+            name: 'TicketVolume',
+            meta: {
+                title: 'volume',
+                icon: 'vn:volume',
+            },
+            component: () => import('src/pages/Ticket/Card/TicketVolume.vue'),
+        },
+        {
+            path: 'expedition',
+            name: 'TicketExpedition',
+            meta: {
+                title: 'expedition',
+                icon: 'vn:package',
+            },
+            component: () => import('src/pages/Ticket/Card/TicketExpedition.vue'),
+        },
+        {
+            path: 'service',
+            name: 'TicketService',
+            meta: {
+                title: 'services',
+                icon: 'vn:services',
+            },
+            component: () => import('src/pages/Ticket/Card/TicketService.vue'),
+        },
+        {
+            path: 'package',
+            name: 'TicketPackage',
+            meta: {
+                title: 'packages',
+                icon: 'vn:bucket',
+            },
+            component: () => import('src/pages/Ticket/Card/TicketPackage.vue'),
+        },
+        {
+            path: 'components',
+            name: 'TicketComponents',
+            meta: {
+                title: 'components',
+                icon: 'vn:components',
+            },
+            component: () => import('src/pages/Ticket/Card/TicketComponents.vue'),
+        },
+
+        {
+            path: 'sale-tracking',
+            name: 'TicketSaleTracking',
+            meta: {
+                title: 'saleTracking',
+                icon: 'assignment',
+            },
+            component: () => import('src/pages/Ticket/Card/TicketSaleTracking.vue'),
+        },
+        {
+            path: 'dms',
+            name: 'TicketDms',
+            meta: {
+                title: 'dms',
+                icon: 'cloud_upload',
+            },
+            component: () => import('src/pages/Ticket/Card/TicketDms.vue'),
+        },
+        {
+            path: 'boxing',
+            name: 'TicketBoxing',
+            meta: {
+                title: 'boxing',
+                icon: 'science',
+            },
+            component: () => import('src/pages/Ticket/Card/TicketBoxing.vue'),
+        },
+        {
+            path: 'sms',
+            name: 'TicketSms',
+            meta: {
+                title: 'sms',
+                icon: 'sms',
+            },
+            component: () => import('src/pages/Ticket/Card/TicketSms.vue'),
+        },
+    ],
+};
+
+export default {
+    name: 'Ticket',
+    path: '/ticket',
+    meta: {
+        title: 'tickets',
+        icon: 'vn:ticket',
+        moduleName: 'Ticket',
+        keyBinding: 't',
+        menu: ['TicketList', 'TicketAdvance', 'TicketWeekly', 'TicketFuture'],
+    },
+    component: RouterView,
+    redirect: { name: 'TicketMain' },
     children: [
         {
             name: 'TicketMain',
             path: '',
-            component: () => import('src/components/common/VnSectionMain.vue'),
-            redirect: { name: 'TicketList' },
+            component: () => import('src/components/common/VnModule.vue'),
+            redirect: { name: 'TicketIndexMain' },
             children: [
                 {
-                    path: 'list',
-                    name: 'TicketList',
-                    meta: {
-                        title: 'list',
-                        icon: 'view_list',
-                    },
+                    path: '',
+                    name: 'TicketIndexMain',
+                    redirect: { name: 'TicketList' },
                     component: () => import('src/pages/Ticket/TicketList.vue'),
+                    children: [
+                        {
+                            name: 'TicketList',
+                            path: 'list',
+                            meta: {
+                                title: 'list',
+                                icon: 'view_list',
+                            },
+                        },
+                        ticketCard,
+                    ],
                 },
                 {
                     path: 'create',
@@ -86,170 +258,5 @@ export default {
                 },
             ],
         },
-        {
-            name: 'TicketCard',
-            path: ':id',
-            component: () => import('src/pages/Ticket/Card/TicketCard.vue'),
-            redirect: { name: 'TicketSummary' },
-            children: [
-                {
-                    path: 'summary',
-                    name: 'TicketSummary',
-                    meta: {
-                        title: 'summary',
-                        icon: 'launch',
-                    },
-                    component: () => import('src/pages/Ticket/Card/TicketSummary.vue'),
-                },
-                {
-                    path: 'basic-data',
-                    name: 'TicketBasicData',
-                    meta: {
-                        title: 'basicData',
-                        icon: 'vn:settings',
-                    },
-                    component: () =>
-                        import('src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue'),
-                },
-                {
-                    path: 'sale',
-                    name: 'TicketSale',
-                    meta: {
-                        title: 'sale',
-                        icon: 'vn:lines',
-                    },
-                    component: () => import('src/pages/Ticket/Card/TicketSale.vue'),
-                },
-                {
-                    path: 'request',
-                    name: 'TicketPurchaseRequest',
-                    meta: {
-                        title: 'purchaseRequest',
-                        icon: 'vn:buyrequest',
-                    },
-                    component: () =>
-                        import('src/pages/Ticket/Card/TicketPurchaseRequest.vue'),
-                },
-                {
-                    path: 'tracking',
-                    name: 'TicketTracking',
-                    meta: {
-                        title: 'tracking',
-                        icon: 'vn:eye',
-                    },
-                    component: () => import('src/pages/Ticket/Card/TicketTracking.vue'),
-                },
-                {
-                    path: 'log',
-                    name: 'TicketLog',
-                    meta: {
-                        title: 'log',
-                        icon: 'history',
-                    },
-                    component: () => import('src/pages/Ticket/Card/TicketLog.vue'),
-                },
-                {
-                    path: 'observation',
-                    name: 'TicketNotes',
-                    meta: {
-                        title: 'notes',
-                        icon: 'vn:notes',
-                    },
-                    component: () => import('src/pages/Ticket/Card/TicketNotes.vue'),
-                },
-                {
-                    path: 'picture',
-                    name: 'TicketPicture',
-                    meta: {
-                        title: 'pictures',
-                        icon: 'vn:photo',
-                    },
-                    component: () => import('src/pages/Ticket/Card/TicketPicture.vue'),
-                },
-                {
-                    path: 'volume',
-                    name: 'TicketVolume',
-                    meta: {
-                        title: 'volume',
-                        icon: 'vn:volume',
-                    },
-                    component: () => import('src/pages/Ticket/Card/TicketVolume.vue'),
-                },
-                {
-                    path: 'expedition',
-                    name: 'TicketExpedition',
-                    meta: {
-                        title: 'expedition',
-                        icon: 'vn:package',
-                    },
-                    component: () => import('src/pages/Ticket/Card/TicketExpedition.vue'),
-                },
-                {
-                    path: 'service',
-                    name: 'TicketService',
-                    meta: {
-                        title: 'services',
-                        icon: 'vn:services',
-                    },
-                    component: () => import('src/pages/Ticket/Card/TicketService.vue'),
-                },
-                {
-                    path: 'package',
-                    name: 'TicketPackage',
-                    meta: {
-                        title: 'packages',
-                        icon: 'vn:bucket',
-                    },
-                    component: () => import('src/pages/Ticket/Card/TicketPackage.vue'),
-                },
-                {
-                    path: 'components',
-                    name: 'TicketComponents',
-                    meta: {
-                        title: 'components',
-                        icon: 'vn:components',
-                    },
-                    component: () => import('src/pages/Ticket/Card/TicketComponents.vue'),
-                },
-
-                {
-                    path: 'sale-tracking',
-                    name: 'TicketSaleTracking',
-                    meta: {
-                        title: 'saleTracking',
-                        icon: 'assignment',
-                    },
-                    component: () =>
-                        import('src/pages/Ticket/Card/TicketSaleTracking.vue'),
-                },
-                {
-                    path: 'dms',
-                    name: 'TicketDms',
-                    meta: {
-                        title: 'dms',
-                        icon: 'cloud_upload',
-                    },
-                    component: () => import('src/pages/Ticket/Card/TicketDms.vue'),
-                },
-                {
-                    path: 'boxing',
-                    name: 'TicketBoxing',
-                    meta: {
-                        title: 'boxing',
-                        icon: 'science',
-                    },
-                    component: () => import('src/pages/Ticket/Card/TicketBoxing.vue'),
-                },
-                {
-                    path: 'sms',
-                    name: 'TicketSms',
-                    meta: {
-                        title: 'sms',
-                        icon: 'sms',
-                    },
-                    component: () => import('src/pages/Ticket/Card/TicketSms.vue'),
-                },
-            ],
-        },
     ],
 };

From d29ecd0a2b570cc38d02afa52f3ff5bf64485f13 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Mon, 23 Dec 2024 09:47:00 +0000
Subject: [PATCH 28/34] fix: use value intead computedRef

---
 src/pages/Customer/components/CustomerSamplesCreate.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pages/Customer/components/CustomerSamplesCreate.vue b/src/pages/Customer/components/CustomerSamplesCreate.vue
index a75dfa1b2a7..665e136e41c 100644
--- a/src/pages/Customer/components/CustomerSamplesCreate.vue
+++ b/src/pages/Customer/components/CustomerSamplesCreate.vue
@@ -107,7 +107,7 @@ const setParams = (params) => {
 
 const getPreview = async () => {
     const params = {
-        recipientId: entityId,
+        recipientId: entityId.value,
     };
     const validationMessage = validateMessage();
     if (validationMessage) return notify(t(validationMessage), 'negative');

From bf41f338b70158f40045a0caf976589d8e5424e8 Mon Sep 17 00:00:00 2001
From: provira <provira@verdnatura.es>
Date: Mon, 23 Dec 2024 12:13:58 +0100
Subject: [PATCH 29/34] feat: refs #7079 created VnLocation front test

---
 src/components/common/VnLocation.vue          |   2 +-
 .../common/__tests__/VnLocation.spec.js       | 100 ++++++++++++++++++
 2 files changed, 101 insertions(+), 1 deletion(-)
 create mode 100644 src/components/common/__tests__/VnLocation.spec.js

diff --git a/src/components/common/VnLocation.vue b/src/components/common/VnLocation.vue
index a8840f24327..f5822218727 100644
--- a/src/components/common/VnLocation.vue
+++ b/src/components/common/VnLocation.vue
@@ -26,7 +26,7 @@ const locationProperties = [
     (obj) => obj.country?.name,
 ];
 
-const formatLocation = (obj, properties) => {
+const formatLocation = (obj, properties = locationProperties) => {
     const parts = properties.map((prop) => {
         if (typeof prop === 'string') {
             return obj[prop];
diff --git a/src/components/common/__tests__/VnLocation.spec.js b/src/components/common/__tests__/VnLocation.spec.js
new file mode 100644
index 00000000000..920afced861
--- /dev/null
+++ b/src/components/common/__tests__/VnLocation.spec.js
@@ -0,0 +1,100 @@
+import { createWrapper } from 'app/test/vitest/helper';
+import VnLocation from 'components/common/VnLocation.vue';
+import { vi, afterEach, expect, it, beforeEach, describe } from 'vitest';
+
+function buildComponent(data) {
+    return createWrapper(VnLocation, {
+        global: {
+            stubs: [''],
+            props: {
+                location: data
+            }
+        },
+    }).vm;
+}
+
+afterEach(() => {
+    vi.clearAllMocks();
+});
+
+describe('formatLocation', () => {
+    let locationBase;
+
+    beforeEach(() => {
+        locationBase = {
+            postcode: '46680',
+            city: 'Algemesi',
+            province: { name: 'Valencia' },
+            country: { name: 'Spain' }
+        };
+    });
+
+    it('should return the postcode, city, province and country', () => {
+        const location = { ...locationBase };
+        const vm = buildComponent(location);
+        const parts = vm.formatLocation(location);
+        expect(parts).toEqual('46680, Algemesi(Valencia), Spain');
+    });
+
+    it('should return the postcode and country', () => {
+        const location = { ...locationBase, city: undefined };
+        const vm = buildComponent(location);
+        const parts = vm.formatLocation(location);
+        expect(parts).toEqual('46680, Spain');
+    });
+
+    it('should return the city, province and country', () => {
+        const location = { ...locationBase, postcode: undefined };
+        const vm = buildComponent(location);
+        const parts = vm.formatLocation(location);
+        expect(parts).toEqual('Algemesi(Valencia), Spain');
+    });
+
+    it('should return the country', () => {
+        const location = { ...locationBase, postcode: undefined, city: undefined, province: undefined };
+        const vm = buildComponent(location);
+        const parts = vm.formatLocation(location);
+        expect(parts).toEqual('Spain');
+    });
+});
+
+describe('showLabel', () => {
+    let locationBase;
+
+    beforeEach(() => {
+        locationBase = {
+            code: '46680',
+            town: 'Algemesi',
+            province: 'Valencia',
+            country: 'Spain'
+        };
+    });
+
+    it('should show the label with postcode, city, province and country', () => {
+        const location = { ...locationBase };
+        const vm = buildComponent(location);
+        const label = vm.showLabel(location);
+        expect(label).toEqual('46680, Algemesi(Valencia), Spain');
+    });
+
+    it('should show the label with postcode and country', () => {
+        const location = { ...locationBase, town: undefined };
+        const vm = buildComponent(location);
+        const label = vm.showLabel(location);
+        expect(label).toEqual('46680, Spain');
+    });
+
+    it('should show the label with city, province and country', () => {
+        const location = { ...locationBase, code: undefined };
+        const vm = buildComponent(location);
+        const label = vm.showLabel(location);
+        expect(label).toEqual('Algemesi(Valencia), Spain');
+    });
+
+    it('should show the label with country', () => {
+        const location = { ...locationBase, code: undefined, town: undefined, province: undefined };
+        const vm = buildComponent(location);
+        const label = vm.showLabel(location);
+        expect(label).toEqual('Spain');
+    });
+});
\ No newline at end of file

From dd36b35bf7f6f8e64b95323efe7c71bd9b487a89 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Mon, 23 Dec 2024 17:56:09 +0100
Subject: [PATCH 30/34] fix: refs #8197 staticParams and redirect

---
 src/components/NavBar.vue                             |  1 +
 src/components/common/VnSection.vue                   | 11 ++++++++---
 src/components/ui/VnPaginate.vue                      |  5 ++---
 src/components/ui/VnSearchbar.vue                     |  6 ++++--
 src/composables/useArrayData.js                       |  1 +
 src/pages/Account/AccountList.vue                     |  7 +++++--
 src/pages/Customer/CustomerList.vue                   |  1 -
 .../integration/vnComponent/VnSearchBar.spec.js       |  5 ++---
 8 files changed, 23 insertions(+), 14 deletions(-)

diff --git a/src/components/NavBar.vue b/src/components/NavBar.vue
index 9b0393489cb..08c2410f1e0 100644
--- a/src/components/NavBar.vue
+++ b/src/components/NavBar.vue
@@ -59,6 +59,7 @@ const pinnedModulesRef = ref();
                     'no-visible': !stateQuery.isLoading().value,
                 }"
                 size="xs"
+                data-cy="loading-spinner"
             />
             <QSpace />
             <div id="searchbar" class="searchbar"></div>
diff --git a/src/components/common/VnSection.vue b/src/components/common/VnSection.vue
index e6afea4b6a3..e69e586b5df 100644
--- a/src/components/common/VnSection.vue
+++ b/src/components/common/VnSection.vue
@@ -34,15 +34,20 @@ const $props = defineProps({
         type: Object,
         default: null,
     },
+    redirect: {
+        type: Boolean,
+        default: true,
+    },
 });
 
 const sectionValue = computed(() => $props.section ?? $props.dataKey);
-
+let arrayData;
 onBeforeMount(() => {
     if ($props.dataKey)
-        useArrayData($props.dataKey, {
+        arrayData = useArrayData($props.dataKey, {
             searchUrl: 'table',
             ...$props.arrayDataProps,
+            navigate: $props.redirect,
         });
 });
 </script>
@@ -63,12 +68,12 @@ onBeforeMount(() => {
                 <VnTableFilter
                     v-if="rightFilter && columns"
                     :data-key="dataKey"
+                    :array-data="arrayData"
                     :columns="columns"
                 />
             </slot>
         </template>
     </RightMenu>
-
     <slot name="body" v-if="sectionValue == $route.name" />
     <RouterView v-else />
 </template>
diff --git a/src/components/ui/VnPaginate.vue b/src/components/ui/VnPaginate.vue
index 42f558f89b8..a2ccd5d92d1 100644
--- a/src/components/ui/VnPaginate.vue
+++ b/src/components/ui/VnPaginate.vue
@@ -112,7 +112,6 @@ onMounted(async () => {
 
 onBeforeUnmount(() => {
     arrayData.resetPagination();
-    arrayData.reset(['currentFilter', 'userParams', 'userFilter']);
 });
 
 watch(
@@ -142,7 +141,7 @@ const addFilter = async (filter, params) => {
 async function fetch(params) {
     useArrayData(props.dataKey, params);
     arrayData.resetPagination();
-    await arrayData.fetch({ append: false, updateRouter: mounted.value });
+    await arrayData.fetch({ append: false });
     return emitStoreData();
 }
 
@@ -217,7 +216,7 @@ defineExpose({
 <template>
     <div class="full-width">
         <div
-            v-if="!props.autoLoad && !store.data && !isLoading"
+            v-if="!store.data && !store.data?.length && !isLoading"
             class="info-row q-pa-md text-center"
         >
             <h5>
diff --git a/src/components/ui/VnSearchbar.vue b/src/components/ui/VnSearchbar.vue
index 6a9de44cba7..4e284d8e45b 100644
--- a/src/components/ui/VnSearchbar.vue
+++ b/src/components/ui/VnSearchbar.vue
@@ -100,7 +100,9 @@ onMounted(() => {
 });
 
 async function search() {
-    const staticParams = Object.entries(store.userParams);
+    const staticParams = Object.keys(store.userParams ?? {}).length
+        ? store.userParams
+        : store.defaultParams;
     arrayData.resetPagination();
 
     const filter = {
@@ -112,7 +114,7 @@ async function search() {
 
     if (!props.searchRemoveParams || !searchText.value) {
         filter.params = {
-            ...Object.fromEntries(staticParams),
+            ...staticParams,
             search: searchText.value,
         };
     }
diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js
index f4b30438af4..1a91cc50b50 100644
--- a/src/composables/useArrayData.js
+++ b/src/composables/useArrayData.js
@@ -64,6 +64,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
                     store[option] = userOptions.keepOpts?.includes(option)
                         ? Object.assign(defaultOpts, store[option])
                         : defaultOpts;
+                    if (option === 'userParams') store.defaultParams = store[option];
                 }
             }
         }
diff --git a/src/pages/Account/AccountList.vue b/src/pages/Account/AccountList.vue
index 997e3104142..7004abcf161 100644
--- a/src/pages/Account/AccountList.vue
+++ b/src/pages/Account/AccountList.vue
@@ -1,10 +1,11 @@
 <script setup>
 import { useI18n } from 'vue-i18n';
-import { computed } from 'vue';
+import { computed, ref } from 'vue';
 import VnTable from 'components/VnTable/VnTable.vue';
 import AccountSummary from './Card/AccountSummary.vue';
 import { useSummaryDialog } from 'src/composables/useSummaryDialog';
 import VnSection from 'src/components/common/VnSection.vue';
+import FetchData from 'src/components/FetchData.vue';
 
 const { t } = useI18n();
 const { viewSummary } = useSummaryDialog();
@@ -12,6 +13,7 @@ const filter = {
     include: { relation: 'role', scope: { fields: ['id', 'name'] } },
 };
 const dataKey = 'AccountList';
+const roles = ref([]);
 const columns = computed(() => [
     {
         align: 'left',
@@ -29,7 +31,7 @@ const columns = computed(() => [
             component: 'select',
             name: 'roleFk',
             attrs: {
-                url: 'VnRoles',
+                options: roles,
                 optionValue: 'id',
                 optionLabel: 'name',
             },
@@ -103,6 +105,7 @@ function exprBuilder(param, value) {
 </script>
 
 <template>
+    <FetchData url="VnRoles" @on-fetch="(data) => (roles = data)" auto-load />
     <VnSection
         :data-key="dataKey"
         :columns="columns"
diff --git a/src/pages/Customer/CustomerList.vue b/src/pages/Customer/CustomerList.vue
index 865287aeb38..b9b63208523 100644
--- a/src/pages/Customer/CustomerList.vue
+++ b/src/pages/Customer/CustomerList.vue
@@ -419,7 +419,6 @@ function handleLocation(data, location) {
         :columns="columns"
         redirect="customer"
         :right-search="false"
-        auto-load
     >
         <template #more-create-dialog="{ data }">
             <VnSelect
diff --git a/test/cypress/integration/vnComponent/VnSearchBar.spec.js b/test/cypress/integration/vnComponent/VnSearchBar.spec.js
index b8621118cfc..885e5d6b3b0 100644
--- a/test/cypress/integration/vnComponent/VnSearchBar.spec.js
+++ b/test/cypress/integration/vnComponent/VnSearchBar.spec.js
@@ -7,10 +7,10 @@ describe('VnSearchBar', () => {
     beforeEach(() => {
         cy.viewport(1280, 720);
         cy.login('developer');
-        cy.visit('#/customer/list');
+        cy.visit('#/account/list');
     });
 
-    it('should redirect to customer summary page', () => {
+    it('should redirect to account summary page', () => {
         searchAndCheck('1', employeeId);
         searchAndCheck('salesPerson', salesPersonId);
     });
@@ -20,7 +20,6 @@ describe('VnSearchBar', () => {
         checkTableLength(2);
 
         cy.clearSearchbar();
-
         cy.writeSearchbar('0{enter}');
         checkTableLength(0);
     });

From cfb35d23b436b37b0e71275d5fc25a435b40d19a Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Mon, 23 Dec 2024 18:16:50 +0100
Subject: [PATCH 31/34] perf: refs #8197 perf

---
 src/pages/Account/AccountAcls.vue | 2 +-
 src/stores/useArrayDataStore.js   | 1 -
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/pages/Account/AccountAcls.vue b/src/pages/Account/AccountAcls.vue
index b4eeb0648d4..6d357166121 100644
--- a/src/pages/Account/AccountAcls.vue
+++ b/src/pages/Account/AccountAcls.vue
@@ -151,7 +151,7 @@ const deleteAcl = async ({ id }) => {
         <template #body>
             <VnTable
                 ref="tableRef"
-                data-key="AccountAcls"
+                :data-key="dataKey"
                 :create="{
                     urlCreate: 'ACLs',
                     title: 'Create ACL',
diff --git a/src/stores/useArrayDataStore.js b/src/stores/useArrayDataStore.js
index 47bc06fd6fe..e0d8b792932 100644
--- a/src/stores/useArrayDataStore.js
+++ b/src/stores/useArrayDataStore.js
@@ -56,7 +56,6 @@ export const useArrayDataStore = defineStore('arrayDataStore', () => {
     }
 
     return {
-        state,
         get,
         set,
         clear,

From 17b178d9f13aa6ea2c588438d137a325e58a8ab9 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Tue, 24 Dec 2024 09:06:17 +0000
Subject: [PATCH 32/34] fix: orderBy priority

---
 src/pages/Claim/ClaimList.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pages/Claim/ClaimList.vue b/src/pages/Claim/ClaimList.vue
index d561a69f7f6..6b9fa77a094 100644
--- a/src/pages/Claim/ClaimList.vue
+++ b/src/pages/Claim/ClaimList.vue
@@ -131,7 +131,7 @@ const STATE_COLOR = {
     <VnTable
         data-key="ClaimList"
         url="Claims/filter"
-        :order="['priority ASC', 'created ASC']"
+        :order="['t.priority ASC', 'created ASC']"
         :columns="columns"
         redirect="claim"
         :right-search="false"

From 9dabe11cd005711818adeaa0185100ca20d7af83 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Tue, 24 Dec 2024 12:02:58 +0100
Subject: [PATCH 33/34] fix(AccountList): use $refs

---
 src/pages/Account/AccountList.vue | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/pages/Account/AccountList.vue b/src/pages/Account/AccountList.vue
index 0a4d6df4ed8..c1c75fcee6a 100644
--- a/src/pages/Account/AccountList.vue
+++ b/src/pages/Account/AccountList.vue
@@ -152,12 +152,13 @@ function exprBuilder(param, value) {
     >
         <template #body>
             <VnTable
+                ref="tableRef"
                 :data-key="dataKey"
                 :columns="columns"
                 :create="{
                     urlCreate: 'VnUsers',
                     title: t('Create user'),
-                    onDataSaved: ({ id }) => tableRef.redirect(id),
+                    onDataSaved: ({ id }) => $refs.tableRef.redirect(id),
                     formInitialData: {},
                 }"
                 default-mode="table"

From 89acb338a9ebbaf521e93ca39828c65af53cba1a Mon Sep 17 00:00:00 2001
From: provira <provira@verdnatura.es>
Date: Tue, 24 Dec 2024 12:18:36 +0100
Subject: [PATCH 34/34] refactor: refs #7079 removed useless code

---
 .../common/__tests__/VnLocation.spec.js       | 25 ++++++-------------
 1 file changed, 8 insertions(+), 17 deletions(-)

diff --git a/src/components/common/__tests__/VnLocation.spec.js b/src/components/common/__tests__/VnLocation.spec.js
index 920afced861..65fdae96099 100644
--- a/src/components/common/__tests__/VnLocation.spec.js
+++ b/src/components/common/__tests__/VnLocation.spec.js
@@ -5,7 +5,6 @@ import { vi, afterEach, expect, it, beforeEach, describe } from 'vitest';
 function buildComponent(data) {
     return createWrapper(VnLocation, {
         global: {
-            stubs: [''],
             props: {
                 location: data
             }
@@ -32,29 +31,25 @@ describe('formatLocation', () => {
     it('should return the postcode, city, province and country', () => {
         const location = { ...locationBase };
         const vm = buildComponent(location);
-        const parts = vm.formatLocation(location);
-        expect(parts).toEqual('46680, Algemesi(Valencia), Spain');
+        expect(vm.formatLocation(location)).toEqual('46680, Algemesi(Valencia), Spain');
     });
 
     it('should return the postcode and country', () => {
         const location = { ...locationBase, city: undefined };
         const vm = buildComponent(location);
-        const parts = vm.formatLocation(location);
-        expect(parts).toEqual('46680, Spain');
+        expect(vm.formatLocation(location)).toEqual('46680, Spain');
     });
 
     it('should return the city, province and country', () => {
         const location = { ...locationBase, postcode: undefined };
         const vm = buildComponent(location);
-        const parts = vm.formatLocation(location);
-        expect(parts).toEqual('Algemesi(Valencia), Spain');
+        expect(vm.formatLocation(location)).toEqual('Algemesi(Valencia), Spain');
     });
 
     it('should return the country', () => {
         const location = { ...locationBase, postcode: undefined, city: undefined, province: undefined };
         const vm = buildComponent(location);
-        const parts = vm.formatLocation(location);
-        expect(parts).toEqual('Spain');
+        expect(vm.formatLocation(location)).toEqual('Spain');
     });
 });
 
@@ -73,28 +68,24 @@ describe('showLabel', () => {
     it('should show the label with postcode, city, province and country', () => {
         const location = { ...locationBase };
         const vm = buildComponent(location);
-        const label = vm.showLabel(location);
-        expect(label).toEqual('46680, Algemesi(Valencia), Spain');
+        expect(vm.showLabel(location)).toEqual('46680, Algemesi(Valencia), Spain');
     });
 
     it('should show the label with postcode and country', () => {
         const location = { ...locationBase, town: undefined };
         const vm = buildComponent(location);
-        const label = vm.showLabel(location);
-        expect(label).toEqual('46680, Spain');
+        expect(vm.showLabel(location)).toEqual('46680, Spain');
     });
 
     it('should show the label with city, province and country', () => {
         const location = { ...locationBase, code: undefined };
         const vm = buildComponent(location);
-        const label = vm.showLabel(location);
-        expect(label).toEqual('Algemesi(Valencia), Spain');
+        expect(vm.showLabel(location)).toEqual('Algemesi(Valencia), Spain');
     });
 
     it('should show the label with country', () => {
         const location = { ...locationBase, code: undefined, town: undefined, province: undefined };
         const vm = buildComponent(location);
-        const label = vm.showLabel(location);
-        expect(label).toEqual('Spain');
+        expect(vm.showLabel(location)).toEqual('Spain');
     });
 });
\ No newline at end of file