diff --git a/src/components/common/VnCard.vue b/src/components/common/VnCard.vue
index 620dc2ad2..21cdc9df5 100644
--- a/src/components/common/VnCard.vue
+++ b/src/components/common/VnCard.vue
@@ -1,12 +1,15 @@
 <script setup>
-import { onBeforeMount } from 'vue';
-import { useRouter, onBeforeRouteUpdate, onBeforeRouteLeave } from 'vue-router';
+import { onBeforeMount, computed } from 'vue';
+import { useRoute, useRouter, onBeforeRouteUpdate, onBeforeRouteLeave } 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';
 
+const emit = defineEmits(['onFetch']);
+
 const props = defineProps({
+    id: { type: Number, required: false, default: null },
     dataKey: { type: String, required: true },
     url: { type: String, default: undefined },
     idInWhere: { type: Boolean, default: false },
@@ -16,10 +19,13 @@ const props = defineProps({
     searchDataKey: { type: String, default: undefined },
     searchbarProps: { type: Object, default: undefined },
     redirectOnError: { type: Boolean, default: false },
+    visual: { type: Boolean, default: true },
 });
 
+const route = useRoute();
 const stateStore = useStateStore();
 const router = useRouter();
+const entityId = computed(() => props.id || route?.params?.id);
 const arrayData = useArrayData(props.dataKey, {
     url: props.url,
     userFilter: props.filter,
@@ -35,7 +41,7 @@ onBeforeMount(async () => {
 
     const route = router.currentRoute.value;
     try {
-        await fetch(route.params.id);
+        await fetch(entityId.value);
     } catch {
         const { matched: matches } = route;
         const { path } = matches.at(-1);
@@ -51,8 +57,7 @@ onBeforeRouteUpdate(async (to, from) => {
             router.push({ name, params: to.params });
         }
     }
-    const id = to.params.id;
-    if (id !== from.params.id) await fetch(id, true);
+    if (entityId.value !== to.params.id) await fetch(to.params.id, true);
 });
 
 async function fetch(id, append = false) {
@@ -61,14 +66,17 @@ async function fetch(id, append = false) {
     else if (!regex.test(props.url)) arrayData.store.url = `${props.url}/${id}`;
     else arrayData.store.url = props.url.replace(regex, `/${id}`);
     await arrayData.fetch({ append, updateRouter: false });
+    emit('onFetch', arrayData.store.data);
 }
 function hasRouteParam(params, valueToCheck = ':addressId') {
     return Object.values(params).includes(valueToCheck);
 }
 </script>
 <template>
-    <VnSubToolbar />
-    <div :class="[useCardSize(), $attrs.class]">
-        <RouterView :key="$route.path" />
-    </div>
+    <template v-if="visual">
+        <VnSubToolbar />
+        <div :class="[useCardSize(), $attrs.class]">
+            <RouterView :key="$route.path" />
+        </div>
+    </template>
 </template>
diff --git a/src/components/ui/CardDescriptor.vue b/src/components/ui/CardDescriptor.vue
index b7d03127c..5f9a89d64 100644
--- a/src/components/ui/CardDescriptor.vue
+++ b/src/components/ui/CardDescriptor.vue
@@ -1,375 +1,38 @@
 <script setup>
-import { onBeforeMount, watch, computed, ref } from 'vue';
-import { useI18n } from 'vue-i18n';
-import SkeletonDescriptor from 'components/ui/SkeletonDescriptor.vue';
-import { useArrayData } from 'composables/useArrayData';
-import { useSummaryDialog } from 'src/composables/useSummaryDialog';
-import { useState } from 'src/composables/useState';
-import { useRoute, useRouter } from 'vue-router';
-import { useClipboard } from 'src/composables/useClipboard';
-import VnMoreOptions from './VnMoreOptions.vue';
+import { ref } from 'vue';
+import VnDescriptor from './VnDescriptor.vue';
 
 const $props = defineProps({
-    url: {
-        type: String,
-        default: '',
-    },
-    filter: {
-        type: Object,
-        default: null,
-    },
-    title: {
-        type: String,
-        default: '',
-    },
-    subtitle: {
+    id: {
         type: Number,
-        default: null,
+        default: false,
     },
-    dataKey: {
-        type: String,
-        default: null,
-    },
-    summary: {
+    card: {
         type: Object,
         default: null,
     },
-    width: {
-        type: String,
-        default: 'md-width',
-    },
-    toModule: {
-        type: String,
-        default: null,
-    },
 });
 
-const state = useState();
-const route = useRoute();
-const router = useRouter();
-const { t } = useI18n();
-const { copyText } = useClipboard();
-const { viewSummary } = useSummaryDialog();
-let arrayData;
-let store;
-let entity;
-const isLoading = ref(false);
-const isSameDataKey = computed(() => $props.dataKey === route.meta.moduleName);
-const DESCRIPTOR_PROXY = 'DescriptorProxy';
-const moduleName = ref();
-const isSameModuleName = route.matched[1].meta.moduleName !== moduleName.value;
-defineExpose({ getData });
-
-onBeforeMount(async () => {
-    arrayData = useArrayData($props.dataKey, {
-        url: $props.url,
-        userFilter: $props.filter,
-        skip: 0,
-        oneRecord: true,
-    });
-    store = arrayData.store;
-    entity = computed(() => {
-        const data = store.data ?? {};
-        if (data) emit('onFetch', data);
-        return data;
-    });
-
-    // It enables to load data only once if the module is the same as the dataKey
-    if (!isSameDataKey.value || !route.params.id) await getData();
-    watch(
-        () => [$props.url, $props.filter],
-        async () => {
-            if (!isSameDataKey.value) await getData();
-        },
-    );
-});
-
-function getName() {
-    let name = $props.dataKey;
-    if ($props.dataKey.includes(DESCRIPTOR_PROXY)) {
-        name = name.split(DESCRIPTOR_PROXY)[0];
-    }
-    return name;
-}
-const routeName = computed(() => {
-    let routeName = getName();
-    return `${routeName}Summary`;
-});
-
-async function getData() {
-    store.url = $props.url;
-    store.filter = $props.filter ?? {};
-    isLoading.value = true;
-    try {
-        const { data } = await arrayData.fetch({ append: false, updateRouter: false });
-        state.set($props.dataKey, data);
-        emit('onFetch', data);
-    } finally {
-        isLoading.value = false;
-    }
-}
-
-function getValueFromPath(path) {
-    if (!path) return;
-    const keys = path.toString().split('.');
-    let current = entity.value;
-
-    for (const key of keys) {
-        if (current[key] === undefined) return undefined;
-        else current = current[key];
-    }
-    return current;
-}
-
-function copyIdText(id) {
-    copyText(id, {
-        component: {
-            copyValue: id,
-        },
-    });
-}
-
 const emit = defineEmits(['onFetch']);
-
-const iconModule = computed(() => {
-    moduleName.value = getName();
-    if ($props.toModule) {
-        return router.getRoutes().find((r) => r.name === $props.toModule.name).meta.icon;
-    }
-    if (isSameModuleName) {
-        return router.options.routes[1].children.find((r) => r.name === moduleName.value)
-            ?.meta?.icon;
-    } else {
-        return route.matched[1].meta.icon;
-    }
-});
-
-const toModule = computed(() => {
-    moduleName.value = getName();
-    if ($props.toModule) return $props.toModule;
-    if (isSameModuleName) {
-        return router.options.routes[1].children.find((r) => r.name === moduleName.value)
-            ?.redirect;
-    } else {
-        return route.matched[1].path.split('/').length > 2
-            ? route.matched[1].redirect
-            : route.matched[1].children[0].redirect;
-    }
-});
+const entity = ref();
 </script>
 
 <template>
-    <div class="descriptor" data-cy="cardDescriptor">
-        <template v-if="entity && !isLoading">
-            <div class="header bg-primary q-pa-sm justify-between">
-                <slot name="header-extra-action">
-                    <QBtn
-                        round
-                        flat
-                        dense
-                        size="md"
-                        :icon="iconModule"
-                        color="white"
-                        class="link"
-                        :to="toModule"
-                    >
-                        <QTooltip>
-                            {{ t('globals.goToModuleIndex') }}
-                        </QTooltip>
-                    </QBtn>
-                </slot>
-                <QBtn
-                    @click.stop="viewSummary(entity.id, $props.summary, $props.width)"
-                    round
-                    flat
-                    dense
-                    size="md"
-                    icon="preview"
-                    color="white"
-                    class="link"
-                    v-if="summary"
-                    data-cy="openSummaryBtn"
-                >
-                    <QTooltip>
-                        {{ t('components.smartCard.openSummary') }}
-                    </QTooltip>
-                </QBtn>
-                <RouterLink :to="{ name: routeName, params: { id: entity.id } }">
-                    <QBtn
-                        class="link"
-                        color="white"
-                        dense
-                        flat
-                        icon="launch"
-                        round
-                        size="md"
-                        data-cy="goToSummaryBtn"
-                    >
-                        <QTooltip>
-                            {{ t('components.cardDescriptor.summary') }}
-                        </QTooltip>
-                    </QBtn>
-                </RouterLink>
-                <VnMoreOptions v-if="$slots.menu">
-                    <template #menu="{ menuRef }">
-                        <slot name="menu" :entity="entity" :menu-ref="menuRef" />
-                    </template>
-                </VnMoreOptions>
-            </div>
-            <slot name="before" />
-            <div class="body q-py-sm">
-                <QList dense>
-                    <QItemLabel header class="ellipsis text-h5" :lines="1">
-                        <div class="title">
-                            <span
-                                v-if="$props.title"
-                                :title="getValueFromPath(title)"
-                                :data-cy="`${$attrs['data-cy'] ?? 'cardDescriptor'}_title`"
-                            >
-                                {{ getValueFromPath(title) ?? $props.title }}
-                            </span>
-                            <slot v-else name="description" :entity="entity">
-                                <span
-                                    :title="entity.name"
-                                    :data-cy="`${$attrs['data-cy'] ?? 'cardDescriptor'}_description`"
-                                    v-text="entity.name"
-                                />
-                            </slot>
-                        </div>
-                    </QItemLabel>
-                    <QItem>
-                        <QItemLabel
-                            class="subtitle"
-                            :data-cy="`${$attrs['data-cy'] ?? 'cardDescriptor'}_subtitle`"
-                        >
-                            #{{ getValueFromPath(subtitle) ?? entity.id }}
-                        </QItemLabel>
-                        <QBtn
-                            round
-                            flat
-                            dense
-                            size="sm"
-                            icon="content_copy"
-                            color="primary"
-                            @click.stop="copyIdText(entity.id)"
-                        >
-                            <QTooltip>
-                                {{ t('globals.copyId') }}
-                            </QTooltip>
-                        </QBtn>
-                    </QItem>
-                </QList>
-                <div
-                    class="list-box q-mt-xs"
-                    :data-cy="`${$attrs['data-cy'] ?? 'cardDescriptor'}_listbox`"
-                >
-                    <slot name="body" :entity="entity" />
-                </div>
-            </div>
-            <div class="icons">
-                <slot name="icons" :entity="entity" />
-            </div>
-            <div class="actions justify-center" data-cy="descriptor_actions">
-                <slot name="actions" :entity="entity" />
-            </div>
-            <slot name="after" />
-        </template>
-        <SkeletonDescriptor v-if="!entity || isLoading" />
-    </div>
-    <QInnerLoading
-        :label="t('globals.pleaseWait')"
-        :showing="isLoading"
-        color="primary"
-    />
-</template>
-
-<style lang="scss">
-.body {
-    background-color: var(--vn-section-color);
-    .text-h5 {
-        font-size: 20px;
-        padding-top: 5px;
-        padding-bottom: 0px;
-    }
-    .q-item {
-        min-height: 20px;
-
-        .link {
-            margin-left: 10px;
-        }
-    }
-    .vn-label-value {
-        display: flex;
-        padding: 0px 16px;
-        .label {
-            color: var(--vn-label-color);
-            font-size: 14px;
-
-            &:not(:has(a))::after {
-                content: ':';
+    <component
+        :is="card"
+        :id
+        :visual="false"
+        v-bind="$attrs"
+        @on-fetch="
+            (data) => {
+                entity = data;
+                emit('onFetch', data);
             }
-        }
-        .value {
-            color: var(--vn-text-color);
-            font-size: 14px;
-            margin-left: 4px;
-            overflow: hidden;
-            text-overflow: ellipsis;
-            white-space: nowrap;
-            text-align: left;
-        }
-        .info {
-            margin-left: 5px;
-        }
-    }
-}
-</style>
-
-<style lang="scss" scoped>
-.title {
-    overflow: hidden;
-    text-overflow: ellipsis;
-    span {
-        color: var(--vn-text-color);
-        font-weight: bold;
-    }
-}
-.subtitle {
-    color: var(--vn-text-color);
-    font-size: 16px;
-    margin-bottom: 2px;
-}
-.list-box {
-    .q-item__label {
-        color: var(--vn-label-color);
-        padding-bottom: 0%;
-    }
-}
-.descriptor {
-    width: 256px;
-    .header {
-        display: flex;
-        align-items: center;
-    }
-    .icons {
-        margin: 0 10px;
-        display: flex;
-        justify-content: center;
-        .q-icon {
-            margin-right: 5px;
-        }
-    }
-    .actions {
-        margin: 0 5px;
-        justify-content: center !important;
-    }
-}
-</style>
-<i18n>
-    en:
-        globals:
-            copyId: Copy ID
-    es:
-        globals:
-            copyId: Copiar ID
-</i18n>
+        "
+    />
+    <VnDescriptor v-model="entity" v-bind="$attrs">
+        <template v-for="(_, slotName) in $slots" #[slotName]="slotData" :key="slotName">
+            <slot :name="slotName" v-bind="slotData ?? {}" :key="slotName" />
+        </template>
+    </VnDescriptor>
+</template>
diff --git a/src/components/ui/EntityDescriptor.vue b/src/components/ui/EntityDescriptor.vue
new file mode 100644
index 000000000..a5dced551
--- /dev/null
+++ b/src/components/ui/EntityDescriptor.vue
@@ -0,0 +1,78 @@
+<script setup>
+import { onBeforeMount, watch, computed, ref } from 'vue';
+import { useArrayData } from 'composables/useArrayData';
+import { useState } from 'src/composables/useState';
+import { useRoute } from 'vue-router';
+import VnDescriptor from './VnDescriptor.vue';
+
+const $props = defineProps({
+    url: {
+        type: String,
+        default: '',
+    },
+    filter: {
+        type: Object,
+        default: null,
+    },
+    dataKey: {
+        type: String,
+        default: null,
+    },
+});
+
+const state = useState();
+const route = useRoute();
+let arrayData;
+let store;
+let entity;
+const isLoading = ref(false);
+const isSameDataKey = computed(() => $props.dataKey === route.meta.moduleName);
+defineExpose({ getData });
+
+onBeforeMount(async () => {
+    arrayData = useArrayData($props.dataKey, {
+        url: $props.url,
+        userFilter: $props.filter,
+        skip: 0,
+        oneRecord: true,
+    });
+    store = arrayData.store;
+    entity = computed(() => {
+        const data = store.data ?? {};
+        if (data) emit('onFetch', data);
+        return data;
+    });
+
+    // It enables to load data only once if the module is the same as the dataKey
+    if (!isSameDataKey.value || !route.params.id) await getData();
+    watch(
+        () => [$props.url, $props.filter],
+        async () => {
+            if (!isSameDataKey.value) await getData();
+        },
+    );
+});
+
+async function getData() {
+    store.url = $props.url;
+    store.filter = $props.filter ?? {};
+    isLoading.value = true;
+    try {
+        const { data } = await arrayData.fetch({ append: false, updateRouter: false });
+        state.set($props.dataKey, data);
+        emit('onFetch', data);
+    } finally {
+        isLoading.value = false;
+    }
+}
+
+const emit = defineEmits(['onFetch']);
+</script>
+
+<template>
+    <VnDescriptor v-model="entity" v-bind="$attrs" :module="dataKey">
+        <template v-for="(_, slotName) in $slots" #[slotName]="slotData" :key="slotName">
+            <slot :name="slotName" v-bind="slotData ?? {}" :key="slotName" />
+        </template>
+    </VnDescriptor>
+</template>
diff --git a/src/components/ui/VnDescriptor.vue b/src/components/ui/VnDescriptor.vue
new file mode 100644
index 000000000..47da98d74
--- /dev/null
+++ b/src/components/ui/VnDescriptor.vue
@@ -0,0 +1,318 @@
+<script setup>
+import { computed, ref } from 'vue';
+import { useI18n } from 'vue-i18n';
+import SkeletonDescriptor from 'components/ui/SkeletonDescriptor.vue';
+import { useSummaryDialog } from 'src/composables/useSummaryDialog';
+import { useRoute, useRouter } from 'vue-router';
+import { useClipboard } from 'src/composables/useClipboard';
+import VnMoreOptions from './VnMoreOptions.vue';
+
+const entity = defineModel({ type: Object, default: null });
+const $props = defineProps({
+    title: {
+        type: String,
+        default: '',
+    },
+    subtitle: {
+        type: Number,
+        default: null,
+    },
+    summary: {
+        type: Object,
+        default: null,
+    },
+    width: {
+        type: String,
+        default: 'md-width',
+    },
+    module: {
+        type: String,
+        default: null,
+    },
+    toModule: {
+        type: String,
+        default: null,
+    },
+});
+
+const route = useRoute();
+const router = useRouter();
+const { t } = useI18n();
+const { copyText } = useClipboard();
+const { viewSummary } = useSummaryDialog();
+const DESCRIPTOR_PROXY = 'DescriptorProxy';
+const moduleName = ref();
+const isSameModuleName = route.matched[1].meta.moduleName !== moduleName.value;
+
+function getName() {
+    let name = $props.module;
+    if ($props.module.includes(DESCRIPTOR_PROXY)) {
+        name = name.split(DESCRIPTOR_PROXY)[0];
+    }
+    return name;
+}
+const routeName = computed(() => {
+    let routeName = getName();
+    return `${routeName}Summary`;
+});
+
+function getValueFromPath(path) {
+    if (!path) return;
+    const keys = path.toString().split('.');
+    let current = entity.value;
+
+    for (const key of keys) {
+        if (current[key] === undefined) return undefined;
+        else current = current[key];
+    }
+    return current;
+}
+
+function copyIdText(id) {
+    copyText(id, {
+        component: {
+            copyValue: id,
+        },
+    });
+}
+
+const emit = defineEmits(['onFetch']);
+
+const iconModule = computed(() => {
+    moduleName.value = getName();
+    if ($props.toModule) {
+        return router.getRoutes().find((r) => r.name === $props.toModule.name).meta.icon;
+    }
+    if (isSameModuleName) {
+        return router.options.routes[1].children.find((r) => r.name === moduleName.value)
+            ?.meta?.icon;
+    } else {
+        return route.matched[1].meta.icon;
+    }
+});
+
+const toModule = computed(() => {
+    moduleName.value = getName();
+    if ($props.toModule) return $props.toModule;
+    if (isSameModuleName) {
+        return router.options.routes[1].children.find((r) => r.name === moduleName.value)
+            ?.redirect;
+    } else {
+        return route.matched[1].path.split('/').length > 2
+            ? route.matched[1].redirect
+            : route.matched[1].children[0].redirect;
+    }
+});
+</script>
+
+<template>
+    <div class="descriptor" data-cy="vnDescriptor">
+        <template v-if="entity && entity?.id">
+            <div class="header bg-primary q-pa-sm justify-between">
+                <slot name="header-extra-action">
+                    <QBtn
+                        round
+                        flat
+                        dense
+                        size="md"
+                        :icon="iconModule"
+                        color="white"
+                        class="link"
+                        :to="toModule"
+                    >
+                        <QTooltip>
+                            {{ t('globals.goToModuleIndex') }}
+                        </QTooltip>
+                    </QBtn>
+                </slot>
+                <QBtn
+                    @click.stop="viewSummary(entity.id, summary, width)"
+                    round
+                    flat
+                    dense
+                    size="md"
+                    icon="preview"
+                    color="white"
+                    class="link"
+                    v-if="summary"
+                    data-cy="openSummaryBtn"
+                >
+                    <QTooltip>
+                        {{ t('components.smartCard.openSummary') }}
+                    </QTooltip>
+                </QBtn>
+                <RouterLink :to="{ name: routeName, params: { id: entity.id } }">
+                    <QBtn
+                        class="link"
+                        color="white"
+                        dense
+                        flat
+                        icon="launch"
+                        round
+                        size="md"
+                        data-cy="goToSummaryBtn"
+                    >
+                        <QTooltip>
+                            {{ t('components.vnDescriptor.summary') }}
+                        </QTooltip>
+                    </QBtn>
+                </RouterLink>
+                <VnMoreOptions v-if="$slots.menu">
+                    <template #menu="{ menuRef }">
+                        <slot name="menu" :entity="entity" :menu-ref="menuRef" />
+                    </template>
+                </VnMoreOptions>
+            </div>
+            <slot name="before" />
+            <div class="body q-py-sm">
+                <QList dense>
+                    <QItemLabel header class="ellipsis text-h5" :lines="1">
+                        <div class="title">
+                            <span
+                                v-if="title"
+                                :title="getValueFromPath(title)"
+                                :data-cy="`${$attrs['data-cy'] ?? 'vnDescriptor'}_title`"
+                            >
+                                {{ getValueFromPath(title) ?? title }}
+                            </span>
+                            <slot v-else name="description" :entity="entity">
+                                <span
+                                    :title="entity.name"
+                                    :data-cy="`${$attrs['data-cy'] ?? 'vnDescriptor'}_description`"
+                                    v-text="entity.name"
+                                />
+                            </slot>
+                        </div>
+                    </QItemLabel>
+                    <QItem>
+                        <QItemLabel
+                            class="subtitle"
+                            :data-cy="`${$attrs['data-cy'] ?? 'vnDescriptor'}_subtitle`"
+                        >
+                            #{{ getValueFromPath(subtitle) ?? entity.id }}
+                        </QItemLabel>
+                        <QBtn
+                            round
+                            flat
+                            dense
+                            size="sm"
+                            icon="content_copy"
+                            color="primary"
+                            @click.stop="copyIdText(entity.id)"
+                        >
+                            <QTooltip>
+                                {{ t('globals.copyId') }}
+                            </QTooltip>
+                        </QBtn>
+                    </QItem>
+                </QList>
+                <div
+                    class="list-box q-mt-xs"
+                    :data-cy="`${$attrs['data-cy'] ?? 'vnDescriptor'}_listbox`"
+                >
+                    <slot name="body" :entity="entity" />
+                </div>
+            </div>
+            <div class="icons">
+                <slot name="icons" :entity="entity" />
+            </div>
+            <div class="actions justify-center" data-cy="descriptor_actions">
+                <slot name="actions" :entity="entity" />
+            </div>
+            <slot name="after" />
+        </template>
+        <SkeletonDescriptor v-if="!entity" />
+    </div>
+    <QInnerLoading :label="t('globals.pleaseWait')" :showing="!entity" color="primary" />
+</template>
+
+<style lang="scss">
+.body {
+    background-color: var(--vn-section-color);
+    .text-h5 {
+        font-size: 20px;
+        padding-top: 5px;
+        padding-bottom: 0px;
+    }
+    .q-item {
+        min-height: 20px;
+
+        .link {
+            margin-left: 10px;
+        }
+    }
+    .vn-label-value {
+        display: flex;
+        padding: 0px 16px;
+        .label {
+            color: var(--vn-label-color);
+            font-size: 14px;
+
+            &:not(:has(a))::after {
+                content: ':';
+            }
+        }
+        .value {
+            color: var(--vn-text-color);
+            font-size: 14px;
+            margin-left: 4px;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+            text-align: left;
+        }
+        .info {
+            margin-left: 5px;
+        }
+    }
+}
+</style>
+
+<style lang="scss" scoped>
+.title {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    span {
+        color: var(--vn-text-color);
+        font-weight: bold;
+    }
+}
+.subtitle {
+    color: var(--vn-text-color);
+    font-size: 16px;
+    margin-bottom: 2px;
+}
+.list-box {
+    .q-item__label {
+        color: var(--vn-label-color);
+        padding-bottom: 0%;
+    }
+}
+.descriptor {
+    width: 256px;
+    .header {
+        display: flex;
+        align-items: center;
+    }
+    .icons {
+        margin: 0 10px;
+        display: flex;
+        justify-content: center;
+        .q-icon {
+            margin-right: 5px;
+        }
+    }
+    .actions {
+        margin: 0 5px;
+        justify-content: center !important;
+    }
+}
+</style>
+<i18n>
+    en:
+        globals:
+            copyId: Copy ID
+    es:
+        globals:
+            copyId: Copiar ID
+</i18n>
diff --git a/src/pages/Account/Alias/Card/AliasDescriptor.vue b/src/pages/Account/Alias/Card/AliasDescriptor.vue
index 7f6992bf0..957047cc3 100644
--- a/src/pages/Account/Alias/Card/AliasDescriptor.vue
+++ b/src/pages/Account/Alias/Card/AliasDescriptor.vue
@@ -4,7 +4,7 @@ import { useRoute, useRouter } from 'vue-router';
 import { useI18n } from 'vue-i18n';
 import { useQuasar } from 'quasar';
 
-import CardDescriptor from 'components/ui/CardDescriptor.vue';
+import EntityDescriptor from 'components/ui/EntityDescriptor.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
 
 import axios from 'axios';
@@ -48,7 +48,7 @@ const removeAlias = () => {
 </script>
 
 <template>
-    <CardDescriptor
+    <EntityDescriptor
         ref="descriptor"
         :url="`MailAliases/${entityId}`"
         data-key="Alias"
@@ -63,7 +63,7 @@ const removeAlias = () => {
         <template #body="{ entity }">
             <VnLv :label="t('role.description')" :value="entity.description" />
         </template>
-    </CardDescriptor>
+    </EntityDescriptor>
 </template>
 
 <i18n>
diff --git a/src/pages/Account/Card/AccountDescriptor.vue b/src/pages/Account/Card/AccountDescriptor.vue
index 49328fe87..eb0a9013c 100644
--- a/src/pages/Account/Card/AccountDescriptor.vue
+++ b/src/pages/Account/Card/AccountDescriptor.vue
@@ -1,7 +1,7 @@
 <script setup>
 import { ref, computed, onMounted } from 'vue';
 import { useRoute } from 'vue-router';
-import CardDescriptor from 'components/ui/CardDescriptor.vue';
+import EntityDescriptor from 'components/ui/EntityDescriptor.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
 import AccountDescriptorMenu from './AccountDescriptorMenu.vue';
 import VnImg from 'src/components/ui/VnImg.vue';
@@ -20,7 +20,7 @@ onMounted(async () => {
 </script>
 
 <template>
-    <CardDescriptor
+    <EntityDescriptor
         ref="descriptor"
         :url="`VnUsers/preview`"
         :filter="{ ...filter, where: { id: entityId } }"
@@ -78,7 +78,7 @@ onMounted(async () => {
                 </QIcon>
             </QCardActions>
         </template>
-    </CardDescriptor>
+    </EntityDescriptor>
 </template>
 <style scoped>
 .q-item__label {
diff --git a/src/pages/Account/Role/Card/RoleDescriptor.vue b/src/pages/Account/Role/Card/RoleDescriptor.vue
index 051359702..698bea4fa 100644
--- a/src/pages/Account/Role/Card/RoleDescriptor.vue
+++ b/src/pages/Account/Role/Card/RoleDescriptor.vue
@@ -2,7 +2,7 @@
 import { computed } from 'vue';
 import { useRoute } from 'vue-router';
 import { useI18n } from 'vue-i18n';
-import CardDescriptor from 'components/ui/CardDescriptor.vue';
+import EntityDescriptor from 'components/ui/EntityDescriptor.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
 import axios from 'axios';
 import useNotify from 'src/composables/useNotify.js';
@@ -32,7 +32,7 @@ const removeRole = async () => {
 </script>
 
 <template>
-    <CardDescriptor
+    <EntityDescriptor
         url="VnRoles"
         :filter="{ where: { id: entityId } }"
         data-key="Role"
@@ -47,7 +47,7 @@ const removeRole = async () => {
         <template #body="{ entity }">
             <VnLv :label="t('role.description')" :value="entity.description" />
         </template>
-    </CardDescriptor>
+    </EntityDescriptor>
 </template>
 <style scoped>
 .q-item__label {
diff --git a/src/pages/Claim/Card/ClaimDescriptor.vue b/src/pages/Claim/Card/ClaimDescriptor.vue
index d789b63d3..76ede81ed 100644
--- a/src/pages/Claim/Card/ClaimDescriptor.vue
+++ b/src/pages/Claim/Card/ClaimDescriptor.vue
@@ -6,7 +6,7 @@ import { toDateHourMinSec, toPercentage } from 'src/filters';
 import TicketDescriptorProxy from 'pages/Ticket/Card/TicketDescriptorProxy.vue';
 import ClaimDescriptorMenu from 'pages/Claim/Card/ClaimDescriptorMenu.vue';
 import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue';
-import CardDescriptor from 'components/ui/CardDescriptor.vue';
+import EntityDescriptor from 'components/ui/EntityDescriptor.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
 import VnUserLink from 'src/components/ui/VnUserLink.vue';
 import { getUrl } from 'src/composables/getUrl';
@@ -44,7 +44,7 @@ onMounted(async () => {
 </script>
 
 <template>
-    <CardDescriptor
+    <EntityDescriptor
         :url="`Claims/${entityId}`"
         :filter="filter"
         title="client.name"
@@ -147,7 +147,7 @@ onMounted(async () => {
                 </QBtn>
             </QCardActions>
         </template>
-    </CardDescriptor>
+    </EntityDescriptor>
 </template>
 <style scoped>
 .q-item__label {
diff --git a/src/pages/Customer/Card/CustomerDescriptor.vue b/src/pages/Customer/Card/CustomerDescriptor.vue
index 8978c00f1..cd18cf2c9 100644
--- a/src/pages/Customer/Card/CustomerDescriptor.vue
+++ b/src/pages/Customer/Card/CustomerDescriptor.vue
@@ -7,7 +7,7 @@ import { toCurrency, toDate } from 'src/filters';
 
 import useCardDescription from 'src/composables/useCardDescription';
 
-import CardDescriptor from 'components/ui/CardDescriptor.vue';
+import EntityDescriptor from 'components/ui/EntityDescriptor.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
 import CustomerDescriptorMenu from './CustomerDescriptorMenu.vue';
 import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue';
@@ -54,7 +54,7 @@ const debtWarning = computed(() => {
 </script>
 
 <template>
-    <CardDescriptor
+    <EntityDescriptor
         :url="`Clients/${entityId}/getCard`"
         :summary="$props.summary"
         data-key="Customer"
@@ -232,7 +232,7 @@ const debtWarning = computed(() => {
                 </QBtn>
             </QCardActions>
         </template>
-    </CardDescriptor>
+    </EntityDescriptor>
 </template>
 
 <i18n>
diff --git a/src/pages/Entry/Card/EntryDescriptor.vue b/src/pages/Entry/Card/EntryDescriptor.vue
index 313ed3d72..560114283 100644
--- a/src/pages/Entry/Card/EntryDescriptor.vue
+++ b/src/pages/Entry/Card/EntryDescriptor.vue
@@ -6,7 +6,7 @@ import { toDate } from 'src/filters';
 import { getUrl } from 'src/composables/getUrl';
 import { useQuasar } from 'quasar';
 import { usePrintService } from 'composables/usePrintService';
-import CardDescriptor from 'components/ui/CardDescriptor.vue';
+import EntityDescriptor from 'components/ui/EntityDescriptor.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
 import TravelDescriptorProxy from 'src/pages/Travel/Card/TravelDescriptorProxy.vue';
 import axios from 'axios';
@@ -145,7 +145,7 @@ async function deleteEntry() {
 </script>
 
 <template>
-    <CardDescriptor
+    <EntityDescriptor
         :url="`Entries/${entityId}`"
         :filter="entryFilter"
         title="supplier.nickname"
@@ -264,7 +264,7 @@ async function deleteEntry() {
                 </QBtn>
             </QCardActions>
         </template>
-    </CardDescriptor>
+    </EntityDescriptor>
 </template>
 <i18n>
 es:
diff --git a/src/pages/InvoiceIn/Card/InvoiceInDescriptor.vue b/src/pages/InvoiceIn/Card/InvoiceInDescriptor.vue
index eb673c546..e8df27511 100644
--- a/src/pages/InvoiceIn/Card/InvoiceInDescriptor.vue
+++ b/src/pages/InvoiceIn/Card/InvoiceInDescriptor.vue
@@ -5,7 +5,7 @@ import { useI18n } from 'vue-i18n';
 import axios from 'axios';
 import { toCurrency, toDate } from 'src/filters';
 import VnLv from 'src/components/ui/VnLv.vue';
-import CardDescriptor from 'components/ui/CardDescriptor.vue';
+import EntityDescriptor from 'components/ui/EntityDescriptor.vue';
 import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
 import filter from './InvoiceInFilter.js';
 import InvoiceInDescriptorMenu from './InvoiceInDescriptorMenu.vue';
@@ -84,7 +84,7 @@ async function setInvoiceCorrection(id) {
 }
 </script>
 <template>
-    <CardDescriptor
+    <EntityDescriptor
         ref="cardDescriptorRef"
         data-key="InvoiceIn"
         :url="`InvoiceIns/${entityId}`"
@@ -159,7 +159,7 @@ async function setInvoiceCorrection(id) {
                 </QBtn>
             </QCardActions>
         </template>
-    </CardDescriptor>
+    </EntityDescriptor>
 </template>
 <style lang="scss" scoped>
 .q-dialog {
diff --git a/src/pages/InvoiceOut/Card/InvoiceOutDescriptor.vue b/src/pages/InvoiceOut/Card/InvoiceOutDescriptor.vue
index 2402c0bf6..b93b8c8b7 100644
--- a/src/pages/InvoiceOut/Card/InvoiceOutDescriptor.vue
+++ b/src/pages/InvoiceOut/Card/InvoiceOutDescriptor.vue
@@ -3,7 +3,7 @@ import { ref, computed } from 'vue';
 import { useRoute } from 'vue-router';
 import { useI18n } from 'vue-i18n';
 
-import CardDescriptor from 'components/ui/CardDescriptor.vue';
+import EntityDescriptor from 'components/ui/EntityDescriptor.vue';
 import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
 import InvoiceOutDescriptorMenu from './InvoiceOutDescriptorMenu.vue';
@@ -34,7 +34,7 @@ function ticketFilter(invoice) {
 </script>
 
 <template>
-    <CardDescriptor
+    <EntityDescriptor
         ref="descriptor"
         :url="`InvoiceOuts/${entityId}`"
         :filter="filter"
@@ -93,5 +93,5 @@ function ticketFilter(invoice) {
                 </QBtn>
             </QCardActions>
         </template>
-    </CardDescriptor>
+    </EntityDescriptor>
 </template>
diff --git a/src/pages/Item/Card/ItemDescriptor.vue b/src/pages/Item/Card/ItemDescriptor.vue
index 84e07a293..09f63a3b1 100644
--- a/src/pages/Item/Card/ItemDescriptor.vue
+++ b/src/pages/Item/Card/ItemDescriptor.vue
@@ -3,7 +3,7 @@ import { computed, ref, onMounted } from 'vue';
 import { useRoute } from 'vue-router';
 import { useI18n } from 'vue-i18n';
 
-import CardDescriptor from 'src/components/ui/CardDescriptor.vue';
+import EntityDescriptor from 'src/components/ui/EntityDescriptor.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
 import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
 import ItemDescriptorImage from 'src/pages/Item/Card/ItemDescriptorImage.vue';
@@ -90,7 +90,7 @@ const updateStock = async () => {
 </script>
 
 <template>
-    <CardDescriptor
+    <EntityDescriptor
         data-key="Item"
         :summary="$props.summary"
         :url="`Items/${entityId}/getCard`"
@@ -162,7 +162,7 @@ const updateStock = async () => {
                 </QBtn>
             </QCardActions>
         </template>
-    </CardDescriptor>
+    </EntityDescriptor>
 </template>
 
 <i18n>
diff --git a/src/pages/Item/ItemType/Card/ItemTypeDescriptor.vue b/src/pages/Item/ItemType/Card/ItemTypeDescriptor.vue
index 972f4cad9..106b005bf 100644
--- a/src/pages/Item/ItemType/Card/ItemTypeDescriptor.vue
+++ b/src/pages/Item/ItemType/Card/ItemTypeDescriptor.vue
@@ -2,7 +2,7 @@
 import { computed } from 'vue';
 import { useRoute } from 'vue-router';
 
-import CardDescriptor from 'components/ui/CardDescriptor.vue';
+import EntityDescriptor from 'components/ui/EntityDescriptor.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
 import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
 import filter from './ItemTypeFilter.js';
@@ -25,7 +25,7 @@ const entityId = computed(() => {
 });
 </script>
 <template>
-    <CardDescriptor
+    <EntityDescriptor
         :url="`ItemTypes/${entityId}`"
         :filter="filter"
         title="code"
@@ -46,5 +46,5 @@ const entityId = computed(() => {
                 :value="entity.category?.name"
             />
         </template>
-    </CardDescriptor>
+    </EntityDescriptor>
 </template>
diff --git a/src/pages/Order/Card/OrderCard.vue b/src/pages/Order/Card/OrderCard.vue
index 7dab307a0..11dbbe532 100644
--- a/src/pages/Order/Card/OrderCard.vue
+++ b/src/pages/Order/Card/OrderCard.vue
@@ -6,9 +6,11 @@ import filter from './OrderFilter.js';
 
 <template>
     <VnCard
-        data-key="Order"
+        :data-key="$attrs['data-key'] ?? 'Order'"
         url="Orders"
         :filter="filter"
         :descriptor="OrderDescriptor"
+        v-bind="$attrs"
+        v-on="$attrs"
     />
 </template>
diff --git a/src/pages/Order/Card/OrderDescriptor.vue b/src/pages/Order/Card/OrderDescriptor.vue
index f34549c1e..ee66bb57e 100644
--- a/src/pages/Order/Card/OrderDescriptor.vue
+++ b/src/pages/Order/Card/OrderDescriptor.vue
@@ -4,10 +4,10 @@ import { useRoute } from 'vue-router';
 import { useI18n } from 'vue-i18n';
 import { toCurrency, toDate } from 'src/filters';
 import { useState } from 'src/composables/useState';
-import filter from './OrderFilter.js';
-import CardDescriptor from 'components/ui/CardDescriptor.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
 import FetchData from 'components/FetchData.vue';
+import OrderCard from './OrderCard.vue';
+import CardDescriptor from 'src/components/ui/CardDescriptor.vue';
 import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue';
 
 const DEFAULT_ITEMS = 0;
@@ -24,11 +24,14 @@ const route = useRoute();
 const state = useState();
 const { t } = useI18n();
 const getTotalRef = ref();
+const total = ref(0);
 
 const entityId = computed(() => {
     return $props.id || route.params.id;
 });
 
+const orderTotal = computed(() => state.get('orderTotal') ?? 0);
+
 const setData = (entity) => {
     if (!entity) return;
     getTotalRef.value && getTotalRef.value.fetch();
@@ -38,9 +41,6 @@ const setData = (entity) => {
 const getConfirmationValue = (isConfirmed) => {
     return t(isConfirmed ? 'globals.confirmed' : 'order.summary.notConfirmed');
 };
-
-const orderTotal = computed(() => state.get('orderTotal') ?? 0);
-const total = ref(0);
 </script>
 
 <template>
@@ -54,12 +54,12 @@ const total = ref(0);
         "
     />
     <CardDescriptor
-        ref="descriptor"
-        :url="`Orders/${entityId}`"
-        :filter="filter"
+        v-bind="$attrs"
+        :id="entityId"
+        :card="OrderCard"
         title="client.name"
         @on-fetch="setData"
-        data-key="Order"
+        module="Order"
     >
         <template #body="{ entity }">
             <VnLv
diff --git a/src/pages/Order/Card/OrderDescriptorProxy.vue b/src/pages/Order/Card/OrderDescriptorProxy.vue
index 04ebb054a..1dff1b620 100644
--- a/src/pages/Order/Card/OrderDescriptorProxy.vue
+++ b/src/pages/Order/Card/OrderDescriptorProxy.vue
@@ -12,6 +12,11 @@ const $props = defineProps({
 
 <template>
     <QPopupProxy>
-        <OrderDescriptor v-if="$props.id" :id="$props.id" :summary="OrderSummary" />
+        <OrderDescriptor
+            v-if="$props.id"
+            :id="$props.id"
+            :summary="OrderSummary"
+            data-key="OrderDescriptor"
+        />
     </QPopupProxy>
 </template>
diff --git a/src/pages/Route/Agency/Card/AgencyDescriptor.vue b/src/pages/Route/Agency/Card/AgencyDescriptor.vue
index 46aa44be9..64b33cc06 100644
--- a/src/pages/Route/Agency/Card/AgencyDescriptor.vue
+++ b/src/pages/Route/Agency/Card/AgencyDescriptor.vue
@@ -3,7 +3,7 @@ import { computed } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useRoute } from 'vue-router';
 import { useArrayData } from 'src/composables/useArrayData';
-import CardDescriptor from 'components/ui/CardDescriptor.vue';
+import EntityDescriptor from 'components/ui/EntityDescriptor.vue';
 import VnLv from 'components/ui/VnLv.vue';
 
 const props = defineProps({
@@ -21,7 +21,7 @@ const { store } = useArrayData();
 const card = computed(() => store.data);
 </script>
 <template>
-    <CardDescriptor
+    <EntityDescriptor
         data-key="Agency"
         :url="`Agencies/${entityId}`"
         :title="card?.name"
@@ -31,5 +31,5 @@ const card = computed(() => store.data);
         <template #body="{ entity: agency }">
             <VnLv :label="t('globals.name')" :value="agency.name" />
         </template>
-    </CardDescriptor>
+    </EntityDescriptor>
 </template>
diff --git a/src/pages/Route/Card/RouteDescriptor.vue b/src/pages/Route/Card/RouteDescriptor.vue
index c57e51473..ee42d8e76 100644
--- a/src/pages/Route/Card/RouteDescriptor.vue
+++ b/src/pages/Route/Card/RouteDescriptor.vue
@@ -1,7 +1,7 @@
 <script setup>
 import { ref, computed, onMounted } from 'vue';
 import { useRoute } from 'vue-router';
-import CardDescriptor from 'components/ui/CardDescriptor.vue';
+import EntityDescriptor from 'components/ui/EntityDescriptor.vue';
 import useCardDescription from 'composables/useCardDescription';
 import VnLv from 'components/ui/VnLv.vue';
 import { dashIfEmpty, toDate } from 'src/filters';
@@ -41,13 +41,12 @@ const getZone = async () => {
     zone.value = zoneData.name;
 };
 const data = ref(useCardDescription());
-const setData = (entity) => (data.value = useCardDescription(entity.code, entity.id));
 onMounted(async () => {
     getZone();
 });
 </script>
 <template>
-    <CardDescriptor
+    <EntityDescriptor
         :url="`Routes/${entityId}`"
         :filter="filter"
         :title="null"
@@ -69,7 +68,7 @@ onMounted(async () => {
         <template #menu="{ entity }">
             <RouteDescriptorMenu :route="entity" />
         </template>
-    </CardDescriptor>
+    </EntityDescriptor>
 </template>
 <i18n>
 es:
diff --git a/src/pages/Route/Roadmap/RoadmapDescriptor.vue b/src/pages/Route/Roadmap/RoadmapDescriptor.vue
index bc9230eda..dfa692feb 100644
--- a/src/pages/Route/Roadmap/RoadmapDescriptor.vue
+++ b/src/pages/Route/Roadmap/RoadmapDescriptor.vue
@@ -2,7 +2,7 @@
 import { computed } from 'vue';
 import { useRoute } from 'vue-router';
 import { useI18n } from 'vue-i18n';
-import CardDescriptor from 'components/ui/CardDescriptor.vue';
+import EntityDescriptor from 'components/ui/EntityDescriptor.vue';
 import VnLv from 'components/ui/VnLv.vue';
 import { dashIfEmpty, toDateHourMin } from 'src/filters';
 import SupplierDescriptorProxy from 'pages/Supplier/Card/SupplierDescriptorProxy.vue';
@@ -30,7 +30,7 @@ const entityId = computed(() => {
 </script>
 
 <template>
-    <CardDescriptor
+    <EntityDescriptor
         :url="`Roadmaps/${entityId}`"
         :filter="filter"
         data-key="Roadmap"
@@ -52,7 +52,7 @@ const entityId = computed(() => {
         <template #menu="{ entity }">
             <RoadmapDescriptorMenu :route="entity" />
         </template>
-    </CardDescriptor>
+    </EntityDescriptor>
 </template>
 <i18n>
 es:
diff --git a/src/pages/Route/Vehicle/Card/VehicleDescriptor.vue b/src/pages/Route/Vehicle/Card/VehicleDescriptor.vue
index 10c9fa9e2..bab7fa998 100644
--- a/src/pages/Route/Vehicle/Card/VehicleDescriptor.vue
+++ b/src/pages/Route/Vehicle/Card/VehicleDescriptor.vue
@@ -2,7 +2,7 @@
 import { computed } from 'vue';
 import { useRoute } from 'vue-router';
 import VnLv from 'src/components/ui/VnLv.vue';
-import CardDescriptor from 'components/ui/CardDescriptor.vue';
+import EntityDescriptor from 'components/ui/EntityDescriptor.vue';
 import axios from 'axios';
 import useNotify from 'src/composables/useNotify.js';
 
@@ -20,7 +20,7 @@ const route = useRoute();
 const entityId = computed(() => props.id || route.params.id);
 </script>
 <template>
-    <CardDescriptor
+    <EntityDescriptor
         :url="`Vehicles/${entityId}`"
         data-key="Vehicle"
         title="numberPlate"
@@ -54,7 +54,7 @@ const entityId = computed(() => props.id || route.params.id);
             <VnLv :label="$t('globals.model')" :value="entity.model" />
             <VnLv :label="$t('globals.country')" :value="entity.countryCodeFk" />
         </template>
-    </CardDescriptor>
+    </EntityDescriptor>
 </template>
 <i18n>
 es:
diff --git a/src/pages/Shelving/Card/ShelvingDescriptor.vue b/src/pages/Shelving/Card/ShelvingDescriptor.vue
index 5e618aa7f..2405467da 100644
--- a/src/pages/Shelving/Card/ShelvingDescriptor.vue
+++ b/src/pages/Shelving/Card/ShelvingDescriptor.vue
@@ -2,7 +2,7 @@
 import { computed } from 'vue';
 import { useRoute } from 'vue-router';
 import { useI18n } from 'vue-i18n';
-import CardDescriptor from 'components/ui/CardDescriptor.vue';
+import EntityDescriptor from 'components/ui/EntityDescriptor.vue';
 import VnLv from 'components/ui/VnLv.vue';
 import ShelvingDescriptorMenu from 'pages/Shelving/Card/ShelvingDescriptorMenu.vue';
 import VnUserLink from 'src/components/ui/VnUserLink.vue';
@@ -24,7 +24,7 @@ const entityId = computed(() => {
 });
 </script>
 <template>
-    <CardDescriptor
+    <EntityDescriptor
         :url="`Shelvings/${entityId}`"
         :filter="filter"
         title="code"
@@ -45,5 +45,5 @@ const entityId = computed(() => {
         <template #menu="{ entity }">
             <ShelvingDescriptorMenu :shelving="entity" />
         </template>
-    </CardDescriptor>
+    </EntityDescriptor>
 </template>
diff --git a/src/pages/Shelving/Parking/Card/ParkingDescriptor.vue b/src/pages/Shelving/Parking/Card/ParkingDescriptor.vue
index 07b168f87..0e01238a0 100644
--- a/src/pages/Shelving/Parking/Card/ParkingDescriptor.vue
+++ b/src/pages/Shelving/Parking/Card/ParkingDescriptor.vue
@@ -1,7 +1,7 @@
 <script setup>
 import { computed } from 'vue';
 import { useRoute } from 'vue-router';
-import CardDescriptor from 'components/ui/CardDescriptor.vue';
+import EntityDescriptor from 'components/ui/EntityDescriptor.vue';
 import VnLv from 'components/ui/VnLv.vue';
 import filter from './ParkingFilter.js';
 const props = defineProps({
@@ -16,7 +16,7 @@ const route = useRoute();
 const entityId = computed(() => props.id || route.params.id);
 </script>
 <template>
-    <CardDescriptor
+    <EntityDescriptor
         data-key="Parking"
         :url="`Parkings/${entityId}`"
         title="code"
@@ -28,5 +28,5 @@ const entityId = computed(() => props.id || route.params.id);
             <VnLv :label="$t('parking.pickingOrder')" :value="entity.pickingOrder" />
             <VnLv :label="$t('parking.sector')" :value="entity.sector?.description" />
         </template>
-    </CardDescriptor>
+    </EntityDescriptor>
 </template>
diff --git a/src/pages/Supplier/Card/SupplierDescriptor.vue b/src/pages/Supplier/Card/SupplierDescriptor.vue
index 462bdf853..2863784ab 100644
--- a/src/pages/Supplier/Card/SupplierDescriptor.vue
+++ b/src/pages/Supplier/Card/SupplierDescriptor.vue
@@ -3,7 +3,7 @@ import { ref, computed, onMounted } from 'vue';
 import { useRoute } from 'vue-router';
 import { useI18n } from 'vue-i18n';
 
-import CardDescriptor from 'components/ui/CardDescriptor.vue';
+import EntityDescriptor from 'components/ui/EntityDescriptor.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
 
 import { toDateString } from 'src/filters';
@@ -61,7 +61,7 @@ const getEntryQueryParams = (supplier) => {
 </script>
 
 <template>
-    <CardDescriptor
+    <EntityDescriptor
         :url="`Suppliers/${entityId}`"
         :filter="filter"
         data-key="Supplier"
@@ -136,7 +136,7 @@ const getEntryQueryParams = (supplier) => {
                 </QBtn>
             </QCardActions>
         </template>
-    </CardDescriptor>
+    </EntityDescriptor>
 </template>
 
 <i18n>
diff --git a/src/pages/Ticket/Card/TicketDescriptor.vue b/src/pages/Ticket/Card/TicketDescriptor.vue
index 743f2188c..96920231c 100644
--- a/src/pages/Ticket/Card/TicketDescriptor.vue
+++ b/src/pages/Ticket/Card/TicketDescriptor.vue
@@ -4,7 +4,7 @@ import { useRoute } from 'vue-router';
 import { useI18n } from 'vue-i18n';
 import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
 import DepartmentDescriptorProxy from 'pages/Worker/Department/Card/DepartmentDescriptorProxy.vue';
-import CardDescriptor from 'components/ui/CardDescriptor.vue';
+import EntityDescriptor from 'components/ui/EntityDescriptor.vue';
 import TicketDescriptorMenu from './TicketDescriptorMenu.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
 import { toDateTimeFormat } from 'src/filters/date';
@@ -57,7 +57,7 @@ function getInfo() {
 </script>
 
 <template>
-    <CardDescriptor
+    <EntityDescriptor
         :url="`Tickets/${entityId}`"
         :filter="filter"
         data-key="Ticket"
@@ -155,7 +155,7 @@ function getInfo() {
                 </QBtn>
             </QCardActions>
         </template>
-    </CardDescriptor>
+    </EntityDescriptor>
 </template>
 
 <i18n>
diff --git a/src/pages/Travel/Card/TravelDescriptor.vue b/src/pages/Travel/Card/TravelDescriptor.vue
index 922f89f33..d4903f794 100644
--- a/src/pages/Travel/Card/TravelDescriptor.vue
+++ b/src/pages/Travel/Card/TravelDescriptor.vue
@@ -3,7 +3,7 @@ import { computed, ref } from 'vue';
 import { useRoute } from 'vue-router';
 import { useI18n } from 'vue-i18n';
 
-import CardDescriptor from 'components/ui/CardDescriptor.vue';
+import EntityDescriptor from 'components/ui/EntityDescriptor.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
 import useCardDescription from 'src/composables/useCardDescription';
 import TravelDescriptorMenuItems from './TravelDescriptorMenuItems.vue';
@@ -31,7 +31,7 @@ const setData = (entity) => (data.value = useCardDescription(entity.ref, entity.
 </script>
 
 <template>
-    <CardDescriptor
+    <EntityDescriptor
         :url="`Travels/${entityId}`"
         :title="data.title"
         :subtitle="data.subtitle"
@@ -79,7 +79,7 @@ const setData = (entity) => (data.value = useCardDescription(entity.ref, entity.
                 </QBtn>
             </QCardActions>
         </template>
-    </CardDescriptor>
+    </EntityDescriptor>
 </template>
 
 <i18n>
diff --git a/src/pages/Worker/Card/WorkerDescriptor.vue b/src/pages/Worker/Card/WorkerDescriptor.vue
index 910c92fd8..060520e84 100644
--- a/src/pages/Worker/Card/WorkerDescriptor.vue
+++ b/src/pages/Worker/Card/WorkerDescriptor.vue
@@ -2,7 +2,7 @@
 import { computed, ref } from 'vue';
 import { useRoute } from 'vue-router';
 import { useI18n } from 'vue-i18n';
-import CardDescriptor from 'src/components/ui/CardDescriptor.vue';
+import EntityDescriptor from 'src/components/ui/EntityDescriptor.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
 import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue';
 import VnChangePassword from 'src/components/common/VnChangePassword.vue';
@@ -52,7 +52,7 @@ const handlePhotoUpdated = (evt = false) => {
 };
 </script>
 <template>
-    <CardDescriptor
+    <EntityDescriptor
         ref="cardDescriptorRef"
         :data-key="dataKey"
         :summary="$props.summary"
@@ -167,7 +167,7 @@ const handlePhotoUpdated = (evt = false) => {
                 </QBtn>
             </QCardActions>
         </template>
-    </CardDescriptor>
+    </EntityDescriptor>
     <VnChangePassword
         ref="changePassRef"
         :submit-fn="
diff --git a/src/pages/Worker/Department/Card/DepartmentDescriptor.vue b/src/pages/Worker/Department/Card/DepartmentDescriptor.vue
index 4b7dfd9b8..820658593 100644
--- a/src/pages/Worker/Department/Card/DepartmentDescriptor.vue
+++ b/src/pages/Worker/Department/Card/DepartmentDescriptor.vue
@@ -4,7 +4,7 @@ import { useRoute, useRouter } from 'vue-router';
 import { useI18n } from 'vue-i18n';
 import { useVnConfirm } from 'composables/useVnConfirm';
 import VnLv from 'src/components/ui/VnLv.vue';
-import CardDescriptor from 'src/components/ui/CardDescriptor.vue';
+import EntityDescriptor from 'src/components/ui/EntityDescriptor.vue';
 
 import axios from 'axios';
 import useNotify from 'src/composables/useNotify.js';
@@ -40,7 +40,7 @@ const removeDepartment = async () => {
 const { openConfirmationModal } = useVnConfirm();
 </script>
 <template>
-    <CardDescriptor
+    <EntityDescriptor
         ref="DepartmentDescriptorRef"
         :url="`Departments/${entityId}`"
         :summary="$props.summary"
@@ -95,7 +95,7 @@ const { openConfirmationModal } = useVnConfirm();
                 </QBtn>
             </QCardActions>
         </template>
-    </CardDescriptor>
+    </EntityDescriptor>
 </template>
 
 <i18n>
diff --git a/src/pages/Zone/Card/ZoneDescriptor.vue b/src/pages/Zone/Card/ZoneDescriptor.vue
index 27676212e..f2bcc1247 100644
--- a/src/pages/Zone/Card/ZoneDescriptor.vue
+++ b/src/pages/Zone/Card/ZoneDescriptor.vue
@@ -2,7 +2,7 @@
 import { computed } from 'vue';
 import { useRoute } from 'vue-router';
 
-import CardDescriptor from 'components/ui/CardDescriptor.vue';
+import EntityDescriptor from 'components/ui/EntityDescriptor.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
 import { toTimeFormat } from 'src/filters/date';
 import { toCurrency } from 'filters/index';
@@ -25,7 +25,7 @@ const entityId = computed(() => {
 </script>
 
 <template>
-    <CardDescriptor :url="`Zones/${entityId}`" :filter="filter" data-key="Zone">
+    <EntityDescriptor :url="`Zones/${entityId}`" :filter="filter" data-key="Zone">
         <template #menu="{ entity }">
             <ZoneDescriptorMenuItems :zone="entity" />
         </template>
@@ -36,5 +36,5 @@ const entityId = computed(() => {
             <VnLv :label="$t('list.price')" :value="toCurrency(entity.price)" />
             <VnLv :label="$t('zone.bonus')" :value="toCurrency(entity.bonus)" />
         </template>
-    </CardDescriptor>
+    </EntityDescriptor>
 </template>
diff --git a/test/cypress/integration/vnComponent/VnLog.spec.js b/test/cypress/integration/vnComponent/VnLog.spec.js
index e857457a7..ae0013150 100644
--- a/test/cypress/integration/vnComponent/VnLog.spec.js
+++ b/test/cypress/integration/vnComponent/VnLog.spec.js
@@ -25,7 +25,7 @@ describe('VnLog', () => {
 
     it('should show claimDescriptor', () => {
         cy.dataCy('iconLaunch-claimFk').first().click();
-        cy.dataCy('cardDescriptor_subtitle').contains('1');
+        cy.dataCy('vnDescriptor_subtitle').contains('1');
         cy.dataCy('iconLaunch-claimFk').first().click();
     });
 });
diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js
index de25959b2..fe8d38e79 100755
--- a/test/cypress/support/commands.js
+++ b/test/cypress/support/commands.js
@@ -371,7 +371,7 @@ Cypress.Commands.add('validateContent', (selector, expectedValue) => {
 });
 
 Cypress.Commands.add('openActionsDescriptor', () => {
-    cy.get('[data-cy="cardDescriptor"] [data-cy="descriptor-more-opts"]').click();
+    cy.get('[data-cy="vnDescriptor"] [data-cy="descriptor-more-opts"]').click();
 });
 
 Cypress.Commands.add('openUserPanel', () => {
@@ -466,16 +466,16 @@ Cypress.Commands.add('validateDescriptor', (toCheck = {}) => {
 
     const popupSelector = popup ? '[role="menu"] ' : '';
 
-    if (title) cy.get(`${popupSelector}[data-cy="cardDescriptor_title"]`).contains(title);
+    if (title) cy.get(`${popupSelector}[data-cy="vnDescriptor_title"]`).contains(title);
     if (description)
-        cy.get(`${popupSelector}[data-cy="cardDescriptor_description"]`).contains(
+        cy.get(`${popupSelector}[data-cy="vnDescriptor_description"]`).contains(
             description,
         );
     if (subtitle)
-        cy.get(`${popupSelector}[data-cy="cardDescriptor_subtitle"]`).contains(subtitle);
+        cy.get(`${popupSelector}[data-cy="vnDescriptor_subtitle"]`).contains(subtitle);
 
     for (const index in listbox)
-        cy.get(`${popupSelector}[data-cy="cardDescriptor_listbox"] > *`)
+        cy.get(`${popupSelector}[data-cy="vnDescriptor_listbox"] > *`)
             .eq(index)
             .should('contain.text', listbox[index]);
 });